tags:

views:

252

answers:

5

What, exactly, is numbers in the following declaration, if it is not an address constant?

int main() {
    int numbers[3] = {1,2,3};
    return 0;
}

Disassembling the program shows that 1, 2, and 3 are dynamically placed on the local stack space, rather than the whole array being treated as a constant. Hence, {1,2,3} does not have static storage duration, so numbers is not an address constant, as per the C99 spec.

C99, Section 6.6.9: "An address constant is a null pointer, a pointer to an lvalue designating an object of static storage duration, or a pointer to a function designator..."

However, adding the line numbers++ after the declaration causes the following compile error in GCC 4.1.2:

error: invalid lvalue in increment

So it is constant, but isn't an address constant. Does anybody know the official name of this type of constant in C99 (or similar)?

A: 

numbers can't be changed because it must always point to the first element in the array.

SilverSun
Right, I understand it's a constant. But what is the official name of this type of constant?
Carlo
`numbers` can't be changed because it is an array, not a pointer.
Hasturkun
+8  A: 

numbers is a non-constant, automatic, array variable of the function main.

Because it is automatic and non-constant it can not have static storage.

because it is an array variable (and not you'll notice a pointer) it can not be incremented.

Note that you can do

int main() {
    int numbers[3] = {1,2,3};
    int *n = numbers+1;
    n++;
    return 0;
}
dmckee
Sounds plausible, but the term "array variable" is not in the C99 spec at all. Is this the exact distinction being drawn?
Carlo
@Carlo: I can't answer you abot the standard. Just about the semantics. You can't use the post-increment operator on `numbers` because it is an array-typed variable, and that operation is not defined on arrays; and it's storage can not live in the data segment (or any other mechanism for deploying static storage) because the variable is non-constant and automatic.
dmckee
Found it! In Section 6.3.2.1.3, it states that "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."
Carlo
Basically, there is an essential distinction between "array" and "pointer" variables, which is that the former is not an lvalue. Thanks!
Carlo
@Carlo: Er... No, your final conclusion is absolutely incorrect. Array *is* an lvalue. The result of array-to-pointer conversion is not an lvalue, but the array itself is. For example, if you declare `double d`, variable `d` is an lvalue, but the result of `(int) d` expression is not an lvalue. The same thing happens with an array. `numbers` is an lvalue, but `(int *) numbers` is not an lvalue, except that in this case the `(int *)` conversion is performed implictly.
AndreyT
Sorry, I meant: the former is converted to a pointer type which is not an lvalue (although true pointers are lvalues).
Carlo
Actually, a question: You say an array is an lvalue. But how could you use it as an lvalue? It can't be something like `numbers[0] = 1`, since `numbers[0]` is an int, not an array.
Carlo
An array is a lvalue, but not a *modifiable* lvalue. (6.3.2.1) And the operand of the postfix increment must be a *modifiable* lvalue. (6.5.2.4)
schot
caf
@caf, there is one more case: When used as the operand of `sizeof`. (As used in the common idiom `nelem = sizeof a / sizeof a[0]`).
schot
caf
+8  A: 

You seem to be inventing something that isn't relly there, terminology-wise.

numbers is an array. It is an automatic object of type int[3]. It cannot be used to form address constants in C, since in C address constants require objects with static storage duration.

If your numbers was declared with static storage duration, then the result of array-to-pointer conversion applied to numbers would be an address constant. numbers + 1 as well as &numbers[2] would also be address constant in that case.

You are right to note that { 1, 2, 3 } doesn't have static storage duration. In fact, it has no storage duration at all. It is not an object, but merely a piece of syntactic sugar called aggregate initializer. If you wanted it to become an anonymous object, you'd have to use the compound literal syntax: (int[]) { 1, 2, 3 }, but it wouldn't work in the above context anyway.

numbers++ will not compile simply because the result of array-to-pointer conversion is not an lvalue. You can't apply ++ to a non-lvalue. Whether something is constant or not is irrelevant.

You seem to making a strange conclusion that if you can't modify it it must be a constant. That's totally incorrect. In C terminology the property of being constant has very little to do with the property of being modifiable. The term constant refers to values that are known at compile time. Values that are not known at compile time are never called constants, even if they are not modifiable. This is an example of that: the address of an automatic array is not known at compile time, so even though that address is not modifiable, it is still not an address constant.

AndreyT
Yes. The fact that an array-to-pointer conversion is not an lvalue is what I was interested in. However, I was expecting a semantically cleaner distinction than the "an array is implicitly like a pointer except is not an lvalue" answer that the spec apparently gives.
Carlo
@Carlo: That's *not* what the standard says. It says that an array is an "aggregate type" (just like structure types) - an array is *"a contiguously allocated nonempty set of objects with a particular member object type"* .
caf
@Carlo: Array is not "like a pointer" in general. Array is "like a pointer" only in a well-defined set of specific contexts. Operator `++` just happens to be one of those contexts.
AndreyT
+1  A: 

Arrays are not pointers.

Johannes Dahlström
A: 

The reason it's not an address constant is because it's an address on the stack, and that location depends on what is above it in the call stack. Something can't be a constant if its value isn't known until run time.

It's not a variable either, which is why you can't increment it. It's just the name of a storage location, which happens to be an offset on the base pointer. numbers[0] is a valid l-value, but numbers is not.

Mike Dunlavey
Auto variables are typically allocated on a stack, but that is an implementation detail. Some embedded-systems allocated automatic variables at link time (either recursion is forbidden, or possibly-recursive calls will copy the variables to a pseudo-stack). Unfortunately, even when the auto variables are at linker-determined constant addresses, there's still AFAICT no way to exploit that fact within C.
supercat
@supercat: interesting. thx
Mike Dunlavey