One thing that always confused me , the character pointer.
It's after long four years that I am again lingering into c.
Take for example the mentioned case .Why does char pointer behave in this way ? How can we directly address the contents of the pointee when it points to nothing or is it like char pointer stores stuffs other than addresses !
#include <stdio.h>
#include <stdlib.h>
int main()
{
char* charPtr="I cant understand why";
int* intPtr=60;
printf("%d\n", intPtr); //displays 60
printf("%p\n", intPtr); // displays the hex value of 60
printf("%s\n", charPtr); // displays the wh0le string
printf("%p\n", charPtr); // displays the start address of the string
return 0;
}
Next the int pointer , How can it accept the value 60 and where does it get stored ?
leaving aside the char pointer and malloc ,I thought the basic idea of the pointer was to get an address to point to !
why does these cases
*intptr = 60 ; // should be setting the pointee's value to 60
intptr = 60 ; // sets the address
throw compilation error while
int* intPtr=60;
sneaked in without getting an address ( or is 60 taken as the address ,if so why is this not acceptable not in the former case)of the the pointee !
I guess I am missing something here but hey ! Guess what ? they told me to search in SO !
EDIT : Giving the address pointed to by the char pointer to an int pointer also throws in no error !
int8_t* intPtr= (int8_t*)0x80485c8 ; // works without casting too ! I guess addresses are acceptable.
Dereferencing it will give a value equivalent to the first I of the string .Is this a good practise or there exists any other explanation to this leaving out thier byte bit size allocation such as an int can hold a char and so.. ?
As hmjd pointed out the ' initialisation syntax ' is the problem ! I have no problem writing my own code but trouble arises when modifying someone's code !
How can we directly address the contents of the pointee when it points to nothing or is it like char pointer stores stuffs other than addresses !
I think the confusion is the initialisation syntax. This:
char* charPtr="I cant understand why";
does not dereference charPtr. It is equivalent to:
char* charPtr;
charPtr = "I cant understand why";
Both code snippets store the address of the string literal "I cant understand why" to the charPtr. There is no dereferencing of a pointer that points to nothing occurring. A pointer variable, of any type, can store an address only.
This:
int* intPtr=60;
stores an address of 60 in intPtr: no int assignment or deferencing is occuring. No int variable exists at this point. The compiler should have emitted a warning at this line. Any attempt to deference intPtr will most likely cause a crash.
In C, a string literal like "I can't understand why" is stored as an array of char such that the memory is available over the lifetime of the program (all addresses are pulled out of thin air and are not meant to represent any specific platform or architecture):
Item Address 0x00 0x01 0x02 0x03
----- ------- ---- ---- ---- ----
"I..." 0x00080000 'I' ' ' 'c' 'a'
0x00008004 'n' ''' 't' ' '
0x00008008 'u' 'n' 'd' 'e'
0x0000800C 'r' 's' 't' 'a'
0x00008010 'n' 'd' ' ' 'w'
0x00008014 'h' 'y' 0x00 0x??
The string literal is also an array expression, and in most contexts an expression of type "N-element array of T" will be converted to type "pointer to T", and its value will be the address of the first element of the array (the exceptions are when the array expression is an operand of the sizeof or unary & operators, or is a string literal being used to initialize an array in a declaration).
So when you write
char* charPtr = "I can't understand why";
you're copying the address of the string literal to charPtr:
Item Address 0x00 0x01 0x02 0x03
---- ------- ---- ---- ---- ----
charPtr 0xffbe4000 0x00 0x08 0x00 0x00
Note that if the declaration had been
char str[] = "I can't understand why";
str would have been allocated as an array of char long enough to hold the string, and the contents of the string would have been copied to it:
Item Address 0x00 0x01 0x02 0x03
----- ------- ---- ---- ---- ----
str 0xffbe4000 'I' ' ' 'c' 'a'
0xffbe4004 'n' ''' 't' ' '
0xffbe4008 'u' 'n' 'd' 'e'
0xffbe400C 'r' 's' 't' 'a'
0xffbe4010 'n' 'd' ' ' 'w'
0xffbe4014 'h' 'y' 0x00 0x??
When you write
int* intPtr = 60;
you're initializing the pointer value with 60, not setting it to point to an anonymous integer with the value 60:
Item Address 0x00 0x01 0x02 0x03
---- ------- ---- ---- ---- ----
intPtr 0xffbe4004 0x00 0x00 0x00 0x3C
Address 60 is most likely not a valid address, so attempting to dereference intPtr would most likely lead to undefined behavior.
Had you written something like
int x = 60;
int *intPtr = &x;
then you'd have a situation like this:
Item Address 0x00 0x01 0x02 0x03
---- ------- ---- ---- ---- ----
x 0xffbe4004 0x00 0x00 0x00 0x3C
intPtr 0xffbe4008 0xff 0xbe 0x40 0x04
In this case, the value of intPtr is the address of x.
Finally, note that initialization and assignment are not the same thing.
T *x = value;
does not dereference x and assign value to the result; it assigns value directly to x. The type of value is treated as T *. Note that you should be getting warnings on
int *intPtr = 60;
along the lines of "making pointer from integer without cast".
When you write :
char* charPtr = "I can't understand why";
This means that the base address of the string "I can't understand why" is being assigned to
charPtr because the string literal also is a pointer to that string.
It can be viewed as :
This means that , in charPtr the base address of the whole string is being stored .Now this is what you have done in your code .
char *charPtr="i cant understand why";
Adding to that , if you print statements like :
printf("%c","i cant understand why"[0]);//prints i
printf("%c","i cant understand why"[2]);//prints c
These two printf's justify my concepts that the string "i cant understand why" is itself a pointer to the char array in which the string is being stored.
Your intPtr is initialized to point to the absolute memory address 60. There is no backing store here.
Since you are not dereferencing the pointer itself, you are never trying to read the address 60 which would probably crash your program depending on the environment.
Instead you are passing the pointer value into printf which takes pretty much anything as arguments and interprets the values as you specify in the format string. In your case it will interpret the address of the pointer instead of the pointer value. The address is 60 so that is displayed. Had you used *intPtr instead it would have probably crashed.
Related
I try to assign a value to the second index of pointer, it gives me a warning
"[Warning] assignment makes integer from pointer without a cast"
and it doesn't run this program. I wonder, where am I making a mistake?
#include<stdio.h>
void main()
{
char *p="John";
*(p+2)="v";
printf("%s",p);
}
Firstly, In your code,
char *p="John";
p points to a string literal, and attempt to modify a string literal invokes undefined behavior.
Related, C11, chapter §6.4.5
[...] If the program attempts to modify such an array, the behavior is
undefined.
If you want to modify , you need an array, like
char p[]="John";
Secondly, *(p+2)="v"; is wrong, as "" denotes a string, whereas you need a char (Hint: check the type os *(p+2)). Change that to
*(p+2)='v';
To elaborate the difference, quoting C11, chapter §6.4.4.4, for Character constants
An integer character constant is a sequence of one or more multibyte characters enclosed
in single-quotes, as in 'x'.
and chapter §6.4.5, string literals
A character string literal is a sequence of zero or more multibyte characters enclosed in
double-quotes, as in "xyz".
Thirdly, as per the C standards, void main() should at least be int main(void).
You are assigning a pointer to a string literal to an element of another string literal.
First, you need to change the pointer and make it an array, so any modification is legal, this is an example
char p[] = "John";
then you need to replace "v" which is a string literal consisting of two characters 'v' and '\0', to 'v' which is the ascii value for the letter v as an integer
*(p + 2) = 'v';
also, this is the third element and not the second, because the first element is p[0].
You're making two mistakes.
First, you are attempting to modify the contents of a string literal; this invokes undefined behavior, meaning any of the following are possible (and considered equally correct): your code may crash, it may run to completion with no issues, it may run to completion but not alter the string literal, it may leave your system in a bad state, etc. Your compiler may reject the code completely, although I don't know of any compiler that does so.
If you want to be able to modify the contents of a string, then you need to set aside storage for that string, instead of just pointing to a string literal:
char p[] = "John"; // copies the contents of the string literal "John" to p
// p is sized automatically based on the length of the
// string literal
Here's a hypothetical memory map showing the result of that declaration and initialization:
Address Item 0x00 0x01 0x02 0x03
------- ---- ---- ---- ---- ----
0x8000 "John" 'J' 'o' 'h' 'n'
0x8004 0x00 0x?? 0x?? 0x?? // 0x?? represents a random byte value
...
0xfffdc100 p 'J' 'o' 'h' 'n'
0xfffdc104 0x00 0x?? 0x?? 0x??
The string literal that lives at 0x8000 should not be modified; the string that lives in p at 0xfffdc100 may be modified (although the buffer will only have enough space to store up to a 4-character string).
Your second mistake (and the one causing the compiler to complain) is in this line:
*(p+2)="v";
The expression "v" is a string literal and has type "2-element array of char"; since it's not the operand of the sizeof or unary & operators, the type "decays" to "pointer to char", and the value of the expression is the address of the string literal "v".
The expression *(p + 2) has type char (it resolves to a single character value), which is an integral type, not a pointer type. You cannot assign pointer values to non-pointer objects.
You can easily fix that by changing that line to
*(p + 2) = 'v'; // note single quote vs double quote
This time, instead of trying to assign the address of a string literal to p[2], you're assigning the value of a single character.
Let's say we have a character pointer like this:
static char *example[]={"doctor","who","hello"};
I'm slightly confused at what is happening in this instance.
Does the pointer example simply have its own address?
Do the strings I have within the array each have a memory address different to *example
and each other?
Does the pointer example just simply reference each address and also initialize what's in
each address as shown by the declaration of the strings?
I'm only being using C for a few months, so I'm just trying to grasp the nuts and bolts of things, and I hear pointers are single-handedly the most important aspect of C.
Does the variable example simply have it's own address?
Yes, the variable has its own address.
Do the strings I have within the array each have a memory address different to *example
and each other?
Yes, each string has its own address, different from each other's. It's also different from the address of the variable example. The expression *example, however, is not the same as the variable example. See the next.
Does the pointer example just simply reference each address and also initialize what's in each address as shown by the declaration of the strings?
The variable example references the array of strings in the sense that the value of example (not its address) is the address of the array.
static char* example[] declares example as an array of pointers to char. The array is initialized to three string literals, so example[0] is "doctor", example[1] is "who", and example[2] is "hello".
Since example is an array, the array identifier example is going to evaluate to the address of the array's first element. If you try something like this:
printf ("%p %p %p\n", example, &example, &example[0]);
you'll see that they all have the same value. All these, however, are semantically different types.
example has the type array of pointers to char
&example has the type pointer to array of pointers to char
&example[0] has the type pointer to pointer to char.
Each element of the array has its own address. Try this:
printf ("%p %p %p\n", &example[0], &example[1], &example[2]);
The first will be the same address as the array, but the others will be offset from that address by the size of a pointer on your system (typically four bytes for a 32-bit system, 8 bytes for a 64-bit system).
The char that each pointer in your array example is pointing to is the first char of a string literal. Each string literal has its own address, probably in a read-only memory segment. You can try this too:
printf ("%p %p\n", &example[0], example[0]);
&example[0] is the address of the first pointer in the array of pointers to char.
example[0] is the first pointer in the array of pointers. Think of an array of int. Each element of that array would have an address and a value, the latter being an int. Since example is an array of pointers, each element of example is going to have an address and a value, the latter being a pointer.
You can repeat the exercise for &example[1], etc:
printf ("%p %p\n", &example[1], example[1]);
printf ("%p %p\n", &example[2], example[2]);
To sum up:
The array of pointers to char has the same address as its first element.
Each subsequent element, i.e., each subsequent pointer in the array, has its own address.
Each of those pointers points to (the first char of) a string, which has its own address.
Hope that's clear.
If you declare
char *example[] = {"doctor","who","hello"};
You get an array, of size 3, each of which holds one char* pointer. It is the same as
char *example[3] = {"doctor","who","hello"};
But, we really need to see how example is declared in your code to answer.
A picture might help. The following is not meant to represent any particular architecture - all address values are pulled out of thin air.
Item Address 0x00 0x01 0x02 0x03
---- ------- ---- ---- ---- ----
0x00004000 'd' 'o' 'c' 't'
0x00004004 'o' 'r' 0x00 'w'
0x00004008 'h' 'o' 0x00 'h'
0x0000400c 'e' 'l' 'l' 'o'
0x00004010 0x00 ?? ?? ??
... ... ...
example: 0x80001000 0x00 0x00 0x40 0x00
0x80001004 0x00 0x00 0x40 0x07
0x80001008 0x00 0x00 0x40 0x0b
The string literals "doctor", "who", and "hello" are all stored in such a way that they are allocated over the lifetime of the program (they have static storage duration), and they may be stored in a read-only data segment1.
The string literal "doctor" is stored at address 0x00004000, "who" is stored at address 0x00004007, and "hello" is stored at address 0x0000400b.
The array example also has static storage duration by virtue of the static keyword, but in practice will probably be stored in a different memory area than the string literals. The array starts at address 0x80001000, and each element occupies four bytes, so &example[0] == 0x80001000, &example[1] == 0x80001004, and &example[2] == 0x80001008.
Note that there's no separate storage location for a variable named example that points to the first element of the array. In C, the address of the first element is the same as the address of the array itself. This is important, and is one of the most consistently misunderstood aspects of the C language. Arrays are not pointers; rather, an array expression will be converted ("decay") to a pointer expression in most circumstances2, and the value of the expression will be the address of the first element. If you're really interested, the reasoning for this behavior is outlined in this paper by Dennis Ritchie; scroll down to the "Embryonic C" section for the explanation.
The value stored in example[0] is the address of the string literal "doctor". Similarly, example[1] stores the address of the string literal "who", and example[3] stores the address of the string literal "hello".
1. Because of this, the behavior on trying to modify the contents of a string literal is undefined - it may work as expected, it may crash, or it may fail to compile altogether. The compiler is not required to handle the situation in any particular way, so the behavior will vary between different compilers. You should never attempt to modify the contents of a string literal.
2. The exceptions to this rule are when the array expression is an operand of the sizeof or unary & operators, or is a string literal being used to initialize another array in a declaration.
I am trying to learn pointers in C but is getting mixed up with the following concepts:
char *string = "hello"
char *string2;
What is the main difference between:
A.) *string2 = string;
then
B.) string2 = "bye";
Some pictures may help.
Assume the following memory map (addresses are completely arbitrary and don't reflect any known architecture):
Item Address 0x00 0x01 0x02 0x03
---- ------- ---- ---- ---- ----
"hello" 0x00501234 'h' 'e' 'l' 'l'
0x00501238 'o' 0x00
"bye" 0x0050123A 'b' 'y'
0x0050123C 'e' 0x00 0x?? 0x??
...
string 0x80FF0000 0x00 0x50 0x12 0x34
string2 0x80FF0004 0x?? 0x?? 0x?? 0x??
This shows the situation after the declarations. "hello" and "bye" are string literals, stored as arrays of char "somewhere" in memory, such that they are available over the lifetime of the program. Note that attempting to modify the contents of string literals invokes undefined behavior; you don't want to pass string literals (or pointer expressions like string that evaluate to the addresses of string literals) as arguments to functions like scanf, strtok, fgets, etc.
string is a pointer to char, containing the address of the string literal "hello". string2 is also a pointer to char, and its value is indeterminate (0x?? represents an unknown byte value).
When you write
string2 = "bye";
you assign the address of "bye" (0x0050123A) to string2, so our memory map now looks like this:
Item Address 0x00 0x01 0x02 0x03
---- ------- ---- ---- ---- ----
"hello" 0x00501234 'h' 'e' 'l' 'l'
0x00501238 'o' 0x00
"bye" 0x0050123A 'b' 'y'
0x0050123C 'e' 0x00 0x?? 0x??
...
string 0x80FF0000 0x00 0x50 0x12 0x34
string2 0x80FF0004 0x00 0x50 0x12 0x3A
Seems simple enough, right?
Now let's look at the statement
*string2 = string;
There are a couple of problems here.
First, a digression - declarations in C are centered around the types of expressions, not objects. string2 is a pointer to a character; to access the character value, we must dereference string2 with the unary * operator:
char x = *string2;
The type of the expression *string2 is char, so the declaration becomes
char *string2;
By extension, the type of the expression string2 is char *, or pointer to char.
So when you write
*string2 = string;
you're attempting to assign a value of type char * (string) to an expression of type char (*string2). That's not going to work, because char * and char are not compatible types. This error shows up at translation (compile) time. If you had written
*string2 = *string;
then both expressions have type char, and the assignment is legal.
However, if you haven't assigned anything to string2 yet, its value is indeterminate; it contains a random bit string that may or may not correspond to a valid, writable address. Attempting to deference a random, potentially invalid pointer value invokes undefined behavior; it may appear to work fine, it may crash outright, it may do anything in between. This problem won't show up until runtime. Even better, if you assigned the string literal "bye" to string2, then you run into the problem described above; you're trying to modify the contents of a string literal. Again, that's a problem that's not going to show up until runtime.
There are some subtle inferences being made by other answerers, missing the POV of a newbie.
char *string = "hello";
Declares a pointer variable which is initialized to point at a character array (a good type match traditionally).
The statement
*string = "hello";
dereferences what should be a pointer variable and assigns a value to the pointed location. (It is not a variable declaration; that has to be done above it somewhere.) However, since string has type char *—so *string has type char—and the right side of the assignment is an expression with a pointer value, there is a type mismatch. This can be fixed in two ways, depending on the intent of the statement:
string = "hello"; /* with "char *" expressions on both sides */
or
*string = 'h'; /* with "char" expressions on both sides */
The first reassigns string to point to memory containing a sequence of characters (hello\000). The second assignment changes the character pointed to by string to the char value h.
Admittedly, this is a slightly confusing subject which all C programmers go through a little pain learning to grasp. The pointer declaration syntax has a slightly different (though related) effect than the same text in a statement. Get more practice and experience writing and compiling expressions involving pointers, and eventually my words will make perfect sense.
*string can be read as "whatever string points to", which is a char. Assigning "bye" to it makes no sense.
A C string is just an array of characters. C string literals like "hello" above could be viewed as "returning" a pointer to the first element of the character array, { 'h', 'e', 'l', 'l', 'o' }.
Thus, char *string = "bye" makes sense while char string = "bye" doesn't.
char * is a pointer to a character. Literals such as "hello" returns a pointer to the first character of the string. Therefore, string = "bye" is meaningful making string point to the first character of string "bye".
*string, on the other hand, is the character pointed by string. It's not a pointer but an 8-bit integer. This is why the assignment *string = "bye" is meaningless and will probably lead to a segmentation fault as the memory segment where "bye" stored is read-only.
AFTER EDIT:
The difference is that A) will not compile, and if it did, it's undefined behavior, because you're dereferencing an uninitialized pointer.
Also, please don't change your question drastically after posting it.
Let us say we have a array of pointers:
char *ptr[30];
Now this works fine, and doesn't seems to do anything unexpected!
I can input names easily.
scanf("%s", ptr[1]);
scanf("%s", ptr[2]);
scanf("%s", ptr[3]);
printf("%s\n", ptr[1]);
printf("%s\n", ptr[2]);
printf("%s\n", ptr[3]);
My question is if a pointer can be used in this way to store end number of names, then why is malloc used.? and in this case ptr[1] does not point to the character in the input but to a new input itself.
for eg if ptr has mukul, ptr[1] should point to 'u'
and if space is not allocated when a pointer is declared like this, what are the limits.?
The pointer cannot be used that way. It might "work" for you sometimes by sheer chance, but using a pointer without allocating memory for it will cause undefined behavior - meaning that your program may behave erratically and give you unexpected results.
Remember, just because your program compiles and runs doesn't mean it is correct. In the C language there is a whole class of errors known as "undefined behavior", many of which the compiler will happily let you do without complaining. These include overwriting a buffer, using an uninitialized variable, or dereferencing a pointer that doesn't point to a legitimately allocated memory block. Programs that exhibit undefined behavior may even appear to work normally sometimes - but they are usually very unstable and prone to crash.
If you use what is in your example, you just owerwrite other locations what come after your ptr array. Most compilers should actually give at least a warning. Your program would crash on most systems, you were just very lucky.
When you define a pointer like:
char *ptr = 0; // NULL pointer: dereferencing it will crash
puts(ptr); // crash
You merely create a link to a place in memory:
ptr = "string"; // dereferencing it will show the string
puts(ptr); // displaying "string"
So having an array of pointers merely creates a list of references to other variables.
To reference a place in memory, you then have to assign variables to your pointers, or allocate memory for each pointer.
You've allocated space for 30 pointers, but you haven't initialized them to point anywhere meaningful. Unless you declared the array outside of a function, each element in the array will contain some random bit string that may or may not correspond to a writable memory location. If we drew a picture, it would look something like this (all addresses are pulled out of thin air; don't assume this corresponds to any real architecture):
Item Address 0x00 0x01 0x02 0x03
---- ------- ---- ---- ---- ----
ptr 0xbbc81230 0x?? 0x?? 0x?? 0x??
0xbbc81234 0x?? 0x?? 0x?? 0x??
0xbbc81238 0x?? 0x?? 0x?? 0x??
...
0xbbc812a8 0x?? 0x?? 0x?? 0x??
where 0x?? represents a random byte value. For the behavior you've described, each of the random values just happens to point to writable memory, and writing over whatever's stored there just happens to not have any immediate ill effects.
Bad juju: it looks like your code is working properly when in reality it's behaving very badly, and can lead to some nasty runtime problems elsewhere in your program that's a pain to debug.
You will need to explicitly set each element of the ptr array to point to a valid memory location before attempting to write through it.
Suppose we add the following code:
ptr[0] = malloc(strlen("foo") + 1);
strcpy(ptr[0], "foo");
ptr[1] = malloc(strlen("bar") + 1);
strcpy(ptr[1], "bar");
We've dynamically allocated some extra memory to hold a couple of strings, and stored the pointers to those new buffers to ptr[0] and ptr[1].
Our picture would now look something like this:
Item Address 0x00 0x01 0x02 0x03
---- ------- ---- ---- ---- ----
0x80ff0000 'f' 'o' 'o' 0x00
...
0x80ffcdc0 'b' 'a' 'r' 0x00
...
ptr 0xbbc81230 0x80 0xff 0x00 0x00
0xbbc81234 0x80 0xff 0xcd 0xc0
0xbbc81238 0x?? 0x?? 0x?? 0x??
...
0xbbc812a8 0x?? 0x?? 0x?? 0x??
ptr[0] now contains the address of a buffer sized to hold 4 char values, and we copy the string "foo" to that buffer. Similarly, ptr[1] contains the address of another 4-byte buffer that now contains the string "bar".
#include<stdio.h>
#include<string.h>
int main()
{
char a[]="aaa";
char *b="bbb";
strcpy(a,"cc");
printf("%s",a);
strcpy(b,"dd");
printf("%s",b);
return 0;
}
We could not modify the contents of the array but the above program does not show any compile time error.When run it printed cc and terminated. The contents of the array i think will get stored in the read only section of the data segment and so its not possible to change the value of array as its a const.But here in the above program the value got changed to cc and the program terminated.The value got changed here why is it so.Please help me understand.
Array is not a constant datatype but a literal string like "aaa" is. You cannot modify it's content.
Here's a hypothetical memory map showing how the string literals, array, and pointer all relate to each other:
Item Address 0x00 0x01 0x02 0x03
---- ------- ---- ---- ---- ----
"aaa" 0x00040000 'a' 'a' 'a' 0x00
"bbb" 0x00040004 'b' 'b' 'b' 0x00
"cc" 0x00040008 'c' 'c' 0x00 ???
"dd" 0x0004000C 'd' 'd' 0x00 ???
...
a 0x08000000 'a' 'a' 'a' 0x00
b 0x08000004 0x00 0x04 0x00 0x00
This is the situation at line 6 in your code, after a and b have been declared and initialized. The string literals "aaa", "bbb", "cc", and "dd" all reside somewhere in memory such that they exist over the lifetime of the program. They are stored as arrays of char (const char in C++). Attempting to modify the contents of a string literal (in the case of this hypothetical, attempting to write to any memory location starting with 0x0004) invokes undefined behavior. Some platforms store string literals in read-only memory, some store them in writable memory, but in all cases, they should be treated as though they are unwritable.
The object a is an array of char, and it has been initialized with the contents of the string literal "aaa". The object b is a pointer to char, and it has been initialized with the address of the string literal "bbb". In the line
strcpy(a, "cc");
you're copying the contents of the string literal "cc" to a; after the line is executed, your memory map looks like this:
Item Address 0x00 0x01 0x02 0x03
---- ------- ---- ---- ---- ----
"aaa" 0x00040000 'a' 'a' 'a' 0x00
"bbb" 0x00040004 'b' 'b' 'b' 0x00
"cc" 0x00040008 'c' 'c' 0x00 ???
"dd" 0x0004000C 'd' 'd' 0x00 ???
...
a 0x08000000 'c' 'c' 0x00 0x00
b 0x08000004 0x00 0x04 0x00 0x00
So when you print a to standard output, you should see the string cc. Note: printf is buffered, so it's possible that output may not be written to the terminal immediately - either add a newline character to the format string (printf("%s\n", a);) or call fflush(stdout); after the printf to make sure all your output shows up.
In line 9, you attempt to copy the contents of the string literal "dd" to the location pointed to by b; unfortunately, b points to another string literal, which as mentioned above invokes undefined behavior. At this point, your program could literally do anything from run as expected to crash outright to anything in between. This could be part of the reason you only see the output for cc.
You have several arrays in your program. Some of them are modifiable, others are not. So, your original question ("Are arrays const datatypes?") is not really answerable in any meaningful way.
String literals (like "aaa") are arrays, but they are not modifiable. Note that in C language string literals are not really const (For example array "aaa", has type char[4], not const char[4]). However, it is still explicitly prohibited by the language to attempt to modify string literals. The compiler is not required to catch such attempts. Also, no run-time error is guaranteed to happen when you make such an attempt. The behavior is simply undefined. When you do strcpy(b,"dd"), you attempt to modify a non-modifiable array - the behavior is undefined. Anything can happen.
As for the ordinary array a in your code sample, it is declared as modifiable. So, you can modify it as much as you want. When you do strcpy(a, "cc"), you copy string "cc" into your array a. So, that exactly what you observe in your experiment whe you print the content of a. Nothing unusual here.
char* != char[]!
In this case, a is a buffer to a writeable area which is first filled with the content of a literal string, which is stored in a read-only area, while b is a pointer which directly points to a read-only area! Here is a sample code to help you understand :
#include <stdio.h>
#include <string.h>
#define literal "test"
int main() {
char a[] = literal
char b[] = literal;
char* c = literal;
char* d = literal;
printf("%s (%p)\n", a, a);
printf("%s (%p)\n", b, b);
printf("%s (%p)\n", c, c);
printf("%s (%p)\n", d, d);
return 42;
}
By executing this code, you'll see that even if all 4 string printed are the same, a and b addresses different areas in memory, while c and d point to a unique third area adress. In addition, you should see a big difference in address range between the first two and the last one : here, the location in different areas of memory (read/write and read-only) is made obvious.
Edit : just to insist, a fifth printf (printf("%s (%p)\n", literal, literal);) would print the same than c and d lines.
Arrays are not const datatypes, but literals are. The string literals "aaa" and "bbb" cannot be modified (or they can, but the result is undefined).
As pointed by Paul R, char a[] = "aaa"; is fine because it will use the string literal to initialize an array (which as I mentioned aren't const datatypes). It's char *b = "bbb"; the problem here, since you later try to modify the contents of the string literal itself, and not a copy.
b is pointing toward a string bbb which is stored in read-only memory.which cannot be changed by strcpy() or anything.