3

How do I call a function that takes an anonymous struct in C?

Such as this function

void func(struct { int x; } p)
{
    printf("%i\n", p.x);
}
Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335
aganm
  • 1,245
  • 1
  • 11
  • 30

5 Answers5

5

When a function declaration that provides a prototype is in scope, the arguments to calls to that function must be have types compatible with those declared in the prototype, where "compatible" has a specific meaning defined by the standard:

Two types have compatible type if their types are the same. Additional rules for determining whether two types are compatible are [not relevant to the case in question]. Moreover, two structure, union, or enumerated types declared in separate translation units are compatible if their tags and members satisfy the following requirements: If one is declared with a tag, the other shall be declared with the same tag. If both are completed anywhere within their respective translation units, then the following additional requirements apply: there shall be a one-to-one correspondence between their members such that each pair of corresponding members are declared with compatible types; [...] and if one member of the pair is declared with a name, the other is declared with the same name. For two structures, corresponding members shall be declared in the same order. [...]

(C11, 6.2.7/1)

Being "the same type" in the sense meant by the standard is a matter of scope, and there is no way for a caller of your function to satisfy that because the scope of the structure declaration in a function parameter list is restricted to the function definition in which it appears, if any, or to the parameter list alone if it appears only in a prototype.

To call that function, then, you must somehow take advantage of the additional rules for type compatibility, which are applicable (only) if func() is defined in a different translation unit than the caller. In that case, the easiest thing to do is probably to typdef a compatible type in the caller's translation unit:

typedef struct { int i; } one_int;

You would then prototype the function like so in that TU:

void func(one_int p);

and you would call it with a parameter of type one_int. For example,

one_int oi = { 1 };
func(oi);

Do note, however, that although the above prototype is compatible with the function definition given in the question as long as they appear in different translation units, the two cannot appear in the same translation unit, so you cannot follow the usual recommendation that each .c file #include the header(s) declaring its functions.

Overall, it would be far better to lift the struct declaration out of the prototype, either with the help of a typedef such as is demonstrated above, or by giving it a tag, either one of which provides for it to be referenced from code away from its definition.

John Bollinger
  • 160,171
  • 8
  • 81
  • 157
  • You're basically saying the same thing that everyone else is, just using more words. tl;dr: The type must be declared outside the function prototype, using an actual name. – Robert Harvey Mar 05 '21 at 19:16
  • 1
    No, @RobertHarvey, everyone else is saying that the function is uncallable as defined. That is not true. But it does need to be declared differently in the caller's TU than it is in the one where it is defined. There is at least one other option than the one given here, involving declaring the function without a prototype, but I decided that the answer was already long enough without, and the declared-but-unprototyped alternative is not better than the one presented. – John Bollinger Mar 05 '21 at 19:20
  • This is pretty weird and looks like a loophole – Eugene Sh. Mar 05 '21 at 19:39
  • @EugeneSh., the word "loophole" suggests that it somehow gets around the intent of the standard. I would agree that it is a bit technical, but I don't see any reason to think that the standard specifically intends to provide for the declaration of functions that cannot be called. – John Bollinger Mar 05 '21 at 19:42
  • How are the statements in your comment _"...the function is uncallable as defined. That is not true."_ and _"But it does need to be declared differently..."_? not conflicting? – ryyker Mar 05 '21 at 19:43
  • 2
    @ryyker, the definition of the function does not need to be altered from the one presented by the OP. The calling TU just needs to declare it in a way that does not lexically match the definition (but is nevertheless compatible with it). – John Bollinger Mar 05 '21 at 19:46
3

Although useful for some applications, a bare naked anonymous struct cannot be passed as a function argument all by itself in C. It needs some help, eg either a typedef handle, or a as a member of a named struct.

anonymous struct by itself:

struct {
    int x;
};  

as a function argument results in the warning:

"expected ‘struct ’ but argument is of type ‘struct ’ void func(struct { int x; } p)"

(noted by @Eugene Sh)

Help with a Typedef:

typedef struct 
{
   int x;
}anon_s;

void func(anon_s *p)
{
    printf("%i\n", p->x);
}

int main(void)
{
      anon_s anon = {10};
      func(&anon);
      return 0;
}

Or, carried by a surrogate with either a name or typedefed:

struct anon{
    // Anonymous struct
   struct
   {
        int x;
   };
};    
    
ryyker
  • 22,849
  • 3
  • 43
  • 87
  • `void func(struct { int x; } p)` actually compiles, so I assume there must be a way to call it without actually changing the function signature. – Robert Harvey Mar 05 '21 at 18:29
  • @RobertHarvey Doesn't look like the case. https://ideone.com/wr8Jev – Eugene Sh. Mar 05 '21 at 18:32
  • @RobertHarvey - Red faced reply... I have never done it. Nor honestly do I know how it would be done, and I can I think of no compelling reason to – ryyker Mar 05 '21 at 18:34
  • @EugeneSh. What is failing to compile there is the function call, not the function declaration. – Robert Harvey Mar 05 '21 at 18:35
  • @RobertHarvey The declaration gives a warning. Then the last *prog.c:3:29: note: expected ‘struct ’ but argument is of type ‘struct ’ void func(struct { int x; } p)* hints that there is no way to make a parameter compatible with it. – Eugene Sh. Mar 05 '21 at 18:36
  • Yep, that does seem to be the case. – Robert Harvey Mar 05 '21 at 18:36
  • @RobertHarvey, that the declaration is valid does not mean that it has to be useful. But in this case, it does happen to be true that the function can be called, albeit only from a different translation unit. See my answer. – John Bollinger Mar 05 '21 at 19:13
2

As type of the anonymous struct is visible only within the body of func() you could use it to call func() recursively.

void func(struct { int x; } p)
{
    printf("x=%d\n", p.x);
    func(p);
}

The code compiles fine except a warning mentioned in other answers.

Thus it is theoretically possible to call a function with anonymous struct as parameter. The initial call is a challange. I do not think that that there any portable way of invoking an initial call. Probably doable by casting a pointer func() to other type of function pointer that is binary-compatible for a given implementation.

void func(struct { int x; } p)
{
    printf("x=%d\n", p.x);
    if (p.x --> 0)
        func(p);
}

int main() {
    // call through a pointer to function with unspecified number of arguments. 
    void (*func_p)() = func;
    struct almost_p { int x; } p = { 10 };
    func_p(p);
    return 0;
}

Ii worked perfectly on my machine but it's non-portable and likely could trigger UB due incompatibility of struct {...} and struct almost_p.

However, if the argument is a pointer to anonymous struct then the situation looks more promising.

void func(struct { int x; } *p);

One can make an initial call as func(NULL). Within func, the actual p object can be constructed with calloc(). After that you can call it func() recursively or store instance p in a global variable of type void*.

Example:

void func(struct { int x; } *p) {
    if (!p) p = calloc(1, sizeof *p);
    printf("x=%d\n", p->x);
    if (p->x++ < 10)
        func(p);
}

int main() {
    func(NULL);
    return 0;
}

It compiles fine and outputs:

x=0
x=1
x=2
x=3
x=4
x=5
x=6
x=7
x=8
x=9
x=10
tstanisl
  • 13,520
  • 2
  • 25
  • 40
1

If you compile your program with warnings enabled you will get a warning like

warning: anonymous struct declared inside parameter list will not be visible outside of this definition or declaration
 void func(struct { int x; } p)
           ^~~~~~

Therefor you need to give the record a name and use this name in the function declaration, for instance

typedef struct {
    int x;
} T;

void func(T p) 
{
    printf("%i\n", p.x);
}
August Karlstrom
  • 10,773
  • 7
  • 38
  • 60
1

For starters you have no anonymous structure. You have an unnamed structure. The notion anonymous structure has a specific semantic in C.

You can not call the function because the structure type even if it will be named is not visible outside the function block scope.

You need to declare the structure outside the function parameter list assigning to it a name or an alias using typedef.

Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335