5

This is a follow-up to this question.

I'm trying to avoid using an explicit typedef to copy one array to another through casts like this:

#include <stdio.h>

int main(void)
{
  int i;
  int dst[] = { 10, 20, 30 }, src[] = { 1, 2, 3 };

  *(struct{int _[3];}*)dst = *(struct{int _[3];}*)src;

  for (i = 0; i < 3; i++) printf("%d\n", dst[i]);
  return 0;
}

With gcc I'm getting arrcpy.c:8: error: incompatible types in assignment, however with Open Watcom it compiles fine (and works as I expect it, printing 1 through 3).

Is the gcc's behavior per the standard or not? If it is, what's the relevant chapter and section? I can't understand why two identical type definitions struct{int _[3];} aren't the same (or compatible) in the gcc's eyes.

EDIT: I know full well it's a bad coding style. The question is about a different thing. I'm curious if there's a logical rationale behind the gcc's behavior, if it's legit.

Community
  • 1
  • 1
Alexey Frunze
  • 61,140
  • 12
  • 83
  • 180

4 Answers4

6

The gcc behavior is right, the types are two unrelated unnamed structures. Each of those structs, while having the same memory layout, have different names. If you really want to do that, then use a typedef.

K-ballo
  • 80,396
  • 20
  • 159
  • 169
  • 1
    Even with the `typedef`, the type-punning would result in **undefined behavior**. +1 anyway for addressing OP's question with a correct answer on why the code is invalid rather than advice on alternatives which OP seems to already understand "should" be used. – R.. GitHub STOP HELPING ICE Oct 25 '11 at 00:53
1

Why wouldn't you just use memcpy?

#include <stdio.h>
#include <string.h>

int main(void)
{
  int i;
  int dst[] = { 10, 20, 30 }, src[] = { 1, 2, 3 };

  memcpy(dst, src, 3 * sizeof(int));

  for (i = 0; i < 3; i++) printf("%d\n", dst[i]);
  return 0;
}

or for the size, instead of 3: sizeof(dst)/sizeof(dst[0])

EDIT: With your edit, I can only assume that, as with outis' answer, the compiler sees the two struct definitions as just that, two different types of structs. Even though they may contain the same data, they're two different types.

Community
  • 1
  • 1
AusCBloke
  • 18,014
  • 6
  • 40
  • 44
1

Basically, type equivalence isn't structural equivalence in C. C uses a nominative type system.

According to § 6.7.2.1-7 of C99:

The presence of a struct-declaration-list in a struct-or-union-specifier declares a new type, within a translation unit. The struct-declaration-list is a sequence of declarations for the members of the structure or union. If the struct-declaration-list contains no named members, the behavior is undefined. The type is incomplete until after the } that terminates the list.

struct-declaration-list and struct-or-union-specifier come from the C grammar (§ 6.7.2.1):

struct-or-union-specifier:
    struct-or-union identifieropt { struct-declaration-list }

struct-or-union:
    struct
    union

Even if two different structs have the same memory layout, they are different types.

If you want to avoid polluting the global namespace, you can declare the struct locally to the function in which you use it.

#include <stdio.h>

int main(void) {
    // struct T is only visible in main()
    struct T {int _[3];};
    int i;
    int dst[] = { 10, 20, 30 }, src[] = { 1, 2, 3 };

    *(struct T*)dst = *(struct T*)src;

    for (i = 0; i < 3; i++) printf("%d\n", dst[i]);

    return 0;
}

void fails(void) {
    // will cause a compilation error, because struct T is an incomplete type.
    struct T t;
}
outis
  • 75,655
  • 22
  • 151
  • 221
1

The two struct definitions are not compatible:

From C99 6.2.7:

Two types have compatible type if their types are the same. Additional rules for determining whether two types are compatible are described in 6.7.2 for type specifiers, in 6.7.3 for type qualifiers, and in 6.7.5 for declarators. Moreover, two structure, union, or enumerated types declared in separate translation units are compatible if their tags and members satisfy the following requirements...

The types aren't the same. If they were declared in separate translation units, they would be compatible, however.

However, even if they were compatible, your code would still invoke undefined behavior, for at least two reasons:

  1. It uses the name "_" which is reserved.
  2. Accessing objects as a different type (except structs through char pointers, unions, etc.) isn't allowed.
wnoise
  • 9,764
  • 37
  • 47
  • Are you referring to 7.1.3?: `All identifiers that begin with an underscore are always reserved for use as identifiers with file scope in both the ordinary and tag name spaces.` – Alexey Frunze Oct 25 '11 at 01:11