I was told that the array name is an non-modifiable l-value in C, but it is still confusing.
Someone said that the array name can not be placed on the left side of the formula because it is converted to a pointer that is not l-value.
My question is Here:
is an array name l-value?
Is there any difference between what means l-value in c and c++?
is an array name l-value?
Yes, in both C and C++.
Is there any difference between what means l-value in c and c++?
Yes, but not of great significance. Here is the definition from C11, paragraph 6.3.2.1/1:
An lvalue is an expression (with an object type other than void) that potentially designates an object
C also includes a footnote (#64) expanding on that, which includes:
The name ''lvalue'' comes originally from the assignment expression E1 = E2, in which the left operand E1 is required to be a (modifiable) lvalue. It is perhaps better considered as representing an object
''locator value''. [...] An
obvious example of an lvalue is an identifier of an object.
Here is the definition from C++14, paragraph 3.10/1:
An lvalue (so called, historically, because lvalues could appear on
the left-hand side of an assignment expression) designates a function
or an object.
If you read carefully, you will notice that in C, an lvalue only potentially designates an object, whereas in C++, no room is left for unfulfilled potential -- an lvalue does designate an object or function. You'll also then notice that C++ includes function designators among its lvalues, whereas C does not. In practice, these distinctions are more technical than deeply meaningful. And neither of them affects the answer to your question (1).
You'll also note that neither definition is written in terms of how or where an lvalue can be used. That follows from the definition and other specifications; it is not a defining characteristic.
In both C and C++, an array's identifier designates an object -- the array -- and it is therefore an lvalue. Whether such an lvalue may in fact appear as the left operand in an assignment expression is an entirely separate question.
In the context of C:
6.3.2.1 Lvalues, arrays, and function designators
1 An lvalue is an expression (with an object type other than void) that potentially
designates an object;64) if an lvalue does not designate an object when it is evaluated, the
behavior is undefined. When an object is said to have a particular type, the type is
specified by the lvalue used to designate the object. A modifiable lvalue is an lvalue that
does not have array type, does not have an incomplete type, does not have a const-qualified type, and if it is a structure or union, does not have any member (including,
recursively, any member or element of all contained aggregates or unions) with a const-qualified type.
2 Except when it is the operand of the sizeof operator, the _Alignof operator, the
unary & operator, the ++ operator, the -- operator, or the left operand of the . operator
or an assignment operator, an lvalue that does not have array type is converted to the
value stored in the designated object (and is no longer an lvalue); this is called lvalue
conversion. If the lvalue has qualified type, the value has the unqualified version of the
type of the lvalue; additionally, if the lvalue has atomic type, the value has the non-atomic
version of the type of the lvalue; otherwise, the value has the type of the lvalue. If the
lvalue has an incomplete type and does not have array type, the behavior is undefined. If
the lvalue designates an object of automatic storage duration that could have been
declared with the register storage class (never had its address taken), and that object
is uninitialized (not declared with an initializer and no assignment to it has been
performed prior to use), the behavior is undefined.
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.
64) The name ‘‘lvalue’’ comes originally from the assignment expression E1 = E2, in which the left
operand E1 is required to be a (modifiable) lvalue. It is perhaps better considered as representing an
object ‘‘locator value’’. What is sometimes called ‘‘rvalue’’ is in this International Standard described
as the ‘‘value of an expression’’.
An obvious example of an lvalue is an identifier of an object. As a further example, if E is a unary
expression that is a pointer to an object, *E is an lvalue that designates the object to which E points.
C 2011 Online Draft
Summarizing:
An array expression (that is, any expression of array type) is indeed an lvalue; however, unless it is the operand of the sizeof, _Alignof, or unary & operators, that expression gets converted ("decays") to an expression of pointer type whose value is the address of the first element of the array, and that converted pointer expression is not an lvalue, and thus cannot be the target of an assignment.
That is, if you declare a as
T a[N]; // for any type `T`
then the expression a has type "N-element array of T". If a is not the operand of the sizeof, unary &, or _Alignof operators, it will be converted to an expression of type "pointer to T", and its value will be the same as &a[0], and that value cannot be the target of an assignment (it's logically the same as writing 2 = 3 - you're trying to assign a value to a value, not an object, which doesn't work).
Related
In C11 standard
6.3.2.1 Lvalues, arrays, and function designators
1 An lvalue is an expression (with an object type other than
void) that potentially designates an object;) if an lvalue does
not designate an object when it is evaluated, the behavior is
undefined. When an object is said to have a particular type,
the type is specified by the lvalue used to designate the object.
Is it correct that
A lvalue has a type, just as a value (which I believe mean the same as rvalue) does?
Or, only a rvalue can have a type, while a lvalue doesn't?
A lvalue has the same type as the value stored in the object designated by the lvalue?
Or, a lvalue has a pointer type, because it designates an object which is actually a memory slot with a memory address and size?
Or, lvalues and rvalues have different types? pointer types are still rvalue types, and pointers are not lvalues themselves, because the C11 standard says
if E is a unary expression that is a pointer to an object, *E is an lvalue that designates the object to which E points.
An lvalue is an expression (specified in the text you quoted already). All expressions have a type. Therefore all lvalues have types.
The standard doesn't have an explicit statement "all expressions have a type"; however in section 6.5 Expressions where all possible expression syntax is enumerated, the definition of each expression specifies the type of that expression.
The type of an lvalue might differ from the type of the object designated by the lvalue, e.g. after int x; then *(unsigned int *)&x is an lvalue of type unsigned int designating an object of type int. The strict aliasing rule specifies in which circumstances it is permitted to access an object via an lvalue of a different type to the object.
6.3.2.1 Lvalues, arrays, and function designators in the C11 standard says
A modifiable lvalue is an lvalue that
does not have array type, does not have an incomplete type, does not have a const-
qualified type, and if it is a structure or union, does not have any member (including,
recursively, any member or element of all contained aggregates or unions) with a const-
qualified type.
Is an lvalue of a function type a modifiable lvalue or not?
The quote doesn't mention function types, but in reality, I think an lvalue of a function type is not a modifiable lvalue. (lvalues of array types and lvalues of function types also share some similarity: both are converted to the addresses of arrays and of functions.)
Thanks.
Is an lvalue of a function type a modifiable lvalue or not?
The question contains a misnomer. An lvalue by definition only designates an object. Earlier in the paragraph you cite (p1), it specifies
An lvalue is an expression (with an object type other than void) that potentially designates an object;
Functions aren't counted in the definition of objects, so there aren't lvalues of a function type.
Instead, there's a separate category for function types. In section 6.3.2.1 as well, paragraph 4:
A function designator is an expression that has function type. Except when it is the operand of the sizeof operator, the _Alignof operator, or the unary & operator, a function designator with type ''function returning type'' is converted to an expression that has type ''pointer to function returning type''.
So the question is mostly moot. Expressions that specify things in terms of lvalues do not have to concern themselves about function types. Instead, if applicable, the specification for the expression will mention how it operates with a "function designator"
I think an lvalue of a function type is not a modifiable lvalue
Correct, but that is again on account of not being counted among lvalues at all.
This question already has answers here:
What is the reasoning behind the naming of "lvalue" and "rvalue"?
(6 answers)
Closed 3 years ago.
I've heard the terms lvalue and rvalue come up when working with pointers.
However, I don't fully understand their meaning.
What are lvalues and rvalues?
Note 1: This is a question about C's lvalues and rvalues, not C++'s. It's also about their functionality, not their naming.
Note 2: I already fully understand these concepts. This is meant as a canonical duplicate target.
I've got a longer answer here, but basically C11 draft n1570 6.3.2.1p1:
An lvalue is an expression (with an object type other than void) that potentially designates an object [...]
C11 n1570 Footnote 64:
64) The name lvalue comes originally from the assignment expression E1 = E2, in which the left operand E1 is required to be a (modifiable) lvalue. It is perhaps better considered as representing an object locator value. What is sometimes called rvalue is in this International Standard described as the value of an expression. An obvious example of an lvalue is an identifier of an object. As a further example, if E is a unary expression that is a pointer to an object, *E is an lvalue that designates the object to which E points.
Not all lvalues are modifiable, i.e. can appear on the left side of an assignment. Examples of unmodifiable lvalues are those that
have array type,
have incomplete type
are const-qualified
structs or unions that have const-qualified members either directly or recursively
An lvalue can be converted to a value of an expression through lvalue conversion. I.e. in
int a = 0, b = 1;
a = b;
both a and b are lvalues, as they both potentially - and actually - designate objects, but b undergoes lvalue conversion on the right-hand side of the assignment, and the value of the expression b after lvalue conversion is 1.
"Potentially designating an object" means that given int *p;, *p designates an object of type int iff p points to an object of type int - but *p is an lvalue even if p == NULL or indeterminate.
According to the C Reference Manual (3rd Edition):
An lvalue is an expression that refers to an object in such a way that
the object may be examined or altered. Only an lvalue expression may
be used on the left-hand side of an assignment. An expression that is
not an lvalue is sometimes called an rvalue because it can only appear
on the right-hand side of an assignment. An lvalue can have an
incomplete array type, but not void.
This question is inspired by answers to this question.
Following code has potential for undefined behaviour:
uint64_t arr[1]; // Uninitialized
if(arr[0] == 0) {
C standard specifies that uninitialized variable with automatic storage duration has indeterminate value, which is either unspecified or trap representation. It also specifies that uintN_t types have no padding bits, and size and range of values are well defined; so trap representation for uint64_t is not possible.
So I conclude that uninitialized value itself is not undefined behavior. What about reading it?
6.3.2.1 Lvalues, arrays, and function designators
...
Except when it is the operand of the sizeof operator, the _Alignof operator, the unary & operator, the ++ operator, the -- operator, or the left operand of the . operator or an assignment operator, an lvalue that does not have array type is converted to the value stored in the designated object (and is no longer an lvalue); this is called lvalue
conversion. ... -- irrelevant text removed --
... If
the lvalue designates an object of automatic storage duration that could have been declared with the register storage class (never had its address taken), and that object is uninitialized (not declared with an initializer and no assignment to it has been performed prior to use), the behavior is undefined.
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.
Question: Does subscripting array count as taking the address of an object?
Following text seems to imply that subscripting array requires conversion to a pointer, which seems impossible to do without taking address:
6.5.2.1 Array subscripting
Constraints
One of the expressions shall have type ‘‘pointer to complete object type’’, the other
expression shall have integer type, and the result has type ‘‘type’’.
Semantics
A postfix expression followed by an expression in square brackets [] is a subscripted
designation of an element of an array object. The definition of the subscript operator []
is that E1[E2] is identical to (*((E1)+(E2))). Because of the conversion rules that
apply to the binary + operator, if E1 is an array object (equivalently, a pointer to the
initial element of an array object) and E2 is an integer, E1[E2] designates the E2-th element of E1 (counting from zero).
This makes §6.3.2.1 paragraph 3 seem weird. How could array have register storage class at all, if subscription requires conversion to a pointer?
Yes, array subscripting counts as taking the address, as per the part you quoted in 6.5.2.1. The expression E1 must have its address taken.
Therefore the special case of UB in 6.3.2.1 does not apply to array indexing. If array indices are used, it is not relevant if the array could be stored with register storage duration or not (a variable having its address taken cannot use register storage duration).
You are correct in assuming that reading an uninitialized stdint.h type with indeterminate value, which has its address taken, does not invoke undefined behavior (guaranteed by C11 7.20.1.1), but merely unspecified behavior. The value could be anything and it can be non-deterministic between several reads, but it cannot be a trap.
"Reading an uninitalized variable is always UB" is a wide-spread but incorrect myth.
Further information with normative sources in this answer.
As much as I go over the C11 standard, I can't see a resolution to the question of whether lvalues are converted to rvalues when they appear as the controlling expression in a _Generic expression. For example, does the following function return 1 or 0?
int func()
{
const int x;
return _Generic( x, int: 1, default: 0 );
}
In the standard, lvalue conversion is defined as follows (6.3.2.1.2):
Except when it is the operand of the sizeof operator, the _Alignof operator, the unary & operator, the ++ operator, the -- operator, or the left operand of the . operator or an assignment operator, an lvalue that does not have array type is converted to the value stored in the designated object (and is no longer an lvalue); this is called lvalue conversion. If the lvalue has qualified type, the value has the unqualified version of the type of the lvalue; additionally, if the lvalue has atomic type, the value has the non-atomic version of the type of the lvalue; otherwise, the value has the type of the lvalue...
It apparently follows that func returns 1. The controlling expression, 'x', undergoes lvalue conversion as it does not appear in one of the excepted circumstances. Therefore, it has type int and 1 is selected.
On the other hand, the standard says (6.5.1.1.2):
...The type name in a generic association shall specify a complete object type other than a variably modified type...
and (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...
The only reason to explicitly rule out variably modified types that I can see is to allow for arrays but not for variable length arrays (since the type is already required to be a complete object type). Since it seems clear that lvalue conversion and pointer conversion either both apply or neither applies to _Generic controlling expressions, this suggests that conversion does not take place. Otherwise the exclusion of variably modified types is superfluous and extremely confusing.
This seems to me to justify the argument that lvalue conversion is implicitly only relevant to evaluated statements, and the controlling expression of a _Generic is not evaluated. By this understanding, there is no lvalue conversion, and in the example given 'x' has its unconverted type of const int and so func returns 0.
Is there anything in the standard that clarifies this issue? In particular, would func have the same return value on any complying implementation? Finally, are the popular compilers (e.g., clang, gcc, etc.) consistent on this issue?
I should note that I am using the committee draft of the standard, so if the final version clarifies this then please let me know.