0

I am trying to understand pointers and char arrays in C programming. I have a struct called player defined as such:

typedef struct player{
    char* name;
    float ppm;
} player;

And I have the following in Main:

int main()
{
  player* head = (player*) malloc(sizeof(player));
  char* meName = &(head->name);
  (*head).name = "potato";
  (*head).name = "Paul";
  (*head).ppg =7.6;
  //printf("player is named %s\n", *meName); //First print
  printf("player is named %s\n", meName); //second print
  printf("player is named %s\n", (*head).name); //third print
  return 0;
}

Why does my first print cause a segmentation fault, and why do my other two prints output the following:

player is named k0@
player is named Paul

If I am not mistaken meName should be pointing to a memory address that is then changed by using another pointer to that same memory address. In fact, if I print it as in my 2nd print, it shows that address. Why is dereferencing it cause for a segFault when I clearly set the variable to point to a memory space that was altered right after? Why would it not change to Paul? And why would it throw a segfault in the first place?

Also, I'd appreciate an explanation of the arrow -> vs the * used to dereference pointers.

Qiu
  • 5,651
  • 10
  • 49
  • 56
farid99
  • 712
  • 2
  • 7
  • 25
  • [Don't cast the result of malloc (and friends)](http://stackoverflow.com/q/605845). Also, `(*head).name` is equivalent to the nicer `head->name`. And the parentheses in this expression are clutter: `&(head->name)`. Anyway, `meName` has type `char*`, not `char**`. My advice: Compile your code with full warnings, and heed them all. – Deduplicator Sep 27 '14 at 15:53

3 Answers3

3

This is incorrect, and you should get a warning:

char* meName = &(head->name);

meName is a pointer to a pointer to char, not simply a pointer to char. Here is how you can fix it:

char** meName = &(head->name);

Of course printing should look like this - i.e. the way that you have commented out:

printf("player is named %s\n", *meName);
Sergey Kalinichenko
  • 714,442
  • 84
  • 1,110
  • 1,523
  • So when I do _**`head->name`**_, this is returning a pointer to the string "name", and when I get the address of that by using ampersand, I am getting a pointer to a pointer. Is that how this went? In that case, why doesn't _**`char* meName = head->name;`**_ compile either? In this case _**'meName'**_ is a pointer to a char, and _**`head->name`**_ is also a pointer to a _**`char`**_, if I am not mistaken. – farid99 Sep 27 '14 at 16:07
  • I did not mean to use double asterisks on that at all. The formatting for comments is beyond me. – farid99 Sep 27 '14 at 16:13
  • @mufasa56 `->` operator dereferences `head`, giving you a pointer to `char`. The "take address" `&` operator makes it a pointer to pointer to char. – Sergey Kalinichenko Sep 27 '14 at 16:18
1

First off all your printfs use the %s code. That means the second argument will be interpreted as a pointer to (i.e. the address of) a byte representing a character. Printf will print the value of that byte (as a character), then the value of the next byte in memory, and so on until it encounters a byte whose value is 0.

I'll take your print statements one at a time.

printf("player is named %s\n", *meName);

The *meName means the character at location meName. This character will be an integer between -128 and +127. (In fact it is 107 -- see below.) Now you are using this number, 107, as if it were the address of (the first character of) a null-terminated string. Chances are that this number is not a valid address, so when printf tries to read the character at that address (107), it's outside of your process's memory range and so you get a segmentation fault. (Usually segmentation fault means you've tried to read or write memory at an address your process is not allowed to access.)

Second

printf("player is named %s\n", meName);

This is better because meName is at least a pointer. So what does it point to. The initialization

meName = &(head->name);

means that meName is the address of (i.e. points to) the field head->name. Note that this is not actually a character, it is a pointer to a character. So what happens is that printf prints out the first byte of the head->name as if it were a character. It happens to print as k, so that first byte is 107 (the ascii number for a k). Then it prints the second byte of the pointer, and so on until there happens to be a byte that is 00000000. Why your compiler accepted the initialization is not clear to me, as the type on the left is "pointer to character" and the type on the right is "pointer to pointer to character". It should have generated at least a warning error.

Third printf

printf("player is named %s\n", (*head).name);

This looks at address head and finds a structure. The value of the name field of that structure is the address of the first byte of a five byte sequence 'P' 'a' 'u' 'l' 0. printf follows this pointer value to find the 'P' (which it prints), then it increments the pointer, and follows it again to find the 'a', and so on until it finds the 0 byte and at that point it stops.

Note that because printf is a bit special, its arguments are not type checked by the compiler. That is why your first printf compiled even though the type of the second argument was not char* as the %s directive indicates it should be.

Theodore Norvell
  • 15,366
  • 6
  • 31
  • 45
  • I forgot to address the use of `head->name` vs. `(*head).name`. They mean *exactly the same thing*. Most prefer `head->name` because it is slightly easier to read and slightly easier to type. – Theodore Norvell Sep 27 '14 at 16:40
  • That makes perfect sense. So when I want to declare a new "string", the item on the left of the equal sign should be a _pointer_ and the item on the right should be the actual set of chars? so if I say char array[] = "Paul" then array is now a pointer to the memory address of the character 'P'. Yes? – farid99 Sep 27 '14 at 16:44
  • Not quite. In most cases a string literal like `"Paul"` stands for an array of chars. But in C an array of characters is easily converted to a pointer to a char. In a sense your assignment `(*head).name = "Paul"` is just short hand for `(*head).name = &("Paul")[0]`, which means that the address of the first item of that 5 char array is assigned to the pointer `head->name`. But there is an exception; the string literal has a whole different meaning when used to initialize arrays: when you declare `char array[] = "Paul" ;`, this is short for `char array[5] = {'P','a','u','l',0};`. – Theodore Norvell Sep 27 '14 at 17:56
  • Note that: `char array[];` declares that `array` is an array of chars. But `char array[]` as a parameter declaration declares that `array` is a pointer to `char` -- a `char*`, as we like to say. One thing aC programmers should know and many seem never to have learnt is that arrays and pointers are not the same thing. Try `char array[] = "Paul" ;` and `char *pointer = "Paul" ;`. If you pass both `array` and `pointer` to printf, both will print "Paul". But if you print the `sizeof` both, you will find that `array` has size 5 and `pointer` has size 4 or 8. – Theodore Norvell Sep 27 '14 at 18:11
0
int main()
{
  player* head = (player*) malloc(sizeof(player));
  char* meName = &(head->name);
  (*head).name = "potato";
  (*head).name = "Paul";
  (*head).ppg =7.6;
  //printf("player is named %s\n", *meName); //First print
  printf("player is named %s\n", meName); //second print
  printf("player is named %s\n", (*head).name); //third print
  return 0;
}

This code has serious type errors (some of which would be caught by the compiler if you used appropriate warning levels) and does not use best practices (and also won't compile due to typos). Contrast it to

int main(void)
{
  player* head = malloc(sizeof *head);
  char** pname = &head->name;

  head->name = "potato";
  head->name = "Paul";
  head->ppm = 7.6;

  printf("address of player name is %p\n", pname);
  printf("player is named %s\n", *pname);
  printf("ditto: %s\n", head->name);

  return 0;
}
Jim Balter
  • 16,163
  • 3
  • 43
  • 66