简体   繁体   中英

Pointers in C, I'm so confused

Currently I'm following this pdf on C pointers. The code below was meant as a demonstration but confused me beyond belief. Firstly, my understanding of pointers to arrays is that they are initialized in heap memory with the memory address of the first element in the array hence why: ptr = &my_array[0]; /* and */ ptr = my_array; ptr = &my_array[0]; /* and */ ptr = my_array; are the same thing. My first issue with this code is that he created a pointer that doesn't point to a memory address. I thought pointer initialization would usually involve the & and the value of pA would be the memory address of the first element of the array, but when he prints the pointer to the screen THE WHOLE STRING PRINTS OUT. Why? On top of that, when I put another * on pA it prints the first character of the array. Why? On top of that, he uses ++ on *pA and *pB which makes no sense to me because the output shows that *pA is the first character in the array not a memory address.

Sorry this concept has been tough for me to wrap my head around. Is the PDF just bad at explaining pointers or am I grossly misunderstanding something?

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

char strA[80] = "A string to be used for demonstration purposes";
char strB[80];

int main(int argc, char *argv[]){
    char *pA; /* a pointer to type character */
    char *pB; /* another pointer to type character */
    puts(strA); /* show string A */
    pA = strA; /* point pA at string A */
    puts(pA); /* show what pA is pointing to */
    pB = strB; /* point pB at string B */
    putchar('\n'); /* move down one line on the screen */
    printf("\n%c\n", *pA);
    while(*pA != '\0') /* line A (see text) */
    {
        *pB++ = *pA++; /* line B (see text) */
    }
    *pB = '\0'; /* line C (see text) */
    puts(strB); /* show strB on screen */
    return 0;
}

Output:

A string to be used for demonstration purposes
A string to be used for demonstration purposes


A
A string to be used for demonstration purposes

EDIT: Thanks for the help everyone:), sorry about the noobie question. I read Programming in C by Stephen G. Kochan and its amazing section on pointers helped a lot.

char strA[80] = "A string to be used for demonstration purposes";

This makes an 80 character long space in the global section of the executable - it isn't dynamically allocated so it doesn't need to be deallocated. If strA is used as a pointer it is equivalent to &strA[0] as you expect.

Those 80 characters are filled with the string on the right side of the equals - including the terminating character at the end of the string. That terminating character is really important because that's how you find the end of the printable data in the array.

puts(strA); /* show string A */

When you puts(strA) , that function prints EVERY character from the address passed in up to that terminating character - there is a loop inside that function.

pA = strA; /* point pA at string A */
puts(pA); /* show what pA is pointing to */

Then pA is filled with the value of &strA[0] and then when puts(pA); is called, the function receives the same address it received the last time so it does the same thing as last time: it prints EVERY character from the address passed in up to that terminating character.

printf("\n%c\n", *pA);
while(*pA != '\0') /* line A (see text) */

The * character gives you the contents at the location of the pointer - so *strA gives the same thing as strA[0] - the first character of the string. And *pA is the same as *strA because they both point to the same address.

*pB++ = *pA++; /* line B (see text) */

The reason *pA++ confuses you is because you don't know whether the * or the ++ is done first. What happens is: pA++ increments the pointer and returns the original value it had. Then the * takes that original value of the pointer and gives its content - the character where pA originally pointed.

According to the C Standard (6.3.2.1 Lvalues, arrays, and function designators)

3 Except when it is the operand of the sizeof operator or the unary & operator, or is a string literal used to initialize an array, an expression that has type ''array of type'' is converted to an expression with type ''pointer to type'' that points to the initial element of the array object and is not an lvalue. If the array object has register storage class, the behavior is undefined.

So these two assignment statements

ptr = &my_array[0]; 

and

ptr = my_array;

are equivalent because the array designator my_array used as an initializer is implicitly converted to pointer to its first element.

In C strings are character arrays that contain sequences of characters terminated with the zero terminating character '\0' .

For example the string literal "Hello" is stored as a character array with this sequence of characters

{ 'H', 'e', 'l', 'l', 'o', '\0' }

The function puts is specially designated to output strings.

It is declared the following way

int puts(const char *s);

the calls of the function

puts(strA);
puts(pA)

are equivalent because again the array designator strA used as an argument in the first call is implicitly converted to pointer to its first element.

Dereferencing a pointer like in this call

 printf("\n%c\n", *pA);

you get the object pointed to by the pointer. As the pointer pA points to the first character of the array strA then this character is outputted.

As a follow up to Vlad's answer, which is great, I just wanted to add a small bit of code which really helped me to understand pointers. I find that it is really helpful to make predictions and then write code and make observations until I understand.

[ttucker@zim stackoverflow]$ cat ptr.c 
#include <stdio.h>

int main() {
    char *foo = "stuff";
    while (*foo != '\0') {
        printf("%lu: %c (%d), %lu\n", &foo, *foo, *foo, foo);
        foo++;
    }
    return 0;
}


[ttucker@zim stackoverflow]$ gcc -o ptr ptr.c


[ttucker@zim stackoverflow]$ ./ptr 
140730183740528: s (115), 94216543457284
140730183740528: t (116), 94216543457285
140730183740528: u (117), 94216543457286
140730183740528: f (102), 94216543457287
140730183740528: f (102), 94216543457288

# A second execution is included to show that the memory addresses change.

[ttucker@zim stackoverflow]$ ./ptr 
140723360085232: s (115), 94198997999620
140723360085232: t (116), 94198997999621
140723360085232: u (117), 94198997999622
140723360085232: f (102), 94198997999623
140723360085232: f (102), 94198997999624

Firstly, my understanding of pointers to arrays is that they are initialized in heap memory with the memory address of the first element in the array hence why: ptr = &my_array[0]; /* and */ ptr = my_array; ptr = &my_array[0]; /* and */ ptr = my_array; are the same thing.

The only time "heap" memory comes into play is when you're using malloc , calloc , or realloc , or library functions that call them behind the scene. pA and pB occupy the same memory as any other type of variable declared in that scope.

but when he prints the pointer to the screen THE WHOLE STRING PRINTS OUT.

The puts function takes the address of the first character in a string and "walks" down the string, printing each character, until it sees the string terminator, sort of like

void myputs( const char *str ) // str points to the first character in a 0-terminated string
{
  while ( *str )         // while we haven't seen the string terminator
    putchar( *str++ );   // print the current character and advance the pointer    
}

On top of that, he uses ++ on *pA and *pB which makes no sense to me because the output shows that *pA is the first character in the array not a memory address.

The line

*pB++ = *pA++;

is roughly equivalent to writing:

*pB = *pA;
pB = pB + 1;
pA = pA + 1;

Postfix ++ has higher precedence than unary * , so *pA++ is parsed as *(pA++) - you're dereferencing the result of pA++ . The result of pA++ is the current value of pA , and as a side effect pA is incremented. Since pA and pB are pointers, adding 1 advances them to point to the next object in the sequence, not necessarily the next byte (although in this particular case, the next object is the next byte).

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM