3

Say I have a struct defined like so:

typedef struct MyStruct {
    double a[3];
    double b[6];
} MyStruct;

I pass the structures to a function to perform some operations. For example:

void MyFcn(MyStruct *out, const MyStruct *in) {
    out->a[2] = in->b[5];
    /* do more stuff */
}

If I want to qualify the array pointers MyStruct.a and MyStruct.b as not having overlapping memory with the restrict keyword, is there any way of doing this?

Perhaps most compilers would optimize assuming that MyStruct.a and MyStruct.b point to contiguous memory block without any aliasing anyways, and there's no point in adding the restrict qualifier? I have no idea.

I know I could simply make a and b pointers instead of arrays like so

typedef struct MyStruct {
    double *restrict a;
    double *restrict b;
} MyStruct;

but considering the size of the arrays are known, this approach makes it difficult to debug overflows and unnecessarily complicates variable initializations (requires dynamic memory allocations or compound literals and designated initializers).

EDIT

From the comments below, I need to clarify that I intended for the example to better describe the question, not constrain it to that use case. The answers did clarify that struct members cannot have overlapping memory (thats' what unions are for).

However, the question still remains for different function inputs/outputs. For any number of function inputs/outputs with different struct definitions, would the compiler optimize if there are possibilities of aliased arrays between structs? If it would not, how to give the restrict keyword?

ChisholmKyle
  • 449
  • 3
  • 10
  • Two members of a struct *cannot* alias. You need a union for that. – EOF Aug 04 '15 at 21:46
  • 2
    Is not `void MyFcn(MyStruct * restrict out, const MyStruct * restrict in)` sufficient? add `restrict` to function, not `struct`? – chux - Reinstate Monica Aug 04 '15 at 21:48
  • @EOF "Two members of the _same_ struct cannot alias" is true. But could `out` and `in` point could to slightly overlapping memory - even if it is bad practice? – chux - Reinstate Monica Aug 04 '15 at 21:50
  • @chux: sure, but that's not the question, as far as I understand. – EOF Aug 04 '15 at 21:52
  • The question is done with a little ambiguity. First, the OP is referring to `MyStruct.a` and `MyStruct.b`, asking for not-overlaping, which seems to referring to "the same structure", so the EOF's answer applies. But later, the OP wrote an example of accessing different members of two different pointed structures. So chux's answer applies. – pablo1977 Aug 04 '15 at 21:59
  • @chux No they could not point to slightly overlapping memory. Either `out == in`, or they point to different objects (or null pointers) – M.M Aug 04 '15 at 22:57
  • @Matt McNabb Example: `union { struct { double d[6]; MyStruct x; } s2; MyStruct y; } u; MyFcn(&u.s2.x,&u.y);` overlaps `out->a[2]` with `in->b[5]`. – chux - Reinstate Monica Aug 04 '15 at 23:23
  • @Matt McNabb So `in,out` could point to slightly overlapping memory, but that is to UB. It that due to aliasing rules or something else? – chux - Reinstate Monica Aug 05 '15 at 00:06
  • It is good to know that two members of the same struct cannot alias, but I might have been misleading by giving an example function. The question is intended to be more general and could be phrased "Given an arbitrary number of function input/output pointers to structs, whether they are const or have different definitions, how to tell the compiler their member arrays point to data that is not and will not be overlapped in that function?" – ChisholmKyle Aug 05 '15 at 03:27
  • I noticed @chux answer to add restrict to the function prototype. This would answer my question! Would the `restrict` keyword apply to ALL the data the struct points to? – ChisholmKyle Aug 05 '15 at 03:59
  • @ChisholmKyle your question still isn't really clear. Using `restrict` on the function parameters means that they may not point to overlapping space (such as nested structs). It doesn't make sense to talk about a `restrict` struct member. Perhaps you should show some real code – M.M Aug 05 '15 at 04:55
  • @chux I think it's due to the *active member* rule for unions but not completely sure – M.M Aug 05 '15 at 05:00
  • @Matt McNabb Found "active member rule for unions" in C++, but not C. Not sure then this is UB in C - it is all quite deep. – chux - Reinstate Monica Aug 05 '15 at 16:08
  • @MattMcNabb you're right - the array types in MyStruct won't likely ever be aliased with the other structs (and apparently can't in most scenarios). The discussion seemed to shift towards chux's union example and whether it's UB and whether the compiler is happy to assume a and b arrays are not aliased. I think several answers did hint at or mention to use restrict keyword on the struct in the function but I liked the clarity chux's answer provided. – ChisholmKyle Aug 06 '15 at 14:17
  • @MattMcNabb Also, my understanding is that you can use the restrict keyword on struct members so long as they are pointers. – ChisholmKyle Aug 06 '15 at 14:18
  • @chux: Whether the Standard allows it or not, gcc will assume that `foo->x` will not alias with `bar->y` if neither `foo` nor `bar` is a union type, whether they are the same structure type or different structure types. – supercat Jul 12 '16 at 21:26

2 Answers2

2

"If I want to qualify the array pointers MyStruct.a and MyStruct.b ...". a and b are arrays (for the first typedef), not pointers and definitive no "array pointers" - that would be something like

char a[10];
... &a ....

Also, if they are implicitly converted to a pointer, these cannot alias for the same struct. If you need distinct pointers to them, you can qualify those restrict. However, you do not use pointers to these arrays in your example code, so no need.

The only pointers which might alias are in and out. This is a matter of intended usage. I would recommend they actually should not, so you might qualify them restrict and - possibly in additionally const if applicable.

Btw: only pointer types can be restricted and arrays are no pointers, but decay to them for most operations.

Oh, and: If the sizes of the arrays are fixed, I agree that making the fields pointers is nonsense and just inefficient bloat.

too honest for this site
  • 12,050
  • 4
  • 30
  • 52
0

I'm out a limb here having little experience with restrict. Based on study, this appears to be right. So making this wiki for all to modify.


Add restrict to the function parameters (and not the typedef). By adding restrict to a pointer, the compiler may assume that only that pointer will access/change the data it points to. This allows for optimizations desired by OP as the pointers do not alias each others data.

typedef struct MyStruct {
    double a[3];
    double b[6];
} MyStruct;

void MyFcn(MyStruct * restrict out, const MyStruct *restrict in);

So now MyFcn() can assume in its use of in and out will not point to the same data. Calling code should also never do MyFcn(&data, &data) as that breaks the contract as calling code sent pointers to overlapping memory.

Some restrict specifications

An object that is accessed through a restrict-qualified pointer has a special association with that pointer. This association, ... requires that all accesses to that object use, directly or indirectly, the value of that particular pointer. The intended use of the restrict qualifier ... is to promote optimization, ... C11dr §6.7.3 8

Types other than pointer types whose referenced type is an object type shall not be restrict-qualified. §6.7.3 2

A translator is free to ignore any or all aliasing implications of uses of restrict. §6.7.3.1 6


Even one restrict parameter may make sense.

char buf[100];
void foo1(char * restrict p1) {
  /* foo1() knows p1 does not overlap global `buf`. */
  strcpy(buf, p1);  
}
Community
  • 1
  • 1
chux - Reinstate Monica
  • 143,097
  • 13
  • 135
  • 256
  • I think `restrict` makes no difference in the last case here, because the source and destination overlap in `strcpy` it is UB anyway – M.M Aug 05 '15 at 21:33
  • @Matt McNabb Sorry if last case is not clear. Source and destination do not overlap as the call usage would be like `char s[] = "Hello"; foo1(s); puts(buf);`. `foo1()`, knowing `p1` is `restrict`, _knows_, `p1` does not overlap `buf`, is free to use that condition and so can make more efficient code. IAC, a function call with only 1 pointer parameter being `restrict` is hard to provide an rational example. Free open to edit if you see a better one. – chux - Reinstate Monica Aug 05 '15 at 21:46