A question on string literals

J

Joe Wright

Chris said:
[ snip ]

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.
I know you Chris and I love you like a brother but given..
int foo = 2, bar = 3;

&foo - is the address of foo, a 'value' of type (int*)
sizeof foo - is a value (4 at my house) of type (size_t)
++foo - is a value (now 3) of type (int)

None of these have 'object' context as I see it. What are you trying to
tell us here?
 
K

Keith Thompson

Joe Wright said:
Chris said:
[ snip ]
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.
I know you Chris and I love you like a brother but given..
int foo = 2, bar = 3;

&foo - is the address of foo, a 'value' of type (int*)
sizeof foo - is a value (4 at my house) of type (size_t)
++foo - is a value (now 3) of type (int)

None of these have 'object' context as I see it. What are you trying
to tell us here?

In "&foo", he's talking about the context in which the name "foo"
appears, not the entire expression. The argument to the unary "&"
operator must be an lvalue, so foo is in an "object context". Similarly,
the argument of unary "++" must be an lvalue.

He's mistaken about sizeof, which doesn't require an lvalue.
 
J

junky_fellow

Keith said:
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.

That means arr1 can never be used as an lvalue. The moment the operator
sizeof or & is applied on to it, it becomes an rvalue.
But the question is why such a limitation ?
Why not the following is allowed ?

int arr1[3];
arr1 = {1,2,3};
 
K

Keith Thompson

Keith said:
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.

That means arr1 can never be used as an lvalue. The moment the operator
sizeof or & is applied on to it, it becomes an rvalue.
But the question is why such a limitation ?
Why not the following is allowed ?

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

Allowing that kind of thing would drastically change the language and
break existing code.

For example, if it were legal to assign array values, it should also
be legal to pass array values as function arguments. This would
change the meaning of
printf("hello, world\n");
so it would pass an array value, not a pointer value, to printf().
Either that, or we'd have to treat arrays differently in different
contexts.

There are languages that support array value in ways that C doesn't,
so it's not inherently a bad idea; it's just not the way C does
things, and it's a few decades too late to change it.

Conceivably the language could be changed to allow array values only
in contexts that are currently illegal, but I don't think the result
would be coherent.
 
T

Tim Rentsch

A modifiable lvalue is an expression that is used directly
to write-access an object, namely with increment/decrement
or assignment operators. Arrays (or any expression with
array type) are never used directly in this way, but only
indirectly after being converted to a pointer and then
dereferenced or indexed. An array object is a "modifiable"
object; an array expression is not a modifiable lvalue.

Arrays need to be lvalues only so they can be used as
operands of the address-of operator. The address-of
operator needs only an lvalue for its operand, not a
modifiable lvalue. Hence arrays don't need to be modifiable
lvalues.

(Please note the response above is to what junky_fellow
wrote.)

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

An array is never a modifiable lvalue; 6.3.2.1 p1.
 
K

Keith Thompson

pete said:
"A modifiable lvalue is an lvalue that does not have array type, "

Right. The comma is critical; there are more criteria. (I mention
that because I mis-read it the first time.)

(I added the "(necessarily)" because I was too lazy to think it
through and/or look it up. It wasn't meant to imply that an array can
sometimes be a modifiable lvalue, though I see that it could be read
that way.)
 
T

Tim Rentsch

Keith Thompson said:
Right. The comma is critical; there are more criteria. (I mention
that because I mis-read it the first time.)

(I added the "(necessarily)" because I was too lazy to think it
through and/or look it up. It wasn't meant to imply that an array can
sometimes be a modifiable lvalue, though I see that it could be read
that way.)

Ah, now I see what you meant: an array wouldn't *have* to
be a non-modifiable lvalue. I believe that statement is
correct; the standard could have been written so array
expressions were modifiable lvalues, and nothing would
change, because in every place where it might be relevant
the array expression would be converted to a pointer, and so
would be illegal anyway. Probably 6.3.2.1 p1 excludes array
expressions from being modifiable lvalues so as to make it
absolutely clear that arrays can't be used with =, ++, etc.
 
K

Keith Thompson

Tim Rentsch said:
Ah, now I see what you meant: an array wouldn't *have* to
be a non-modifiable lvalue. I believe that statement is
correct; the standard could have been written so array
expressions were modifiable lvalues, and nothing would
change, because in every place where it might be relevant
the array expression would be converted to a pointer, and so
would be illegal anyway. Probably 6.3.2.1 p1 excludes array
expressions from being modifiable lvalues so as to make it
absolutely clear that arrays can't be used with =, ++, etc.

I'm glad you were able to read into what I wrote something cleverer
than what I intended. In fact, I was just being lazy. If I had spent
a minute or two doing more research, I could have dropped the
"(necessarily)"; I didn't bother because it didn't seem relevant to my
point. I should have realized that irrelevance doesn't prevent long
threads. :cool:}

Yes, I suppose the "does not have array type" phrase in 6.3.2.1p1 is,
strictly speaking, redundant, but it's a *good* redundancy. Without
it, if arr is an array object, then the name "arr" in "sizeof arr" or
"&arr" would be a modifiable lvalue, but it wouldn't matter because
neither operator modifies its argument. But I think the wording is
cleaner as it is.
 
P

PRadyut

The output for the three elements are
ello
ello
ello

while in the code if i go with str++
the o/p is

ello
Hello
Hello

Please clarify

Thanks

Pradyut
http://pradyut.tk
http://spaces.msn.com/members/oop-edge/
http://groups-beta.google.com/group/oop_programming
India
char *str1 = "Hello";
char arr1[] = { "Hello" };
char arr2[] = { 'H', 'e', 'l', 'l', 'o' };

Is it legal to modify str1, arr1 and arr2 ?

Depends on what you mean.

/* BEGIN new.c */

#include <stdio.h>
#include <string.h>

int main(void)
{
char *str1 = "Hello";
char arr1[] = { "Hello" };
char arr2[] = { 'H', 'e', 'l', 'l', 'o' };

strcpy(arr1, ++str1);
strcpy(arr2, arr1);
puts(str1);
puts(arr1);
puts(arr2);
return 0;
}

/* END new.c */
 
T

Tim Rentsch

Keith Thompson said:
I'm glad you were able to read into what I wrote something cleverer
than what I intended. In fact, I was just being lazy. If I had spent
a minute or two doing more research, I could have dropped the
"(necessarily)"; I didn't bother because it didn't seem relevant to my
point. I should have realized that irrelevance doesn't prevent long
threads. :cool:}

I usually look for the "smartest" reading in what people
write. It's often right, and it rarely hurts to give
someone the benefit of any doubt. Also I figure looking
for the best possible interpretation benefits me, since
when I do I sometimes find significant points that I
wouldn't otherwise.

Yes, I suppose the "does not have array type" phrase in 6.3.2.1p1 is,
strictly speaking, redundant, but it's a *good* redundancy. Without
it, if arr is an array object, then the name "arr" in "sizeof arr" or
"&arr" would be a modifiable lvalue, but it wouldn't matter because
neither operator modifies its argument. But I think the wording is
cleaner as it is.

Agreed.
 
C

Chris Torek

Chris said:
&foo - object context
sizeof foo - object (or maybe even "sizeof") context [snippage]
and so on.

In "&foo", he's talking about the context in which the name "foo"
appears, not the entire expression. The argument to the unary "&"
operator must be an lvalue, so foo is in an "object context". Similarly,
the argument of unary "++" must be an lvalue.

Keith is right about this.
He's mistaken about sizeof, which doesn't require an lvalue.

This is true, and is why I said "or maybe even `sizeof context'".
Sizeof is even more exceptional than unary-&.

Finally, I would like to note that -- while it was never added to
C99 -- there have been various proposals, over the years, to
implement array assignment in C. I suspect that C1X or C2X (if
these ever come about) may eventually do so -- and if so, I believe
the "most natural" way to handle array assignment is to add a rule
that says, in:

a = b

if "a" has type "array N of T", b is converted to a value of type
"pointer to T" (and if it does not match a diagnostic is required);
then the code acts much like a call of the form:

memcpy(&a[0], b, sizeof a)

except that the type of the result is "pointer to T" (rather than
"pointer to void").

If this *were* added to C, the left side of a simple asignment
operator would join the ranks of the "exceptions" for the
array-to-pointer conversion rule.

Of course, there are also "obvious" and "natural" (to me anyway)
interpretations for:

arr += arithmetic_type;
arr -= arithmetic_type;
arr++; /* same as arr += 1 */
arr--; /* same as arr -= 1 */
/* and so on for all arithmetic and logical operators */

which involve replicated arithmetic applying the right-hand-side
value (or implied 1, for ++ and --) to each element of the array,
with the value of the expression as a whole again being "pointer
to first element of array". (Some might object that this renders
identical values for arr++ and ++arr, to which I say: "so what?" :) )

All of these interpretations arise from one single central idea:
naming an array always just names the entire array, but the "value"
of an array (when a value is needed) is computed by finding a
pointer to its first element. If a value is *not* needed, the
array still names the entire array.

Again, this is not quite what the actual C standards (C89 and C99
both) say, but it delivers the same result, in what I think is a
simpler, yet ultimately more powerful, way.
 
T

Tim Rentsch

Chris Torek said:
[snip]

Finally, I would like to note that -- while it was never added to
C99 -- there have been various proposals, over the years, to
implement array assignment in C. I suspect that C1X or C2X (if
these ever come about) may eventually do so -- and if so, I believe
the "most natural" way to handle array assignment is to add a rule
that says, in:

a = b

if "a" has type "array N of T", b is converted to a value of type
"pointer to T" (and if it does not match a diagnostic is required);
then the code acts much like a call of the form:

memcpy(&a[0], b, sizeof a)

except that the type of the result is "pointer to T" (rather than
"pointer to void").

One would hope [0] that for an array assignment 'a = b' both
'a' and 'b' would have to be array expressions (and perhaps
even of identical numbers of elements). The chance of
memory being illegally accessed if b were allowed to be just
a pointer (which is implied, although not actually stated,
in Chris's comments), seems like too big a risk to allow
without needing an overt effort from the programmer. If a
pointer value were present for the right hand side, it could
always be cast to a suitable array type.

Also, to be consistent, the result of the array assignment
expression would still be of array type, so

a = b = c;

could be done.


[0] In the sense that at least one would hope because I
would, and I also would hope others would also.

If this *were* added to C, the left side of a simple asignment
operator would join the ranks of the "exceptions" for the
array-to-pointer conversion rule.

Likewise the right hand side of an assignment expression where
the left hand side has array type.

Of course, there are also "obvious" and "natural" (to me anyway)
interpretations for:

arr += arithmetic_type;
arr -= arithmetic_type;
arr++; /* same as arr += 1 */
arr--; /* same as arr -= 1 */
/* and so on for all arithmetic and logical operators */

which involve replicated arithmetic applying the right-hand-side
value (or implied 1, for ++ and --) to each element of the array,
with the value of the expression as a whole again being "pointer
to first element of array". (Some might object that this renders
identical values for arr++ and ++arr, to which I say: "so what?" :) )

If extensions like these were going to be made, it seems
like it would be important to preserve the semantic
properties that C developers are used to for scalar
variables. In particular, for

a = b++;
a = ++b;

with both sides being arrays, it would be strange if the
two statements above had identical behavior. Certainly
it violates "The Law of Least Astonishment".

I don't know how useful (or non-useful) these kinds of
operators would be for array types, but certainly it seems
like there's a strong possibility of semantic confusion.
(Also programmer confusion, but who cares about that? :)

All of these interpretations arise from one single central idea:
naming an array always just names the entire array, but the "value"
of an array (when a value is needed) is computed by finding a
pointer to its first element. If a value is *not* needed, the
array still names the entire array.

Perhaps this needs to be said differently. An expression
of array type remains an array when the context where it's
used needs an array; otherwise the array-type expression
is converted into a pointer to the array's first element,
as usual.

To be the most useful, it seems like array "assignment"
would also need to extend to passing of function parameters
and function results. There might be some difficulties
here. That's not to say that they couldn't be worked out,
but it's easy to imagine that there would be some unexpected
consequences.

Again, this is not quite what the actual C standards (C89 and C99
both) say, but it delivers the same result, in what I think is a
simpler, yet ultimately more powerful, way.

I believe C would benefit from having arrays be more
completely integrated into the language, including at least
assignment and parameter/result passing. However, I suspect
the effort required for working out all the details for that
is more than many C experts might guess.
 

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

Forum statistics

Threads
473,755
Messages
2,569,537
Members
45,022
Latest member
MaybelleMa

Latest Threads

Top