Understanding char array[] and string - c++

I am new to programming. I am learning C as my first programming language. I found something strange to understand.
I have learnt that in C we can represent a String as a sequence of characters like this (using a char array):
char status[10] = "Married";
I have learnt that the problem of this approach is that we have to tell the size of the status array during compilation.
But now I have learned we can use a char pointer to denote an string like -
char status[10] = "Married";
char *strPtr;
strPtr = status;
I don't understand it properly. My questions are -
How can I get char at index 4 (that is i in Married) using the strPtr?
In status there is a null character (\0) at the end of the string represented by the char array - M-a-r-r-i-e-d-\0. So by using the null character (\0) we can understand the end of the string. When we use strPtr, how can we understand the end of the string?

char *strPtr;
strPtr = status;
Now your pointer strPtr is pointing to the first character in the array and you can do
int i =0;
while( strPtr[i] != '\0')
{
printf("%c ",strPtr[i]);
i++;
}
*strPtr is called dereferencing the pointer to get the value stored in the location the pointer is pointing to.
Make a note that
strPtr[4] = *(strPtr +4);
Both will get you the value stored at the index 4 of the array.
Note the difference between a pointer and a array name:
----------------------------------
| s | t | r | i | n | g | \0 |
----------------------------------
|
strPtr
status
strPtr ++ will make your pointer point to the next element in the array.
| s | t | r | i | n | g | \0 |
----------------------------------
|
strPtr
Whereas you can't do this for the array name
status++ is not allowed because an array is not a modifiable lvalue.

Good to know:
char status[10] = "Married";
is just syntax sugar for the equivalent:
char status[10]; // allocate 10 Bytes on stack
status[0] = 'M';
status[1] = 'a';
...
status[6]= 'd';
status[7] = '\0'; // same as 0
Nothing more, nothing less.
Also:
char c = status[3];
is exactly the same as
char c = *(status+3);

The expression status[10] is mere syntactic sugar for *(status+10).
The \0 termination is used under the hood to check for the end, if you were implementing some string-handler yourself you could do this too, or you could ignore it and use some other parameter size given with the string, or you could (don't!) choose anything else as the termination symbol.
This isn't just true of char arrays, or 'strings', a C array is just a pointer to a contiguous block of like-typed stuff with a compile-time check that your 'array' subscripts don't go beyond the 'end' specified at time of declaration. With the *(array+offset) notation, you need to check this for yourself.

To get character at index 4 strPtr, you just use strPtr[4] (this also work for status).
To get the end of the string when using strPtr, you need to go through the characters and look for the terminating \0. This is what printf("%s", strPtr) does when it prints the string (and also when it parses the "%s" expression, which is just another string). To find a number of valid characters in the string in C, you use strlen() function. Oh, and make sure you dont do something like this:
char a[3];
strcpy(a, "Hello!");
As this will write 7 bytes into a three-byte memory space, and hence overwrite something you don't want overwritten.

I'm going to make a provocative statement: the way to think of this is that C doesn't have strings. C only has arrays of char. And despite its name, char is actually a numeric type ('A', for example, is just a funny way to write a number, usually 65).
An array of char is not really different from an array of int or any other array of numeric type; it's just that the language offers some extra ways to write objects of type char and arrays of them, and there is a general convention (systematized with functions like strlen) for how to interpret data stored in char arrays as being representations of strings.
char status[10]; // declares an array of `char` of length 10.
char *strPtr; // declare a pointer to `char`
strPtr = status; // make `strPtr` point to the first element of `status`
// Declare an array of 6 `char`, and initialize it.
char hello[6] = {'H', 'e', 'l', 'l', 'o', '\0'};
// Shorthand notation for initializing an array of 6 `char` as above
char world[6] = "World";
// I want to store numeric data in this one!
char other[6] = {0, 1, 2, 3, 4, 5};
// "World" is shorthand for a constant array of 6 `char`. This is
// shorthand for telling the compiler to actually put that array in
// memory someplace, and initialize worldPtr to point to that memory.
const char *worldPtr = "World";
// This does the same thing as above. But it's still a *constant* array.
// You should *never* do this; it should be syntactically illegal to
// make a nonconstant `char*` to point to it. This is only allowed for
// historical reasons.
char *helloPtr = "Hello";

The '\0' at the end of the string is a useless add-on designed for easy or safety. You can tell string last character by using 'sizeof' like this:
char status[] = "Married";
size_t szLastCharstatus = sizeof(status) / sizeof(status[0]) - 2;
char chLastChar = status[szLastCharstatus];
Detailed explanation:
sizeof(status)
Returns the number of bytes array occpuies.
sizeof(status[0])
Returns the number of bytes first element occupies (and so the rest).
The division between those 2 values gives us the number of elements in the array. To access the last element now we need to subtract one 2 times because elements in array count from zero and because the last character in the string is '\0'.
Also note that arrays are not pointers and vice-versa. Arrays have an implicit conversion to pointer of their first element, constant size and their own type. They can be passed around by pointers or by value (using structure hack is required for the second).
Note that I'm using 'size_t' which is a type-def of a variable storing some size of data.

Related

Is NUL set automatically, if I provide an extra element for it in the declaration of the respective char array?

Is '\0' set automatically if I provide an extra element for it, but left it in the initialization string?
Like:
char a[6] = {"Hello"}; // <- Is NUL set here automatically?
I´ve did one experiment with C and C++:`
C:
#include <stdio.h>
int main()
{
char NEWYEAR[16] = {"Happy New Year!"};
printf("%s\n",NEWYEAR);
return 0;
}
Output:
Happy New Year!
C++:
#include <iostream>
int main()
{
char NEWYEAR[16] = {"Happy New Year!"};
std::cout << NEWYEAR << std::endl;
return 0;
}
Output:
Happy New Year!
The compilers did not threw an error or warning and the result is as desired. So it might seem to work correctly. But is that really true?
Is everything correct by doing so?
Is this maybe bad programming style?
Does this cause any issues?
It is more complex than that
char a[6] = "Hello";
will initialize the array of characters to Hello\0, because Hello has an implicit terminating zero.
char a[6] = "Hello\0";
would be valid in C, but invalid in C++ because the literal is 7 characters long, having both an implicit terminator and explicit embedded null character. C allows the literal to drop the implicit terminator. C11 6.7.9p14:
An array of character type may be initialized by a character string literal or UTF-8 string literal, optionally enclosed in braces. Successive bytes of the string literal (including the terminating null character if there is room or if the array is of unknown size) initialize the elements of the array.
char a[5] = "Hello";
would be valid C, resulting in a char array that does not contain a zero-terminated string. It is invalid in C++.
(emphasis mine). It means that the implicit terminating null is optionally added, if there is room in the array, but it does not need to.
And
char a[4] = "Hello";
in C would bring the literal Hell, because while it is a constraint violation in C (C11 6.7.9p2),
No initializer shall attempt to provide a value for an object not contained within the entity being initialized.
attempting to initialize more elements than there are items in a list usually just generates a warning in many compilers and is then often ignored by programmers. The paragraph 14 does not have an exception for anything other besides the implicit terminator.
And lastly
char a[7] = "Hello";
in both C and C++ would result in a character array of 7 elements containing the characters Hello\0\0, because in an array having an initializer, the elements not explicitly initialized by the initializer will be default-initialized as if initialized by literal 0. In this case the first 6 elements will be initialized explicitly and the 7th implicitly.
Given the possibility of silently truncating the terminator in C, it is better to just omit the array size and write
char a[] = "Hello";
This will declare a as array of 6 elements, just like char a[6] = "Hello";, but you cannot mistype the array size.
If there's space for the null-terminator then it will be added.
In C (but not C++) if the size of the array is the length of the string except the null-terminator, then the null-terminator will not be added. So e.g.
char a[5] = "Hello";
is valid, but there won't be a null-terminator in the array.
It's not valid to provide a smaller size than the string length.

pointer to string and char catch 22

I'm studying on pointers and I'm stuck when I see char *p[10]. Because something is misunderstood. Can someone explain step-by-step and blow-by-blow why my logic is wrong and what the mistakes are and where did I think wrong and how should I think. Because I want to learn exactly. Also what about int *p[10]; ? Besides, for example x is a pointer to char but just char not chars. But how come char *x = "possible";
I think above one should be right but, I have seen for char *name[] = { "no month","jan","feb" }; I am really confused.
Your char *p[10] diagram shows an array where each element points to a character.
You could construct it like this:
char f = 'f';
char i = 'i';
char l1 = 'l';
char l2 = 'l';
char a1 = 'a';
char r1 = 'r';
char r2 = 'r';
char a2 = 'a';
char y = 'y';
char nul = '\0';
char *p[10] = { &f, &i, &l1, &l2, &a1, &r1, &r2, &a2, &y, &nul };
This is very different from the array
char p[10] = {'f', 'i', 'l', 'l', 'a', 'r', 'r', 'a', 'y', '\0'};
or
char p[10] = "fillarray";
which are arrays of characters, not pointers.
A pointer can equally well point to the first element of an array, as you've probably seen in constructions like
const char *p = "fillarray";
where p holds the address of the first element of an array defined by the literal.
This works because an array can decay into a pointer to its first element.
The same thing happens if you make an array of pointers:
/* Each element is a pointer to the first element of the corresponding string in the initialiser. */
const char *name[] = { "no month","jan","feb" };
You would get the same results with
const char* name[3];
name[0] = "no month";
name[1] = "jan";
name[2] = "feb";
char c = 'a';
Here, c is a char, typically a single byte of ASCII encoded data.
char* ptr = &c;
ptr is a char pointer. In C, all it does is point to a memory location and doesn't make any guarantees about what is at that location. You could use a char* to pass a char to a function to allow the function to allow the function to make changes to that char (pass by reference).
A common C convention is for a char* to point to a memory location where several characters are stored in sequence followed by the null character \0. This convention is called a C string:
char const* cstr = "hello";
cstr points to a block of memory 6 bytes long, ending with a null character. The data itself cannot be modified, though the pointer can be changed to point to something else.
An array of chars looks similar, but behaves slightly differently.
char arr[] = "hello";
Here arr IS a memory block of 6 chars. Since arr represents the memory itself, it cannot be changed to point to another location. The data can be modified though.
Now,
char const* name[] = { "Jan", " Feb"..., "Dec"};
is an array of pointer to characters.
name is a block of memory, each containing a pointer to a null-terminated string.
In the diagram, I think string* was accidentally used instead of char*. The difference between the left and the right, is not a technical difference really, but a difference in the way a char* is used. On the left each char* points to a single character, whereas in the one on the right, each char* points to a null-terminated block of characters.
Both are right.
A pointer in C or C++ may point either to a single item (a single char) or to the first in an array of items (char[]).
So a char *p[10]; definition may point to 10 single characters or 10 arrays (i.e. 10 strings).
Let’s go back to basics.
First, char *p is simply a pointer. p contains nothing more than a memory address. That memory address can point to anything, anywhere. By convention, we have always used NULL (or, I hate this method, assigning it to zero – yeah, they are the same “thing”, but NULL has traditionally been used in conjunction with pointers, so when you’re eyes flit across the code, you see NULL – you think “pointer”).
Anyway, that memory address being pointed to can contain anything. So, to use within the language, we type it, in this case it is a pointer to a character (char *p). This can be overridden by type casting, but that’s for a later time.
Second, we know anytime we see p[10], that we are dealing with an array. Again, the array can be an array of characters, an array of ints, etc. – but it’s still an array.
Your example: char *p[10], is then nothing more than an array of 10 character pointers. Nothing more, nothing less. Your problem comes in because you are trying to force the “string” concept onto this. There ain’t no strings in C. There ain’t no objects in C. The concept of a NULL-terminated string can most certainly be used. But a “string” in C is nothing more than an array of characters, terminated by a NULL (or, if you use some of the appropriate functions, you can use a specific number of characters – strncpy instead of strcpy, etc.). But, for all its appearance, and apparent use, there are no strings in C. They are nothing more than arrays of characters, with a few supporting functions that happen to stop going through the array when a NULL is encountered.
So – char a[10] – is simply an array of characters that is 10 characters long. You can fill it with any characters you wish. If one of those is the NULL character, then that terminates what is typically called a “C-style string”. There are functions that support this type of character array (i.e. “string”), but it is still a use of a character array.
Your confusion comes in because you are trying to mix C++ string objects, and forcing that concept onto C arrays of characters. As ugoren noted – your examples are both correct – because you are dealing with arrays of character pointers, NOT strings. Again, putting a NULL somewhere in that character array is happily supported by several C functions that give you the ability to work with a “string-like” concept – but they are not truly strings. Unless of course, you want to phrase it that a string is nothing more than one character following another – an array.

Difference between char* and char[] (2) [duplicate]

This question already has answers here:
Struggling to get number of chars in char* [duplicate]
(2 answers)
What does sizeof(&array) return?
(4 answers)
Closed 9 years ago.
I tried to calculate the length of a character array in the following ways:
char *s="abcde";
int n=sizeof(s)/sizeof(s[0]);
cout<<n;
n comes out to be constant value 4, no matter how long the string is. Whereas had i declared the array as
char s[]="abc";
int n=sizeof(s)/sizeof(s[0]);
cout<<n;
The output still remains 4. I understand that in the second case, it includes the concluding character '\0', hence the output.
The only thing i didn't understand is why i get a constant output in first case.
With char *s you make a pointer s that points to some other memory. With char s[] = ... you make a an array of N characters.
Maybe it's easier if you look at it like this:
For the pointer version, char *s = "abcde", it will be something like
+---+ +-----------+
| s | ---> | "abcde\n" |
+---+ +-----------+
While for the case with the array, char s[] = "abc" it will be like
+---------+
| "abc\0" |
+---------+
This should make it easy to see why you can't use sizeof on the pointer, as it returns the size of the pointer and not what it points to. I have also added the string terminator that exists for all string literals, and this is why you get the size 4 for the array, it actually is four characters.
char *s is a pointer to either a char, or a sequence of char. On a 32bit architecture it will be 4 bytes wide, so sizeof(s) will be 4. A single character is (usually) 1 byte, so sizeof(s[0]) will be 1. Therefore, n will be 0.
When you use a char[] type the compiler treats it a a fixed length sequence, it's just working out how long the sequence will be for you, in your case it's 4 characters long. However, if you had:
char s[]="Hello, world";
int n=sizeof(s)/sizeof(s[0]);
Then n would be 13, as there the 12 character you entered, plus the null terminator at the end.
In this code snippet
char *s="abcde";
int n=sizeof(s)/sizeof(s[0]);
cout<<n;
s is a pointer. So sizeof( s ) is equal to the size of pointers in the system. In your system the size of a pointer is equal to 4. As the type of s[0] is char then its size equal to 1 and you get value 4.
In the second code snippet
char s[]="abc";
int n=sizeof(s)/sizeof(s[0]);
cout<<n;
s is an array. Its size is determined by the size of the initializer. As string literal "abc" has size equal to 4 because the terminating zero is also counted then the size of array s is 4. If you for example would write
char s[]="abcde";
int n=sizeof(s)/sizeof(s[0]);
cout<<n;
then the size of the string literal is equal to 6 and correspondingly the size of the array will be also equal to 6.
You could the same code rewrite the following way
char s[6]="abcde";
int n=sizeof(s)/sizeof(s[0]);
cout<<n;
If you write this code as
char s[10]="abcde";
int n=sizeof(s)/sizeof(s[0]);
cout<<n;
then the size of the array will be equal to 10 though the size of the string literal is equal to 6. All other elements of the array that have no initializer will be zero-initialized. That is the array would look as
[a][b][c][d][e]['\0']['\0']['\0']['\0']['\0']
In the first case, s is an object of type char*. sizeof(s) evaluates to the size of this "pointer to char" object, which is 4 (in your execution environment), not the length of the string that s points to (which strlen(s) evaluates to).

how to understand char * ch="123"?

How should I understand char * ch="123"?
'1' is a char, so I can use:
char x = '1';
char *pt = &x;
But how do I understand char *pt="123"? Why can the char *pt point to string?
Is pt's value the first address value for "123"? If so, how do I get the length for the string pointed to by pt?
That is actually a really good question, and it is the consequence of several oddities in the C language:
1: A pointer to a char (char*) can of course also point to a specific char in an array of chars. That is what pointer arithmetic relies on:
// create an array of three chars
char arr[3] = { 'a', 'b', 'c'};
// point to the first char in the array
char* ptr = &arr[0]
// point to the third char in the array
char* ptr = &arr[2]
2: A string literal ("foo") is actually not a string as such, but simply an array of chars, followed by a null byte. (So "foo" is actually equivalent to the array {'f', 'o', 'o', '\0'})
3: In C, arrays "decay" into pointers to the first element. (This is why many people incorrectly says that "there is no difference between arrays and pointers in C"). That is, when you try to assign an array to a pointer object, it sets the pointer to point to the first element of the array. So given the array arr declared above, you can do char* ptr = arr, and it means the same as char* ptr = &arr[0].
4: In every other case, syntax like this would make the pointer point to an rvalue (loosely speaking, a temporary object, which you can't take the address of), which is generally illegal. (You can't do int* ptr = &42). But when you define a string literal (such as "foo"), it does not create an rvalue. Instead, it creates the char array with static storage. You're creating a static object, which is created when the program is loaded, and of course a pointer can safely point to that.
5: String literals are actually required to be marked as const (because they are static and read-only), but because early versions of C did not have the const keyword, you are allowed to omit the const specifier (at least prior to C++11), to avoid breaking old code (but you still have to treat the variable as read-only).
So char* ch = "123" really means:
write the char array {'1', '2', '3', '\0'} into the static section of the executable (so that when the program is loaded into memory, this variable is created in a read-only section of memory)
when this line of code is executed, create a pointer which points to the first element of this array
As a bonus fun fact, this differs from char ch[] = "123";, which instead means
write the char array {'1', '2', '3', '\0'} into the static section of the executable (so that when the program is loaded into memory, this variable is created in a read-only section of memory)
when this line of code is executed, create an array on the stack which contains a copy of this statically allocated array.
char* ptr = "123"; is compatible and almost equivalent to char ptr[] = { '1', '2', '3', '\0' }; (see http://ideone.com/rFOk3R).
In C a pointer can point to one value or an array of contiguous values. C++ inherited this.
So a string is just an array of character (char) ended by a '\0'. And a pointer to char can point to an array of char.
The length is given by the number of character between the begining and the terminal '\0'. Exemple of C strlen giving you the length of the string:
size_t strlen(const char * str)
{
const char *s;
for (s = str; *s; ++s) {}
return(s - str);
}
An yes it fails horribly if there is no '\0' at the end.
A string literal is an array of N const char where N is the length of the literal including the implicit NUL terminator. It has static storage duration and it's implementation defined where it is stored. From here on, it's the same a with a normal array - it decays to a pointer to its first character - that's a const char*. What you have there is not legal (not anymore since onset of C++11 standard) in C++, it should be const char* ch = "123";.
You can get the length of a literal with sizeof operator. Once it decays to a pointer, though, you need to iterate through it and find the terminator (that's what strlen function does).
So, with a const char* ch; you get a pointer to a constant character type that can point to a single character, or to the start of an array of characters (or anywhere between the start and the end). The array can be dynamically, autimatically or statically allocated and can be mutable or not.
In something like char ch[] = "text"; you have an array of characters. This is syntatic sugar for a normal array initializer (as in char ch[] = {'t','e','x','t','\0'}; but note that the literal will still be loaded at the start of the program). What hapens here is:
an array with automatic storage duration is allocated
its size is deduced from the size of the literal by the compiler
the contents of the literal are copied to the array
As a result, you have a region of storage that you can use at will (unlike literals, which must not be written into).
There are no strings in C, but there are pointers to characters.
*pt is indeed not pointing to a string, but to a single characters (the '1').
However, some functions take char* as argument assume that the byte on the address following the address that their argument points to, is set to 0 if they are not to operate on it.
In your example, if you tried using pt on a function which expects a "null terminated string" (basically, which expects that it will encounter a byte with a value of 0 when it should stop processing data) you will run into a segmentation fault, as x='1' gives x the ascii value of the 1 character, but nothing more, whereas char* pt="123" gives pt the value of the address of 1, but also puts into that memory, the bytes containing ascii values of 1, 2,3 followed by a byte with a value of 0 (zero).
So the memory (in a 8 bit machine) may look like this:
Address = Content (0x31 is the Ascii code for the character 1 (one))
0xa0 = 0x31
0xa1 = 0x32
0xa2 = 0x33
0xa3 = 0x00
Let's suppose that you in the same machine char* otherString = malloc(4),suppose that malloc returns a value of 0xb0, which is now the value of otherString, and we wanted to copy our "pt" (which would have a value of 0xa0) into otherString, the strcpy call would look like so:
strcpy( otherString, pt );
The same as
strcpy( 0xb0, 0x0a );
strcpy would then take the value of address 0xa0 and copy it into 0xb0, it would increment it's pointers to "pt" to 0xa1, check if 0xa1 is zero, if it is not zero, it would increment it's pointer to "otherString" and copy 0xa1 into 0xb1, and so on, until it's "pt" pointer is 0xa3, in this case, it will return as it detected that the end of the "string" has been reached.
This is of cause, not 100% how it goes on, and it could be implemented in many different ways.
Here is one http://fossies.org/dox/glibc-2.18/strcpy_8c_source.html
A pointer to an array?
A pointer points to only one memory address. The phrase that a pointer points to an array is only used in a loose sense---a pointer cannot really store multiple addresses at the same time.
In your example, char *ch="123", the pointer ch is really pointing to the first byte only. You can write code like the following, and it will make perfect sense:
char *ch = new char [1024];
sprintf (ch, "Hello");
delete [] ch;
char x = '1';
ch = &x;
Please note the use of the pointer ch to point to both the memory allocated by new char [1024] line as well as the address of the variable x, while still being the same pointer type.
C-style strings are null terminated
Strings in C used to be null terminated, i.e., a special '\0' was added to the end of the string and assumed to be there for all char * based functions (such as strlen and printf) This way, you can determine the length of the string by starting at the first byte and continue till you find the byte containing 0x00.
A verbose, sample implementation of anstrlen style function would be
int my_strlen (const char *startAddress)
{
int count = 0;
char *ptr = startAddress;
while (*ptr != 0)
{
++count;
++ptr;
}
return count;
}
char* pt = "123"; does two things:
1. creates the string literal "123" in ROM (this is usually in .text section)
2. creates a char* which is assigned the beginning of memory location where the string is located.
because of this operations like pt[1] = '2'; are illegal as you would be attempting to write to ROM memory.
But you can assign the pointer to some other memory location without any problems.

What is a char*?

Why do we need the *?
char* test = "testing";
From what I understood, we only apply * onto addresses.
This is a char:
char c = 't';
It can only hold one character!
This is a C-string:
char s[] = "test";
It can hold multiple characters. Another way to write the above is:
char s[] = {'t', 'e', 's', 't', 0};
The 0 at the end is called the NUL terminator. It denotes the end of a C-string.
A char* stores the starting memory location of a C-string.1 For example, we can use it to refer to the same array s that we defined above. We do this by setting our char* to the memory location of the first element of s:
char* p = &(s[0]);
The & operator gives us the memory location of s[0].
Here is a shorter way to write the above:
char* p = s;
Notice:
*(p + 0) == 't'
*(p + 1) == 'e'
*(p + 2) == 's'
*(p + 3) == 't'
*(p + 4) == 0 // NUL
Or, alternatively:
p[0] == 't'
p[1] == 'e'
p[2] == 's'
p[3] == 't'
p[4] == 0 // NUL
Another common usage of char* is to refer to the memory location of a string literal:
const char* myStringLiteral = "test";
Warning: This string literal should not be changed at runtime. We use const to warn the programmer (and compiler) not to modify myStringLiteral in the following illegal manner:
myStringLiteral[0] = 'b'; // Illegal! Do not do this for const char*!
This is different from the array s above, which we are allowed to modify. This is because the string literal "test" is automatically copied into the array at initialization phase. But with myStringLiteral, no such copying occurs. (Where would we copy to, anyways? There's no array to hold our data... just a lonely char*!)
1 Technical note: char* merely stores a memory location to things of type char. It can certainly refer to just a single char. However, it is much more common to use char* to refer to C-strings, which are NUL-terminated character sequences, as shown above.
The char type can only represent a single character. When you have a sequence of characters, they are piled next to each other in memory, and the location of the first character in that sequence is returned (assigned to test). Test is nothing more than a pointer to the memory location of the first character in "testing", saying that the type it points to is a char.
You can do one of two things:
char *test = "testing";
or:
char test[] = "testing";
Or, a few variations on those themes like:
char const *test = "testing";
I mention this primarily because it's the one you usually really want.
The bottom line, however, is that char x; will only define a single character. If you want a string of characters, you have to define an array of char or a pointer to char (which you'll initialize with a string literal, as above, more often than not).
There are real differences between the first two options though. char *test=... defines a pointer named test, which is initialized to point to a string literal. The string literal itself is allocated statically (typically right along with the code for your program), and you're not supposed to (attempt to) modify it -- thus the preference for char const *.
The char test[] = .. allocates an array. If it's a global, it's pretty similar to the previous except that it does not allocate a separate space for the pointer to the string literal -- rather, test becomes the name attached to the string literal itself.
If you do this as a local variable, test will still refer directly to the string literal - but since it's a local variable, it allocates "auto" storage (typically on the stack), which gets initialized (usually from a normal, statically allocated string literal) on every entry to the block/scope where it's defined.
The latter versions (with an array of char) can act deceptively similar to a pointer, because the name of an array will decay to the address of the beginning of the array anytime you pass it to a function. There are differences though. You can modify the array, but modifying a string literal gives undefined behavior. Conversely, you can change the pointer to point at some other chars, so something like:
char *test = "testing";
if (whatever)
test = "not testing any more";
...is perfectly fine, but trying to do the same with an array won't work (arrays aren't assignable).
The main thing people forgot to mention is that "testing" is an array of chars in memory, there's no such thing as primitive string type in c++. Therefore as with any other array, you can't reference it as if it is an element.
char* represents the address of the beginning of the contiguous block of memory of char's. You need it as you are not using a single char variable you are addressing a whole array of char's
When accessing this, functions will take the address of the first char and step through the memory. This is possible as arrays use contiguous memory (i.e. all of the memory is consecutive in memory).
Hope this clears things up! :)
Using a * says that this variable points to a location in memory. In this case, it is pointing to the location of the string "testing". With a char pointer, you are not limited to just single characters, because now you have more space available to you.
In C a array is represented by a pointer to the first element in it.