0

I am learning about pointers. I don't understand what the difference between the char variables is.

#include <stdio.h>

int main() {
    char *cards = "JQK";
    char cards2[] = "JQK";

    printf("%c\n", cards[2]);
    printf("%c\n", cards2[2]);
}

I experimented with them in the printf() and they seem to be working the same way except that cards2[] can't be resigned while cards can. Why is this the case?

TheCaptain
  • 135
  • 2
  • 8

1 Answers1

1

The difference is second is an array object initialized with the content of the string literal. First one is char* which contains the address of the string literal. String literals are array - this array is converted into pointer to first element and then that is assigned to char*.

The thing is, arrays are non modifiable lvalue - it can't appear in the left side of = assignment operator. A Pointer can (not marked const) can. And as the pointer is pointing to a string literal - you shouldn't try to modify it. Doing so will invoke undefined behavior.

Pointer and arrays are not the same thing - arrays decay into pointer in most cases. Here also that happened with those string literals when used in the right hand side of assignment in the pointer initialization case. Second one is different as it is explicitly mentioned that this will copy the content of the string literal to a the array declared - this is why this is modifiable unlike the previous case(Here cards2).

To clarify a bit - first let's know what is going on and what is the difference between array and pointer?

Here I have said that string literals are arrays. From §6.4.5¶6

In translation phase 7, a byte or code of value zero is appended to each multibyte character sequence that results from a string literal or literals.78) The multibyte character sequence is then used to initialize an array of static storage duration and length just sufficient to contain the sequence. For character string literals, the array elements have type char, and are initialized with the individual bytes of the multibyte character sequence. For UTF-8 string literals, the array elements have type char, and are initialized with the characters of the multibyte character sequence, as encoded in UTF-8.

This is what is written in standard - it is posted to show you that string literals are indeed a character array of static storage duration. This is how it is. Now the question is when we write

char *cards = "JQK";

So now we have an array which is in the left hand side of a pointer declaration. What will happen?

Now comes the concept of array decaying. Most of the cases array is converted into pointers to the first element of it. This may seem strange at first but this is what happens. For example,

int a[]={1,2,3};

Now if you write a[i] this is equivalent to *(a+i) and a is the decayed pointer to the first element of the array. Now you are asking to go to the position a+i and give the value that is in that address, which is 3 for i=2. Same thing happened here.

The pointer to the first element of the literal array is assigned to cards. Now what happened next? cards points to the first element of the literal array which is J.

Story doesn't end here. Now standard has imposed a constraint over string literal. You are not supposed to change it - if you try to do that it will be undefined behavior - undefined behavior as the name implies is not defined by the standard. You should avoid it. So what does that mean? You shouldn't try to change the string literal or whatever the cards points to. Most implementation put string literals in read only section - so trying to write to it will be erroneous.

Being said that, what happened in the second case? Only thing is - this time we say that

char cards2[] = "JQK";

Now cards2 is an array object - to the right of the assignment operator there is string literal again. What will happen? From §6.7.9¶14

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.

The thing is now it means that - you can put the string literal to the right side of the assignment. The char array will be initialized. Here that is what is being done. And this is modifiable. So you can change it unlike the previous case. That is a key difference here.

Also if you are curious are there cases when we see an array as array and not as pointer - the whole rule is stated here.

From §6.3.2.1¶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.

This is all there is to this. This I hope will give you a clear idea than it gave before.

user2736738
  • 30,591
  • 5
  • 42
  • 56
  • Please sir - let me know my mistakes. It helps. Please. – user2736738 Mar 15 '18 at 06:25
  • 1
    This is one of the most frequently asked questions about C that there is. There are thousands of carefully-written answers to it out there. If you're going to write yet another answer to this question, it had better be a really, really good one! But this one, I'm afraid, is not. Anyone who already understands arrays and pointers in C will understand your answer, but a beginner, I'm afraid, will be as confused by it as when he started. – Steve Summit Mar 15 '18 at 07:41
  • 1
    @SteveSummit.: I took the challenge. Hope it is not that bad now. Not a great one like most people answers here - but yes maybe OP will get some help. – user2736738 Mar 15 '18 at 09:19