Variables and Data Types

Variable Declaration

<type> variable_name;

Variable assignment

Using the assignment operator (=)

variable_name = <value>; 

By default, if no values are assigned to the variable, the variable value is the old content of the address now associated to variable_name.

The assignment is from right to left:

var1=var2=value;

Simple Data types

Integer

char

The values of the char type can vary from computer to computer (different machines may be based on a different character set).

The most widely used character set is ASCII (often extended to Latin-1 with characters from Western Europe and Africa).

Type
Description

signed char

8-bit integer

unsigned char

8-bit unsigned integer

int

The integer ranges are listed in the following MACROs of the library <limits.h>:

  • INT_MIN

  • INT_MAX

  • LONG_MIN

  • LONG_MAX

Number formats: If you prepend the following simbols to the integers, you can specify the numbers in a different format:

Format specifier
Format type

O

Octal

0X

Hexadecimal

Enum

  1. Declaration of the enum without its name definition:

    enum {VALUE0, VALUE1, ...} var1, var2, ...;

    It defines a set of variables var1, var2, ... that can have one of the values VALUE0, VALUE1, ...

    For example:

    enum { FALSE,TRUE }  b1, b2 ;
  2. Declaration and definition of the enum

    enum  NOME_ENUM{VALORE0, VALORE1, ...} ;
    enum NOME_ENUM  var1, var2, ..;

    For example:

    enum Bool{ FALSE,TRUE } ;			
    enum Bool b1, b2 ;
  3. Type definition for the enum

    typedef  enum {VALORE0, VALORE1, ...}nome_Tipo ;
    nome_Tipo  var1, var2, …;

    For example:

    typedef enum {FALSE,TRUE}Bool;
    Bool  b1,b2;

Integer association

  • By default, the values in the enum are associated in order to the integers 0, 1, 2, ...

    For example:

    enum Bool{ FALSE,TRUE };
    enum Bool b1, b2;
    b1 = FALSE;
    b2 = TRUE;

    Hence, the values are:

    b1 = 0;
    b2 = 1;
  • the integers associated to the enum values can be modified as follows:For example:

    enum Bool{ FALSE=13,TRUE=3 };
    enum Bool b1, b2;
    b1 = FALSE;
    b2 = TRUE;

    Hence, the values are:

    b1 = 13;
    b2 = 3;

Floating point

Type
Values range
Digits

float

1.17 × 10⁻³⁸ ÷ 3.4 × 10³⁸

6 digits

double

2.2 × 10⁻³⁰⁸ ÷ 1.8 × 10³⁰⁸

15 digits

Type conversion

  • Implicit conversion

    1. logical or arithmetic expression with expressions of different types

    var_Float+var_Int

    The int variable is converted to float in the sum, because the float has a broader range than int.

    1. assignment conversion of the variable in the RIGHT side is converted to the type of the variable in the LEFT side

    2. parameter of a function the variable passed as a parameter is converted to the one required

    3. return statement if the return value of a function is different from the required one, the value is automatically converted to the defined one

  • Explicit conversion

(data_type)  expression

The expression is forcibly cast to data_type

Variable Dimension

It returns the size (number of bytes)

sizeof(data_type)			
sizeof variable_name			

Type definition

#define directive

  • Definition of the new type

    #define new_type type
  • Definition of the possible values

    #define value_name value

For example:

#define BOOL int
#define TRUE  1
#define FALSE  0

typedef

typedef type new_type;

It creates a new data type (new_type), that it is always a type variable.

typedef float Dollars;
Dollars cash_in, cash_out;

Advantages:

  • Portability

  • Documentation & readability

Aggregate variables

Vector (Array)

It contains elements of the same type.

type vector_name[constant_integer_expression];
  • type: type of the elements contained in the array

  • constant_integer_expression: number of elements in the array This is usually specified by:

    • MACROs (recommended practice)

    • Magic numbers, i.e., values written directly in the declaration, large enough to ensure the array is never full

C99: Variable Length Array (VLA)

type vector_name[expression];

The length of the array is defined at runtime (usually, the number inside the square brackets is a variable that is later provided as input).

Indexing operator (subscripting)

vector_name[i]

For example:

examples[3] = 2;

Sequential continuity

The elements of the array are stored sequentially in memory.

Note: C does not check array indices during read or write operations to be FASTER (to avoid wasting clock cycles).

If you write to an array using an invalid index, you might overwrite other data in memory.

type a[N];

Array size

sizeof(a)/sizeof(a[0])
  • sizeof(a) – size in bytes of the array a

  • sizeof(a[0]) – size in bytes of the first element (i.e., the size of the element type)

Initialization

  • Explicit initialization of all elements

    int a[10] = {1,2,3,…,10};
  • Explicit initialization of the first 3 elements and implicit initialization of the remaining elements to 0

    int a[10] = {1,2,3};
  • Automatic assignment of the array length (# of initialized elements)

    int a[] = {1,2,3,…,10};

C99:

int a[10] = {[1]=3,[5]=12};

Designators allow you to initialize specific elements or fields of arrays and structs by name or index, improving clarity and flexibility.

Management of Partially Filled Arrays

Arrays are usually created with larger sizes because continuous allocations would require more intervention from the operating system.

  • Resizing due to the insertion of a new element

    If the space occupied by the array is full, a new memory area must be allocated using realloc():

    • If there is contiguous space after the array, the pointer to the same memory area is returned.

    • Otherwise, a copy of the array is made to a new memory area, and only then is the new element added.

    Usually, very large arrays are defined to minimize the number of reallocations, and therefore reduce requests to the operating system.

  • Removing an element from the array without wasting memory

Structures

  • The components are called fields (members in C terminology)

  • The components can be simple types or other data structures

  • The components are defined by names, and you access them via these names (not by position as with arrays)

Declaration of a structure

You can declare it in a .h file, but I need to use macros to avoid multiple declarations, because a struct can only be declared once.

First method

struct structure_tag
{	
	field_type1  field_name1 ;
	field_type2  field_name2 ;

};

In memory, the structure members are allocated contiguously in the same order they are declared.

Depending on the architecture, there may be gaps between fields to ensure memory alignment.

Padding If a field occupies less than the number of bits in which the architecture organizes memory (e.g., 32-bit or 64-bit), and the next field doesn’t fit in the remaining space, the compiler inserts uninitialized gaps between fields for alignment purposes.

  • These padding areas are sometimes called "dirty" memory from previous usage.

  • Padding bytes are not addressable — you can't access or use them directly.

For example:

struct test	
{	
	short s;
	int i;
};

Second method

Declaration of a new data type (new_type_name):

typedef struct structure_tag
{	
	field_type1  field_name1 ;
	field_type2  field_name2 ;

}new_type_name;

Declaration of a struct variable

First method:

struct structure_tag variable_name;

Second method:

new_type_name variable_name;

Access to the structure fields

variable_name.field_name

Example:

struct test t;
t.s = 10;
t.i = -15;

Initialization of the structure

First method

Each field that is not explicitly initialized is zeroed out bit by bit by the compiler.

  1. List of fields separated by commas (traditional syntax)

struct  structure_tag  variable_name = {value1, value2, ...};
  1. Field declarations using colons (Not Standard - GCC syntax before standardization)

struct  structure_tag  variable_name = {fieldName1:value1, ...};
  1. Assignment using field names - designated initializers (C99 standard syntax, also supported by GCC)

struct  structure_tag  variable_name = {.fieldName1:value1, ...};

Examples:

struct item {int id; char name[20]; int value; int priv;};
  1. Traditional syntax

    struct item i1 = {3, "John", 45};
  2. Not standard

    struct item i2 = {id: 3, name: "John", value: 45};
  3. C99 syntax

    struct item i3 = {.id = 3, .name = "John", .value = 45};

Second method

new_type_name variable_name = {value1,value2,...};

Example:

typedef struct test {short s; int i;} test_t;
test_t t = {10, -15};

Assignment of the variable

variable_name1 = variable_name2;

A deep copy of the fields of variable_name2 is made into the fields of variable_name1 (if both are struct variables).

Comparison between structure variables

You cannot directly compare two structure variables in C (e.g., using the == operator). If there is padding within the structure, the two variables will never be equal, even if the fields are the same. This is because the padding bytes are not initialized and can affect the overall memory representation of the struct.

Example:

struct test {short s; int i;} t1, t2;
  • t1 == t2 ➡️ NO (due to padding)

  • t1.s == t2.s && t1.i == t2.i ➡️ YES

Pointers to structures

Declaration of the pointer:

struct structure_tag *pointer_name;

Access to the structure fields:

(*pointer_name).field_name
pointer_name->field_name									

Bit fields

A group of bits organized into fields.

Useful for making better use of hardware on systems with limited memory

You can define data types with sizes smaller than the smallest standard data type (usually char), by using bit fields.

Bit field declaration

struct structure_tag
{	
	unsigned  field_name1:bit_size1;
	unsigned  field_name2:bit_size2;

}

Each field specifies the number of bits (bit_size) that make up the field

A field can also be:

  • Anonymous (without field_name and :) used for padding of number_bit bits (cannot be used to store values)

  • Anonymous with zero size (without field_name and :, but with size 0) forces the alignment of the following field to the next memory boundary (padding)

Example:

Union

Aggregate data type that can contain multiple fields as alternatives.

Declaration

  • In memory, space is allocated for the largest field

  • It is a kind of “variant record” where the fields overlap in memory, so only one is valid at a given time

First method

union union_tag 	
{	
	field_type1  field_name1; 
	field_type2  field_name2;

}	

Example:

union datum	
{	
	int i;
	double d;
};

Second method

typedef union union_tag 	
{	
	field_type1  field_name1; 
	field_type2  field_name2;

} new_type_name;

Example:

typedef union datum	
{	
	int i;
	double d;
} datum_t;

Declaration of a struct variable

First method

union union_tag variable_name;

Second method

type_name variable_name;

Access to the union fields

variable_name.field_name

You can access a union member only if the last assignment to the union was made through that member. (C provides no way to determine which member of a union was last assigned)

Last updated