Skip to content

C Type Casting

Type casting (or type conversion) is the process of converting a value from one data type to another. C allows both implicit (automatic) and explicit (manual) type casting.

The compiler automatically performs implicit conversions when mixing different data types in expressions, typically to avoid data loss by converting to a “wider” or higher-ranking type.

Key implicit conversion rules:

  • Integer Promotion: Types smaller than int or unsigned int (like char, short, _Bool, bit fields) are automatically promoted to int or unsigned int before arithmetic operations.
  • Usual Arithmetic Conversions: When an operation involves operands of different arithmetic types, the compiler converts them to a common type based on a hierarchy. The general rule is to convert to the type with the highest rank.

Hierarchy for Usual Arithmetic Conversions (Simplified):

Section titled “Hierarchy for Usual Arithmetic Conversions (Simplified):”
  1. If either operand is long double, convert the other to long double.
  2. Else if either operand is double, convert the other to double.
  3. Else if either operand is float, convert the other to float.
  4. Else (both operands are integer types): Perform integer promotions first.
  5. If types are still different:
    • If both are signed or both unsigned, convert the lower-rank type to the higher-rank type (int < long < long long).
    • If unsigned type has rank >= signed type, convert signed to unsigned.
    • If signed type can represent all values of unsigned type, convert unsigned to signed.
    • Otherwise, convert both to the unsigned version of the signed type.
#include <stdio.h>
int main(void) {
short s = 100;
char c = 5;
int result;
// s and c are promoted to 'int' before addition
result = s + c;
printf("Result (short + char): %d\n", result); // Output: 105
return 0;
}
#include <stdio.h>
int main(void) {
int i = 10;
float f = 5.5f;
double d = 0.25;
double result;
// In i + f: i is converted to float, result is float.
// Then (float result) + d: float result is converted to double.
result = i + f + d; // Common type becomes double
printf("Result (int + float + double): %lf\n", result); // Output: 15.750000
return 0;
}

You can explicitly force a conversion using the cast operator: (type_name) expression.

The expression’s value is converted to the specified type_name. Explicit casting is necessary when:

  • You want to override implicit conversion rules (e.g., force floating-point division from integers).
  • You need to convert pointer types (use with extreme caution!).
  • You want to make a potentially lossy conversion explicit for clarity.

Example: Integer Division vs. Floating-Point Division

Section titled “Example: Integer Division vs. Floating-Point Division”
#include <stdio.h>
int main(void) {
int sum = 17;
int count = 5;
double mean_implicit; // Implicit conversion happens *after* division
double mean_explicit; // Explicit cast *before* division
// Implicit conversion: sum / count is integer division (17 / 5 = 3)
// Then the integer 3 is converted to double 3.0
mean_implicit = sum / count;
// Explicit cast: (double)sum converts sum to 17.0 first.
// Then 17.0 / 5 performs floating-point division.
mean_explicit = (double)sum / count;
printf("Mean (implicit conversion after int division): %.6f\n", mean_implicit); // Output: 3.000000
printf("Mean (explicit cast before division) : %.6f\n", mean_explicit); // Output: 3.400000
return 0;
}

The cast operator (double) has higher precedence than division /, so sum is converted first.

Casting pointers can be necessary (e.g., with malloc which returns void*) but is dangerous if types are incompatible.

#include <stdlib.h>
int main(void) {
// malloc returns void*, must be cast to the desired pointer type
int *int_ptr = (int*)malloc(10 * sizeof(int));
if (!int_ptr) return 1;
char *char_ptr = NULL;
// Potentially DANGEROUS cast if alignment/size is wrong
// char_ptr = (char*)int_ptr;
free(int_ptr);
return 0;
}
  • Loss of Information: Casting from a wider type to a narrower type (e.g., double to int, long to short) can truncate the value or lead to overflow.
  • Undefined Behavior: Casting between incompatible pointer types and dereferencing can violate alignment rules or misinterpret data, leading to crashes or incorrect results.
  • Signed/Unsigned Issues: Casting between signed and unsigned types can change how the bit pattern is interpreted, especially for negative numbers.

While implicit conversions happen automatically, it’s often considered good practice to use explicit casts ((type_name)) when a conversion is intentional, especially if it might involve data loss or change in representation, to make the intent clear to other readers.