C Data Types Tutorial

In this C tutorial we learn what primitive, derived and user-defined data types are.

We also cover the primitive types, the void type and the difference between signed and unsigned types.

Here's a table of contents of what you'll learn in this lesson:
(click on a link to skip to its section)

Let's jump right in.

What is a data type

When we create data containers, such as variables, we have to specify what type of data the container can store.

For example, if we want to store numbers, we would tell the compiler that the data container will only contain floating point numbers or whole integer numbers.

The types of data we use will determine how much space the container takes up in memory. The size of these data types may change depending on a 32 or 64-bit operating system and the compiler used.

 int number = 10; // whole number (4 bytes)

In the example above, the number variable is an int (integer) type. An int type has a size of 4 bytes on a 64-bit operating system, and a size of 2 bytes on a 32-bit operating system.

Data types in C are classified into three broad categories.

  • Primitive data type
  • Derived data type
  • User defined type

The table below lists the primitive data types in C.

TypeSize (in bytes)Format Specifier
signed char1%c
unsigned char1%c
long double10 to 16%Lf
short int2%hd
int2 to 4%d
unsigned int2 to 4%u
long int4 to 8%li
long long int8%lli
unsigned long int4%lu
unsigned long long int8%llu

In this tutorial we will look at the primitive types. We will also give an overview of derived types, user-defined types and other data types such as bool.

How to get the byte size of a type with sizeof

To check the size of a data container of a specific type, we use the sizeof() function.

To use the function, we specify the name of our data container in between the parentheses.

#include <stdio.h>

int main()
    char letter = 'A';

    printf( "%d byte(s)", sizeof(letter) );

    return 0;

In the example above, we check the size of the char letter variable and print the result to the console. The sizeof() function will return the size as an integer value, so we use the %d format specifier in the printf() function to print it to the console.

We cover functions in more detail in the tutorial lesson on functions .

The primitive char type

A primitive char is the smallest unit of the machine. A char is an integer type that can hold a basic ASCII character set.

An ASCII character is a letter that corresponds to a whole number. For example, the capital letter A would be the number 65 and the lowercase letter a would be the number 97.

When we specify a character like ‘A’, the compiler will convert it into its ASCII equivalent.

 char letter = 'A';

You can see a complete ASCII lookup table hereOpens up in a new page . The Dec column shows the number it’s converted into.

The primitive float type

A primitive float is a number with a decimal point. A float defines a single precision point whereas a double defines a double precision point.

The number of digits after the decimal point is known as its precision. The precision of a float type is 6 decimal places.

 float number = 22.83;

Even though we specify the float keyword as the variable’s type, the compiler will consider the float as a double.

In combination with the float keyword, we have to add the suffix f to the end of the number for the compiler to consider it as a float.

 float number = 22.83f;

The example above is the correct way to initialize a float. The suffix can be an uppercase F or a lowercase f.

The primitive double type

A primitive double is another number with a decimal point. A double defines a double precision point whereas a float defines a single precision point.

The number of digits after a decimal point is known as the number’s precision. The precision of a double type is 15 decimal places.

 double number = 0.299792671254910;

The double type does not need a suffix added to the number like the float does.

As mentioned in the float section, if the f suffix is not added to a float type, the compiler will consider it as a double.

The primitive short type

The short type is a version of the int type that allows a smaller range of whole numbers to be stored.

A short can only store a number from −32,767 to +32,767, so if we know our number will not go above that, we can use it.

 short number = 24522;

If the number we store goes beyond -32,767 or +32767, int overflow could occur which may cause unexpected results.

The primitive int type

The int type can store whole numbers, or integers, between -2,147,483,648 and +2,147,483,647 (-2.1 billion to +2.1 billion).

 int number = -1996553411;

If the number we store goes beyond negative or positive 2.1 billion, int overflow could occur which may cause unexpected results.

The primitive long type

The primitive long type is an integer type that allows us to store whole numbers between -9,223,372,036,854,775,808 and 9,223,372,036,854,775,807 (-9.2 quintillion to 9.2 quintillion).

 long number = 786860323568;

If we store a value beyond the range of a long, int overflow could occur which may cause unexpected results.

The primitive void type

The void type is the absence of a type, it means “no type”. In C we cannot create data containers like variables with a void type.

The void type is used to signify that a function does not return a value.

void function()
	// no return statement
	// because of void type

In the example above, the function doesn’t need a return statement because it doesn’t need to return anything.

We cover functions in more detail in the tutorial on functions .

Signed vs Unsigned - What is the difference

In C, signed and unsigned are type modifiers, which means they change the type when we specify them. These type modifiers are also known as sign qualifiers.

  • A signed type will be able to accept negative values.
  • An unsigned type will only be able to accept 0 and positive values.

As an example, let’s consider a short.

A signed short can store a value between −32,767 to +32,767. An unsigned short can store a value between 0 and 65,535.

It’s the same amount of numbers, but the range is different between them.

By default a short is signed and doesn’t need us to declare it explicitly with a keyword. But, if we want to make an unsigned type we use the keyword unsigned .

// signed
short sNumber = -14235;

// unsigned
unsigned short uNumber = 9877;

Derived data types

Derived data types are types that are derived from primitive types, such as the unsigned variants of the primitive types.

A derived data type is defined by using qualifiers with one of the primitive types. These qualifiers modify the behavior and property of the type.

 long double number;

In the example above, the long keyword before the double is considered the qualifier and will define a double with the minimum size of 12 bytes.

We’ve already used some of these without knowing it. For example, both short and long are derived types.

  • short is abbreviated from short int.
  • long is abbreviated from long int.

Other data types

The other types in C are:

  • bool type
  • complex type

User-defined data types

A user defined type is a type that the user decides on. We define our own type from them based on what we need.

The user-defined types in C are:

  • arrays
  • structs
  • unions
  • enums

We go into more depth later on in the course on each of these user-defined types.

Further reading

For more information on the subject of types in C, please see the links below.

Summary: Points to remember

  • A data type is simply the type of data that our variables, constants, arrays etc. can hold.
    • Data types are categorized as primitive data types, derived data types and user-defined data types.
  • We can check the size of a type with the sizeof() function.
  • The void type is the absence of a type and is only used on functions.
  • Derived data types are derived from primitive types with qualifiers.
  • User-defined data types allow developers to define their own data types.