Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 4 years ago.
Improve this question
Please take a look at this code.
#include <stdio.h>
int main()
{
char *p;
p = "%d";
p++;
p++;
printf(p-2,23);
return 0;
}
I have the following questions
1) How can a pointer to a character data type can hold a string data type?
2) What happens when p is incremented twice?
3) How can the printf()can print a string when no apparent quotation marks are used?
"How can a pointer to a character data type can hold a string data type?" Well, it's partly true that in C, type 'pointer to char' is the string type. Any function that operates on strings (including printf) will be found to accept these strings via parameters of type char *.
"How can printf() print a string when no apparent quotation marks are used?" There's no rule that says you need quotation marks to have a string! That thing with quotation marks is a string constant or string literal, and it's one way to get a string into your program, but it's not at all the only way. There are lots of ways to construct (and manipulate, and modify) strings that don't involve any quotation marks at all.
Let's draw some pictures representing your code:
char *p;
p is a pointer to char, but as you correctly note, it doesn't point anywhere yet. We can represent it graphically like this:
+-----------+
p: | ??? |
+-----------+
Next you set p to point somewhere:
p = "%d";
This allocates the string "%d" somewhere (it doesn't matter where), and sets p to point to it:
+---+---+---+
| % | d |\0 |
+---+---+---+
^
|
\
\
\
|
+-----|-----+
p: | * |
+-----------+
Next, you start incrementing p:
p++;
As you said, this makes p point one past where it used to, to the second character of the string:
+---+---+---+
| % | d |\0 |
+---+---+---+
^
|
|
|
|
|
+-----|-----+
p: | * |
+-----------+
Next,
p++;
Now we have:
+---+---+---+
| % | d |\0 |
+---+---+---+
^
|
/
/
/
|
+-----|-----+
p: | * |
+-----------+
Next you called printf, but somewhat strangely:
printf(p-2,23);
The key to that is the expression p-2. If p points to the third character in the string, then p-2 points to the first character in the string:
+---+---+---+
| % | d |\0 |
+---+---+---+
^ ^
+----|----+ |
p-2: | * | /
+---------+/
/
|
+-----|-----+
p: | * |
+-----------+
And that pointer, p-2, is more or less the same pointer that printf would have received if you're more conventionally called printf("%d", 23).
Now, if you thought printf received a string, it may surprise you to hear that printf is happy to receive a char * instead — and that in fact it always receives a char *. If this is surprising, ask yourself, what did you thing printf did receive, if not a pointer to char?
Strictly speaking, a string in C is an array of characters (terminated with the '\0' character). But there's this super-important secret fact about C, which if you haven't encountered yet you will real soon (because it's really not a secret at all):
You can't do much with arrays in C. Whenever you mention an array in an expression in C, whenever it looks like you're trying to do something with the value of the array, what you get is a pointer to the array's first element.
That pointer is pretty much the "value" of the array. Due to the way pointer arithmetic works, you can use pointers to access arrays pretty much transparently (almost as if the pointer was the array, but of course it's not). And this all applies perfectly well to arrays of (and pointers to) characters, as well.
So since a string in C is an array of characters, when you write
"%d"
that's an array of three characters. But when you use it in an expression, what you get is a pointer to the array's first element. For example, if you write
printf("%d", 23);
you've got an array of characters, and you're mentioning it in an expression, so what you get is a pointer to the array's first element, and that's what gets passed to printf.
If we said
char *p = "%d";
printf(p, 23);
we've done the same thing, just a bit more explicitly: again, we've mentioned the array "%d" in an expression, so what we get as its value is a pointer to its first element, so that's the pointer that's used to initialize the pointer variable p, and that's the pointer that gets passed as the first argument to printf, so printf is happy.
Up above, I said "it's partly true that in C, type 'pointer to char' is the string type". Later I said that "a string in C is an array of characters". So which is it? An array or a pointer? Strictly speaking, a string is an array of characters. But like all arrays, we can't do much with an array of characters, and when we try, what we get is a pointer to the first element. So most of the time, strings in C are accessed and manipulated and modified via pointers to characters. All functions that operate on strings (including printf) actually receive pointers to char, pointing at the strings they'll manipulate.
the following explains each statement in the posted code:
#include <stdio.h>// include the header file that has the prototype for 'printf()'
int main( void ) // correct signature of 'main' function
{
char *p; // declare a pointer to char, do not initialize
p = "%d"; // assign address of string to pointer
p++; // increment pointer (so points to second char in string
p++; // increment pointer (so points to third char in string
printf(p-2,23);// use string as 'format string' in print statement,
// and pass a parameter of 23
return 0; // exit the program, returning 0 to the OS
}
1) How can a pointer to a character data type can hold a string data type?
Ans: String is not a basic data type in C. String is nothing but a continuous placement of char in memory until '\0' is encountered.
2) What happens when p is incremented twice?
Ans: It now points to the '\0' character.
3) How can the printf()can print a string when no apparent quotation marks are used
Ans: A string is always represented in quotation marks so extra quotes are not needed.
1. How can a pointer to a character data type can hold a string data type?
-> Char pointer will hold the address of char datatype, since string is collection of char datatypes. Hence char pointer can hold the string data type..
2. What happens when p is incremented twice?
-> When you assign the char pointer to string pointer will point to first char. So when you increment the pointer twice, it will hold the address of 3rd char, in your case it is'\0';
3. How can the printf()can print a string when no apparent quotation marks are used?
-> printf(p-2,23); Uses string as format identifier in your case it is "%d".
Related
This question already has answers here:
What is the difference between char s[] and char *s?
(14 answers)
String literals: pointer vs. char array
(1 answer)
Closed 1 year ago.
This post was edited and submitted for review 1 year ago and failed to reopen the post:
Original close reason(s) were not resolved
hello to all programmers, I can't understand something
char a[]="hello"
char* b="salam"
the first question is why can't we modify 2,for example b[0]='m', I know that 2 gets stored as compile time constant BUT I can't understand what does it mean and what is the quiddity of 2 ?
and second question:
3.
char a[]="hello";
char* c=a;
c[0]='r';
Now we can modify and then print c, but we couldn't modify 2 ! why?
I can't understand the concept of those pointers please explain it to me
char a[] = "hello;" is a null terminated array of characters, the array will be initialized with the charaters you specify and the size of it will be deduced by the compiler, in this case it will have space for 6 characters, these are mutable, the charaters are copied to the array, you can change them at will. e.g. a[0] = 'x' will change hello to xello.
char* c = a; just makes the pointer c point to a, the same operations can be performed in c as you are really operating in a.
char* b = "salam" is a different animal, b is a pointer to a string literal, these are not meant to be modified, they don't get stored in an array like a, they are read only and are usually stored in some read only section of memory, either way the behavior of editing b is undefined, i.e. b[0] = 'x' is illegal as per the language rules.
char a[]="hello";
This creates an array like this:
+---+---+---+---+---+----+
a: | h | e | l | l | o | \0 |
+---+---+---+---+---+----+
The array is modifiable and you can write other characters to it later if you like (although you cannot write more than 5 or 6 of them).
char* b="salam";
This uses a string literal to create a constant string somewhere, that variable b is then a pointer to. I like to draw it like this:
+-------+
b: | * |
+---|---+
|
V
+---+---+---+---+---+----+
| s | a | l | a | m | \0 |
+---+---+---+---+---+----+
There are two differences here: (1) b is a pointer, not an array as a was. (2) the string here (that b points to) is probably in nonwritable memory. But a was definitely in writable memory.
char* c=a;
Now c is a pointer, pointing at the earlier-declared array a. The picture looks like this:
+---+---+---+---+---+----+
a: | h | e | l | l | o | \0 |
+---+---+---+---+---+----+
^
|
\
|
+---|---+
c: | * |
+-------+
And the array a was modifiable, so there's no problem doing c[0] = 'r', and we end up sounding like Scooby-Doo and saying:
+---+---+---+---+---+----+
a: | r | e | l | l | o | \0 |
+---+---+---+---+---+----+
^
|
\
|
+---|---+
c: | * |
+-------+
The key difference (which can be quite subtle) is that a string literal in source code like "hello" can be used in two very different ways. When you say
char a[] = "hello";
the string literal is used as the initial value of the array a. But the array a is an ordinary, modifiable array, and there's no problem writing to it later.
Most other uses of string literals, however, work differently. When you say
char *b = "salam";
or
printf("goodbye\n");
those string literals are used to create and initialize "anonymous" string arrays somewhere, which are referred to thereafter via pointers. The arrays are "anonymous" in that they don't have names (identifiers) to refer to them, and they're also usually placed in read-only memory, so you're not supposed to try to write to them.
Let's start of with your first question:
We have 2 strings, a and b
char a[] = "hello";
char *b = "salam";
The first string can be modified, this is because it uses a different memory segment than the second string. It is stored in the data segment of the program, and we have write access to the data segment so we can modify it.
The second string is a pointer to a string, we cannot modify string literals (pointers to strings) since c specifies that this is undefined behavior.
The address of b will just point to somewhere in the program where that string is stored. This string should preferably be declared const since it can't be modified anyways.
const char *b = "salam";
Now let's look at the second question:
The code you provided for the second question is perfectly valid,
char a[] = "hello";
char *c = a;,
c[0] = 'r';
We have a, which stores the actual string and if using ASCII it consists of 6 bytes 'h', 'e', 'l', 'l', 'o', '\0'
c points to a we can verify this with this code
#include <stdio.h>
int main(void) {
char a[] = "hello";
char *c = a;
c[0] = 'r';
printf("a: %p\nc: %p\n", &a, &*c);
}
And we'll get output as such
a: 0x7ffe3c94ecf2
c: 0x7ffe3c94ecf2
They both point to the same address, the start of the array when we do
c[0] // It essentially means *(c + 0) = in other words the address which c points to + 0 and then we subscript this is how subscripting works a[1] = *(a + 1), etc...
So pretty much c in this case points to
0x7ffe3c94ecf2
c + 0 =
0x7ffe3c94ecf2
Access that address and modify the character.
According to the definition of printf, it says that first argument should be an array i.e char* followed by ellipses ... i.e variable arguments after that. If I write:
printf(3+"helloWorld"); //Output is "loWorld"`
According to the definition shouldn't it give an error?
Here is the definition of printf:
#include <libioP.h>
#include <stdarg.h>
#include <stdio.h>
#undef printf
/* Write formatted output to stdout from the format string FORMAT. */
/* VARARGS1 */
int __printf(const char *format, ...) {
va_list arg;
int done;
va_start(arg, format);
done = vfprintf(stdout, format, arg);
va_end (arg);
return done;
}
#undef _IO_printf
ldbl_strong_alias(__printf, printf);
/* This is for libg++. */
ldbl_strong_alias(__printf, _IO_printf);
This is not an error.
If you pass "helloWorld" to printf, the string literal is converted to a pointer to the first character.
If you pass 3+"helloWorld", you're adding 3 to a pointer to the first character, which results in a pointer to the 4th character. This is still a valid pointer to a string, it's just not the whole string that was defined.
3+"helloWorld" is of char * type (after conversion in call to printf). In C, the type of a string literal is char []. When passed as an argument to a function, char [] will convert to pointer to its first element (array to pointer conversion rule). Therefore, "helloWorld" will be converted to a pointer to the element h and 3+"helloWorld" will move the pointer to the 4th element of the array "helloWorld".
From Pointer Arithmetic:
If the pointer P points at an element of an array with index I, then
P+N and N+P are pointers that point at an element of the same array with index I+N
P-N is a pointer that points at an element of the same array with index {tt|I-N}}
The behavior is defined only if both the original pointer and the result pointer are pointing at elements of the same array or one past the end of that array. ....
The type of string literal is char[N], where N is size of string (including null terminator).
From C Standard#6.3.2.1p3 [emphasis mine]
3 Except when it is the operand of the sizeof operator, the _Alignof operator, or the unary & operator, or is a string literal used to initialize an array, an expression that has type ''array of type'' is converted to an expression with type ''pointer to type'' that points to the initial element of the array object and is not an lvalue. If the array object has register storage class, the behavior is undefined.
So, in the expression
3+"helloWorld"
"helloWorld", which is of type char [11] (array of characters), convert to pointer to character that points to the initial element of the array object.
Which means, the expression is:
3 + P
where P is pointer to initial element of "helloWorld" string
----------------------------------------------
| h | e | l | l | o | W | o | r | l | d | \0 |
----------------------------------------------
^
|
P (pointer pointing to initial element of array)
when 3 gets added to pointer P, the resulting pointer will be pointing to 4th character:
----------------------------------------------
| h | e | l | l | o | W | o | r | l | d | \0 |
----------------------------------------------
^
|
P (after adding 3 the resulting pointer pointing to 4th element of array)
This resulting pointer of expression 3+"helloWorld" will be passed to printf(). Note that the first parameter of printf() is not array but pointer to a null-terminated string and the expression 3+"helloWorld" resulting in pointer to 4th element of "helloWorld" string. Hence, you are getting output "loWorld".
The answers from dbush and haccks are concise and illuminating, and I upvoted the one from haccks in support of the bounty offered by dbush.
The only thing that I find left unsaid is that the way the question title is phrased makes me wonder if the OP would think that e.g. this also should produce an error:
char sometext[] = {'h', 'e', 'l', 'l', 'o', '\0'};
printf (sometext);
since there is no string literal involved at all. The OP needs to understand that one should never think that a function call that takes a char * argument can "only allow taking a string literal as [that] argument".
The answers from dbush and haccks hint at this by mentioning the conversion of a string literal to a char * (and how adding an integer to that evaluates), but I feel that it's worth pointing out explicitly that anything that is treated as a char * can be used, even things not converted from a string literal.
printf(3+"helloWorld"); //Output is "loWorld"
It will not give error because String in C Language String is array of characters and Array name give base address of array.
In case of printf(3+"helloWorld"); 3+"helloWorld" is giving the address of fourth element of array of charecters i.e of String
This is still a valid pointer to a string i.e char*
printf only allow taking a char* as the first argument
The first argument to printf is declared as const char *format. It means printf should be passed a pointer to char and the characters pointed to by this pointer will not be changed by printf. There are additional constraints on this first argument:
it should point to a proper C string, that is an array of characters terminated by a null byte.
it may contain conversion specifiers, which must be properly constructed and the corresponding arguments must be passed as extra arguments to printf, with the expected types and order as derived from the format string.
Passing a string constant such as "helloWorld" as the format argument is the most common way to invoke printf. String constants are arrays of char terminated by a null byte which should not be modified by the program. Passing them to functions expecting a pointer to char will cause a pointer to their first byte to be passed, as is the case for all arrays in C.
The expression "helloWorld" + 3 or 3 + "helloWorld" evaluates to a pointer to the 4th byte of the string. It is equivalent to the expression 3 + &("helloWorld"[0]), &("helloWorld"[3]) or simply &"helloWorld"[3]. As a matter of fact, it is also equivalent to &3["helloWorld"] but this latest form is only used for pathological obfuscation.
printf does not use the bytes that precede the format argument, so passing 3 + "helloWorld" is equivalent to passing "loWorld" and produces the same output.
To be more precise, the first argument to printf() function is not an array of chars but a pointer to array of chars. the difference between them is the same difference between byval and byref in VB world.
a pointer can be incremented and decremented using (++ and --) or applying arithmetic operations (+ and -).
in your case you are passing a pointer to "helloWorld" incremented by three, thus it points to the forth element of the array of chars "helloWorld".
lets simplify this in asm pseudo code
MOV EAX, offset ("helloWorld")
ADD EAX, 3
PUSH EAX
CALL printf
you maybe think that 3+"hello word" do concatenation between 3 and "hello world", but in C concatenation is done otherwise. the simplest way to do is sprintf(buff, "%d%s",3,"hello wrord");
It's pretty strange, but not wrong. By doing 3 + you are moving your pointer to a different location.
The same thing work when you initialize a char *:
char *str1 = "Hello";
char *str2 = 2 + str1;
str2 is now equal to "llo".
I have a two part question:
Understand output from sizeof
Understand how strings are stored in variables (e.g. bits and ram)
Question 1
I'm trying to understand the output from the following piece of C code.
printf("a: %ld\n", sizeof("a")); // 2
printf("abc: %ld\n", sizeof("abc")); // 4
It always seems to be one larger than the actual number of characters specified.
The docs suggest that the returned value represents the size of the object (in this case a string) in bytes. So if the size of a gives us back 2 bytes, then I'm curious how a represents 16 bits of information.
If I look at the binary representation of the ASCII character a I can see it is 01100001. But that's only showing 3 bits out of 1 byte being used.
Question 2
Also, how do large strings get stored into a variable in C? Am I right in thinking that they have to be stored within an array, like so:
char my_string[5] = "hello";
Interestingly when I have some code like:
char my_string = "hello";
printf("my_string: %s\n", my_string);
I get two compiler errors:
- incompatible pointer to integer conversion initializing 'char' with an expression of type 'char [6]'
- format specifies type 'char *' but the argument has type 'char'
...which I don't understand. Firstly it states the type is presumed to be a size of [6] when there's only 5 characters. Secondly the mention of a pointer here seems odd to me? Why does printf expect a pointer and why does not specifying the length of the variable/array result in a pointer to integer error?
By the way I seemingly can set the length of the variable/array to 5 rather than 6 and it'll work as I'd expect it to char my_string[5] = "hello";.
I'm probably just missing something very basic/fundamental about how bits and strings work in C.
Any help understanding this would be appreciated.
The first part of the question is due to the way strings are stored in C. Strings in C are nothing more than a series of characters (char) with a \0 added at the end, which is the reason you're seeing a +1 when you do sizeof. Notice in your second part if you were to say char my_string[4] = "hello"; you'd also get a compiler error saying there wasn't enough size for this string. That's also related to this.
Now onto the second part, strings themselves are a series of characters. However, you don't store every character by themselves in a variable. You instead have a pointer to these series of characters that will allow you to access them from some part of memory. Additional information regarding pointers and strings in C can be found here: Pointer to a String in C
In C, a string is a sequence of character values followed by a zero valued terminator. For example, the string "hello" is the sequence of character values {'h', 'e', 'l', 'l', 'o', 0 }1. Strings (including string literals) are stored as arrays of char (or wchar_t for wide-character strings). To account for the terminator, the size of the array must always be one greater than the number of characters in the string:
char greeting[6] = "hello";
The storage for greeting will look like
+---+
greeting: |'h'| greeting[0]
+---+
|'e'| greeting[1]
+---+
|'l'| greeting[2]
+---+
|'l'| greeting[3]
+---+
|'o'| greeting[4]
+---+
| 0 | greeting[5]
+---+
Storage for a string literal is largely the same2:
+---+
"hello": |'h'| "hello"[0]
+---+
|'e'| "hello"[1]
+---+
|'l'| "hello"[2]
+---+
|'l'| "hello"[3]
+---+
|'o'| "hello"[4]
+---+
| 0 | "hello"[5]
+---+
Yes, you can apply the subscript operator [] to a string literal just like any other array expression.
Except when it is the operand of the sizeof or unary & operators, or is a string literal used to initialize a character array in a declaration, an expression of type "N-element of T" will be converted ("decay") to an expression of type "pointer to T", and the value of the expression will be the address of the first element of the array. So, the string literal "hello" is an expression of type "6-element array of char". If I pass that literal as an argument to a function like
printf( "%s\n", "hello" );
then both of the string literal expressions "%s" and "hello" are converted from "4-element array of char"3 and "6-element array of char" to "pointer to char", so what printf receives are pointer values, not array values.
You've already seen two exceptions to the conversion rule. You saw it in your code when you used the sizeof operator and got a value one more than you expected. sizeof evaluates to the number of bytes required to store the operand. Because of the zero terminator, it takes N+1 bytes to store an N-character string.
The second exception is the declaration of the greeting array above; since I'm using the string literal to initialize the array, the literal is not converted to a pointer value first. Note the you can write that declaration as
char greeting[] = "hello";
In that case, the size of the array is taken from the size of the initializer.
The third exception occurs when the array expression is the operand of the unary & operator. Instead of evaluating to a pointer to a pointer to char (char **), the expression &greeting evaluates to type "pointer to 6-element array of char", or char (*)[6].
The length of a string is the number of characters before to zero terminator. All the standard library functions that deal with strings expect to see that terminator. The size of the array to store that string must be at least one greater than the maximum length of the string you intend to store.
Sometimes you'll see people write '\0' instead of a naked 0 to represent a string terminator; they mean the same thing.
Storage for string literals is allocated at program startup and held until the program terminates. String literals may be stored in a read-only memory segment; attempting to modify the contents of a string literal results in undefined behavior.
'\n' counts as a single character.
There is a simple C program.
#include<stdio.h>
int main()
{
char *s = "abcde";
printf("%s\n", s);
printf("%s\n", *s); /* This is wrong */
return 0;
}
This is my thought:
variable s is a char * pointer to the string abcde. So variable s is a memory address , and the memory address store the string abced.
The %s format string in function printf() is formatting a string. I don't know why s is the string. The variable s is a char * pointer and *s is the string of abcde, isn't it?
In C, "strings" are NUL-terminated arrays of characters.
The code char *s = "abcde"; does two things:
First, it allocates (in read-only program data) some (unnamed) memory, and populates it with "abcde":
1000 1001 1002 1003 1004 1005
_________________________________________
| a | b | c | d | e | \0 |
|______|______|______|______|______|______|
Then, in the stack frame of main, a pointer to char is allocated, named s, and its value is initialized with the address of your string. In my example, s = 1000.
1000 1001 1002 1003 1004 1005
_________________________________________
| a | b | c | d | e | \0 |
|______|______|______|______|______|______|
^
|
|
s = 1000
The %s format specifier tells printf to expect the address of a NUL-terminated string as the corresponding argument..
When you pass s, you are doing just that: telling printf that your string lives at address 1000. printf goes to that address, and starts reading the characters there (a, b, c...) until it encounters a NUL ('\0') character, at which point it stops. It has now read your string.
When you pass *s, two things happen. First, the program de-references the pointer. Since it is a pointer-to-char, that means it reads one character from the memory at 1000. The result of this is 'a', which is the decimal number 97. Now, this value is passed to printf (as before), and printf still thinks it's an address. However, 97 is an invalid address, and your program crashes.
If you need to brush up on your understanding of pointers:
How do pointer to pointers work in C?
Everything you need to know about pointers in C
Pointer Basics
and *s is the string of abcde, isn't it?
No. *s is a char, the first char in the string, so it's a. *(s + 1) is b, *(s + 2) is c, and so on. A char* is an address which points, or refers, to some number of chars.
You are lying to printf with that second call and invoking undefined behavior. The character a is passed and interpreted as an address using it's integral value. That's going to lead to bad things.
printf expects a pointer to char when you use the %s format specifier and that's what you have to pass it. So, what ends up happening is that printf reads past (char*)'a' looking for a null terminator. It may or may not find one before segfaulting, but it's UB either way.
Turn your warnings on.
In c, a string is just a string of characters in memory terminated with a null character ('\0') which tells you where the string stops. There are no string objects. As such when you provide a 'string' to printf you pass it a pointer to where the string is held and it figures out the rest.
when printf meet with a string , It automatically put out the string from start address "a" to the end "\0"; This is special. While printf deal with string, it obey this rule. And for a string, "*a" is the first character of the string , not the whole string . So when you want to put out a string , give the start address to printf. "a"."(a+1)"...can help you put out the character you want in the string.
Q: The variable s is a char * pointer and *s is the string of abcde, isn't it?
No.
*s is a specific character pointed to by s. In the question code, it is the character 'a'.
If it helps, *s results in the same character as s[0]. In otherwords, (for the question code) *s is the value of the first character in the array (or string) of characters.
On the other hand, s is a char * that can store an address. It can store any address in memory you like. In the question code, s was initialized to point at a specific static string (or array of characters).
You can print the specific address pointed to by s:
printf("s points to %p\n", s);
You can print the specific character pointed to by s:
printf("The character pointed to by s: %c\n", *s);
You can print the string (or array) of characters pointed to by s:
printf("s points to an array of characters: %s", s);
This question already has answers here:
Closed 12 years ago.
Possible Duplicates:
What is the difference between char s[] and char *s in C?
What is the difference between char a[]="string"; and char *p="string";?
The first one is array the other is pointer.
The array declaration "char a[6];" requests that space for six characters be set aside, to be known by the name "a." That is, there is a location named "a" at which six characters can sit. The pointer declaration "char *p;" on the other hand, requests a place which holds a pointer. The pointer is to be known by the name "p," and can point to any char (or contiguous array of chars) anywhere.
The statements
char a[] = "hello";
char *p = "world";
would result in data structures which could be represented like this:
+---+---+---+---+---+---+
a: | h | e | l | l | o |\0 |
+---+---+---+---+---+---+
+-----+ +---+---+---+---+---+---+
p: | *======> | w | o | r | l | d |\0 |
+-----+ +---+---+---+---+---+---+
It is important to realize that a reference like x[3] generates different code depending on whether x is an array or a pointer. Given the declarations above, when the compiler sees the expression a[3], it emits code to start at the location "a," move three past it, and fetch the character there. When it sees the expression p[3], it emits code to start at the location "p," fetch the pointer value there, add three to the pointer, and finally fetch the character pointed to. In the example above, both a[3] and p[3] happen to be the character 'l', but the compiler gets there differently.
You can use search there are tons of explanations on the subject in th internet.
char a[]="string"; //a is an array of characters.
char *p="string";// p is a string literal having static allocation. Any attempt to modify contents of p leads to Undefined Behavior since string literals are stored in read-only section of memory.
No difference. Unless you want to actually write to the array, in which case the whole world will explode if you try to use the second form. See here.
First declaration declares an array, while second - a pointer.
If you're interested in difference in some particular aspect, please clarify your question.
One difference is that sizeof(a)-1 will be replaced with the length of the string at compile time. With p you need to use strlen(p) to get the length at runtime. Also some compilers don't like char *p="string", they want const char *p="string" in which case the memory for "string" is read-only but the memory for a is not. Even if the compiler does not require the const declaration it's bad practice to modify the string pointed to by p (ie *p='a'). The pointer p can be changed to point to something else. With the array a, a new value has to be copied into the array (if it fits).