tags:

views:

914

answers:

12

Hi,

I know that similar questions are posted in SO, but I thought I can ask this in the following the context:

char amessage[] = "now is the time";
char *pmessage = "now is the time";

I read from The C Programming Language, 2nd Edition that the above two statements don't do the same thing.

I always thought that an array is an convenient way to manipulate pointers to store some data, but this is clearly not the case... what are the "non-trivial" differences between arrays and pointers in C?

+1  A: 

Watch this video for a pretty good explanation on pointers - Binky pointer fun

Jeff
+1  A: 

Microsoft has a good article on the subject. KB44463

fpmurphy
+13  A: 

True, but it's a subtle difference. Essentially, the former:

char amessage[] = "now is the time";

Defines an array whose members live in the current scope's stack space, whereas:

char *pmessage = "now is the time";

Defines a pointer that lives in the current scope's stack space, but that references memory elsewhere (in this one, "now is the time" is stored elsewhere in memory, commonly a string table).

Also, note that because the data belonging to the second definition (the explicit pointer) is not stored in the current scope's stack space, it is unspecified exactly where it will be stored and should not be modified.

Edit: As pointed out by Mark, GMan, and Pavel, there is also a difference when the address-of operator is used on either of these variables. For instance, &pmessage returns a pointer of type char**, or a pointer to a pointer to chars, whereas &amessage returns a pointer of type char(*)[16], or a pointer to an array of 16 chars (which, like a char**, needs to be dereferenced twice as litb points out).

Walt W
Mark Ransom
Walt W
GMan
Pavel Minaev
Walt W
Walt W
Wouldn't both of these (because they are being assigned to a string literal) just point to that literal where it was loaded by the program? I don't think either makes a copy on the stack, but I could easily be wrong--it seems like at least the array SHOULD make a copy...
Bill K
@Bill: Nah, cause the array version is actually just a shortcut for array instantiation. So the array is allocated in the stack, and then loaded with the string's data.
Walt W
Johannes Schaub - litb
To get from ``. One will evaluate to the array, and the another one will evaluate to the character.
Johannes Schaub - litb
Walt W
er, check the edit now, is that more correct?
Walt W
This looks like a community wiki entry now...
Adam W
@Adam: As you wish :)
Walt W
@Walt W, yeah the addresses in both cases are the same, just the type differ. However, technically there is no conversion applied to `amessage` prior to applying the address-of operator (likewise, with the `sizeof` operator)
Johannes Schaub - litb
+6  A: 

An array contains the elements. A pointer points to them.

The first is a short form of saying

char amessage[16];
amessage[0] = 'n';
amessage[1] = 'o';
...
amessage[15] = '\0';

That is, it is an array that contains all the characters. The special initialization initializes it for you, and determines it size automatically. The array elements are modifiable - you may overwrite characters in it.

The second form is a pointer, that just points to the characters. It stores the characters not directly. Since the array is a string literal, you cannot take the pointer and write to where it points

char *pmessage = "now is the time";
*pmessage = 'p'; /* undefined behavior! */

This code would probably crash on your box. But it may do anything it likes, because its behavior is undefined.

Johannes Schaub - litb
+2  A: 

The second one allocates the string in some read-only section of the ELF. Try the following:

#include <stdio.h>

int main(char argc, char** argv) {
    char amessage[] = "now is the time";
    char *pmessage = "now is the time";

    amessage[3] = 'S';
    printf("%s\n",amessage);

    pmessage[3] = 'S';
    printf("%s\n",pmessage);
}

and you will get a segfault on the second assignment (pmessage[3]='S').

Itamar Benzaken
That's a very implementation-centric explanation. What if it's a popular compiler that doesn't target ELF (e.g. VC++)?
Pavel Minaev
You *might* get a segfault. That's undefined.
Tadeusz A. Kadłubowski
+3  A: 

Along with the memory for the string "now is the time" being allocated in two different places, you should also keep in mind that the array name acts as a pointer value as opposed to a pointer variable which pmessage is. The main difference being that the pointer variable can be modified to point somewhere else and the array cannot.

char arr[] = "now is the time";
char *pchar = "later is the time";

char arr2[] = "Another String";

pchar = arr2; //Ok, pchar now points at "Another String"

arr = arr2; //Compiler Error! The array name can be used as a pointer VALUE
            //not a pointer VARIABLE
Graphics Noob
A: 

an array is a const pointer. You cannot update its value and make it point anywhere else. While for a pointer you can do.

mkamthan
Arrays are *not* pointers, const or otherwise. In many contexts, the type of an array identifier will implicitly be converted from "N-element array of T" to "pointer to T", but this does not make an array a pointer.
John Bode
agreed.. mistake admitted.. thanks for the clarification John.
mkamthan
+1  A: 

I can't add usefully to the other answers, but I will remark that in Deep C Secrets, Peter van der Linden covers this example in detail. If you are asking these kinds of questions I think you will love this book.


P.S. You can assign a new value to pmessage. You can't assign a new value to amessage; it is immutable.

Norman Ramsey
A: 

If an array is defined so that it's size is available at declaration time, sizeof(p)/sizeof(type-of-array) will return the number of elements in the array.

Tamás Szelei
+1  A: 

The first form (amessage) defines a variable (an array) that lives at the same location as the string "now is the time".

The second form (pmessage) defines a variable (a pointer) that lives in a different location than the string "now is the time".

Try this program out:

#include <inttypes.h>
#include <stdio.h>

int main (int argc, char *argv [])
{
     char  amessage [] = "now is the time";
     char *pmessage    = "now is the time";

     printf("&amessage   : %#016"PRIxPTR"\n", (uintptr_t)&amessage);
     printf("&amessage[0]: %#016"PRIxPTR"\n", (uintptr_t)&amessage[0]);
     printf("&pmessage   : %#016"PRIxPTR"\n", (uintptr_t)&pmessage);
     printf("&pmessage[0]: %#016"PRIxPTR"\n", (uintptr_t)&pmessage[0]);

     printf("&\"now is the time\": %#016"PRIxPTR"\n",
            (uintptr_t)&"now is the time");

     return 0;
}

You'll see that while &amessage is equal to &amessage[0], this is not true for &pmessage and &pmessage[0]. In fact, you'll see that the string stored in amessage lives on the stack, while the string pointed at by pmessage lives elsewhere.

Edit: litb points out that the address of the string stored in amessage is not the same as the address of the string literal that it is initialized from. I should have been more specific when mentioning which copy of the string is located at the same address as the address of amessage.

I've modified the program to illustrate litb's point: the last printf shows the address of the string literal. If your compiler does "string pooling" then there will be only one copy of the string "now is the time" in the executable's data segment -- and you'll see that its address is not the same as the address of amessage. This is because amessage gets a copy of the string when it is initialized.

In the end, the point is that amessage stores the string in its own memory (on the stack), while pmessage points to the string which is stored elsewhere.

Dan Moulding
That's wrong. The array holds a copy of the string literal - it's not the same array.
Johannes Schaub - litb
Maybe I was a bit ambiguous. Let me clarify: there is a variable named amessage. There is a string whose contents are "now is the time". The address of amessage is the same as the address of the "n" in that string. That's the relationship I'm talking about. Granted, there may be other copies of "now is the time" floating about in the program's address space, but I'm talking about the copy that's stored in the array.
Dan Moulding
Now it makes much sense to me. Thanks for the further explanation!
Johannes Schaub - litb
A: 

For this line: char amessage[] = "now is the time";

the compiler will evaluate uses of amessage as a pointer to the start of the array holding the characters "now is the time". The compiler allocates memory for "now is the time" and initializes it with the string "now is the time". You know where that message is stored because amessage always refers to the start of that message. amessage may not be given a new value- it is not a variable, it is the name of the string "now is the time".

This line: char *pmessage = "now is the time";

declares a variable, pmessage which is initialized (given an initial value) of the starting address of the string "now is the time". Unlike amessage, pmessage can be given a new value. In this case, as in the previous case, the compiler also stores "now is the time" elsewhere in memory. For example, this will cause pmessage to point to the 'i' which begins "is the time". pmessage = pmessage + 4;

Ron Jacobs
+5  A: 

Here's a hypothetical memory map, showing the results of the two declarations:

                0x00  0x01  0x02  0x03  0x04  0x05  0x06  0x07
    0x00008000:  'n'   'o'   'w'   ' '   'i'   's'   ' '   't'
    0x00008008:  'h'   'e'   ' '   't'   'i'   'm'   'e'  '\0'
        ...
amessage:
    0x00500000:  'n'   'o'   'w'   ' '   'i'   's'   ' '   't'
    0x00500008:  'h'   'e'   ' '   't'   'i'   'm'   'e'  '\0'
pmessage:
    0x00500010:  0x00  0x00  0x80  0x00

The string literal "now is the time" is stored as a 16-element array of char at memory address 0x00008000. This memory may not be writable; it's best to assume that it's not. You should never attempt to modify the contents of a string literal.

The declaration

char amessage[] = "now is the time";

allocates a 16-element array of char at memory address 0x00500000 and copies the contents of the string literal to it. This memory is writable; you can change the contents of amessage to your heart's content:

strcpy(amessage, "the time is now");

The declaration

char *pmessage = "now is the time";

allocates a single pointer to char at memory address 0x00500010 and copies the address of the string literal to it.

Since pmessage points to the string literal, it should not be used as an argument to functions that need to modify the string contents:

strcpy(amessage, pmessage); /* OKAY */
strcpy(pmessage, amessage); /* NOT OKAY */
strtok(amessage, " ");      /* OKAY */
strtok(pmessage, " ");      /* NOT OKAY */
scanf("%s", amessage);      /* OKAY */
scanf("%s", pmessage);      /* NOT OKAY */

und so weiter. If you changed pmessage to point to amessage:

pmessage = amessage;

then it can be used everywhere amessage can be used.

John Bode
@John Bode, awesome answer :).
mahesh