Lesson 4 - More on the C type system: Data types

C and C++ The C language Basics More on the C type system: Data types

In the previous lesson, Variables and the type system in the C language, we learned the basic data types of the C language (int, double, and char). In today's tutorial, we're going to look at them in more detail and explain how to use them correctly. Today is going to be more theoretical, and the next lesson will be very practical. At the end, we'll make a few simple examples.

Note: Sizes and availability of the following data types may vary according to the used C standard and the computer processor's architecture.

Whole-number data types

Let's look at the table of all of the built-in whole-number data types in the C language, notice the type int, which is already known to us.

Data type Range Size
char -128 to 127 8 bits
short -128 to 127 or -32 768 to 32 767 16 or 32 bits
int -32 768 to 32 767 or -2 147 483 648 to 2 147 483 647 16 or 32 bits (depends on the system)
long int -2 147 483 648 to 2 147 483 647 32 bits
long long int -9 223 372 036 854 775 808 to 9 223 372 036 854 775 807 64 bits

Note: The data type sizes depends on the operating system, meaning whether it's 16-bit or 32/64-bit. The higher values are most probably the relevant ones for you.

You might be thinking it's odd that char is a number since we introduced it as a character in the previous lessons. In fact, we're able to use char as both a number or as a character (we'll either use the "%d" modifier or the "%c" modifier when writing it to the console). This is because characters are internally stored as their numeric codes, those are called ASCII codes.

Another question might be: Why do we have so many data types for storing numbers? The answer is simple, it depends on its size. If the number is large, it consumes more memory. For a user's age, we should select byte since no one can live more than 127 years. Imagine a database with millions of users on some informational system. If we choose int instead of char, it'll occupy 4 times more space. Conversely, if we have a function that calculates the factorial, the range of "int"s will not be enough for us and we'll have to use long int.

We don't have to think hard about the data type choice since we'll use int almost every time. You have to think about it only in case the variables are in an array or collection in general, and there are lots of them. In that case, it's worth it to consider memory requirements. The tables I provided here are mainly for the sake of completeness. The implicit conversion also works between the types, so we can assign some int to a long int variable directly, without having to convert it.

Decimal numbers

For decimal numbers, the choice is simpler, we can only choose between two data types. They differ in the range of values, and also in precision, i.e. in the number of decimal places. The datatype double is twice as precise as float, which you probably deduced from its name.

Data type Range Precision Size
float 1.2E-38 to 3.4E+38 6 digits 32 bits
double 2.3E-308 to 1.7E+308 15 digits 64 bits
long double 3.4E-4932 to 1.1E+4932 19 digits 80 bits

Beware, due to the fact that decimal numbers are stored in your computer in a binary system, there is some precision loss. Although the deviation is almost negligible, if you're programming, e.g. a financial system, don't use these data types for storing money since it could lead to slight deviations.

When we need to assign a value to a float variable in the source code, we have to use the f suffix. With doubles, we don't write a suffix at all since double is the default decimal type:

float f = 3.14f;
double d = 2.72;

As the decimal separator in a source code, we use dots, regardless of our OS regional settings.


Let's take a closer look at the char data type. Char represents one character. Characters are declared with apostrophes in C:

char c = 'A';
printf("%c", c);

Here's what we do when we need a char to represent a number:

char i = 127;
printf("%d", i);

Unsigned types

Most of the data types can be prefixed with the unsigned keyword. For example, we can create a variable of the unsigned int type. Beware, this doesn't work for decimal numbers. Unsigned means that the number can't be negative (can't carry information about the +/- sign). Therefore, the resulting variable is always non-negative, and it can carry a number twice as big as before since the negative part of the type is not used. Let's make an example with char:

Type Range
char -128 to 127
unsigned char 0 to 255

Now, let's start coding :) We'll create a "table" of various data types and their sizes. First thing's first, let's create a new project, name it Table and modify the code to look like this:

#include <stdio.h>
#include <stdlib.h>
#include <limits.h>

int main(int argc, char** argv) {
        return (EXIT_SUCCESS);

I removed the gray doc comments just to make the code clearer, feel free to keep them in your project. The change here is in adding limits.h which allows us to access functions for determining the highest possible value of data types.

We use the sizeof() function to determine the data type size, it returns its size in bytes.

We're going to use constants (we'll cover them later on) from the limits.h file which we included.

Let's try it out:

int intSize = sizeof(int); // Stores the int size
printf("Int occupies %d bytes \n", intSize);

The result should look like this:

Console application
Int occupies 4 bytes

Now, we've verified that the sizeof() function returns the size of a given data type. Let's try out the limits.h constants which return the highest possible value of a given type:

int maxValue = INT_MAX; // Stores the maximal value of int
printf("The maximal value stored in int is %d \n", maxValue);

This should be the result (if you don't have a different processor):

Console application
The maximal value stored in int is 2147483647

Since we're now aware of how the constants work, we can get back to programming our console table :) The code will look like this:

int maxInt = INT_MAX;
short maxShort = SHRT_MAX;
char maxChar = CHAR_MAX;
char sizeInt = sizeof(int);
char sizeShort = sizeof(short);
char sizeChar = sizeof(char);
printf("INT: occupies %d bytes and the maximal value is %d\n", sizeInt, maxInt);
printf("SHORT: occupies %d bytes and the maximal value is %d\n", sizeShort, maxShort);
printf("CHAR: occupies %d bytes and the maximal value is %d\n", sizeChar, maxChar);

The result:

Console application
INT: occupies 4 bytes and the maximal value is 2147483647 SHORT: occupies 2 bytes and the maximal value is 32767 CHAR: occupies 1 bytes and the maximal value is 127

There's still a lot to go over and lots of other data types that we haven't covered. Regardless, there is a time for everything. In the next lesson, Conditions (branching) in the C language, we'll introduce conditions and then loops, then we'll have enough knowledge to create interesting programs :)


  Activities (4)

Article has been written for you by David Capka
The author is a programmer, who likes web technologies and being the lead/chief article writer at ICT.social. He shares his knowledge with the community and is always looking to improve. He believes that anyone can do what they set their mind to.
Unicorn College Author learned IT at the Unicorn College - prestigious college providing IT and economical education.

Do you like this article?
Nobody has rated this just yet, be the first one!





To maintain quality of discussion, we only allow registered members to comment. Sign in. If you're new, Sign up, it is free.

Nobody has commented yet - be the first!