Type Casting in C Programming

Type casting in C programming language is a way to force an expression of one data type to another data type. We generally use type casting to convert a variable of one data type to another data type. If we want to perform floating point arithmetic with a integer variable then we have to first type cast integer variable into float. Below is the syntax of caste operator.

(data_type) expression;
  • data_type : It is a valid target data type name.
  • expression : This is a valid C expression, whose value will get converted to data_type.
For Example
int circumference = (int)(2 * 3.142 * radius);
float average = (float)sum/count;
(float)5/2

C Program to show Type Casing of Variables

  • In the below C program, totalMarks/count will produce integer value without type casting.
  • We type casting totalMarks variable to float data type to retain the fractional component of quotient.
  • Variable totalMarks gets converted to float before division.
#include <stdio.h>

main() {
   int i, totalMarks=0, count=5, marks;
   float averageMarks;
   printf("Enter marks of five subjects\n");
   for(i = 0; i < count; i++){
      scanf("%d", &marks);
   totalMarks += marks;
   }
   /* Calculating Average without Type Casting */
   printf("Average marks without Type casting = %d\n", 
       totalMarks/count);
   /* Calculating Average after Type Casting totalMarks to float */
   printf("Average marks after Type casting = %f\n", 
       (float)totalMarks/count);
   
   return 0;
}
Output
Enter marks of five subjects
1 6 2 5 9
Average marks without Type casting = 4
Average marks after Type casting = 4.600000

Example of Explicit Type Conversion

  • Type Casting in Assignment : The process of type casting is commonly employed when allocating a value of one data type to a variable of another data type. Here's an example:
    #include <stdio.h>
    
    int main() {
        int num = 10;
        char character;
    
        // Explicit conversion from int to char
        character = (char)num;
    
        printf("Character: %c\n", character);
    
        return 0;
    }
    
    Here, we assign the value of num to the variable character after casting it to char. You can use this to assign the integer to a character variable.

  • Type Casting in Pointers : The ability to cast pointers from one type to another is known as  pointer type casting, and it is also relevant to pointers. When dealing with dynamic memory allocation, this becomes much more helpful.
    #include <stdio.h>
    
    int main() {
        int *intPtr;
        double *doublePtr;
    
        int num = 42;
        double pi = 3.14159;
    
        // Type casting in pointers
        intPtr = &num;
        doublePtr = (double *)&num;
    
        printf("Integer value: %d\n", *intPtr);
        printf("Double value: %f\n", *doublePtr);
    
        return 0;
    }
    
    A pointer to an integer is assigned the value of an integer variable (&num) in this example. Afterwards, the address is converted to a double pointer (double *) and then assigned to doublePtr. You can use this type casting to make the memory at that address seem like a different data type.

  • Type Casting in Function Arguments : It is usual practice to utilize type casting when giving arguments to functions. You might have to cast the argument to the right type if the function requires a certain data type for its parameters.
    #include <stdio.h>
    
    // Function that takes a double parameter
    void printDouble(double value) {
        printf("Double value: %f\n", value);
    }
    
    int main() {
        int num = 42;
    
        // Explicit conversion from int to 
        // double when calling the function
        printDouble((double)num);
    
        return 0;
    }
    
    The printDouble function in this case takes a double as an argument. The argument is cast to double to conform to the parameter type of the function when it is called with an integer input (num).

Automatic Type conversion in Expressions

When an expression contains variables and constants of different data types, all of them gets converted to a same data types. Compiler converts the data type of all variables to the highest data type variable in the expression, this is known as type promotion.

Here is the ranking of the data types from lower to higher.

int --> unsigned int --> long --> unsigned long --> long long --> unsigned long long --> float --> double --> long double
integer + float becomes float.
Integer gets promoted to float because lower data type operand get converted to the data type of higher operand.

After the automatic type conversion of operands, each pair of operands is of same data type and the result of each operation is same as the data type of both operand.

For Example Let us consider four variables i, f and d of data type int, float and double respectively and expression (i*f)/d
  • First integer i is converted to float. and the outcome of i*f is a float value.
  • Then the outcome of i*f which is a float s converted to double and the outcome of (i*f)/d becomes a double.

C Program to show Automatic Type Promotion

#include <stdio.h>

main(){
   int  i = 2;
   float f = 13.5, result;
   /* Integer gets promoted to float before division */
   result = f/i;
   printf("Quotient of %f/%d = %f\n", f, i, result);
   
   return 0;
}
Output
Quotient of 13.500000/2 = 6.750000

Rules for Implicit Type Conversion

  • Smaller to Larger : An implicit conversion occurs between a smaller and larger data type when a smaller data type variable is used in an operation with a larger data type variable, the smaller type is implicitly converted to the larger type.
    int num = 5;
    // Implicit conversion of int to double
    double result = num + 3.5; e
    

  • Integer Promotion : When Smaller data types (char or short) are involved in operations with an int, they are promoted to int before the operation.
    char a = 10;
    int b = 20;
    // Implicit conversion of char to int
    int result = a + b; 
    

  • Usual Arithmetic Conversions : The operands of arithmetic operations involving various data types are converted to a common type in accordance with a set of guidelines specified by the C standard. For instance, before the operation, the float operand is changed to double if the other operand is double and the first is float.

Best Practices of Using Type Casting in C

To mitigate the potential pitfalls associated with type casting, follow these best practices:
  • Avoid Unnecessary Casts : Whenever feasible, avoid casting, especially when converting between related types or when the compiler may do implicit conversions. Unnecessary casts might lead to more errors and complicate the reading of the code.
    // Unnecessary cast
    double result = (double)intValue; 
    

  • Use Explicit Casts for Safety : When type casting is required, use explicit casts to indicate to other developers that a purposeful conversion is occurring and to make the code easier to comprehend.
    double result = (double)intValue; // Explicit cast
    

  • Be Aware of Potential Loss of Precision : When casting between kinds with varying degrees of precision, be mindful of the possibility of losing precision. Think about if the precision loss is appropriate for your particular use case.
    double doubleValue = 3.14159;
    // Loss of precision
    int intValue = (int)doubleValue; 
    

  • Check for Overflow and Underflow : Look for any possible overflow or underflow situations before to casting from a larger type to a smaller one. Make that the value being cast is inside the target type's representable range.
    long long bigValue = LLONG_MAX;
    if (bigValue <= INT_MAX && bigValue >= INT_MIN) {
        int intValue = (int)bigValue; // Check for overflow
    }
    

  • Handle Sign Extension Appropriately : Take sign extension into consideration when casting between signed and unsigned types, or between types of varying sizes. Carry out further steps if required to handle sign extension properly.
    char charValue = -1;
    // Handle sign extension
    unsigned int unsignedValue = (unsigned int)charValue; 
    

  • Prefer Safer Alternatives : There may be safer substitutes for type casting in some circumstances. For instance, use uintptr_t from the header rather than casting a pointer to an integer and back.
    #include <stdint.h>
    
    int *ptr = ...;
    uintptr_t uintptrValue = (uintptr_t)ptr;
    int *newPtr = (int *)(uintptrValue);
    

  • Understand Pointer Type Compatibility : Understand the guidelines and restrictions related to pointer type compatibility. Casting between unrelated types can result in undefinable behavior, although casting between related pointer types is usually safe.
    int *intPtr = ...;
    // Undefined behavior
    double *doublePtr = (double *)intPtr;