1

I'm reading ISO/IEC 9899:2017 recently,following is the source link https://web.archive.org/web/20181230041359if_/http://www.open-std.org/jtc1/sc22/wg14/www/abq/c17_updated_proposed_fdis.pdf

And what makes me confused is the following statements, because the standard said it is not a valid fragment (because the union type is not visible within function f). But I got the correct return value when I run this code.

#include <stdio.h>

struct t1 { int m; };
struct t2 { int m; };
int f(struct t1 *p1, struct t2 *p2){
  if (p1->m < 0)
  p2->m = -p2->m;
  return p1->m;
}

int g(){
  union U{
    struct t1 s1;
    struct t2 s2;
  } u = {-1};
  return f(&u.s1, &u.s2);
}

int main(){
  int i =  g();
  printf("%d\n",i);
}

So my question is: Is that valid parsing a pointer of union's member which is a structure defined in file scope to another function where union type is not visible to?

  • 1
    A union can't have more than one active member at a time. The function `f` (in the future please copy-paste all text *as text* into your questions) uses both members as they were active simultaneously. Which leads to *undefined behavior*. – Some programmer dude Oct 20 '21 at 16:49
  • Kindly read https://stackoverflow.com/questions/2397984/undefined-unspecified-and-implementation-defined-behavior . Also, there's html version: https://port70.net/~nsz/c/c11/n1570.html#6.5.2.3p8 – KamilCuk Oct 20 '21 at 16:49
  • 1
    Present any code relevant to the question as (code-formatted) text in the question itself. Do not link images of code or other text. – John Bollinger Oct 20 '21 at 16:51
  • Thanks for the advice above,I have presented my code in text format. – Citi-Vincent Oct 20 '21 at 17:14
  • There were some recent questions about this which I can try to find, discussing some proposed fixes to the standard which were not really satisfactory and never implemented. Yes, see https://stackoverflow.com/questions/68963247/why-are-the-results-of-this-code-different-with-and-without-fsanitize-undefine/68967014#68967014 and https://stackoverflow.com/questions/22897037/is-it-undefined-behaviour-to-call-a-function-with-pointers-to-different-elements. – Nate Eldredge Oct 20 '21 at 17:21

1 Answers1

1

But I got the correct return value when I run this code.

A given compiler accepting the code and the compiled program exhibiting the expected observable behavior are not reliable indicators of the code being correct with respect to the language specification. Nor, therefore, are they reliable predictors of whether a different compiler will accept the code or produce a program that exhibits the expected observable behavior.

Conforming compilers emit diagnostics about certain classes of errors, but they are not required to diagnose all errors, and there are some classes of errors that could not be detected at compile time even if the compiler wanted to do. Generally speaking, compilation and / or execution of incorrect code produces undefined behavior, which many times does not involve any error messages, and which sometimes is even the behavior that was expected. Bottom line: that a program produces the correct or expected result does not prove that the program is correct.


As for the main question,

what makes me confused is the following statements, because the standard said it is not a valid fragment (because the union type is not visible within function f).

[...]

So my question is: Is that valid [passing] a pointer of union's member which is a structure defined in file scope to another function where union type is not visible to?

You have misunderstood the nature of the problem. It is not inherent in passing a pointer to a member of a union. Rather, it has to do with accessing more than one member of the same union object.

The general rule arises from

The value of at most one of the members can be stored in a union object at any time

(C17 6.7.2.1/16)

and the so-called strict aliasing rule, paragraph 6.5/7:

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

[a type compatible with the objects effective type, plus / minus qualification, or the corresponding signed / unsigned type of one of the above, or]

  • 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.

The storage for every member of a given union object overlaps the storage of all the others (which is why the union can hold only one at a time), therefore with ...

  union U{
    struct t1 s1;
    struct t2 s2;
  } u = {-1};

... &u.s1 and &u.s2 point to the same storage. With the given initialization, its effective type is struct s1, and it is the initial part of a possibly larger block of storage whose effective type is union U.

Structure types with different tags are never compatible with each other, so the strict aliasing rule would be violated by g() accessing that initial value via the pointer &u.s2, except that the specification carves out a special case:

One special guarantee is made in order to simplify the use of unions: if a union contains several structures that share a common initial sequence (see below), and if the union object currently contains one of these structures, it is permitted to inspect the common initial part of any of them anywhere that a declaration of the completed type of the union is visible.

(C17 6.5.2.3/6)

This is precisely what the example you're looking at is about. Because struct t1 and struct t2 have a common initial sequence consisting of their respective members m, and because the union object in function g() initially does contain a value for its member s1, of type struct t1, it is permitted in g() to access u.s2.m, including indirectly via &u.s2, even though u.s2 is not the member that currently contains a value.

However, 6.5.2.3/6 does not apply in function f(), because type union U is not visible there. Therefore, although it's fine for f() to access p1->m, it produces UB for it to attempt to access p2->m. This is the claim you inquired about.

John Bollinger
  • 160,171
  • 8
  • 81
  • 157
  • Thanks for your reply very much, that's what I really want to see! When I saw it mentioned passing pointer into a function where U is not visible is not valid, I really think the invalid means sth error like an undeclared type of union until I reread the C17 6.5.2.3/6 with the context you provided. – Citi-Vincent Oct 21 '21 at 13:02