Array as compound literal [duplicate]
Asked Answered
Z

4

4

In C99 we can use compound literals as unnamed array.

But are this literals constants like for example 100, 'c', 123.4f, etc.

I noticed that I can do:

((int []) {1,2,3})[0] = 100;

and, I have no compilation error and is guessable that the first element of that unnamed array is modified with 100.

So it seems as array as compound literal are lvalue and not constant value.

Zanazander answered 14/7, 2014 at 15:21 Comment(3)
Just to add: I found no reference in standard (at least in C99 1256 draft) that 100, 'c' and 123.4f are any form of literals. These are defined as integer constant, character constant (also known as integer character constant to put accent on int type) and floating constant respectively.Shapeless
Shafik's answer sums that up; in C99, literal means lvalue literals, and constant means rvalue literals. This is orthogonal to the concept of read-only (i.e. const) .Betterment
what is your question?Jer
N
6

It is an lvalue, we can see this if we look at the draft C99 standard section 6.5.2.5 Compound literals it says (emphasis mine):

If the type name specifies an array of unknown size, the size is determined by the initializer list as specified in 6.7.8, and the type of the compound literal is that of the completed array type. Otherwise (when the type name specifies an object type), the type of the compound literal is that specified by the type name. In either case, the result is an lvalue.

If you want a const version, later on in the same section it gives the following example:

EXAMPLE 4 A read-only compound literal can be specified through constructions like:

(const float []){1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6}

We can find an explanation of the terminology in this Dr Dobb's article The New C: Compound Literals and says:

Compound literals are not true constants in that the value of the literal might change, as is shown later. This brings us to a bit of terminology. The C99 and C90 Standards [2, 3] use the word “constant” for tokens that represent truly unchangeable values that are impossible to modify in the language. Thus, 10 and 3.14 are an integer decimal constant and a floating constant of type double, respectively. The word “literal” is used for the representation of a value that might not be so constant. For example, early C implementations permitted the values of quoted strings to be modified. C90 and C99 banned the practice by saying that any program than modified a string literal had undefined behavior, which is the Standard’s way of saying it might work, or the program might fail in a mysterious way. [...]

Naylor answered 14/7, 2014 at 15:28 Comment(3)
Why, then, are there named as literals if literals are constant?Zanazander
@xdevel2000: literals are not constant (as required for switch cases, for example); they are, maybe, read-only values. Changing a read-only value is Undefined Behaviour: it may work when the moon is full ...Propertied
lvalue does not necessarily mean modifiable lvalue (formal C standard term). Only that the object persists beyond the expression where it was used.Ayres
S
1

As far I remeber you are right, compound literals are lvalues*, you can also take pointer of such literal (which points to its first element):

int *p = (int []){1, 2, 3};
*p = 5; /* modified first element */

It is also possible to apply const qualifier on such compound literal, so elements are read-only:

const int *p = (const int []){1, 2, 3};
*p = 5; /* wrong, violation of `const` qualifier */

*Note this not means it's automatically modifiable lvalue (so it can used as left operand for assignment operator) since it has array type and refering to C99 draft 6.3.2.1 Lvalues, arrays, and function designators:

A modifiable lvalue is an lvalue that does not have array type, [...]

Shapeless answered 14/7, 2014 at 15:28 Comment(0)
B
1

Compound literals are lvalues and it's elements can be modifiable. You can assign value to it. Even pointer to compound literals are allowed.

Bechtold answered 14/7, 2014 at 15:29 Comment(7)
lvalue does not necessarily mean modifiable lvalue (formal C standard term). Only that the object persists beyond the expression where it was used.Ayres
@Lundin; AFAIK, standard mention lvalue as modifiable except when it talk about arrays.Bechtold
Yeah but it doesn't explicitly mention compound literals. The relevant part would be C17 6.3.2.1: 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. To say that this proves that compound literals are read/write is far-fetched. Notably, a compound literal can be an array.Ayres
(Reason I ended up stalking this old post is this fresh duplicate.)Ayres
@Lundin; I can see my mistake now. It was a blunder. The thing is compound literals are unnamed objects and saying it is modifiable doesn't make any sense to me. Because how can you modify an object (put it on the right side of an assignment operator) which is unnamed? What lvalue means here is you can modify it as you modify aggregate type (like arrays where you modify it's element not the array).Bechtold
It doesn't make much sense, but modifying it is a quite possible scenario: int* ptr = (int[]){ ...}; ptr[n] = ...;. The question is if this is well-defined behavior.Ayres
@Lundin; What I mean by modifiable is that it can be used as left operand of the assignment operator and unary ++ operator can be applied on it. In case of array , it is not modifiable. In case of CL, it doesn't make any sense. What you are saying is possible and standard says about CL that it is modifiable.Bechtold
K
1

Referring to the C11 standard draft N1570:

Section 6.5.2.5p4:

In either case, the result is an lvalue.

An "lvalue" is, roughly, an expression that designates an object -- but it's important to note that not all lvalues are modifiable. A simple example:

const int x = 42;

The name x is an lvalue, but it's not a modifiable lvalue. (Expressions of array type cannot be modifiable lvalues, because you can't assign to an array object, but the elements of an array may be modifiable.)

Paragraph 5 of the same section:

The value of the compound literal is that of an unnamed object initialized by the initializer list. If the compound literal occurs outside the body of a function, the object has static storage duration; otherwise, it has automatic storage duration associated with the enclosing block.

The section describing compound literals doesn't specifically say that whether the unnamed object is modifiable or not. In the absence of such a statement, the object is taken to be modifiable unless the type is const-qualified.

The example in the question:

((int []) {1,2,3})[0] = 100;

is not particularly useful, since there's no way to refer to the unnamed object after the assignment. But a similar construct can be quite useful. A contrived example:

#include <stdio.h>
int main(void) {
    int *ptr = (int[]){1, 2, 3};
    ptr[0] = 100;
    printf("%d %d %d\n", ptr[0], ptr[1], ptr[2]);
}

As mentioned above, the array has automatic storage duration, which means that if it's created inside a function, it will cease to exist when the function returns. Compound literals are not a replacement for malloc.

Kailey answered 15/7, 2014 at 2:40 Comment(5)
"The section describing compound literals doesn't specifically say that whether the unnamed object is modifiable or not." Is it correct to state that since (refering to C99 6.3.2.1) "A modifiable lvalue is an lvalue that does not have array type" that such compound literals are not modifiable lvalues ? I tried to explain this in my answer and I assumed that such literals are effectively treated as array types in that matter.Shapeless
@GrzegorzSzpetkowski: A compound literal of array type is an lvalue of array type. It's not a modifiable lvalue, but the elements of the array object may be modifiable.Kailey
"In the absence of such a statement, the object is taken to be modifiable unless the type is const-qualified." How did you come to this conclusion? It seems quite unclear to me. The lack of text saying that a non-const qualified compound literal is a modifiable lvalue could possibly be a standard defect.Ayres
@Ayres A compound literal "provides an unnamed object whose value is given by the initializer list". There's nothing to say that that object is not modifiable. The compound literal itself is an lvalue and is not a modifiable lvalue (because it's of array type), but the object that it provides is not const qualified and so it can be modified. Similarly, given int arr[2] = { 10, 20 };, the name arr is not a modifiable lvalue, but the object created by the definition can be modified.Kailey
@KeithThompson But a compound literal may as well be a struct or a single variable. type* ptr = &(type){...}; *ptr = ...; seems like a possible use-case. Reason I brought this up is there's a similar discussion here: https://mcmap.net/q/1305518/-struct-vs-string-literals-read-only-vs-read-write-duplicate.Ayres

© 2022 - 2024 — McMap. All rights reserved.