8

Someone claimed in a presentation that if you add

#define struct union
#define else

at the beginning of any valid C program, that program would still compile. That seems like a bold claim. Any counterexample you have in mind and prove that guy wrong?

Brian Tompsett - 汤莱恩
  • 5,753
  • 72
  • 57
  • 129
webuster
  • 2,490
  • 18
  • 27
  • 3
    Every reference in the C 2011 standard to “struct” or “union” that discusses syntax uses them both symmetrically. (There is one reference at 6.7.2 2 that uses only “struct” and not “union”, but I think it is referring to both, as they appear in 6.7.2.1.) Further, they share the same tag name space, so one cannot distinguish them by tag behavior. Therefore, there is no syntactic difference. Any way to make a program fail by substituting “union” for “struct” must rely on semantics, not syntax, such as the size issues raised in some of the answers. – Eric Postpischil Feb 05 '14 at 14:56
  • 4
    A trivial counter-example is if the code already contains a different definition of either of the macros. – undur_gongor Feb 05 '14 at 15:09
  • @undur_gongor: That should be an answer. – Eric Postpischil Feb 05 '14 at 15:10

5 Answers5

13

I managed to find a counterexample (C99):

for (int i = 0; i < 5; ++i)
    if (i > 2)
        do_smth();
    else if (i < 4)
        do_smth_else();

This one doesn't compile because if you #define else the variable i goes out of scope. But that doesn't involve the #define struct union thing. Any other ideas?

webuster
  • 2,490
  • 18
  • 27
8
#define struct union

struct OBJ
{
    int i1;
    double d1;
};

int foo()
{
    struct OBJ obj = { 1, 2.0 };

  return 0;
}

C2078: too many initializers

Serve Laurijssen
  • 9,266
  • 5
  • 45
  • 98
  • 1
    Nice answer. You could simplify it into a one-liner: `struct {int a,b;} x = {0,0};`. – Joseph Quinsey Feb 05 '14 at 15:38
  • Also, my version of gcc always seems to compile this, albeit with warnings, unless you use `-Werror`. – Joseph Quinsey Feb 05 '14 at 15:41
  • Wasn't union initialization rules relaxed in C99, together with the introduction of designated initializes? I thought it would allow this kind of initialization. At least I'm pretty sure it allows you to use more than one designated initializer for a union (but I have no idea why you would ever want to do that). – Lundin Feb 05 '14 at 17:57
  • @Lundin: In the absence of nested braces, the initialization of an array of 2-item structures will gobble up two comma-separated items per element; to have the initialization of an array of unions gobble up more than one would represent a breaking change to the language. – supercat Feb 05 '14 at 20:25
  • @supercat How come `union OBJ obj = { .i1=1, .d1=2.0 };` compiles then? Is such code with designated initializers poorly-defined by the standard? – Lundin Feb 06 '14 at 07:44
  • @Lundin: I'm not sure what the standard says about multiple inconsistent initializers, but in your latter example the braces are associated with the union. See my answer above. – supercat Feb 06 '14 at 16:43
5

Another counter-example to the else claim, which I think goes back to K&R C:

void do_something() {}
do
  if (1) do_something(); else do_something();
while(0);

The do is required to be followed by exactly one statement. if (condition) statement; else statement; constitutes one statement, but eliminating the else would cause it to be two statements.

As for struct vs union, given

typedef union {int x, y; } t;
t a[3] = {1,2,3};

I think a compiler would be required to store the 3 into a[2].x (also known as a[2].y), and would have nowhere to store any additional initialization values. If t had been a struct, the 3 would have gone into a[1].x, and there would have been room for three more values.

undur_gongor
  • 15,657
  • 5
  • 63
  • 75
supercat
  • 77,689
  • 9
  • 166
  • 211
  • 1
    The first part is the best answer here, guaranteed to fail in all versions of the language. Second part not so much: might well just trigger a warning. – david.pfx Feb 06 '14 at 13:23
  • @david.pfx: The code exactly as listed above will compile. If two more items had followed after the `3`, what could the compiler plausibly have done with them? – supercat Feb 06 '14 at 16:55
  • My mistake, we're both wrong. S8.5.1/15 says "When a union is initialized with a brace-enclosed initializer, the braces shall only contain an initializer-clause for the first non-static data member of the union". Any struct with {a,b} initializer is a counter example. – david.pfx Feb 08 '14 at 03:08
0

If STATIC_ASSERT() is your favorite static assert macro, then:

struct {int a, b, c, d;} x;
//...
STATIC_ASSERT(sizeof x == 4 * sizeof(int));

will not compile if you have #define struct union.

Joseph Quinsey
  • 9,553
  • 10
  • 54
  • 77
  • Except if `4 * sizeof(int)` happens to be the exact alignment requirement of the given system. The the union will get padding bytes and this will compile fine. – Lundin Feb 05 '14 at 15:07
  • 3
    @Lundin: What is “the exact alignment requirement of the given system”? There is no special alignment requirement for a struct or union of `int` that is not already satisfied by an `int`. No normal C implementation will add padding to either a struct or a union containing only four `int`. (Although the C standard does permit an implementation to add padding for no reason, as I have noted elsewhere.) – Eric Postpischil Feb 05 '14 at 15:18
  • @EricPostpischil "There is no special alignment requirement for a struct or union of int that is not already satisfied by an int". Correct in practice for every mainstream architecture on the market, but not in theory. For example: if you have a union consisting only of int, int is 16 bits and the alignment requirement of the given system is 8 bytes. Then this code will compile when union is defined as struct. Odd, theoretical case I know, but strictly speaking allowed by the C standard. – Lundin Feb 05 '14 at 17:51
  • @Lundin: You have not defined “the alignment requirement of the given system”. If an `int` is 16 bits, then an array of n `int` must have a size of n•16 bits, by the standard’s requirements for arrays and `sizeof`. That means the system **must** be able to access `int` objects at odd multiples of 16 bits; there can be no requirement in the system that all objects be aligned to multiples of eight bytes. What is this alignment requirement that unions must obey but that `int` objects need not? – Eric Postpischil Feb 05 '14 at 18:06
  • @EricPostpischil The alignment requirement is in the hardware, not in the C standard, thus it is implementation-defined... The standard only demands that items of an array are allocated in adjacent memory. There is no requirement in the standard demanding that the CPU must be able to directly access an item of an array that is misaligned. The compiler is free to implement any kind of trick to access such an array member on a system where the CPU cannot. Otherwise, it wouldn't be possible to have any kind of array on systems where the CPU cannot perform misaligned access. – Lundin Feb 06 '14 at 07:32
  • @Lundin: You have not explained anything about why a union must have an alignment that its members need not. If the hardware or C implementation can access a bare `int` at some address, then it can access an `int` in a union at the same address. Not just in practice, in theory too. There is no “alignment requirement of the given system” that makes a union of `int` be aligned differently from an `int`. – Eric Postpischil Feb 06 '14 at 11:51
  • let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/46927/discussion-between-lundin-and-eric-postpischil) – Lundin Feb 06 '14 at 11:59
0
#define else 42

is invalid (and thus might not compile) if you add #define else in front of it.

An identifier currently defined as an object-like macro shall not be redefined by another #define preprocessing directive unless the second definition is an object-like macro definition and the two replacement lists are identical.

(ISO/IEC 9899:1999, §6.10.3, 2)

undur_gongor
  • 15,657
  • 5
  • 63
  • 75
  • Ok, nice one, but I was looking for a way to exploit the `#define`s already added. – webuster Feb 05 '14 at 15:13
  • @webuster: Yes, understood that. That's why I initially added it as a comment only. – undur_gongor Feb 05 '14 at 15:14
  • Interesting. All compilers I've seen treat this as a warning, not an error (presumably for backwards compatibility) unless you specifically add a switch such as `-Werror`. And I seem to recall older compilers which wouldn't even give you a warning. – Joseph Quinsey Feb 05 '14 at 15:19
  • @JosephQuinsey: You are right. I rephrased it a bit. Thanks. – undur_gongor Feb 05 '14 at 15:28
  • @JosephQuinsey: A compiler that does not give a warning does not conform to the C standard; 6.10.3 is a constraints clause, and 5.1.1.3 requires a diagnostic message for any constraint violation. – Eric Postpischil Feb 05 '14 at 16:04