I am trying comparing different declaration of char array (string) in C.
There are two points on which I have compared these combinations mainly (Instead of writing again and again I named them):
- Point change or Point assignment : We are only changing what a pointer points to.
char *a, *b;
a=b //we are doing this - Value Change : We are changing the data to which the pointer is pointing.
char *a;
*a='x' //we are doing this
Below is the code with different combinations . I have about 10 doubts in it. I have to ask them together because they all are somehow linked.
Every doubt is explained in the code. Also the error msg is also added.
As It may be possible that you don’t know answer to every question. So, I have marked different section in the code. And also given number to each question.
If you know answer to any question Please answer with appropriate index.
In the code I have also made my own observations and conclusions which might be wrong. So I have marked them with Conclusion: tag. If you find any conclusion wrong please share/answer.
CODE:
#include <stdio.h>
int main() {
char *p = "Something";//I cant change the data
char q[] = "Wierd"; // I can change to what q points to
// I. ______________________ char*p ___________________________
printf("\nI. ______________________ char*p ___________________________ \n\n");
printf("%s %s\n",p, q);
//*p = 'a';// got segmentation fault as I cant change the Value
p = q;//This is possible because I change the Point
//Now the type p is a char pointer which can't change Value (because I declared it like this) but can change the Point
//and it is now pointing to a memory which is of type a char array.I can change its Value but cant change its Point
//This means there are two different things on both sides of the assignment but gcc doesnot give any error (i.e. it is acceptable)
//That is for Pointer assignment restriction rules of the type of left side var was used and for Value change
//rules of the type of right side var was used
// (1)Why?
*p = 'x';
printf("%s %s\n",p, q);
//Again try to make Something Wierd
p = "Something";
q[0] = 'W';
// II. ______________________ char q[] ___________________________
printf("\nII. ______________________ char q[] ___________________________ \n\n");
printf("%s %s\n",p, q);
//q = p;// This is not possible because for q I cant change Point.
// This is the error comes
//error: incompatible types when assigning to type ‘char[6]’ from type ‘char *’
*q = 'x';//This works fine as this is possible to change Value for q
printf("%s %s\n",p, q);
//Again try to make Something Wierd
p = "Something";
q[0] = 'W';
//____________________________________________________________________/
const char * r = "What";//I cant change the data to what a points to (basic def and const act on same)
char const * s = "Point";//I cant change the data to what a points to (basic def and const act on same)
char * const t = "Pointers";//I cant change the data to what a points to because of basic def and const make c a const that now c can only point to single entity.
const char u[] = "Are";//I cant change the data to what a points to because of const and I can't change to what d points because of basic def of [].
char const v[] = "Trying";//I cant change the data to what a points to because of const and I can't change to what d points because of basic def of [].
//char w const [] = "To make";//This is not possible
//___________________________________________________________________/
// III. ______________________ const char * r ___________________________
printf("\nIII. ______________________ const char * r ___________________________ \n\n");
printf("%s\n",r);
//*r = 'x'; // This is not possible
//Error comes is:
//error: assignment of read-only location ‘*r’
//now the behaviour of r is same as p but instead of getting segmentation fault I got an error at compile time.
//Also the restriction const put here is same as of restriction present with p except(error checking).
//Conclusion : This means writing const here makes no difference in terms of Value and Point. What it was before is the same now.
r = s;
printf("%s %s\n",r ,s);
//*r = 'x';
r = t;
printf("%s %s\n",r ,t);
//*r = 'x';
r = u;
printf("%s %s\n",r ,u);
//*r = 'x';
r=v;
printf("%s %s\n",r ,v);
//*r = 'x';
r=p;
printf("%s %s\n",r ,p);
//*r = 'x';
r=q;
printf("%s %s\n",r ,q);
//*r = 'x';
//For above four cases
//Everything works for Point assignment
//Nothing Works for Value change (Everytime assignment to read-only location error, no segmentation fault)
//Everything Works for Point assignment - This means everything works for the
//rules of the type of varible on the left side for Pointer assignment. (Even for r=u,r=v, r=q).
//(2) WHY this is happening.(Actualy answer related to WHY(1))
//Nothing Works for Value change
//Now this is absurd. On the first look it seems that as the things happen at the time of p=q, here
//for r=u, r=v, r=q same things should had happened. But on the closer inspection you can get that u,v
//have restrictions on Value change because of const.
//But (3)Why no Value change is happening for r=q ?
// (4) Why fot r=p, r=q getting error due to const. not due to segmentation fault.
//Resetting Wierdness
r = "What";
// IV. ______________________ char const * s ___________________________
printf("\nIV. ______________________ char const * s ___________________________ \n\n");
printf("%s\n",s);
//*s = 'x'; // This is not possible
//Error comes is
//error: assignment of read-only location ‘*s’
//Behavious of s is exactly same as r
//Conclusion: Writing const after or before char makes no difference.
s = r;
printf("%s %s\n",s ,s);
//*s = 'x';
s = t;
printf("%s %s\n",s ,t);
//*s = 'x';
s = u;
printf("%s %s\n",s ,u);
//*s = 'x';
s=v;
printf("%s %s\n",s ,v);
//*s = 'x';
s=p;
printf("%s %s\n",s ,p);
//*s = 'x';
s=q;
printf("%s %s\n",s ,q);
//*s = 'x';
//For above four cases
//Everything happens same as with r.
//Resetting Wierdness
s = "Point";
// V. ______________________ char * const t ___________________________
printf("\nV. ______________________ char * const t ___________________________ \n\n");
printf("%s\n",t);
//*t = 'x';//This is not possible
//Error is
//Segmentation-fault
//This means that on Value change the error comes not due to const. It comes for the same reason of p.
//t = r;
printf("%s %s\n",t ,r);
//*t = 'x';
//t = s;
printf("%s %s\n",t ,s);
//*t = 'x';
//t = u;
printf("%s %s\n",t ,u);
//*t = 'x';
//t=v;
printf("%s %s\n",t ,v);
//*t = 'x';
//t=p;
printf("%s %s\n",t ,p);
//*t = 'x';
//t=q;
printf("%s %s\n",t ,q);
//*t = 'x';
//For above four cases
//Nothing Works for Point Assignment
//Nothing works for value change (Everytime segmentation fault, assignment to read-only location error)
//Nothing Works for Point Assignment
//This is understandable
//Nothing works for value change
// (5) Why this is happening. Why left hand side is always given precedence. Why this isn't happening p=q,
//because for value change t=q and p=q are exctly same both pn left side and right side of the assignment.
//Resetting Wierdness
//t = "Pointers"; //No need
// VI. ______________________ const char u[] ___________________________
printf("\nVI. ______________________ const char u[] ___________________________ \n\n");
printf("%s\n",u);
//*u = 'x';//This is not possible
//Error Comes is
//error: assignment of read-only location ‘*(const char *)&u’
//This error comes because of const.
//Conclusion: [] gives the Point restriction and const gives the Value Restriction
//u = r;
printf("%s %s\n",u ,r);
//*u = 'x';
//u = s;
printf("%s %s\n",u ,s);
//*u = 'x';
//u = t;
printf("%s %s\n",u ,t);
//*u = 'x';
//u=v;
printf("%s %s\n",u ,v);
//*u = 'x';
//u=p;
printf("%s %s\n",u ,p);
//*u = 'x';
//u=q;
printf("%s %s\n",u ,q);
//*u = 'x';
//For above four cases
//Nothing Works for Point Assignment
//Nothing works for value change (Everytime assignment to read-only location error, no segmentation fault)
//Nothing Works for Point Assignment
//Error Comes for each is:
//warning: assignment of read-only location ‘u’ [enabled by default]
//error: incompatible types when assigning to type ‘const char[4]’ from type ‘const char *’
//Left side rules are given precedence. (6)Why? (If already not solved in above answers)
//Nothing works for value change
//Left side rules are given precedence. (7)Why? (If already not solved in above answers)
//Resetting Wierdness
//u = "Are";
// VII. ______________________ char const v[] ___________________________
printf("\nVII. ______________________ char const v[] ___________________________ \n\n");
printf("%s\n",v);
//*v = 'x';//This is not possible
//Error Comes is
//error: assignment of read-only location ‘*(const char *)&v’
//This error comes because of const.
//Conclusion: Writing const after or before char makes no difference.
//v = r;
printf("%s %s\n",v ,r);
//*v = 'x';
//v = s;
printf("%s %s\n",v ,s);
//*v = 'x';
//v = t;
printf("%s %s\n",v ,t);
//*v = 'x';
//v=u;
printf("%s %s\n",v ,u);
//*v = 'x';
//v=p;
printf("%s %s\n",v ,p);
//*v = 'x';
//v=q;
printf("%s %s\n",v ,q);
//*v = 'x';
//For above four cases
//Everything works as same with u.
//Resetting Wierdness
//v = "Trying";
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// (8) WHY `const char * a;` and `char const * a` works same?????
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//---------------Now doing more Possible combinations with p and q------------------
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// VIII. ~~~~~~~~~~~~~~~~~~~~~~~~~~~ char *p ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
printf("\nVIII. ~~~~~~~~~~~~~~~~~~~~~~~~~~~ char *p ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ \n\n");
//p = r;
printf("%s %s\n",p ,r);
//*p = 'x';
//p = s;
printf("%s %s\n",p ,s);
//*p = 'x';
//p = t;
printf("%s %s\n",p ,t);
//*p = 'x';
//p=u;
printf("%s %s\n",p ,u);
//*p = 'x';
//p=v;
printf("%s %s\n",p ,v);
//*p = 'x';
//For above four cases
//Point Assignment
//Warning for p=r, p=s is
//warning: assignment discards ‘const’ qualifier from pointer target type [enabled by default]
//Conclusion:Kind of understandable.
// NO Warning for p=t
//For left side type I can do Point assignment and for Right Side I can,t do.
// Left side rules are given precedence. (9)Why? (If already not solved in above answers)
//Warning for p=u, p=v is
//warning: assignment discards ‘const’ qualifier from pointer target type [enabled by default]
//For left side type I can do Point assignment and for Right Side I can,t do.
// Left side rules are given precedence. (10)Why? (If already not solved in above answers)
//Value Change
//Segmentation fault for everything .
//Conclusion: Understndable if assume left hand side are given precedence except for p = q (showed in I.)
//Resetting Wierdness
p = "Something";
// IX. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ char q[] ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
printf("\nIX. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ char q[] ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ \n\n");
//q = r;
printf("%s %s\n",q ,r);
*q = 'x';
//q = s;
printf("%s %s\n",q ,s);
*q = 'x';
//q = t;
printf("%s %s\n",q ,t);
*q = 'x';
//q=u;
printf("%s %s\n",q ,u);
*q = 'x';
//q=v;
printf("%s %s\n",q ,v);
*q = 'x';
//For above four cases
//Point Assignment
//Error for each is:
//error: incompatible types when assigning to type ‘char[6]’ from type ‘const char *’
//Conclusion: Understandable, if assume L.H.S. is given precedence except for p = q (showed in I.)
//Value Change
//Possible for each
//Conclusion: Understndable if assume left hand side are given precedence except for p = q (showed in I.)
//Resetting Wierdness
*q = 'W'; //No need
return 0;
}
Daniel’s Answers is complete. But two more simple doubts:
1) For p case object is a string literal (which one can’t modifiable) i.e. it is the property of right hand side object , while for q on RHS I have the same thing but now it is behaving differently. Why such an inconsistent design.
2) For p if I modify the string literal, the behavior is undefined i.e. Sometimes it is modified and sometimes not. Again why such design. Actually for out of bound array access this is understandable as you access memory that you haven’t allocated before, and sometimes you don’t have permission to access that piece of memory so, segmentation fault. But why sometimes I can modify string literal. Why so. What is the reason behind this.
pis achar*pointing to the first element of an array of tenchars (don’t forget the 0-terminator) that you are not allowed to modify (attempting to modify a string literal is undefined behaviour; most implementations store string literals in read-only memory, then such an attempt would cause a segfault, but it’s possible that an attempt to modify a string literal would modify the array and not crash). You can changepfreely, and when it is changed to point to a modifiable object, you can modify that object through*p.qis an array of sixchars (again, 0-terminator). You can’t assign any value toq, but you can modify the contents of the array.You let
ppoint to the first element of the arrayq. In that context,qis implicitly converted to a pointer to its first element,&q[0], sop = &q[0];is what actually happens. Nowppoints to a modifiable object, thusis allowed and changes the first
charinq.You let
pagain point to the first element of achararray that you are not allowed to modify, and change the first element ofqback to what it was before it was modified throughp, this time usingq.The error message is slightly misleading, you can’t assign arrays. Even with
char hello[] = "Hello"; char world[] = "World";, although both arrays have the same type, you can’t assign,hello = world; produces the same error (since in that context,world` is converted to a pointer to its first element, the error message is not wrong, though).Right,
*qis the same asq[0], so you can use arrays like pointers (and vice versa) in many situations. But not in all, arrays and pointers are different types of things.const char *andchar const *mean exactly the same, a pointer to an unmodifiablechar(which often is the first of an array of such).tis a constant pointer, you can’t change where it points to, but per the type, you can modify the object it points to. In this case, however,tpoints to the firstcharof a string literal, so you are not allowed to change the objecttpoints to (but that’s not a consequence oft‘s type).Again, both mean the same,
uandvare arrays ofconst char, you are not allowed to change the contents of these arrays because of their types.Right, that is invalid syntax, you can’t have a type qualifier between the array name and the brackets.
The type of
rforbids changing the pointed-to object throughr(it can be legitimate to change it through other pointers, though).The conclusion is wrong, if you change
rto point to a modifiable object,qfor example, you can still not change that throughr, the type forbids it. But you could modify it throughp. For pointers to string literals, usually the difference is that*r = 'x';is a compilation failure and*p = 'x';a segmentation fault, but for the general case, modifying throughpis valid (and usually even “works” ifppoints to an element of aconst char arr[10], but that is again undefined behaviour, it just doesn’t normally lead to a crash, in contrats to string literals).Okay, no problem. You change which object
rpoints to, i.e. you changer, but you don’t change the objectrpointed to.Also no problem. If
tpointed to a modifiable object, you could then modify the object throughtbut not throughr. But astpoints to a string literal, you mustn’t modify the objecttpoints to through either, but again, one would be a compilation error, the other probably a segfault.Read
constas “read-only”. Having aconst char *r;means you have a pointer tocharthat the compiler will not allow you to use to modify the pointed-to object, you can use it only to read the object. Whether the object it points to was declared asconstdoesn’t matter, theconstinr‘s declaration only limits whatrcan be used for, not what can be done with the pointed-to object through other pointers.I’m not sure I understand your “Why(3)”, but if I understand correctly, you expected
*r = 'x';to work afterr = q;, since thenrpoints to a modifiable object. Then the answer is what I wrote above, theconstqualifier inr‘s declaration restricts what you can do throughr, it is independent of theconststatus of the pointed-to object. That also answers (4), the type ofrforbids an assignment*r = whatever;.Right, it makes a difference whether the
constappears before or after the*, though.const char *r;declares a pointer you cannot use to modify the pointee, andchar * const x = q;declares a pointer that always points to the same location; you can use it to modify the pointed-to object (if that allows it). Andconst char * const y = q;declares a read-only pointer that you cannot change.You can’t do
*t = 'x';becausethappens to point to a string literal. Had you initialisedtto point toq, that would have been allowed.That’s disallowed because you declared
tto be unmodifiable, you cannot change where it points to.// (5) Why this is happening. Why left hand side is always given precedence. Why this isn’t happening p=q,
//because for value change t=q and p=q are exctly same both pn left side and right side of the assignment.
I’m not sure what the question is. You declared
tto beconst, that means you can’t assign tot. It would be the same forconst int i = 100;, you would not be allowed to writei = 120;in your programme.Right.
Not only “kind of”. You are assigning a
const char*to achar*. The object that is pointed to may be unmodifiable, but trying to modify it through thechar *pis formally valid, and if the pointed-to object is modifiable, usingpto modify it is even legitimate. But if the pointed-to object is not modifiable, a string literal or declared with aconstqualifier, trying to modify it throughpis undefined behaviour. So discarding theconstqualifier is a dangerous thing to do, and the compiler warns about it. It may, however be perfectly legitimate, so it’s only a warning, not an error. You can tell the compiler that you know what you’re doing (whether you do or not) by using an explicit cast.You’re assigning a
char * constto achar*, there’s nothing lost here. Assigning the value oftdoesn’t change it, and theconstafter the*only says that you cannot change the address thattpoints to.Same as for
p = r;.The segmentation fault is only because you’re letting the pointers point to string literals. If you let them point to
char[], the assignments*ptr = 'x';will either work or not compile, depending on whether the pointer was declared to point toconst charor only tochar. If you throw in a few arrays ofconst char, the assignment will of course also not compile forconst char*, but it will compile forchar*(with the warning about discarding theconstqualifier), and running the programme invokes undefined behaviour (it will probably not crash and modify the array contents, but anything could happen).Regarding the additional questions:
I’m not sure what you think is different. In both,
char *p = "Something";andchar *p = q;, the properties of the pointed-to object determine what uses ofpare valid. Neither of the two is unmodifiable by its type (both arechar[N]for someN), but the string literal is unmodifiable as a special case defined by the standard.The declaration
char *p;(without initialisation here, but the presence or absence of one doesn’t matter for that) declarespas a pointer you may use to modify what it points to. But whether such an attempt to modify the pointed-to object is valid, is determined by properties of the pointed-to object.copies the contents of the string literal to the array
q(including the 0-terminator), soqis initialised with a modifiable copy of the string literal. Lettingppoint to somecharin the arrayqmakesppoint to a modifiable object, and such a modification is valid.Letting
ppoint to aconstqualified object,const char c = 'C'; p = &c;, say, is dangerous, since the type ofpdoesn’t prevent*p = 'x';from compiling – after all, the compiler doesn’t know in general whether at that pointppoints to aconstqualified object, or a modifiable object (the assignment may come from aconst char*that was made to point at a modifiable object), or to no valid location at all.Therefore the assignment
p = &c;causes the compiler to emit a warning (at least with the warning level of the compiler sufficiently high, but in gcc and clang for example, it is enabled by default), or even abort the compilation with an error (gcc and clang do that if you pass the-pedantic-errorsflag). The language standard forbids that assignment without explicitly casting theconst char *that&cis to achar*, but requires only a diagnostic, so the compiler is free to make it a warning or an error.If you only use the pointer to read from the pointed-to object while it points to an unmodifiable object, that is a legitimate use, so the assignment isn’t flat-out unconditionally forbidden (with the cast, it’s allowed by the standard and doesn’t even cause a warning, since the cast tells the compiler “I know what I’m doing”).
So whether
*p = 'x';is valid, can only be determined by the properties of the pointed-to object. If the pointed-to object is unmodifiable (or ifpdoesn’t point to a valid object at all), the behaviour is undefined. How undefined behaviour manifests itself is, well, undefined, but in this case, usually either a segmentation fault (if the object resides in a write-protected memory region) or the pointed-to object will be modified as if it were allowed (if only the type makes the object unmodifiable and the implementation doesn’t take extra measures to identify such invalid writes). In the case of string literals, which have – for historical reasons – typechar[N], most often they are stored in the.rodata(read-only data) section of the programme, and an attempt to write to that is detected by the operating system and results in a segfault.Pragmaticism.
The committee defining the language doesn’t like to tie the implementors’ hands. It’s the programmer’s obligation to avoid undefined behaviour, and if (s)he doesn’t, whatever happens happens.
Historically, as far as I know, there were implementations that stored string literals in read-only memory and ones that didn’t. When the language was standardised (almost twenty years after its creation, so there was a lot of diversity in behaviours), it was mostly a write-up of existing common practices. Where behaviour widely differed, due to differences in compilers, libraries or hardware, it was left undefined or implementation-defined, to allow conforming implementations on as many platforms as possible. So some implementations allowed the modification of string literals, others didn’t, and the committee decided to place the burden on the programmer by making the behaviour undefined.