Size of Data Types
Learn about the size of various data types.
Before we start
Our aim is to refine the intuition from the “Memory” lesson in the first chapter. It would be best to reread it before continuing.
Introduction
The C language offers us a variety of data types, such as:
int
for integersfloat
for real numberschar
for characters
However, there are more data types, like
double
and long
. They don’t store a new type of data. The difference is that they allow us to store bigger numbers or have better precision.
For example, long
can store a much bigger number than int
can. Each data type occupies a different amount of space in memory. In other words, each data type has a different size.
To find out the size of a data type, we can use the sizeof
operator. Follow the next example.
#include <stdio.h>int main(){printf("sizeof(char) = %d\n", sizeof(char));printf("sizeof(short) = %d\n", sizeof(short));printf("sizeof(int) = %d\n", sizeof(int));printf("sizeof(float) = %d\n", sizeof(float));printf("sizeof(double) = %d\n", sizeof(double));return 0;}
We see that sizeof(char)
is 1
, but what is the unit of measurement?
Well, the output is in bytes (1 byte = 8 bits):
char
occupies 1 byte of memory or 8 bits.short
occupies 2 bytes of memory or 16 bits.int
occupies 4 bytes of memory or 32 bits.float
occupies 4 bytes of memory or 32 bits.double
occupies 8 bytes of memory or 64 bits.
Note: It is crucial to understand that we should never assume the size of data types. It depends on the platform, compiler, and potentially other factors.
For example, in code, don’t hardcode the size of
int
. Instead, usesizeof(int)
to retrieve it.However, to understand the memory layout of variables, and later arrays, we need to keep the size information in mind.
For this course, we’ll assume the sizes from the above output when making memory drawings and analyzing code.
More on bits and bytes
We can see that the smallest size is 1 byte for char. Therefore, we say that the smallest addressable memory unit is the byte. It’s a fancy way of saying that we don’t have data types with a size smaller than 1 byte (8 bits).
Let’s recall an example discussed previously:
int i = 13;
For this variable, we created the following memory drawing:
We said that the operating system picks a memory location (starting point) and then fills each bit with either 1 or 0, depending on the number. There’s one issue that we didn’t solve. Smaller numbers need fewer bits, and bigger numbers need more bits. How does the computer know where to stop reading the bits for a number?
We have the starting point provided by the address of the variable. We know where to start reading. However, we don’t have the endpoint. If we just read 4 bits like the example, what if a number needs 32 bits? We’ll stop reading way too soon.
The endpoint is determined using the starting point (address) and the size of the data type.
If we have an int
, we know that it occupies 4 bytes = 32 bits of memory. If the starting point is at address x
, the endpoint is before the address x + 32
.
Or, we know that the number occupies the next 32 bits from the starting point (inclusive).
So, in our example, we would need to fill all 32 bits, but our number only has 4. To solve the problem, the other bits (32-4) get filled with 0.
This process sounds harder than it is! Let’s update the drawing to clarify.
As we can see, we used 4 bytes or 32 bits. We placed 1101 and filled the other bits with 0.
We won’t create such drawings because it’s hard to draw 32 or 64 bits and then keep track of them. We only did it once to understand what happens.
Another example
Assume the following variable:
char x = 'c';
If the operating system decides to place the variable x
at address 0x7ffdee2428ec
, then the next 8 bits (1 byte) will be set to the binary representation of c
.
In binary, c
is 1100011. It only needs 7 bits, but we need to fill 8. The solution is to add a 0 as padding: 0
1100011.
In the future, if needed, we’ll draw the bytes only, not the bits (to avoid making huge drawings). Compare the drawing below with the one for an integer with 32 bits. It’s much shorter and easier to understand.
The size of pointers
Let’s try to find the size of various pointers.
#include <stdio.h>int main(){printf("sizeof(char*) = %d\n", sizeof(char *));printf("sizeof(short*) = %d\n", sizeof(short *));printf("sizeof(int*) = %d\n", sizeof(int *));printf("sizeof(float*) = %d\n", sizeof(float *));printf("sizeof(double*) = %d\n", sizeof(double *));return 0;}
We can see that the size of a pointer, no matter its type, is 8 bytes. It’is the size needed to store all possible addresses.
When we think about it, it makes sense. The pointer is just an address to some data, so its size shouldn’t change depending on what data we store. The address ranges from 0 to the amount of memory installed in the computer, and we need enough bytes to store it.
With 8 bytes (64 bits), we can store an address ranging from 0 to - 1.
Proof (optional)
We can prove that with 64 bits, we can store addresses up to - 1.
To do this, we need to know the conversion process from binary to decimal. Assume the following binary number
1101
We start from the right, and for each bit, we add to a sum: bit * .
1 1 0 1
pos3 pos2 pos1 pos0
We end up with: 1 * ...