Type Conversion and Declarations in C

This document explains the rules of type conversion, declarations, as well as object properties described in the C standard. It is not meant to be read as a tutorial, but rather as a quick reference.


Type Conversion

  • Implicit conversion - conversion done automatically by the compiler (no casts) - assignment conversion, or usual arithmetic conversionRULES:

    • integer ranks (excluding extended and enumerated types):
      • long long int, unsigned long long int
      • long int, unsigned long int
      • int, unsigned int
      • short int, unsigned short int
      • char, singed char, unsigned char
      • _Bool
    • integer promotion: an operation that converts any type whose rank is less than int or unsigned int to int (provided it can fit) or else unsigned int
    • Usual arithmetic conversion – conversion done upon non-equivalent types of operands with binary operators, or perhaps mismatch return type from a function or a function parameter/argument:
      • either operand is floating type:
        • float -> double -> long double
        • convert the other operand to the same floating type
      • neither operand is a floating type:
        • First, perform integer promotion on both operands
        • If types of both operands are now the same, the process ends, if not, perform the following rules, and stop at the first one that applies
          1. If both operands have signed types or if both operands have unsigned types, convert the operand whose type rank is lower to the type of the operand whose type rank is higher
          2. If the operand with the unsigned type has a rank HIGHER or EQUAL to the rank of the signed operand, convert the operand with the singed type to the type of the operand with the unsigned type
          3. If the operand with the signed type can represent all of the values of the type with the unsigned operand, convert the unsigned operand to the type of the signed operand
          4. If none of the above rules apply, convert BOTH operands to the unsigned “version” of the type of the signed operand
    • Assignment conversion:
      • The usual arithmetic conversion rules do not apply here. In assignment conversion, the value of the expression on the right is simply converted to the type of the object on the left. An overflow can happen if the value of the expression on the right is too large to be represented using the type of the object on the left. (side here represents the left/rights sides of the = operator)
  • Explicit conversion - type conversion done manually using casts

It is important to understand that not all operators behave the same. For example, when using the binary shift operators (<< and >>), integer promotion is always performed on both operands, regardless of their types or if they match or not.


Declarations

Before we begin, we have to understand that every object in C has 3 properties. These are:

  • Storage duration:

    It determines when the memory for the object is allocted, and when it is realeased/freed.

    • Storage for objects with automatic storage duration is allocated when the block containing the object is entered, and it is deallocated when the block containing the object is terminated.

    • An object with static storage duration stays at the same storage duration as long as the program is running, allowing it to keep its value indefinitely. All objects declared with static storage duration and without an explicit initializer are zeroed-out (be that inside a block using static, or at file scope without a storage class)

  • Scope:

    The scope of the object determines the portion of the program from which the object can be referenced.

    • Block scope: The object is “visible” from its point of declaration up until the end of the enclosing block.
    • File scope: The object is “visible” from its point of declaration up until the end of the enclosing file.
  • Linkeage:

    The linkeage of an object determines to which extent it can be shared among the different “files” (compilation units) in a program.

    • External linkage: An object with external linkage may be shared with multiple files in a program (perhaps all?)
    • Internal linkage: An object with internal linkage is limited to the file in which it is declared and can not be shared with multiple files in a program.
    • No linkage: An object with no linkage cannot be shared at all, and is only visible inside of the function in which it is declared.

Default properties

The default properties of objects depend on the location in which they were declared.

  • objects declared inside a block (including a function body):
    • automatic storage duration
    • block scope
    • no linkage
  • objects declared outside any block (the root of the file):
    • static storage duration
    • file scope
    • external linkage

Syntax

In C, declarations have the following general syntax:

declaration-specifiers declarators_and_initializers ;

Declaration specifiers describe the properties of the objects/functions being declared, while declarators give them names and provide some additional properties.

  • Declaration specifiers (4 categories):

    • Storage classes: There are (4) of them: auto, static, extern, register. At most one can be present in a declaration, and if so, it should come first.
    • Type qualifiers: There are (3) of them: const, volatile, restrict. A declaration may contain zero or more type qualifiers.
    • Type specifiers: void, char, short, int, long, float, double, signed, unsigned. They may be combined, and the order in which they appear doesn’t matter. They also include specifications of structs, unions, and enums, and names created using typedef.
    • Function specifier (C99 only): inline.

    I won’t cover each category of declaration-specifiers separately as it would be too much. Each category and keyword is described thoroughly in Chapter 18 (K.N. King. C).

  • Declarators

    • They provide the declarations with identifying names, along with additional type information (*, [], etc... for for pointers and array types).
    • They may be accompanied by initializers.
    • union, struct, and enum declarations can omit the declarators, in which case they declare just the tag and/or enum constant (as an either complete or incomplete type)