3

Are there limits on what I can do to allocated memory?(standard-wise)

For example

#include <stdio.h>
#include <stdlib.h>

struct str{
    long long a;
    long b;
};

int main(void) 
{
    long *x = calloc(4,sizeof(long));
    x[0] = 2;
    x[3] = 7;
//is anything beyond here legal( if you would exclude possible illegal operations)
    long long *y = x; 
    printf("%lld\n",y[0]); 
    y[0] = 2;
    memset (x,0,16);
    struct str *bar = x;
    bar->b =  4;
    printf("%lld\n",bar->a); 
    return 0;
}

To summarize:

  • Can I recast the pointer to other datatypes and structs, as long as the size fits?
  • Can I read before I write, then?
  • If not can I read after I wrote?
  • Can I use it with a struct smaller than the allocated memory?
M.M
  • 138,810
  • 21
  • 208
  • 365
Kami Kaze
  • 2,069
  • 15
  • 27

3 Answers3

4

Reading from y[0] violates the strict aliasing rule. You use an lvalue of type long long to read objects of effective type long.

Assuming you omit that line; the next troublesome part is memset(x,0,16);. This answer argues that memset does not update the effective type. The standard is not clear.

Assuming that memset leaves the effective type unchanged; the next issue is the read of bar->a.

The C Standard is unclear on this too. Some people say that bar->a implies (*bar).a and this is a strict aliasing violation because we did not write a bar object to the location first.

Others (including me) say that it is fine: the only lvalue used for access is bar->a; that is an lvalue of type long long, and it accesses an object of effective type long long (the one written by y[0] = 2;).

There is a C2X working group that is working on improving the specification of strict aliasing to clarify these issues.

Community
  • 1
  • 1
M.M
  • 138,810
  • 21
  • 208
  • 365
  • [6.5p6](http://port70.net/~nsz/c/c11/n1570.html#6.5p6) implies that `memove` and company modify the effective type. – StoryTeller - Unslander Monica Jan 26 '17 at 11:44
  • @StoryTeller memmove and memcpy certainly do, but I don't think memset comes under the same umbrella. If you say that memset sets the effective type (to char - what else?), then the common idiom `memset(x, 0, n);` to zero-initialize some ints would lead to UB, so I think that would not be a practical interpretation – M.M Jan 26 '17 at 11:46
  • So a read or write on `bar->b` would be okay because it would be the same alias? – Kami Kaze Jan 26 '17 at 12:36
  • @M.M: From what I've seen, the C2X proposals seem more focused on adding even more situations where compilers can ignore potential aliasing rather than adding ways for programmers to say *hey these things might alias*. If the Standard would allow ways by which programmers could say what things might alias and define different levels of alias-compatibility, then code where all forms of aliasing were marked could be safely optimized more aggressively than under present rules, but programmers wouldn't need to use `-fno-strict-aliasing` to make things work. – supercat Feb 10 '17 at 23:33
3

Can I recast the pointer to other datatypes, as long as the size fits?

You can recast1 to any data type that is at most as large as the memory you allocated. You must write a value however to change the effective type of the allcoated object according to 6.5p6

Can I read before I write, then?
If not can I read after I wrote?

No. Except when otherwise specified (calloc is the otherwise)2, the value in the memory is indeterminate. It may contain trap values. A cast in order to reinterpret a value as another type is UB, and a violation of strict aliasing (6.5p7)

Can I use it with a struct smaller than the allocated memory?

Yes, but that's a waste.


1 You'll need to cast to void* first. Otherwise you'd get a rightful complaint from the compiler about incompatible pointer types.
2 Even then some types may trap on a completely 0 bit pattern, so it depends.

StoryTeller - Unslander Monica
  • 165,132
  • 21
  • 377
  • 458
0

Most compilers offer a mode where reads and writes of pointers will act upon the underlying storage, in the sequence they are performed, regardless of the data types involved. The Standard does not require compilers to offer such a mode, but as far as I can tell all quality compilers do so.

According to their published rationale, the authors of the Standard added aliasing restrictions to the language with the stated purpose of avoiding compilers to make pessimistic aliasing assumptions when given code like:

float f;
float test(int *p)
{
  f=1.0f;
  *p = 2;
  return f;
}

Note that in the example given in the rationale [very much like the above], even if it were legal to modify the storage used by f via pointer p, a reasonable person looking at the code would have no reason to think it likely that such a thing would ever happen. On the other hand, many compiler writers recognized that if given something like:

float f;
float test(float *p)
{
  f=1.0f;
  *(int*)p = 2;
  return f;
}

one would have to be deliberately obtuse to think that the code would be unlikely to modify the storage used by a float, and there was consequently no reason why a quality compiler should not regard the write to *(int*)p as a potential write to a float.

Unfortunately, in the intervening years, compiler writers have become increasingly aggressive with type-based aliasing "optimizations", sometimes in ways that go clearly and undeniably beyond what the Standard would allow. Unless a program will never need to access any storage as different types at different times, I'd suggest using -fno-strict-aliasing option on compilers that support it. Otherwise one may have code that complies with the Standard and works today, but fails in a future version of the compiler which has become even more aggressive with its "optimizations".

PS--Disabling type-based aliasing may impact the performance of code in some situations, but proper use of restrict-qualified variables and parameters should avoid the costs of pessimistic aliasing assumptions. With a little care, use of those qualifiers will enable the same optimizations as aggressive aliasing could have done, but much more safely.

supercat
  • 77,689
  • 9
  • 166
  • 211
  • I guess I have to do some reading to fully understand your answer. Thank you for the detailed description. – Kami Kaze Feb 13 '17 at 08:13
  • @KamiKaze: Read up on `restrict`; it can offer performance benefits even when one doesn't disable type-based aliasing, but can become especially important if one does so. – supercat Feb 13 '17 at 15:03