Pointers and references in C + + Part 1

April 19, 2009 · Posted in C / C + +

Pointers and references are two important concepts in computer science. They appear in many programming languages with a dress or a little different, but basically the treatment is the same. In C + +, with pointers skills are essential.

Pointer is a special type of data, variables whose declared aim of this kind may (or not) to some other data in memory. A variable of type pointer holds the address of one other thing in memory. This thing can be a variable, constant, the beginning of a data set, a function, among others.

Declaring Pointers

The declaration of a pointer is usually done by adding a * before the variable name. A notation used much the glue * the name of the type to say symbolically that is declaring a "pointer to type" instead of a "pointer variable", but they are equivalent. For example:

  / / Pointer to an int.
 ptr_num1 ; int * ptr_num1;

 / / Or so.
 ptr_num2 ; int * ptr_num2;

 / / Pointer to a double.
 ptr_double1 ; double * ptr_double1;

 / / Or so.
 ptr_double2 ; double * ptr_double2; 

Once declared, the pointer is, is sized and therefore takes place in memory. The size of a pointer is usually equal to the number of bits of the machine / system in question, for example 4 bytes in a 32-bit.

  17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
  pc ; char c, * pc;
 pi ; int i, * pi;
 pd ; double d, * pd;

 / / The sizeof operator says the size of the type of its argument.
 "Size of char:    " << sizeof ( c ) << " bytes" << endl ; court << "Size of char:" <<sizeof (c) << "bytes" <<endl;
 "Size of int:     " << sizeof ( i ) << " bytes" << endl ; court << "Size of int:" <<sizeof (i) << "bytes" <<endl;
 "Size of double:  " << sizeof ( d ) << " bytes" << endl ; court << "Size of double:" <<sizeof (d) << "bytes" <<endl;

 endl ; court <<endl;

 "Size of char*:   " << sizeof ( pc ) << " bytes" << endl ; court << "Size of char *:" <<sizeof (pc) << "bytes" <<endl;
 "Size of int*:    " << sizeof ( pi ) << " bytes" << endl ; court << "Size of int *:" <<sizeof (pi) << "bytes" <<endl;
 "Size of double*: " << sizeof ( pd ) << " bytes" << endl ; court << "Size of double *" <<sizeof (pd) << "bytes" <<endl; 

Using Pointers

After his statement, the contents of a pointer is a random value, waste of memory, like any variable. For it to be useful we need to give him something. As a pointer holds a memory address, we assign it a valid address.

  17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
  int x, y;
 px, * py ; int * px, * py;

 ; x = 13;
 ; y = 10;

 x ; px = & x;
 y ; & py = y;

 42 ; * Px = 42;
 py ) ++ ; (* Py) + +;

 * px << endl ; court <<* px <<endl;
 * py << endl ; court <<* py <<endl;
 ( * px ) * ( * py ) << endl ; court <<(* px) * (* py) <<endl; 

The operator & (ampersand or ampersand) before the name of a variable means "address", because his name is "address".

What's left of the equality has to be able to hold exactly the same type that is the other side of equality. A pointer stores an address. So to assign an address to a pointer, in this case, you must obtain an address with the & operator.

Except in the case where there is implicit type conversions (such as between numeric types), assign a variable to something with a different type of its causes a compilation error. Fortunately!

After a pointer has to point to a valid address, we can access the contents of that address with the operator * (asterisk), whose formal name is "operator de-reference.

Note that the asterisk in a statement means "pointer to type" in the context of reading or writing the contents of a pointer, means "the contents of the address" or "the content pointed to by." Also be careful not to confuse with the symbol for product arithmetic. To avoid confusion, remember that huge table of precedence of operators, that you just passed the eye and dropped into oblivion. Here you will find that the operator of-reference takes precedence over the multiplication operator. Once that's done you will remember that the operator of-reference is evaluated before the multiplication operator.

  17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
  pa, * pb ; int a, b, c, * pa, * pb;
 ; a = 2;
 ; b = 3;

 a ; pa = & a;
 b ; pb = & b;

 pa * * pb ; c = * pa * * pb;
 pa * * pb ; c = * pa * * pb;
 pa * * pb ; c = * pa * * pb;
 pa ** pb ; c = * pa ** bp;
 * pa ) * ( * pb ) ; c = (* pa) * (* pb);

 "Valor da Pegadinha: " << c << endl ; court << "Value Trivia:" <<c <<endl; 

So when that evidence of algorithms you ask what makes any of the last 5 lines of the example you will say that it multiplies the contents pointed to by pa and pb. It will have to pick up PD because of this! And breaking in real life, if you really need to do this, you will save your 4 generations past and future of 5 excommunication, if you choose to use the last line.

All you can do with a common variable you can do with the content pointed to by a pointer, either reading or writing (unless it is clear that the pointer is const, but that's another rant ...).

Addresses and Pointers

Take for example the code below:

  18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
  int a, b;
 pa, * pb ; int * pa, * pb;

 "Antes de qualquer atribuição: \n " ) ; printf ( "Before any assignment: \ n");

 "&a  = %p \n " , & a ) ; // Endereço válido dado pelo SO. printf ( "& a =% p \ n", & a) / / Valid address given by the OS.
 "&b  = %p \n " , & b ) ; // Endereço válido dado pelo SO. printf ( "& b =% p \ n", & b) / / Valid address given by the OS.
 "&pa = %p \n " , & pa ) ; // Endereço válido dado pelo SO. printf ( "& pa =% p \ n", & pa); / / Valid address given by the OS.
 "&pb = %p \n " , & pb ) ; // Endereço válido dado pelo SO. printf ( "& pb =% p \ n", & pb); / / Valid address given by the OS.

 " \n " ) ; printf ( "\ n");

 "a   = %d \n " , a ) ; // Conteúdo não inicializado (aleatório). printf ( "a =% d \ n", a); / / Content uninitialized (random).
 "b   = %d \n " , b ) ; // Conteúdo não inicializado (aleatório). printf ( "b =% d \ n", b) / / Content uninitialized (random).
 "pa  = %p \n " , pa ) ; // Conteúdo não inicializado (aleatório). printf ( "pa =% p \ n", pa) / / Content uninitialized (random).
 "pb  = %p \n " , pb ) ; // Conteúdo não inicializado (aleatório). printf ( "bp =% p \ n", pb); / / Content uninitialized (random).

 / / Please uncomment the following lines, recompile and run.
 / / printf ( "* pa =% d \ n", * pa) / / What will be (random *)?
 / / printf ( "* pb =% d \ n", * pb); / / What is (random *)?

 " \n Após as inicializações dos ponteiros: \n " ) ; printf ( "\ n After the initialization of pointers: \ n");

 a ; pa = & a;
 b ; pb = & b;

 "&a  = %p \n " , & a ) ; // Mesmo endereço válido dado pelo SO. printf ( "& a =% p \ n", & a); / / Even valid address given by the OS.
 "&b  = %p \n " , & b ) ; // Mesmo endereço válido dado pelo SO. printf ( "& b =% p \ n", & b) / / Even valid address given by the OS.
 "&pa = %p \n " , & pa ) ; // Mesmo endereço válido dado pelo SO. printf ( "& pa =% p \ n", & pa); / / Even valid address given by the OS.
 "&pb = %p \n " , & pb ) ; // Mesmo endereço válido dado pelo SO. printf ( "& pb =% p \ n", & pb); / / Even valid address given by the OS.

 " \n " ) ; printf ( "\ n");

 "a   = %d \n " , a ) ; // Conteúdo não inicializado (aleatório). printf ( "a =% d \ n", a); / / Content uninitialized (random).
 "b   = %d \n " , b ) ; // Conteúdo não inicializado (aleatório). printf ( "b =% d \ n", b) / / Content uninitialized (random).
 "pa  = %p \n " , pa ) ; // Conteúdo inicializado (&a). printf ( "pa =% p \ n", pa) / / initialized Content (& a).
 "pb  = %p \n " , pb ) ; // Conteúdo inicializado (&b). printf ( "bp =% p \ n", pb); / / Content initialized (& b).

 "*pa = %d \n " , * pa ) ; // Conteúdo apontado por pa (*pa == a). printf ( "* pa =% d \ n", * pa); / / Content appointed by pa (* pa == a).
 "*pb = %d \n " , * pb ) ; // Conteúdo apontado por pb (*pb == b). printf ( "* pb =% d \ n", * pb); / / content pointed to by pb (* pb == b).

 " \n Após as inicializações dos inteiros: \n " ) ; printf ( "\ n After the initialization of integers: \ n");

 ; a = 10;
 ; b = 13;

 "&a  = %p \n " , & a ) ; // Mesmo endereço válido dado pelo SO. printf ( "& a =% p \ n", & a); / / Even valid address given by the OS.
 "&b  = %p \n " , & b ) ; // Mesmo endereço válido dado pelo SO. printf ( "& b =% p \ n", & b) / / Even valid address given by the OS.
 "&pa = %p \n " , & pa ) ; // Mesmo endereço válido dado pelo SO. printf ( "& pa =% p \ n", & pa); / / Even valid address given by the OS.
 "&pb = %p \n " , & pb ) ; // Mesmo endereço válido dado pelo SO. printf ( "& pb =% p \ n", & pb); / / Even valid address given by the OS.

 " \n " ) ; printf ( "\ n");

 "a   = %d \n " , a ) ; // Conteúdo inicializado (10). printf ( "a =% d \ n", a); / / Content boots (10).
 "b   = %d \n " , b ) ; // Conteúdo inicializado (13). printf ( "b =% d \ n", b) / / Content boots (13).
 "pa  = %p \n " , pa ) ; // Conteúdo inicializado (&a). printf ( "pa =% p \ n", pa) / / initialized Content (& a).
 "pb  = %p \n " , pb ) ; // Conteúdo inicializado (&b). printf ( "bp =% p \ n", pb); / / Content initialized (& b).

 "*pa = %d \n " , * pa ) ; // Conteúdo apontado por pa (*pa == a == 10). printf ( "* pa =% d \ n", * pa); / / Content appointed by pa (* pa == a == 10).
 "*pb = %d \n " , * pb ) ; // Conteúdo apontado por pb (*pb == b == 13). printf ( "* pb =% d \ n", * pb); / / content pointed to by pb (* pb == b == 13).

 " \n Alterando os valores através dos ponteiros: \n " ) ; printf ( "\ n Changing the values through pointers: \ n");

 7 ; * Pa = 7;
 42 ; * Pb = 42;

 "&a  = %p \n " , & a ) ; // Mesmo endereço válido dado pelo SO. printf ( "& a =% p \ n", & a); / / Even valid address given by the OS.
 "&b  = %p \n " , & b ) ; // Mesmo endereço válido dado pelo SO. printf ( "& b =% p \ n", & b) / / Even valid address given by the OS.
 "&pa = %p \n " , & pa ) ; // Mesmo endereço válido dado pelo SO. printf ( "& pa =% p \ n", & pa); / / Even valid address given by the OS.
 "&pb = %p \n " , & pb ) ; // Mesmo endereço válido dado pelo SO. printf ( "& pb =% p \ n", & pb); / / Even valid address given by the OS.

 " \n " ) ; printf ( "\ n");

 "a   = %d \n " , a ) ; // Conteúdo inicializado (a == *pa ==  7). printf ( "a =% d \ n", a); / / Content start (a == * pa == 7).
 "b   = %d \n " , b ) ; // Conteúdo inicializado (b == *pb == 42). printf ( "b =% d \ n", b) / / Content initialized (b == * pb == 42).
 "pa  = %p \n " , pa ) ; // Conteúdo inicializado (&a). printf ( "pa =% p \ n", pa) / / initialized Content (& a).
 "pb  = %p \n " , pb ) ; // Conteúdo inicializado (&b). printf ( "bp =% p \ n", pb); / / Content initialized (& b).

 "*pa = %d \n " , * pa ) ; // Conteúdo apontado por pa ( 7). printf ( "* pa =% d \ n", * pa); / / Content appointed by pa (7).
 "*pb = %d \n " , * pb ) ; // Conteúdo apontado por pb (42). printf ( "* pb =% d \ n", * pb); / / content pointed to by pb (42). 

The line 18 and 19 it declared respectivamnete two and two pointers to integers.

The lines 23 through 26 shows the address of my variables. These addresses were given by the operating system and are fixed while those variables exist. A variable does not change address during the program, but can change between one execution and another. Who defines what that address is the operating system at the time of the program. The compiler only knows that x bytes are needed for each variable, and of the interpretation (style) that the program will give this space.

Lines 30 to 33 show the contents of these variables, which have not been initialized. It's garbage, the last value that someone (do not know who) put those places in computer memory and which now belong to my variables.

Note that it is safe to uncomment lines 36 and 37 as they try to access the content pointed to by pa and pb, which has not been initialized. During this attempt, the program will try to interpret those values of random garbage as if they are addresses of variables int valid. Depending on the compiler, the OS used and their fate may be harmless or not. What is certain is that the outcome of these lines is unpredictable and sure source of problems.

In lines 41 and 42 the pointers are initialized with the addresses of the integer variables a and b. Since then the prints are repeated.

Note that the addresses of the pointers do not change, what was expected. What changes are its contents. Variables pa and pb receive the addresses of a and b respectively. Note that the content of the hands is exactly the value of the address of the variables a and b that were given by the OS at the beginning of the program. Check with the prints above. The content pointed to by pa and pb is the same content of a and b, which has not been initialized, ie, garbage. However, unlike the previous step, now pointers to valid addresses (addresses of a and b) and that the contents of a and b are garbage memory pointers now point to variables that have been allocated.

In lines 61 and 62 the integer variables are initialized with the values 10 and 13, and the prints repeated again.

This time the coteúdos of a and b are not more garbage. Values are properly known. Note that there is no change in the addressing of variables in relation to the previous steps, only the values.

See also that after a and b have been initialized, the contents pointed to by pa and pb were also changed automatically. This happens precisely because pa and pb point to the same areas of memory used by the variables pa and pb. This means that the content pointed to by a pointer can be modified by changing the contents of the original variables pointed out by him. And finally, lines 81 to 97 show that the converse is also true, ie, changing the content pointed to by a pointer, automatically changes the contents of the original variables pointed out by them.

It is easier to understand this thinking about the phrases in Portuguese than looking at the code.

For now ...

This was the basic operation with pointers. There are still interesting things to be seen as pointers to functions, for example, but that is considered a more advanced topic. Following this series, which is planned for 4 posts, see references, differences and similarities between pointers and references and finally pointers to functions.

Links

Comments

5 Responses to "Pointers and references in C + + Part 1"

  1. [...] With this attempt at series of posts about pointers and references, which began talking about pointers, today discuss [...]

  2. [...] [...] Pointeiros and References

  3. [...] First post of this series, we talk a little about pointers. In the second, we talk about references. Today we will focus on the intimate relationship (ui!) Between pointers and [...]

  4. Fernando on December 26th, 2009 18:50

    Dude, you have to teach me how that makes you post the code as you posted?
    Inside a box notably, marking the lines and highlighting the keywords of the language?

  5. blab on December 28th, 2009 08:48

    No problem!

    The Wordpress has a plugin called WP-Syntax, which uses behind the scenes the GeSHi.

    Once installed you can put your code inside PRE tags, adding the attributes "language" and "line". The language is self-explanatory and has a list of languages in the documentation and the line is the starting line.

    The GeSHi can also be exploited to add this feature in wikis.

    Tip: Before you copy and paste the code, replace all tabs by 4 spaces. This will make the code independent of the style applied to the tab.

    Hugs

Leave a Reply




Powered by WP Hashcash