-1

I would like to know if there is anything that prevents doing this in ANSI C (or anything prior to C99 which has the strict aliasing rule).

const int n = 1000;
double *a = (double *) malloc(n * sizeof(double));

// Weird aliasing
a = (double *) (&a);

f(a, n);
free(a);

this questions comes from the fact that the Intel compiler does vectorize the following code

void f(double *a, int n) {
    int k;
    for (k = 0; k < n; ++k) {
        a[k] = a[k] + 1.0;
    }
}

without the -ansi-aliasing option (by default, Intel compilers don't use the strict aliasing rule). My guess is that it should not as the previous code changes what a points to at the first loop.

Francois

explanation : As an explanation is often asked on the reason to this, you can read one of Chris Lattner post on http://blog.llvm.org/2011/05/what-every-c-programmer-should-know.html in the "Violating Type Rules" section. It seemed to me that he was using the strict aliasing rule and therefore C99 to state that what he does is not valid.

InsideLoop
  • 6,063
  • 2
  • 28
  • 55

2 Answers2

3

No. a has type double*. Therefore &a has type double**. You're casting double** to double*. That's not allowed in any version of C.

For example in C89, see section 3.3:

An object shall have its stored value accessed only by an lvalue that has one of the following types: [28]

  • the declared type of the object,

  • a qualified version of the declared type of the object,

  • a type that is the signed or unsigned type corresponding to the declared type of the object,

  • a type that is the signed or unsigned type corresponding to a qualified version of the declared type of the object,

  • an aggregate or union type that includes one of the aforementioned types among its members (including, recursively, a member of a subaggregate or contained union), or

  • a character type.

[Footnote 28] The intent of this list is to specify those circumstances in which an object may or may not be aliased.

pdw
  • 8,359
  • 2
  • 29
  • 41
  • Is this possible to get a reference on that impossibility. For instance Chris Lattner uses it in one of his blog http://blog.llvm.org/2011/05/what-every-c-programmer-should-know.html . – InsideLoop Aug 11 '14 at 12:22
  • @Mat: He does that in the last piece of code : P = (float*)&P . He says that it is not allowed and my feeling is that he was using the strict aliasing rule to discard it. Is the ANSI C standard available on the web to find out what is allowed in terms of pointer casting ? I can't find it. – InsideLoop Aug 11 '14 at 12:29
  • @Mat: From the link: `float *P; [...] P = (float*)&P;` – alk Aug 11 '14 at 12:32
  • The C11 Standard (draft): http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf @InsideLoop – alk Aug 11 '14 at 12:36
  • @pdw : This is very interesting, as it is exactly the strict aliasing rule. so why do people usually say that strict aliasing was introduced in C99 ? – InsideLoop Aug 11 '14 at 13:01
  • Because people are stupid. Before C99 it was "even more undefined". – R.. GitHub STOP HELPING ICE Aug 11 '14 at 13:12
  • @InsideLoop, I think in C89 the rule was mostly seen as a compatibility measure, to ensure a conforming C compilers could be implemented on machines with weird memory architectures. With C99 people started looking at it as an optimization opportunity (see also the addition of the restrict keyword). In any case GCC only started optimizating based on strict aliasing in 200x. – pdw Aug 11 '14 at 13:22
  • @pdw Thanks. And by default, Intel compilers still don't :-(. Thank you very much anyway. – InsideLoop Aug 11 '14 at 13:41
1
a = (double *) (&a);

That line has nothing to do with "aliasing", nor with the malloc call, nor with the snippet you linked after that. You are simply taking the address where the pointer a is stored (a stack address), then attempt to cast that address to a double*. Then you assign the questionable result to a, creating a memory leak. Since you changed what a points at, the program will crash and burn when you call free().

On the line in question you are casting from double** to double*. The relevant section of the standard is C11 6.3.2.3 (these rules are unchanged from C90 to C11):

A pointer to an object type may be converted to a pointer to a different object type. If the resulting pointer is not correctly aligned for the referenced type, the behavior is undefined. Otherwise, when converted back again, the result shall compare equal to the original pointer. When a pointer to an object is converted to a pointer to a character type, the result points to the lowest addressed byte of the object. Successive increments of the result, up to the size of the object, yield pointers to the remaining bytes of the object.

Meaning, as long as the cast doesn't result in a misalignment, the actual cast is allowed. It seems rather unlikely that a double* and a double** would have different alignments, but still there are no guarantees by the C standard.

As for what happens when you refer to that memory, C11 6.5.3.2/4:

If an invalid value has been assigned to the pointer, the behavior of the unary * operator is undefined.

So please note that your code doesn't make any sense and has no practical use.

Lundin
  • 195,001
  • 40
  • 254
  • 396
  • Note that while the cast from `double*` to `double**` is allowed, you're not allowed to dereference the resulting pointer, see C11 6.5.7. – pdw Aug 11 '14 at 12:41
  • @Lundin : Thanks for your clear explanation. Do you have a link to the ANSI C standard ? – InsideLoop Aug 11 '14 at 12:42
  • @InsideLoop ANSI-C has been obsolete for 25 years, [see this](http://stackoverflow.com/questions/17206568/what-is-the-difference-between-c-c99-ansi-c-and-gnu-c-a-general-confusion-reg/17209532#17209532). As for the C11 standard, it can be purchased from [here](http://www.iso.org/iso/iso_catalogue/catalogue_tc/catalogue_detail.htm?csnumber=57853) or your national standards institute. However, there is a [free draft](http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1548.pdf) version of the standard available. – Lundin Aug 11 '14 at 12:45
  • @Lundin : I clearly know that the code does not make sense. The question came to me in C++ where the code of f, with std::vector does not vectorize with intel compilers without the -ansi-aliasing flag, but does with the -ansi-aliasing flag. I was surprised that the -ansi-aliasing flag was needed to vectorize std::vector but not with a C-Array. – InsideLoop Aug 11 '14 at 12:45
  • @Lundin : ANSI C might be obsolete, but some people who are known to be quite good at C programming don't use C99 nor C2011 : https://lkml.org/lkml/2003/2/26/158 . Besides, the Intel compiler, by default, does not use the strict aliasing rule from C99 as does the Microsoft C compiler. – InsideLoop Aug 11 '14 at 12:48
  • @pdw: C11 6.5.7 is titled "Bitwise shift operators". I don't think it says much about casts or pointers. – Nisse Engström Aug 13 '14 at 20:16
  • @NisseEngström: Sorry, I meant 6.5 p7. – pdw Aug 13 '14 at 22:53