3

What is the unary-& doing here?

int * a = 1990; 
int result = &5[a]; 

If you were to print result you would get the value 2010.

You have to compile it with -fpermissive or it will stop due to errors.

maxywb
  • 2,275
  • 1
  • 19
  • 25
  • I have never seen code that like. Waiting for an answer – taocp Oct 07 '14 at 14:24
  • 3
    See http://stackoverflow.com/questions/381542/with-c-arrays-why-is-it-the-case-that-a5-5a the `-fpermissive` is needed because it makes a pointer out of an integer literal in the first line. –  Oct 07 '14 at 14:24
  • 2
    The `&` doesn't act on the literal `5` but on the expression `5[a]`; `&5[a]` means `&(5[a])`. – M Oehm Oct 07 '14 at 14:26
  • I do not believe this is a duplicate of the cited question, the behavior of `&` is key here and not covered by that question. – Shafik Yaghmour Oct 07 '14 at 14:35
  • Oh come on it is an exact duplicate! Who voted to re-open this? & is nothing special, take the _exact duplicate post_ already linked then add a & to it. This really just seems like rep-farming to me. – Lundin Oct 07 '14 at 14:48
  • @delnan: Making a pointer out of an integer is always illegal in both C and C++. It is not an excuse to use some switch of some specific compiler to override the language rules. – AnT stands with Russia Oct 07 '14 at 14:50
  • @AndreyT No. C11 6.3.2.3 `An integer may be converted to any pointer type. Except as previously specified, the result is implementation-defined, might not be correctly aligned, might not point to an entity of the referenced type, and might be a trap representation.` – Lundin Oct 07 '14 at 14:52
  • C or C++? Please decide. – Griwes Oct 07 '14 at 14:53
  • @Lundin The language [requires a cast for that](http://stackoverflow.com/questions/25725255/why-this-program-compiles-fine-in-c/25725580#25725580). – T.C. Oct 07 '14 at 14:54
  • @AndreyT What gives? It's so obviously completely stupid that it would not ever cross my mind and that I really have better things to do than condemn it every time I come across it. I don't advocate it. I just pinpointed it as the part that requires the flag because, as you say, it's not legal in the standard. –  Oct 07 '14 at 14:55
  • @Griwes It works with both gcc and g++, hence both tags. – maxywb Oct 07 '14 at 14:57
  • @Lundin I was looking for an answer like the one linked and just didn't find it. I fully expect this to be closed again. As for rep farming, I have far better things to be doing than farming some internet rep. – maxywb Oct 07 '14 at 14:59
  • @maxywb, you are writing the code in a single language; tag it with the language you are actually using, not all the others where it also works by a chance... – Griwes Oct 07 '14 at 14:59
  • @Griwes per the discussion in one of the answers below, the relevant section of the spec covers both languages, and, it appears, this works "by chance" in both languages. I'm not removing either of the language tags. – maxywb Oct 07 '14 at 15:02
  • To clarify: making a pointer out of an integer literal is not a so-called "simple assignment", so an explicit cast is required, see 6.5.4 and 6.5.16.1. If you add such a cast, converting an integer to a pointer or vice versa is fine, as per the rule I previously cited. If you do not add such a cast, this will not compile on a standard compiler. To compile strictly conforming C code with gcc, you must type `gcc -std=c11 -pedantic-errors`. – Lundin Oct 07 '14 at 15:02
  • @maxywb, [we have been over this](http://meta.stackoverflow.com/q/252430/809387). – Griwes Oct 07 '14 at 15:04
  • @Lundin: Not again... 6.3.2.3 defined the general rules of all conversions. 6.3.2.3 never says that these conversions will somehow happen for you implicitly, automatically. 6.3. clearly states that the intent of the entire section covers both **implicit** and **explicit** conversions. Now, if you read the rest of the document you will realize that integer-to-pointer conversion in C *always* requires an explicit cast. – AnT stands with Russia Oct 07 '14 at 15:06
  • @maxywb: Even if we overlook the illegal code and the undefined behavior, the behavior of this code will still be highly implementation-dependent. I.e. the answer is no, in general case you will not get 2010 in `result`. – AnT stands with Russia Oct 07 '14 at 15:16
  • @AndreyT Try reading my comment again. – Lundin Oct 07 '14 at 15:23
  • 1
    It is equivalent to &(*(5 + a)). What does it mean? – user966379 Oct 07 '14 at 15:58
  • @AndreyT: "integer-to-pointer conversion in C *always* requires an explicit cast.". No, just *almost* always. `int *ptr = 0;`. (No, that's not relevant to the question.) – Keith Thompson Feb 04 '15 at 21:05

4 Answers4

10

In C, x [y] and y [x] are identical. So &5[a] is the same as &a[5].

gnasher729
  • 51,477
  • 5
  • 75
  • 98
  • 4
    @Thirler: it's an accident of how array access is defined. `a[i]` is evaluated as `*(a+i)`, and the only constraint is that one of `a` or `i` be a pointer and the other be an integer. Addition is commutative, so `*(a + i)` == `*(i + a)` == `i[a]`. You won't typically see the `i[a]` form outside of the [IOCCC](http://ioccc.org/) or similar hackfests. – John Bode Oct 07 '14 at 14:31
  • 1
    @taocp No it isn't good to know, since there doesn't exist any case where you should write artificial nonsense code like this. – Lundin Oct 07 '14 at 14:53
  • @Lundin well knowledge-wise. It is still good to know, though a little wild. – taocp Oct 07 '14 at 14:54
5

&5[a] is the same as &a[5] and the same as a + 5. In your case it's undefined behavior because a points to nowhere.

C11 standard chapter 6.5.6 Additive operators/8 (the same in C++):

If both the pointer operand and the result point to elements of the same array object, or one past the last element of the array object, the evaluation shall not produce an overflow; otherwise, the behavior is undefined.

Anton Savin
  • 40,838
  • 8
  • 54
  • 90
  • 2
    Sure it does: memory address 1990, and since it is an `int*`, `&a[5]` is the address of the element at offset 5 of that; assuming `int`s are 4 bytes, that would be a 20-byte offset, giving the result of 2010. Now, I agree it would likely be a bad idea to try and access (much less change) what is there, but it is perfectly well defined. – Scott Hunter Oct 07 '14 at 14:28
  • @ScottHunter No, the language standard (regardless of whether you commented about C or about C++) doesn't define the conversion of arbitrary integer values to pointers. In this case, it'll likely work, but in general, compilers do optimise based on the assumption that you don't do that, and if you did anyway, things can break in mysterious and interesting ways. –  Oct 07 '14 at 14:38
  • @Scott Hunter: No it doesn't. Neither C nor C++ allow this code `int * a = 1990;`. It doesn't even compile. – AnT stands with Russia Oct 07 '14 at 14:40
  • @ScottHunter no, it's undefined behavior even to add `a` and 5. – Anton Savin Oct 07 '14 at 14:40
  • @AndreyT in C it's valid to assign integer number to pointer (`6.3.2.3/5`) – Anton Savin Oct 07 '14 at 14:48
  • @Anton Savin: False. It C it is illegal. `6.3.2.3/5` tells you what will happen if you convert the integer to a pointer. It does not tell you that the conversion will be made for you implicitly. The rules of initialization and assignment of C language prohibit implicit conversion of integers to pointers. – AnT stands with Russia Oct 07 '14 at 14:51
  • @AndreyT *An integer **may be converted to any pointer type**. Except as previously specified, the result is implementation-defined, might not be correctly aligned, might not point to an entity of the referenced type, and might be a trap representation* – Anton Savin Oct 07 '14 at 14:53
  • 1
    @Anton Savin: One more time: what is says that an integer my be converted to pointer type by an **explicit** conversion. It says that you can do this: `int *p = (int *) 123;`. It does not say that you can do this `int *p = 123;`. You cannot. The beginning of `6.3` clearly says that this section covers both **explicit** and **implicit** conversions. Just because some conversion is mentioned under `6.3...` does not mean that it will be done for you implicitly (i.e. automatically). – AnT stands with Russia Oct 07 '14 at 14:54
  • If you want to know what you can and cannot do in assignment and initialization contexts, you have to read the corresponding sections of the standard (assignment and initialization). And these sections clearly prohibit assigning/initializing pointers with integers. This is actually a common knowledge that has been discussed to death already. I don't know why it keeps popping up and why people continue to see in `6.3...` something that is not really there. – AnT stands with Russia Oct 07 '14 at 14:57
  • @AndreyT ok got it, thanks. Anyway, `int* a = (int*)1990; a + 5;` is undefined behavior. – Anton Savin Oct 07 '14 at 15:01
3

"...unary & on numeric literal"?

Postfix operators in C always have higher priority than prefix ones. In case of &5[a], the [] has higher priority than the &. Which means that in &5[a] the unary & is not applied to "numeric literal" as you seem to incorrectly believe. It is applied to the entire 5[a] subexpression. I.e. &5[a] is equivalent to &(5[a]).

As for what 5[a] means - this is a beaten-to-death FAQ. Look it up.

And no, you don't have "to compile it with -fpermissive" (my compiler tells me it doesn't even know what -fpermissive is). You have to figure out that this

int * a = 1990; 

is not legal code in either C or C++. If anything, it requires an explicit cast

int * a = (int *) 1990; 

not some obscure switch of some specific compiler you happened to be using at the moment. The same applies to another illegal initialization in int result = &5[a].

Finally, even if we overlook the illegal code and the undefined behavior triggered by that 5[a], the behavior of this code will still be highly implementation-dependent. I.e. the answer is no, in general case you will not get 2010 in result.

AnT stands with Russia
  • 312,472
  • 42
  • 525
  • 765
1

You cannot apply the unary & operator to an integer literal, because a literal is not an lvalue.

Due to operator precedence, your code doesn't do that. Since the indexing operator [] binds more tightly than unary &, &5[a] is equivalent to &(5[a]).

Here's a program similar to yours, except that it's valid code, not requiring -fpermissive to compile:

#include <stdio.h>
int main(void) {
    int arr[6];
    int *ptr1 = arr;
    int *ptr2 = &5[ptr1];
    printf("%p %p\n", ptr1, ptr2);
}

As explained in this question and my answer, the indexing operator is commutative (because it's defined in terms of addition, and addition is commutative), so 5[a] is equivalent to a[5]. So the expression &5[ptr1] computes the address of element 5 of arr.

In your program:

int * a = 1990;
int result = &5[a];

the initialization of a is invalid because a is of type int* and 1990 is of type int, and there is no implicit conversion from int to int*. Likewise, the initialization of result is invalid because &5[a] is of type int*. Apparently -fpermissive causes the compiler to violate the rules of the language and permit these invalid implicit conversions.

At least in the version of gcc I'm using, the -fpermissive option is valid only for C++ and Objective-C, not for C. In C, gcc permits such implicit conversions (with a warning) anyway. I strongly recommend not using this option. (Your question is tagged both C and C++. Keep in mind that C and C++ are two distinct, though closely related, languages. They happen to behave similarly in this case, but it's usually best to pick one language or the other.)

Community
  • 1
  • 1
Keith Thompson
  • 254,901
  • 44
  • 429
  • 631