Aliasing Arrays through structs
Asked Answered
L

3

11

I'm reading paragraph 7 of 6.5 in ISO/IEC 9899:TC2.

It condones lvalue access to an object through:

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),

Please refer to the document for what 'aforementioned' types are but they certainly include the effective type of the object.

It is in a section noted as:

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

I read this as saying (for example) that the following is well defined:

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

typedef struct {
    unsigned int x;
} s;

int main(void){
    unsigned int array[3] = {73,74,75};

   s* sp=(s*)&array; 

   sp->x=80;

   printf("%d\n",array[0]);

   return EXIT_SUCCESS;
}

This program should output 80.

I'm not advocating this as a good (or very useful) idea and concede I'm in part interpreting it that way because I can't think what else that means and can't believe it's a meaningless sentence!

That said, I can't see a very good reason to forbid it. What we do know is that the alignment and memory contents at that location are compatible with sp->x so why not?

It seems to go so far as to say if I add (say) a double y; to the end of the struct I can still access array[0] through sp->x in this way.

However even if the array is larger than sizeof(s) any attempt to access sp->y is 'all bets off' undefined behaviour.

Might I politely ask for people to say what that sentence condones rather than go into a flat spin shouting 'strict aliasing UB strict aliasing UB' as seems to be all too often the way of these things.

Lindsy answered 12/1, 2015 at 18:41 Comment(19)
Breaking the strict aliasing rules can cause heavily optimizing compilers to generate code that doesn't do what you intend. I've seen reads/writes get re-ordered (through pointers it thought couldn't possibly point to the same object, but did), causing assignments to seemingly get "missed".Oratorio
I'm well aware of that. That paragraph is explicitly trying to declare what aliasing strictly compliant compilers should and should not respect. What happens when you ask your compiler to ignore some of the more cautious rules and go to town is out of the scope of this question.Lindsy
Without going through the reference doc again, if i remember correctly it actually meant to refer to instances where the array was a member of the struct itself instead of merely being type casted to a pointer of struct type. So if you want to pass an array by value, put it as a data member in a struct and pass the struct by refRepair
@fayyazkl I don't think that fits. The paragraph takes about objects being accessed. Each member of an array s an object. So I'll grant you that if you had an array member and 'lined it up' over an array then that paragraph (by my proposed interpretation) says it can be accessed as an lvalue. But that's a special case of what that paragraph says.Lindsy
Have you read and digested What is the strict aliasing rule? and the links pointed at from there? Do they help?Analogue
@JonathanLeffler I'm quite familiar with what strict aliasing is, why it matters and how it might help and hinder your life. I am old enough to have used Borland Turbo C++ when you essentially had to opt for 'no pointer aliasing' to get decent poke out of most labour intensive numerical analysis methods (lots of big arrays of double) because the poor old optimizer couldn't figure anything out with potentially dozens of apparent aliases that aren't. The question remains what exactly that paragraph permits under strict compliance.Lindsy
@JonathanLeffler So what I'm saying is "What does the paragraph I've highlighted mean if it doesn't mean that the accepted answer you like to is wrong if on your platform unsigned int is 32-bit. Because (as it appears) the para. I highlight goes out of its way explicitly permit that kind of aliasing. BTW, I think this is a rather esotric point and the pure ambiguity of that statement makes me reluctant to rely on it. Who knows whether implementers interpret it correctly? But the question remains. What is that particular sentence saying? Not, please explain aliasing to me. I'm good.Lindsy
Is that ISO/IEC 9899:1999 with TC2 applied? Or is it 9899:2011 with TC2 applied? I'm not clear that 9899:1999 TC2 actually modifies the specification at this point, and the paragraph numbers in the two are the same and the contents at least very similar (I'm about to start checking them verbatim). Paragraph 6 before defines 'effective type'. Section 6.2.7 in both 9899:1999 and 9899:2011 define 'compatible type'. It would be easier if I knew exactly which standard you're referring to, and if you're concerned about C99, why not C11? (I'm not aware of a TC2 for C11, so I'm guessing it's C99.)Analogue
Note, since there is a deleted answer that mentions gcc and -fstrict-aliasing. The gcc documents says that that all levels have various degrees of false positives and negaties and so can not really be used as a reliable indication that the code does/does not violate strict aliasing. The checks fails on many trivial examples.Linus
@JonathanLeffler For the exclusion of doubt this document: open-std.org/jtc1/sc22/wg14/www/docs/n1124.pdf though the phrase appears in other standards including at least some (possibly all) of the C++ standards. It's trying to express something some people think is important. But what?Lindsy
Reading the various pages and posts on this, there seems to be a common misconception that if a X * and a Y * (incompatible) point to overlapping memory locations then they can't both be used to access any sub-object. However it seems to me to be very clear from the wording chosen by the standards that if X and Y both contain a member of the same type, then it is not an aliasing violation to access that member via X and via Y.Kalindi
@MattMcNabb So far the best statement of the situation yet stated. I continue to be unable to interpret that sentence as other than explicitly permitting what you say - accessing object of the same type as members through aggregates that are (overall) not necessarily compatible. If you post that as an answer it may well be that I eventually accept it.Lindsy
I was originally very skeptical about that code could be conforming code but apparently it is although seemingly unintentianlly. I learned something new from this question and it turns out this was indeed an excellent question. cc @JonathonReinhartLinus
@JonathanLeffler that strict-aliasing question does not cover this case, it is a very odd case and apparently a known issue.Linus
@ShafikYaghmour I don't think it's so odd. If you re-read that defect report you cited, you'll see the point is made that it's arguably covered by other points (though not clearly). What seems to be at stake is "Are structs really just the (padded) sum of their members or is it that members in structs can (and/or can't) be used in ways equivalent objects that aren't a member can (and/or can't) be used?" The answer to that really should be an emphatic "structs really are the padded sum of their parts (no more no less)" and if they aren't you start defeating the strengths of the C language.Lindsy
@ShafikYaghmour I have to post this. Obviously the memoirs of Dennis Richie (RIP) are not normative but this sentence is deeply evocative of the issue "early C did not tie structure pointers firmly to the structures they pointed to, and permitted programmers to write pointer->member almost without regard to the type of pointer; such an expression was taken uncritically as a reference to a region of memory designated by the pointer, while the member name specified only an offset and a type." (cm.bell-labs.com/who/dmr/chist.html). It's fascinating how deep this question runs.Lindsy
@DanAllen when I say odd, I mean violates the intuition of a developers in general. I would not consider strict aliasing issues to be common knowledge and this particular case is not covered by any of the strict aliasing article I know of, so this is a rather special case.Linus
@ShafikYaghmour We agree on all that. It is a rather esoteric point but the more I look at it the more I realize it's an important point. I agree it's far from well understood. In part I blame what appears to be a hastily written and poorly scrutinized section of the standard.Lindsy
@DanAllen: At the time the Standard was written, the C language was starting to flourish despite the fact that everything any C program did was, from the point of view of the Standard, Undefined Behavior (the standard that didn't yet exist couldn't impose any requirements on anything). If a behavior would obviously be useful on some platforms but not others, leaving the behavior as Undefined should have simply preserved the status quo. Unfortunately, it has become fashionable to treat the Standard as undefining things that quality implementations would have regarded as defined before.Hereabouts
L
8

The answer to this question is covered in proposal: Fixing the rules for type-based aliasing which we will see, unfortunately was not resolved in 2010 when the proposal was made which is covered in Hedquist, Bativa, November 2010 minutes . Therefore C11 does not contain a resolution to N1520, so this is an open issue:

There does not seem to be any way that this matter will be resolved at this meeting. Each thread of proposed approaches leads to more questions. 1 1:48 am, Thurs, Nov 4, 2010.

ACTION – Clark do more work in

N1520 opens saying (emphasis mine going forward):

Richard Hansen pointed out a problem in the type-based aliasing rules, as follows:

My question concerns the phrasing of bullet 5 of 6.5p7 (aliasing as it applies to unions/aggregates). Unless my understanding of effective type is incorrect, it seems like the union/aggregate condition should apply to the effective type, not the lvalue type.

Here are some more details:

Take the following code snippet as an example:

union {int a; double b;} u;
u.a = 5;

From my understanding of the definition of effective type (6.5p6), the effective type of the object at location &u is union {int a; double b;}. The type of the lvalue expression that is accessing the object at &u (in the second line) is int.

From my understanding of the definition of compatible type (6.2.7), int is not compatible with union {int a; double b;}, so bullets 1 and 2 of 6.5p7 do not apply. int is not the signed or unsigned type of the union type, so bullets 3 and 4 do not apply. int is not a character type, so bullet 6 does not apply.

That leaves bullet 5. However, int is not an aggregate or union type, so that bullet also does not apply. That means that the above code violates the aliasing rule, which it obviously should not.

I believe that bullet 5 should be rephrased to indicate that if the effective type (not the lvalue type) is an aggregate or union type that contains a member with type compatible with the lvalue type, then the object may be accessed.

Effectively, what he points out is that the rules are asymmetrical with respect to struct/union membership. I have been aware of this situation, and considered it a (non-urgent) problem, for quite some time. A series of examples will better illustrate the problem. (These examples were originally presented at the Santa Cruz meeting.)

In my experience with questions about whether aliasing is valid based on type constraints, the question is invariably phrased in terms of loop invariance. Such examples bring the problem into extremely sharp focus.

And the relevant example that applies to this situation would be 3 which is as follows:

struct S { int a, b; };
void f3(int *pi, struct S *ps1, struct S const *ps2)
{
  for (*pi = 0; *pi < 10; ++*pi) {
      *ps1++ = *ps2;
  }
}

The question here is whether the object *ps2 may be accessed (and especially modified) by assigning to the lvalue *pi — and if so, whether the standard actually says so. It could be argued that this is not covered by the fifth bullet of 6.5p7, since *pi does not have aggregate type at all.

**Perhaps the intention is that the question should be turned around: is it allowed to access the value of the object pi by the lvalue ps2. Obviously, this case would be covered by the fifth bullet.

All I can say about this interpretation is that it never occurred to me as a possibility until the Santa Cruz meeting, even though I've thought about these rules in considerable depth over the course of many years. Even if this case might be considered to be covered by the existing wording, I'd suggest that it might be worth looking for a less opaque formulation.

The following discussion and proposed solutions are very long and hard to summarize but seems to end with a removal of the aforementioned bullet five and resolve the issue with adjustments to other parts of 6.5. But as noted above this issues involved were not resolvable and I don't see a follow-up proposal.

So it would seem the standard wording permits the scenario the OP demonstrates although my understanding is that this was unintentional and therefore I would avoid it and it could potentially change in later standards to be non-conforming.

Linus answered 13/1, 2015 at 3:32 Comment(13)
Massive thanks Shafik. Part of that debate is pointing out a failure to cover the behaviour of unions in a section annotated as intended to say what aliasing is and is not conformant. I think it might be intended as symmetry. You can carve out pointers to members such as &(s.a). So why not stick a structure over compatible members? They are (logically) aligned and contain compatible bit patterns. Why should a low-level language such as C forbid such access? I still think that's what the author of that paragraph meant. I wonder who submitted it.Lindsy
PS: Totally agree it's not a recommended technique if only because it's vague, clearly little known and even less understood! I can imagine some process where a clever I/O function 'builds' a structure to a recipe based on types and offsets and then hands them back to the calling program. You would need to rely on this idea that structs can be treated as parts (and this says built up of parts) for such a scheme to be compliant. I do think the fact that I've only half implemented the structure (if you add a double y;) may be unintentional. It doesn't seem to have many legitimate uses.Lindsy
This answer (especially Example 3) really does talk about aliasing an int via a struct type that has int as member. However OP's code only aliases uint as uint. So I think OP's question remains unanswered. (sp->x clearly does not access all of *sp)Kalindi
@m.m bullet five requires includes one of the aforementioned types among itsmembers which applies in the OPs code and is the case example 3 covers and I don't see anything in the proposal that prevents it from applying to this case.Linus
@ShafikYaghmour I've accepted your answer as very usefully finding previous detailed consideration of that paragraph that at least discusses the very issues in play. That's particularly the point that aliasing comes in as a discussion about the type system but goes out as a discussion about optimization! Which I think is important - otherwise the answer would be easy! Below is my attempt to show why allowing something at least a bit like the OP is worthwhile. Thanks again for your attention.Lindsy
@DanAllen yes, optimization is a key issue, even if the language supports the code it is unclear to me whether the various compiler implementations do with such code. Simple examples don't show any optimization going on but I would not count on it.Linus
@ShafikYaghmour Again. I agree. There's no law of the universe that optimizers have understood that section in the same way. That's why it's so poor. It fails clear up the precise thing it is intended to clear up for the precise purpose it was trying to clear it up. Sorry unknown author but 'down vote' from 2015 back to 1999.Lindsy
The quoted text seems to ignore the fact that structs have subobjects. In the last example, the effective type of *ps1 is struct S, however the effective type of ps1->a is (or should be, IMO) int, not struct SKalindi
The Standard is unclear about subobjects in various places (another one is the bounds of pointers to a 1-d subobject of a 2-d array).Kalindi
@ShafikYaghmour: How many optimization opportunities would be lost if type-based aliasing rules were eliminated and programmers had to use "restrict" to say what things would and wouldn't alias? From what I can tell, if the rules had any reason to exist in C89, they essentially evaporated with C99. I find it odd that some compilers are getting increasingly aggressive with the type-based optimizations since the places programmers don't use "restrict" are likely those where speed is of least concern.Hereabouts
Where you say "DR 1520" I believe you mean N1520Kalindi
If I remember correctly, prior to C99 the Committee had indicated that they viewed this as a Quality-of-Implementation matter, and the issue will never be resolved unless or until it is recognized as such. Different kinds of programs have different semantic requirements, and semantics-vs-performance trade-offs that are suitable for some will be unsuitable for others. I think the Standard needs to recognize more than two kinds of implementations, and it may make sense to establish precise rules for some of those kinds...Hereabouts
...but any single set of rules will either impede what should be useful optimizations, make the language useful for many purposes (except on implementations that at least waive them), or both. I don't think it would be hard to support 95%+ of code that presently requires -fno-strict-aliasing while still allowing the majority of useful optimizations that flag would block, but the Standard should also recognize implementations that support an even wider range of programs and those that support fewer programs but achieve more optimizations.Hereabouts
K
1

I think this text does not apply:

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),

sp->x has type unsigned int which is not an aggregate or union type.

In your code there is no strict aliasing violation: it is OK to read unsigned int as unsigned int.

The struct might have different alignment requirements to the array but other than that there is no problem.

Accessing via "an aggregate or union type" would be:

s t = *sp;
Kalindi answered 12/1, 2015 at 21:36 Comment(28)
@JonathanLeffler It could be argued that sp->x means (*sp).x and therefore it is accessing through an aggregate or union type first, and then narrowing the access to only access x ... but in either case it is not a strict aliasing violationKalindi
To differentiate those two cases, s could be increased to have some doubles or other items on the endKalindi
C11, Types 6.2.5.28 says no-go.Endow
@Endow in n1570 that is "A pointer to void shall have the same representation and alignment requirements..." which is unrelatedKalindi
All pointers to structure types shall have the same representation and alignment requirements as each other. All pointers to union types shall have the same representation and alignment requirements as each other. Pointers to other types need not have the same representation or alignment requirements.Endow
c doesn't define that s pointer can even represent a char pointer. It is perfectly valid to have: sizeof(s*) == 2 and sizeof(unsigned int*) == 4 and/or different alignment between the two.Endow
Another problem is that strict aliasing still occurs. You are interpreting a int array as it were a struct. Boom. You use that struct pointer to access one of its elements but that that point it is too late.Endow
@Endow that has nothing to do with this question, the sizes could be as you say and the question remains. It seems you're questioning the validity of the expression (s *)&array however that is explicitly permitted by 6.3.2.3/7Kalindi
If the resulting pointer is not correctly aligned68)for the referenced type, the behavior is undefined. In the case the alignment is different you have ub; this could happen if sizes were different or not.Endow
@Endow unsigned int is being aliased as unsigned int, there is not even any char array in this example, let alone one that is being interpreted as a struct. Anyway, the quoted text says that the strict aliasing rule permits interpreting things as a struct.Kalindi
@Endow my post notes the alignment consideration already. It's only undefined if there is actually an alignment violation. We could add some alignof or other checks to detect or remedy this situation but it is irrelevant to the question, which is a question about aliasing (not alignment).Kalindi
That was a typo I meant the unsigned int array when I said char.Endow
Anyway, the quoted text says that the strict aliasing rule permits interpreting things as a struct. Umm which text is that??Endow
"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),". Structs are aggregates.Kalindi
@MattMcNabb Sure that holds for the member of the struct. But the struct itself is causing strict aliasing. You cannot interpret an unsigned int* as an struct*.Endow
@Endow s contains unsigned int amongst its members therefore it is permitted to alias array[0] as sKalindi
There is no aliasing as struct anyway, sp->x has type unsigned int.Kalindi
@MattMcNabb sp doesn't have type unsigned int*. Strict aliasing violation. ooops >>>>*spEndow
@Endow *sp has type s, and it is permitted to use s to alias unsigned intKalindi
@MattMcNabb That is the problem, you are misreading the standard. This example shows cleary why stackoverflow.com/questions/98650/… and it even uses the same struct setup.Endow
@Endow it's there in black and white. "An object (array[0]) shall have its stored value accessed only by an lvalue expression (sp->x) that has one of the following types: - a type compatible with the effective type of the object". array[0] has type unsigned int and sp->x has type unsigned int. These two types are compatible so there is no violation. It is not even necessary to consider the aliasing of entire struct since this code doesn't do it (although that would be permitted also).Kalindi
@Endow in the example in the accepted answer, unsigned int is aliased as uint32_t which might be incompatible. If the sample used unsigned int *buff = ... then there is no problem.Kalindi
Also there are different rules for malloc'd memory than for named variables (see the term "effective type")Kalindi
It is an aggregate type (s) that includes on of the aforementioned types (unsigned int). That sentence can't be read to say that the member has to be an aggregate type. It (I think) clear that if the member is an aggregate type then the rule applies recursively. I could have struct{ struct { struct { unsigned int x; } } } and still be OK but I'm definitely not obliged to engage in such nesting.Lindsy
@Endow On the one hand that isn't the specification I'm reading. But more importantly, please remember I'm asking what the paragraph I cited means not whether the program I put forward is valid. The relevant paragraph appears in the C11 document I have available. I think it appears in many standards post C99 when aliasing was (supposedly) cleaned up.Lindsy
@Endow I think the example in that answer is problematic, the answer has several issues, way more than easily fixable with minor edits.Linus
I think it might be useful to say that the Standard deliberately refrains from saying that all lvalues of aggregate members' types should be usable to access aggregates in all circumstances. Instead, it expects implementations to support usage patterns beyond those mandated by the Standard when doing so makes sense on the target platform and application field. In other words, support for such patterns is a Quality of Implementation issue.Hereabouts
An implementation that could handle aggregate.member only for character-type members would have been quite obviously lousy, whether or not the Standard were to actually mandate support for that construct. Since the authors of the Standard did not think it necessary or practical to forbid all the ways an implementation could be of such low quality as to be useless, nor to worry about whether obviously-lousy implementations were conforming, there was no real need to mandate support for aggregate.member.Hereabouts
L
1

I confess that the idea that I can lay a struct over a locally defined array in this way is frankly exotic. I still maintain that C99 and all subsequent standards permit it. If fact it's very arguable that members being objects in themselves the first bullet point in 6.7.5 allows it:

a type compatible with the effective type of the object

I think that's M.M's point.

Looking at the problem the other way, let's notice that it's absolutely legitimate (in a strictly conforming environment) to alias the member sp->x as an object in it's own right.

In the context of the code in my OP consider a function with prototype void doit(int* ip,s* sp); the following call is expected to behave logically:

doit(&(sp->x),sp);

NB: Program logic may (of course) may not behave as desired. For example if doit increments sp->x until it exceeds *ip then there's a problem! However what is not allowed in a conformant compiler is for the outcome to be corrupted by artifacts due to the optimizer ignoring aliasing potential.

I maintain that C would be all the weaker if the language required me to code:

int temp=sp->x;
doit(&temp,sp);
sp->x=temp;

Imagine all the cases where any call to any function has to be policed for the potential aliasing access to any part of the structures being passed. Such a language would probably be unusable.

Obviously a hard optimizing (i.e. non-compliant) compiler might make a complete hash of doit() if it doesn't recognize that ip might be an alias of member in the middle of sp. That's irrelevant to this discussion.

To set out when a compiler can (and cannot) make such assumptions is understood as the reason why the standard needs to set very precise parameters around aliasing. That is to give the optimizer some conditions to dis-count. In a low level language such as 'C' it could be reasonable (even desirable) to say that a suitably aligned pointer to an accessible valid bit pattern can be used to access to a value.

It is absolutely established that sp->x in my OP is pointing to a properly aligned location holding a valid unsigned int.

The intelligent concerns are whether the compiler/optimizer agree that's then a legitimate way to access that location or ignorable as undefined behavior.

As the doit() example shows it's absolutely established that a structure can be broken down and treated as individual objects which merely happen to have a special relationship.

This question appears to be about the circumstances when a set of members that happen to have that special relationship can have a structure 'laid over them'.

I think most people will agree that the program at the bottom of this answer performs valid, worthwhile functionality that if associated with some I/O library could 'abstract' a great deal of the work required to read and write structures. You might think there's a better way of doing it, but I'm not expecting many people to think it's not an unreasonable approach.

It operates by exactly that means - it builds a structure member by member then accesses it through that structure.

I suspect some of the people who object to the code in the OP are more relaxed about this. Firstly, it operates on memory allocated from the free-store as 'un-typed' universally aligned storage. Secondly, it builds a whole structure. In the OP I'm pointing the rules (at least appear to permit) that you can line up bits of a structure and so long as you only de-reference those bits everything is OK.

I somewhat share that attitude. I think the OP is slightly perverse and language stretching in a poorly written corner of the standard. Not something to put your shirt on.

However, I absolutely think it would be a mistake to forbid the techniques below as they rule out a logically very valid technique that recognizes structures can be built up from objects just as much as broken down into them.

However I will say that something like this is the only thing I could come up with where this sort of approach seems worthwhile. But on the other hand if you can't pull data apart AND/OR put it together then you quickly start to break the notion at C structures are POD - the possibly padded sum of their parts, nothing more, nothing less.

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

typedef enum {
    is_int, is_double //NB:TODO: support more types but this is a toy.

} type_of;

//This function allocates and 'builds' an array based on a provided set of types, offsets and sizes.
//It's a stand-in for some function that (say) reads structures from a file and builds them according to a provided
//recipe. 
int buildarray(void**array,const type_of* types,const size_t* offsets,size_t mems,size_t sz,size_t count){
    const size_t asize=count*sz;
    char*const data=malloc(asize==0?1:asize);
    if(data==NULL){
        return 1;//Allocation failure.
    }
    int input=1;//Dummy...
    const char*end=data+asize;//One past end. Make const for safety!
    for(char*curr=data;curr<end;curr+=sz){
        for(size_t i=0;i<mems;++i){
            char*mem=curr+offsets[i];
            switch(types[i]){
                case is_int:
                    *((int*)mem)=input++;//Dummy...Populate from file...
                break;
                case is_double:
                    *((double*)mem)=((double)input)+((double)input)/10.0;//Dummy...Populate from file...
                    ++input;
                break;
                default:
                    free(data);//Better than returning an incomplete array. Should not leak even on error conditions.
                    return 2;//Invalid type!
            }
        }
    }
    if(array!=NULL){
        *array=data;
    }else{
        free(data);//Just for fun apparently...
    }
    return 0;
}

typedef struct {
    int a;
    int b;
    double c;
} S;

int main(void) {
    const type_of types[]={is_int,is_int,is_double};
    const size_t offsets[]={offsetof(S,a),offsetof(S,b),offsetof(S,c)};
    S* array=NULL;
    const size_t size=4;

    int err=buildarray((void **)&array,types,offsets,3,sizeof(S),size);
    if(err!=0){
        return EXIT_FAILURE;
    }
    for(size_t i=0;i<size;++i){
        printf("%zu: %d %d %f\n",i,array[i].a,array[i].b,array[i].c);
    }

    free(array);
    return EXIT_SUCCESS;
}

I think it's an interesting tension. C is intended to be that low level high level language and give the programmer almost direct access to machine operations and memory. That means the programmer can fulfill with the arbitrary demands of hardware devices and write highly efficient code. However if the programmer is given absolute control such as my point about an 'if it fits it's OK' approach to aliasing then the optimizer gets its game spoilt. So weirdly it's worth holding a little bit of performance back to return a dividend from the optimizer.

Section 6.5 of the C99 standard tries (and doesn't entirely succeed) to set that boundary out.

Lindsy answered 13/1, 2015 at 18:9 Comment(1)
A compiler would have to be mind-numbingly obtuse not to allow access to a non-character structure member via sp->m, regardless of whether the Standard were to actually require it to do so. Further, the Rationale of the Standard explicitly recognizes that an obtuse compiler could be conforming and yet be of such low quality as to be useless. Putting those together, rather than trying to ponder how N1570 6.5p7 allows things it obviously should, I think it makes more sense to recognize that in some cases it doesn't, but decent compilers will behave as though it does anyway.Hereabouts

© 2022 - 2024 — McMap. All rights reserved.