the accepted answer is good, but not quite complete.
char * test = "abcdefghijklmnopqrstuvwxyz";
A string literal refers to an object of an anonymous array of type char[N] with a static storage duration (which means that it exists for the entire execution of the program), where N is the length of the string plus one for the terminating '\0' . This object is not const , but any attempt to modify it has undefined behavior. (An implementation can make string literals writable if it chooses, but most modern compilers do not.)
The declaration above creates such an anonymous object of type char[27] and uses the address of this first element of the element to initialize test . Thus, an assignment of type test[5] = 'x' tries to change the array and has undefined behavior; usually this will crash your program. (Initialization uses an address because a literal is an array type expression that is implicitly converted in most contexts to a pointer to the first element of the array.)
Note that in C ++, string literals are actually const , and the above declaration would be illegal. In C or C ++, it is better to declare test as a pointer to const char :
const char *test = "abcdefghijklmnopqrstuvwxyz";
therefore, the compiler will warn you if you try to modify the array using test .
(String literals are not const for historical reasons. Before the ANSI C standard of 1989, the keyword const did not exist. The requirement to use it in declarations like yours for safe code, but that would require that the existing code be modified, is that a committee ANSI was trying to avoid. You have to pretend that string literals are const , even if it isn't. If you use gcc, the -Wwrite-strings option -Wwrite-strings compiler to treat string literals as const - which makes gcc a mismatch.)
If you want to change the line referenced by test , you can define it as follows:
char test[] = "abcdefghijklmnopqrstuvwxyz";
The compiler looks at the initializer to determine how big the test should be. In this case, test will be of type char[27] . The string literal still refers to an anonymous read-only array object, but its value is copied to test . (The string literal in the initializer used to initialize the array object is one of the contexts in which the array does not "decay" into a pointer and the rest are the operand of a unary & or sizeof .) Since there are no more references to an anonymous array, the compiler can optimize it.
In this case, test itself is an array containing 26 characters that you specified, plus the terminator '\0' . The lifetime of this array depends on where test declared, which may or may not matter. For example, if you do this:
char *func(void) { char test[] = "abcdefghijklmnopqrstuvwxyz"; return test; }
the caller will receive a pointer to something that no longer exists. If you need to access the array outside the scope in which test is defined, you can define it as static , or you can select it with malloc :
char *test = malloc(27); if (test == NULL) { } strcpy(test, "abcdefghijklmnopqrstuvwxyz";
so that the array continues to exist until you call free() . The non-standard strdup() function does this (it is defined by POSIX, but not ISO C).
Note that test can be a pointer or an array, depending on how you declare it. If you pass test to a string function or to any function that takes char* , it doesn't matter, but something like sizeof test will behave differently depending on whether test pointer or an array.
comp.lang.c FAQ is excellent. Section 8 covers characters and lines, and question 8.5 points to question 1.32, which addresses your specific question. Section 6 covers the often confusing relationship between arrays and pointers.