A question on string literals

P

pete

Chris said:
That is the following is invalid:
char arr1[] = { "Hello" };
arr1 = 0; // Not allowed.

No, that is still allowed: The array 'decomposes' into a pointer and
you can modify a non-const-qualified pointer at will.
Thus, both of these are legal:

Sriram Rajagopalan is right.
The result of the array to pointer conversion is not an lvalue.
Thus, both of these are legal:
arr2[1] = 'a'; /* The array is now 'Hallo' */
arr2 = NULL; /* The array is now unreachable. */

Try to compile it.
 
R

Richard Heathfield

Chris said:
char arr1[] = { "Hello" };
arr1 = 0; // Not allowed.
Correct.


No, that is still allowed: The array 'decomposes' into a pointer

Incorrect. Such decay happens only in value contexts, and this is not such a
context.
and
you can modify a non-const-qualified pointer at will.

Arrays are not pointers.
What's giving me fits is what that is a pointer /to/.

It isn't a pointer. It's an array.
I don't think the
line of code is valid, in other words. It would be just fine if it was
defined like this:

char *arr1[] = { "Hello" };

Nothing wrong with the definition, of course, but:

arr1 = NULL; /* wrong */

would still be an error.
 
P

pete

Richard said:
Chris said:
char arr1[] = { "Hello" };
arr1 = 0; // Not allowed.
Correct.


No, that is still allowed: The array 'decomposes' into a pointer

Incorrect. Such decay happens only in value contexts,
and this is not such a context.

I think it does happen. The standard describes the conversion
as something that happens except in three cases.

N869
6.3.2 Other operands
6.3.2.1 Lvalues and function designators
[#3] Except when it is the operand of the sizeof 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.

arr1 = NULL; /* wrong */

would still be an error.

What kind of an error?

The name of an array, if not converted,
fits the K&R and the standards' descriptions of lvalue.

Regarding
arr1 = NULL;
my compiler says: error C2106: '=' : left operand must be l-value
 
R

Richard Heathfield

pete said:
Richard said:
Chris said:
char arr1[] = { "Hello" };
arr1 = 0; // Not allowed.
Correct.


No, that is still allowed: The array 'decomposes' into a pointer

Incorrect. Such decay happens only in value contexts,
and this is not such a context.

I think it does happen. The standard describes the conversion
as something that happens except in three cases.

It's a constraint violation: "an assignment operator shall have a modifiable
lvalue as its left operand". Therefore, a diagnostic message is required,
and the program is considered incorrect. The Standard does not define how
incorrect programs are translated, or how they behave.
What kind of an error?

A constraint violation.
The name of an array, if not converted,
fits the K&R and the standards' descriptions of lvalue.

Regarding
arr1 = NULL;
my compiler says: error C2106: '=' : left operand must be l-value

This is in accordance with the Standard, which requires a diagnostic for
that code.
 
J

Joe Wright

Chris Barts wrote:
[ snip ]
That is the following is invalid:
char arr1[] = { "Hello" };
arr1 = 0; // Not allowed.


No, that is still allowed: The array 'decomposes' into a pointer and
you can modify a non-const-qualified pointer at will.
No, that is not allowed. arr1 is an array and you attempt to assign to
it. You cannot assign anything to an array.
What's giving me fits is what that is a pointer /to/. I don't think the
line of code is valid, in other words. It would be just fine if it was
defined like this:

char *arr1[] = { "Hello" };

As it stands, however, I don't think the definition is valid.
It is perfectly valid. It initializes arr1[0] (a pointer) with the
address of the literal "Hello".
3. char arr2[] = { 'H', 'e', 'l', 'l', 'o' };

Same comments as above for "arr2" hold true here.


Well, no. arr2 is an array of char of size 5, but it cannot be used
as a string by most standard string-handing functions as it lacks
nul-termination. An array can decompose into a pointer to the first
element under certain conditions (such as when it is passed into or
returned from a function) and that array would decompose into a
pointer to char. Since nothing is const-qualified, you can modify that
array's contents and the value of the pointer it can decompose into.
I believe you have a concept problem here. The name of an array will
'decay' to the address of its first element in 'rvalue' cases. Used in
an lvalue context, the array name does not decay. It is still an array
and you cannot assign anything to an array.
Thus, both of these are legal:

arr2[1] = 'a'; /* The array is now 'Hallo' */
arr2 = NULL; /* The array is now unreachable. */
Still not. You cannot assign to an array.

Do you ever try to compile any of this stuff? You might learn something
and avoid some embarrassment.
 
K

Keith Thompson

pete said:
Richard Heathfield wrote: [...]
arr1 = NULL; /* wrong */

would still be an error.

What kind of an error?

The name of an array, if not converted,
fits the K&R and the standards' descriptions of lvalue.

Regarding
arr1 = NULL;
my compiler says: error C2106: '=' : left operand must be l-value

The error message is slightly misleading. arr1 is an lvalue; the
problem is that it's not a modifiable lvalue.
 
P

pete

Keith said:
pete said:
Richard Heathfield wrote: [...]
arr1 = NULL; /* wrong */

would still be an error.

What kind of an error?

The name of an array, if not converted,
fits the K&R and the standards' descriptions of lvalue.

Regarding
arr1 = NULL;
my compiler says: error C2106: '=' : left operand must be l-value

The error message is slightly misleading. arr1 is an lvalue; the
problem is that it's not a modifiable lvalue.

Why isn't arr1 converted to a nonlvalue pointer in
arr1 = NULL;
as per
N869, 6.3.2.1 Lvalues and function designators, [#3]
?
 
K

Keith Thompson

pete said:
Keith said:
pete said:
Richard Heathfield wrote: [...]
arr1 = NULL; /* wrong */

would still be an error.

What kind of an error?

The name of an array, if not converted,
fits the K&R and the standards' descriptions of lvalue.

Regarding
arr1 = NULL;
my compiler says: error C2106: '=' : left operand must be l-value

The error message is slightly misleading. arr1 is an lvalue; the
problem is that it's not a modifiable lvalue.

Why isn't arr1 converted to a nonlvalue pointer in
arr1 = NULL;
as per
N869, 6.3.2.1 Lvalues and function designators, [#3]
?

Why do you think it isn't?

Assuming arr1 is an array object:
int arr1[10];
arr1 = NULL;
we know the assignment is illegal. If arr1 is implicitly converted to
a non-lvalue pointer value, that would explain why it's illegal.

One compiler I tried complains "incompatible types in assignment",
which doesn't really fit with this theory, but as long as the compiler
produces a diagnostic that doesn't really matter.
 
R

Richard Heathfield

pete said:
Where are you getting that from?

Well, I must admit I was getting it from Chris Torek's constant incantations
of "The Rule", and perhaps I was over-interpreting it. The fact remains
that the conversion you expect is not guaranteed by the Standard for the
simple reason that the code itself violates a constraint.
 
C

Chris Torek

Richard said:
[Array-to-pointer] decay happens only in value contexts,

Well, I must admit I was getting it from Chris Torek's constant incantations
of "The Rule", and perhaps I was over-interpreting it. The fact remains
that the conversion you expect is not guaranteed by the Standard for the
simple reason that the code itself violates a constraint.

Indeed, "The Rule" and the C standards (both of them) are not
literally the same -- but they *are* isomorphic, i.e., both give
the same result. I think my formulation is easier to understand,
as well, because we need the idea of "value context" vs "object
context" in the first place, so that we can figure why:

int x, b;
...
x = b;

copies b's *value* to the *object* named x.

If x used to be 3 before the assignment, and b is 7, why does this
set x to 7, instead of setting b to 3, or setting 3 to 7, or setting
7 to 3? (Of course, only two of those four possibilities even make
sense.) The answer is that the "=" operator demands an object on
its left, and a value on its right. The context on the left of
the "=" is an "object context", and the context on the right is a
"value context". Put down a value when something needs a value
and there is no problem:

x = 3;

Here x is an object and 3 is a value (of the correct type), so this
sets x to 3. Name an object on the right, though, and the operation
automatically fetches the object's value:

x = b;

so if b was 7, x becomes 7 too.

Once you understand the idea of "object context" and "value context",
you simply have to memorize which operators have which context(s):

&foo - object context
sizeof foo - object (or maybe even "sizeof") context
foo + bar - two value contexts
foo = bar - one object context, one value context
++foo - object context

and so on.

Given all of that, applying The Rule becomes easy, and whenever
the result is not "this makes no sense and a diagnostic is required",
it gives the same result as the more complicated way the C standards
put it.
 
T

Tim Rentsch

Richard Heathfield said:
Well, I must admit I was getting it from Chris Torek's constant incantations
of "The Rule", and perhaps I was over-interpreting it. The fact remains
that the conversion you expect is not guaranteed by the Standard for the
simple reason that the code itself violates a constraint.

The reasoning here is backwards. We know that the
conversion must take place, and it is the nature of the
required conversion that gives rise to the constraint
violation. Consider the statement:

array = 0;

and these questions:

Is 'array' a string literal used to initialize
an array? No it is not.

Is 'array' the operand of the unary & operator?
No it is not.

Is 'array' operand of the sizeof operator?
No it is not.

Does the expression 'array' have type "array of type"?
Yes it does.

Therefore 6.3.2.1 p3 (which pete posted) applies:

Except when it is the operand of the sizeof 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 ....

Now consider the effect of putting in for the "...." at
sentence end the text "is a modifiable lvalue" (rather than
the text actually written, "is not an lvalue"). Then the
assignment

array = 0;

would satisfy the 'Constraints' of 6.5.16 p2, and so would
not (under the hypothesis) be a constraint violation.

It is the final four words of 6.3.2.1 p3 that determine the
constraint violation in this case.
 
T

Tim Rentsch

Keith Thompson said:
pete said:
Richard Heathfield wrote: [...]
arr1 = NULL; /* wrong */

would still be an error.

What kind of an error?

The name of an array, if not converted,
fits the K&R and the standards' descriptions of lvalue.

Regarding
arr1 = NULL;
my compiler says: error C2106: '=' : left operand must be l-value

The error message is slightly misleading. arr1 is an lvalue;

Not here it isn't. See 6.3.2.1 p3.
 
T

Tim Rentsch

Tim Rentsch said:
It is the final four words of 6.3.2.1 p3 that determine the
constraint violation in this case.

Small correction - the final four words of the first
sentence. The paragraph in the standard has a second
sentence that was not quoted (about array objects
with register storage class).
 
K

Keith Thompson

Tim Rentsch said:
Keith Thompson said:
pete said:
Richard Heathfield wrote: [...]
arr1 = NULL; /* wrong */

would still be an error.

What kind of an error?

The name of an array, if not converted,
fits the K&R and the standards' descriptions of lvalue.

Regarding
arr1 = NULL;
my compiler says: error C2106: '=' : left operand must be l-value

The error message is slightly misleading. arr1 is an lvalue;

Not here it isn't. See 6.3.2.1 p3.

You're right.
 
T

Tim Rentsch

Chris Torek said:
Richard Heathfield wrote:
[Array-to-pointer] decay happens only in value contexts,

Well, I must admit I was getting it from Chris Torek's constant incantations
of "The Rule", and perhaps I was over-interpreting it. The fact remains
that the conversion you expect is not guaranteed by the Standard for the
simple reason that the code itself violates a constraint.

Indeed, "The Rule" and the C standards (both of them) are not
literally the same -- but they *are* isomorphic, i.e., both give
the same result. I think my formulation is easier to understand,
as well, because we need the idea of "value context" vs "object
context" in the first place, so that we can figure why:

int x, b;
...
x = b;

copies b's *value* to the *object* named x.

If x used to be 3 before the assignment, and b is 7, why does this
set x to 7, instead of setting b to 3, or setting 3 to 7, or setting
7 to 3? (Of course, only two of those four possibilities even make
sense.) The answer is that the "=" operator demands an object on
its left, and a value on its right. The context on the left of
the "=" is an "object context", and the context on the right is a
"value context". Put down a value when something needs a value
and there is no problem:

x = 3;

Here x is an object and 3 is a value (of the correct type), so this
sets x to 3. Name an object on the right, though, and the operation
automatically fetches the object's value:

x = b;

so if b was 7, x becomes 7 too.

Once you understand the idea of "object context" and "value context",
you simply have to memorize which operators have which context(s):

&foo - object context
sizeof foo - object (or maybe even "sizeof") context
foo + bar - two value contexts
foo = bar - one object context, one value context
++foo - object context

and so on.

A difficulty with the above is that the operand of sizeof
isn't an object context: any expression can be used as the
operand of sizeof [1], not just an lvalue expression. Also,
note a difference in behavior: usually,

sizeof &*p == sizeof p

if 'p' is a pointer (eg, to int), but (again, usually)

sizeof &*array != sizeof array

if 'array' is an array, even though both equalities hold if
the 'sizeof's are taken out.

Of course, it's important to understand the distinction
between "object context" and "value context", but trying to
use that distinction to explain what happens with arrays in
conjunction with sizeof seems like a strain. An expression
of array type simply behaves differently than expressions of
other types. It isn't that hard to remember the exception
cases for when an array expression doesn't convert to a
pointer; there are after all only three of them.

Incidentally, note that the distinction between object
context and value context also doesn't work very well for
the third exceptional case for array-type non-conversion,
which is that of a string literal used to initialize an
array:

char *p = "blah"; /* conversion */
char s[] = "blah"; /* no conversion */

On the face of it both instances of "blah" look like they
ought to be object contexts.

=====
[1] Subject to the usual need for parentheses around
operators of lower precedence, and the need to avoid
function types and incomplete types.

Given all of that, applying The Rule becomes easy, and whenever
the result is not "this makes no sense and a diagnostic is required",
it gives the same result as the more complicated way the C standards
put it.

It seems easier (at least to me) to remember that array
expressions always convert to pointers, except when they are
the operand of sizeof or address-of operators (or string
literals used to initialize an array).

That the conversion yields a value, rather than being an
object designator, suffices to explain (and without having
to remember anything extra) why an array expression can't be
assigned to or incremented, any more than the value of a
function call

f() = 3; /* WRONG! */
f() ++; /* WRONG! */

can be assigned to or incremented. Just knowing that
the array conversion is happening is enough to explain
and understand the behavior here.

Certainly there are places where the C standards documents
give explanations that are more complicated than they need
to be. IMO however the rule for when arrays are or aren't
converted to pointers isn't one of them.
 
J

junky_fellow

Keith said:
Tim Rentsch said:
Keith Thompson said:
Richard Heathfield wrote:
[...]
arr1 = NULL; /* wrong */

would still be an error.

What kind of an error?

The name of an array, if not converted,
fits the K&R and the standards' descriptions of lvalue.

Regarding
arr1 = NULL;
my compiler says: error C2106: '=' : left operand must be l-value

The error message is slightly misleading. arr1 is an lvalue;

Not here it isn't. See 6.3.2.1 p3.

You're right.

I also got the similar warning "In this statement, "arr1" is not an
lvalue ,
but occurs in a context that requires one. (needlvalue)"

It looks that arr1 is being converted to (char *)arr1, which is not an
lvalue.
If I do *arr1 = NULL; It compiles without any warning/error.

I always have a confusion that why array is called "non modifiable
lvalue",
because we can always modify the contents of an array.
The only reason that
arr1 = NULL; should not be allowed because arr1 is an array object and
RHS is not compatible with it. That might be the reason, standard
doesn't
allow modifying array object in this manner and to prevent this made it
non modifiable lvalue.
 
T

Tim Rentsch

Joe Wright said:
[snip]
The name of an array will
'decay' to the address of its first element in 'rvalue' cases. Used in
an lvalue context, the array name does not decay. It is still an array
and you cannot assign anything to an array.

Technically incorrect. An array (or any expression of array
type) will convert to a pointer to the first element in
(most) lvalue contexts as well as rvalue contexts. The only
exception (that is an lvalue context) is the operand of the
address-of operator. See 6.3.2.1 p3.

It's true that an array can't be assigned to, but that's
because the array-to-pointer conversion yields a value
rather than an object designator. It's exactly analogous to
trying to use, eg, the result of a function call:

f() = 3; /* WRONG! */
f() ++; /* WRONG! */

These attempts fail for the same reason that using arrays
fail: the result of the expression is a value, much like
3+4, and values aren't eligible for being assigned to.
 
D

Dave Vandervies

Chris Torek said:
Once you understand the idea of "object context" and "value context",
you simply have to memorize which operators have which context(s):

&foo - object context
sizeof foo - object (or maybe even "sizeof") context
foo + bar - two value contexts
foo = bar - one object context, one value context
++foo - object context

and so on.

Memorize? I've always found that taking a half-second to think about
what the operator does to its operand is enough to work out the context
that that operand is being used in.
(Thinking neurons are a lot cheaper than memorization neurons, so think
where possible and only memorize where necessary. Habit neurons are
even cheaper, but they're not very useful here.)


dave
 
K

Keith Thompson

I always have a confusion that why array is called "non modifiable
lvalue", because we can always modify the contents of an array. The
only reason that arr1 = NULL; should not be allowed because arr1 is
an array object and RHS is not compatible with it. That might be the
reason, standard doesn't allow modifying array object in this manner
and to prevent this made it non modifiable lvalue.

An array isn't (necessarily) a non-modifiable lvalue.

C99 6.3.2.1p3 says:

Except when it is the operand of the sizeof 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.

In
arr1 = NULL;
(assuming arr1 is the name of an array object), arr1 doesn't match any
of the exceptions above, so it's converted to a pointer to the initial
element of arr1 -- and this pointer value is explicitly not an lvalue.

(I was mistaken upthread when I said that it's an lvalue but not a
modifiable lvalue; it's not an lvalue at all.)
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Similar Threads

K&R2, exercise 5-4, strend(s,t) 15
Simple question python 2
[memcpy] dst=NULL,size=0 9
Const Issue 2
Parsing a string 44
const problem 1
An array is just a pointer 146
Blue J Ciphertext Program 2

Members online

No members online now.

Forum statistics

Threads
473,768
Messages
2,569,574
Members
45,048
Latest member
verona

Latest Threads

Top