To share this page click on the buttons below;
Types
Variables are a named portion of memory able to contain the data our program is made of, here I describe some more detail about variables, memory and types.
The memory
All programs to be executed need to be in some kind of memory, accessible by the processor. This means that all the variables and the functions you define to build up your program are located in a specific portion of that memory: the compiler (to be correct the linker) reserves a space in memory to contain the variables and the functions and identifies each of those by its address.
There are several different kind of memories, faster, slower, read only and so on, but for our purposes we can depict the memory with a very simple representation: a long series of cells, each one able to contain one byte, each cell is uniquely identified by an address. Memories are digital devices, they stores information using a binary alphabet (an alphabet made only by 0 and 1), each letter of this alphabet (i.e. a 0 or a 1) is a bit, a byte is a set of 8 bits.
A variable or a function in the memory is identified by an address and a size: the compiler groups together more cells of memory to contain information bigger that 1 byte and handles it as a single portion of memory.
Because of the binary nature of the memories (which is due to the underlying electronics) every information (numbers, variables, functions, images and so on) is stored with a binary representation.
The following image summarize the depiction of the memory (more precisely the image shows a memory 4GB long, addresses in fact goes from 0 to 0xFFFFFFF, please note that addresses are expressed using hexadecimal notation):
The assignment
When you assign a variable to a value you are filling up the space in memory associated with that variable with that value (remember that the variable name uniquely identifies a specific memory address). In the same way you can assign a variable to another one: in this case the value contained in the memory space of the second one is copied into the memory space of the first one.
The C operator for the assignment is =
. On the left side you put the variable you want to set the value and on the right side you put the new value you want your variable will assume (or the variable whose value you want to copy into your first variable).
The following simple program (available here) will demonstrate these concepts
#include <stdio.h>
void main(void) {
int a = 0;
int b = 5;
printf("(1) a contains the value %d\n", a);
printf("(2) b contains the value %d\n", b);
a = 45; /* the value 45 is assigned to a */
printf("(3) a contains the value %d\n", a);
printf("(4) b contains the value %d\n", b);
a = b;
printf("(5) a contains the value %d\n", a);
printf("(6) b contains the value %d\n", b);
}
Types
C language defines 4 basic types: characters, integers and real numbers that can be in single (float) or double (double) precision.
Character type is identified by the reserved word char
.
A word is reserved for a certain programming language if it has a specific meaning for that language.
In the same way the integer type is identified by int
, the real number in single precision by float
and the real number in double precision by double
.
Remember that you declare the type of a variable by indicating first the type and then the name of the variable. You can declare more variables of the same type by separating them with commas after the indication of the type (do not forget to close the C statement with a semicolon). The following declares 3 different integers variable on the first row, while on the second row an additional variable is declared and defined, initialized to 0.
int var1,var2,another_variable;
int status = 0;
When I said that C defines four basic types, I lied: the basic types of C languages are five, there is an additional strange type, the void
one.
void
type is used with two different meanings (unfortunately very different):
the first one is no variable, so when you need a function that takes no argument you use void
as argument, in the same way a function returning nothing has void as returning type; the second application of the void type is to indicate any type, but that just works with pointer (so we will see that when we will deal with that argument). You cannot declare a variable of type void
, that is forbidden and the reason is that the compiler will not be able to figure out how much space is needed in memory.
The char type
A char
is a number associated with the ASCII code of a certain character. The ASCII code is a set of 255 numbers each one associated with a particular character: for example the ASCII code of the letter A is 65 and the ASCII code of the character 1 (the character not the value) is 49. To assign a char
variable to a particular character you have to enclose the character with single quotes. This is equivalent to put inside the variable the ASCII value associated with that character. At the very end a char variable is is simple an integer value associated with a character. A char
is one byte long.
If you print a char variable as a char you obtain the associated character, but if you print a char variable as an integer you can see the integer value held by the variable. Perhaps the previous sentence might appears a little bit odd, I hope the following small program might help to clarify.
#include <stdio.h>
void main(void) {
char ch;
void pippo;
ch = 'A';
/* ch printed "as" char -> use %c in printf */
printf("ch is %c (as char)\n", ch);
/* ch printed "as" integer -> use %d in printf */
printf("ch is %d (as integer)\n", ch);
ch = 49;
/* ch printed "as" char -> use %c in printf */
printf("ch is %c (as char)\n", ch);
/* ch printed "as" integer -> use %d in printf */
printf("ch is %d (as integer)\n", ch);
}
You can download the source file of this program from there.
Here we see another placeholder of the printf
function: %c
is the placeholder used to print char
variables.
The output of this small program is something like
The problem of coding characters is a very complex one. The ASCII approach is the one used by the C language since the beginning but many other code have been defined and the support for this code added to C language, but for the moment I will avoid this argument.
The int type
An int
is an integer number. By the default the int
type holds signed integers (so negative and positive numbers are allowed).
You can specify that you desire just positive integer by placing before the reserved word int
another reserved word: unsigned
. It does exist the correspondent signed
indication although being ints signed by default it is not so much used.
The difference between signed and unsigned integers is a matter of range: an int
type in fact is (usually) 4 bytes long. If you choose the unsigned version all the bits in memory can be used to hold information about the value, disregarding the question of the sign because we already know that the value will be positive: so an integer 4 bytes long can contain integers values from 0 up to 4294967295. On the other hand a signed integer must use at least one bit to hold the information about the sign (positive or negative) and so not all the bits of the 4 bytes are available to hold the values: for this reason a signed integer can contain values from -2147483648 to 2147483647.
You can reduce or increase the space in memory used to hold an integer depending upon your needs by placing before the reserved words short
and long
. So a short int
will be a signed integer stored on just 2 bytes and a short unsigned int
will be an unsigned integer stored on just 2 bytes (of course in this case the range of possible values will be significantly decreased). The long
situation is a bit odd (that is due to historical reasons I will not discuss here) so that a long int
is again stored on 4 bytes. To really increase the dimension you need to declare the integer as long long
and you will get a 8 bytes wide integer.
So, depending on the kind of declaration you used, the size (and so the possible range) of the integer variable will change. But what does it happen if you try to fit a bigger integer? It happens what it is called overflow and the variable will take some strange smaller value. This phenomenon is particularly interesting when you increase by one a variable which has already reached the maximum possible value. If you have a 4 bytes unsigned integer holding its maximum value (4294967295) and you sum just 1 to that value what will contain you variable after the sum?
The small program below (available here) will give you the answer
#include <stdio.h>
/*
* program to demonstrate the overflow concept
*/
void main(void) {
unsigned int i = 4294967295;
/* use %u to print unsigned int */
printf("before %u\n", i);
i = i + 1;
/* use %u to print unsigned int */
printf("after %d\n", i);
}
The float and double types
float
and double
types are designed to hold real numbers. Real numbers are numbers with a decimal part. There is a strange and important property of real numbers: between 2 different real numbers there are always an infinite number of real numbers. This makes me think that real numbers are not so much real but probably made up by mathematicians. Anyway all the math of real number is based upon this property.
Real numbers on a PC cannot have this property because of the fact that there is always a specific finite amount of memory to store a number even if real. When you use a real number on a device this is somehow truncated to fit that space. An important parameter here is the eps of the machine which is the smallest amount you have to sum to a number to obtain a new different number. I feel this might appear odd so a little example will probably help.
Suppose that you have a variable that contains the value 0, now suppose that you sum to this value a very tiny quantity. You probably expect that the result of this operation will be equal to this very tiny quantity (because 0 plus something is equal to something, isn't it?). Well this is true for real numbers but not for real numbers stored on a PC. The result of this operation will be different from 0 only if the very tiny quantity is greater or at least equal to the eps. Any quantity lower than that simply does not exist because the hardware cannot manage it.
Fortunately the eps is usually very very tiny and so the calculus made by a PC are really close to the same ones made by using true real numbers. Nevertheless when the precision of the calculus is very important this is something that has to be considered.
Which the difference between float
and double
since both are holding real values?
It is a matter of space and precision (our eps): float
variables are smaller but they have a bigger eps, double
variable are bigger but have a tinier eps, so that calculus made with float
requires less space but are little more inaccurate than the same calculus made with double
.
Size matters
Different types have different dimensions. There is a way to know how much space a variable takes up? The C language has the native function sizeof()
that is able to return the dimension of a variable expressed in bytes.
The following little program shows how the sizeof
function can be used (the source file of the program can be downloaded from here
#include <stdio.h>
/* program to show the use of the sizeof() function*/
void main(void) {
char c;
unsigned char uc;
signed char sc;
int i;
unsigned int ui;
signed int si;
short int shi;
long int li;
float f;
double d;
long long ll;
printf("The size of a char is %d bytes\n", sizeof(c));
printf("The size of an unsigned char is %d bytes\n", sizeof(uc));
printf("The size of a signed char is %d bytes\n", sizeof(sc));
printf("The size of an int is %d bytes\n", sizeof(i));
printf("The size of a short int is %d bytes\n", sizeof(shi));
printf("The size of a long int is %d bytes\n", sizeof(li));
printf("The size of a float is %d bytes\n", sizeof(f));
printf("The size of a double is %d bytes\n", sizeof(d));
printf("The size of a long long is %d bytes\n", sizeof(ll));
}
Compile and run this program, you will understand all the different sizes of the basic types.
The sizeof
function will be very useful in the future when we will meet more complex kind of types like arrays or structures.
Cast
Sometimes there is the need to trait one type as it was another one. To understand why let's anticipate a simple argument: the division. If you have 2 integers variables (suppose a
equal to 11 and b
equal to 2), you can calculate result of the division between those two numbers using the operator /
(so that a / b
is the desired result). Now which is the result? Well, obviously 5.5. Unfortunately that is not an integer value, so if you try to do such operation in a C program you will get 5. That is it. In C the result of a division between integer numbers is an integer, the fractional part is lost. What if you want get the fractional part? First of all your result (the variable used to store the result of this operation) cannot be an integer, it must be at least a double, but also you need to use the division between real number (that you do not have, because they are integers). Fortunately you can get real numbers from integer ones using a cast.
You cast a variable to another type by preceding the variable name with a pair of parenthesis, inside the parenthesis you put the new desired type. So our integer variable a
will be treated by the compiler as a double
if you write (double)a
. So from 2 integer variables you can use the division between real numbers (which returns a real number) with something like:
int a = 11;
int b = 2;
double res = (double)a / (double)b;
/* res is 5.5 */
Casting is the operation that you perform when asking the compiler to treat one kind of variable as another.
You can object that there was the possibility to declare the two variables as double
since the beginning and that is certainly true: but double
type is bigger and you would waste memory (for example, but there can be some other reasons to use integer), with casting the space allocated for the variable is the one needed by the the actual type, they are casted just when operation occurs and that usually is done only inside the processor registers (so no extra space in memory is necessary).
Cast works fine when you cast a smaller variable into a bigger one (because a smaller variable always fits into a bigger one), but you can also cast a bigger variable into a smaller one: in that case however some information might be lost. For example if you cast a double
variable into an int
one, but your fractional part will be lost in translation.
To share this page click on the buttons below;