138

Im trying to assign a function inside a struct, so far I have this code:

typedef struct client_t client_t, *pno;
struct client_t
{
    pid_t pid;
    char password[TAM_MAX]; // -> 50 chars
    pno next;
    
    pno AddClient() 

    {
        /* code */
    }
};

int main()
{
    client_t client;

    // code ..

    client.AddClient();
}
**Error**: *client.h:24:2: error: expected ‘:’, ‘,’, ‘;’, ‘}’ or ‘__attribute__’ before ‘{’ token.*

Which is the correct way to do it ?

4
  • 24
    You cannot have functions in structs in C; you can try to roughly simulate that by function pointers though.
    – Fingolfin
    Commented Jun 11, 2013 at 19:48
  • 2
    Are function pointers an acceptable substitute? stackoverflow.com/a/840703/635678
    – Dan O
    Commented Jun 11, 2013 at 19:50
  • Is it possible to have functions in structs in C++ though? Commented Feb 16, 2023 at 15:02
  • @Fingolfin is there a language that allows to have struct methods, but otherwise is the same as C? Maybe ObjectC or "C with classes"? I don't count C++, because that language contains a lot of other stuff. Commented Feb 26, 2023 at 12:39

7 Answers 7

161

It can't be done directly, but you can emulate the same thing using function pointers and explicitly passing the "this" parameter:

typedef struct client_t client_t, *pno;
struct client_t
{
    pid_t pid;
    char password[TAM_MAX]; // -> 50 chars
    pno next;

    pno (*AddClient)(client_t *); 
};

pno client_t_AddClient(client_t *self) { /* code */ }

int main()
{

    client_t client;
    client.AddClient = client_t_AddClient; // probably really done in some init fn

    //code ..

    client.AddClient(&client);

}

It turns out that doing this, however, doesn't really buy you an awful lot. As such, you won't see many C APIs implemented in this style, since you may as well just call your external function and pass the instance.

6
  • 5
    The X11 API does something sort of like this. See XImage.
    – Hydranix
    Commented Jul 8, 2016 at 0:48
  • 2
    code that lies under the Windows API is full of that. Technically a "window class" is a struct with two callback function pointers and with open end (for that "window class specifics data" you can supply while registering) Commented Feb 6, 2017 at 6:15
  • 1
    followed those steps but when i do struct.function = newFunction the compiler print: error: expected '=', ',', ';', 'asm' or 'attribute' before '.' token
    – Bonfra
    Commented Sep 26, 2018 at 14:32
  • This would also be useful for when you wanted a common target (eg hard drive) which has a read function and a write function, but which varies from one type of hard drive to the nexf
    – Menotdan
    Commented Dec 20, 2019 at 23:53
  • It can buy you a lot if the actual implementation is selected at runtime. If you only have a static function, you always need to re-select within that function on every function call, if you use a function pointer, the selection happens only once. And the selection can even change throughout the lifetime of the struct.
    – Mecki
    Commented Jun 18, 2020 at 13:52
40

As others have noted, embedding function pointers directly inside your structure is usually reserved for special purposes, like a callback function.

What you probably want is something more like a virtual method table.

typedef struct client_ops_t client_ops_t;
typedef struct client_t client_t, *pno;

struct client_t {
    /* ... */
    client_ops_t *ops;
};

struct client_ops_t {
    pno (*AddClient)(client_t *);
    pno (*RemoveClient)(client_t *);
};

pno AddClient (client_t *client) { return client->ops->AddClient(client); }
pno RemoveClient (client_t *client) { return client->ops->RemoveClient(client); }

Now, adding more operations does not change the size of the client_t structure. Now, this kind of flexibility is only useful if you need to define many kinds of clients, or want to allow users of your client_t interface to be able to augment how the operations behave.

This kind of structure does appear in real code. The OpenSSL BIO layer looks similar to this, and also UNIX device driver interfaces have a layer like this.

6
  • 1
    pno refers to what? pls.
    – smalinux
    Commented Mar 2, 2021 at 23:14
  • @smalinux That came from the OP.
    – jxh
    Commented Mar 3, 2021 at 0:48
  • I am impressed with this code style. but I don't know why I get Segmentation fault codepad.org/sAlHy19b I don't understand if AddClient is recursive? will call itself forever.. thx
    – smalinux
    Commented Mar 3, 2021 at 1:38
  • 2
    @smalinux See: stackoverflow.com/a/17622474/315052
    – jxh
    Commented Mar 3, 2021 at 4:50
  • 2
    @LORDTEK See link I provided to smalinux
    – jxh
    Commented Oct 14, 2021 at 14:21
34

How about this?

#include <stdio.h>

typedef struct hello {
    int (*someFunction)();
} hello;

int foo() {
    return 0;
}

hello Hello() {
    struct hello aHello;
    aHello.someFunction = &foo;
    return aHello;
}

int main()
{
    struct hello aHello = Hello();
    printf("Print hello: %d\n", aHello.someFunction());

    return 0;
} 
2
  • 1
    foo function does not refer to any hello struct arguments. So it makes little sense to make it as a struct "method". Commented Feb 26, 2023 at 12:44
  • aHello goes out of scope when you return. this isn't safe C
    – WhyWhat
    Commented May 2, 2023 at 21:33
20

This will only work in C++. Functions in structs are not a feature of C.

Same goes for your client.AddClient(); call ... this is a call for a member function, which is object oriented programming, i.e. C++.

Convert your source to a .cpp file and make sure you are compiling accordingly.

If you need to stick to C, the code below is (sort of) the equivalent:

typedef struct client_t client_t, *pno;
struct client_t
{
        pid_t pid;
        char password[TAM_MAX]; // -> 50 chars
        pno next;

};


pno AddClient(pno *pclient) 
{
    /* code */
}


int main()
{

    client_t client;

    //code ..

    AddClient(client);

}
3
  • 1
    This is uni project and i have to use C. Is there any way i can acomplish what i want in C ?
    – xRed
    Commented Jun 11, 2013 at 19:49
  • Just move that function outside the struct and make it accept a pointer to your instance.
    – user123
    Commented Jun 11, 2013 at 19:51
  • The client.AddClient() thing will not be possible without a bit of complex magic (see the other answer).
    – QSQ
    Commented Jun 11, 2013 at 19:54
11

You are trying to group code according to struct. C grouping is by file. You put all the functions and internal variables in a header or a header and a object ".o" file compiled from a c source file.

It is not necessary to reinvent object-orientation from scratch for a C program, which is not an object oriented language.

I have seen this before. It is a strange thing. Coders, some of them, have an aversion to passing an object they want to change into a function to change it, even though that is the standard way to do so.

I blame C++, because it hid the fact that the class object is always the first parameter in a member function, but it is hidden. So it looks like it is not passing the object into the function, even though it is.

Client.addClient(Client& c); // addClient first parameter is actually 
                             // "this", a pointer to the Client object.

C is flexible and can take passing things by reference.

A C function often returns only a status byte or int and that is often ignored. In your case a proper form might be

 /* add client to struct, return 0 on success */
 err = addClient( container_t  cnt, client_t c);
 if ( err != 0 )
   { 
      fprintf(stderr, "could not add client (%d) \n", err ); 
   }

addClient would be in Client.h or Client.c

1
  • There are pros and cons to the approach. Just because it's the way it has always been done, referring both to the non-OO (do(object, subject)) and the OO (subject.do(object)) strategies of arranging functions, doesn't mean we should stop trying it out. I totally think it's valid to point out this is not the historical way C has been written, and that C doesn't need to be written this way (same goes for many languages, esp thoes from procedural, functional, or logic paradigms), but I don't we need to actively discourage the pattern. It comes with some benefits as well
    – hiljusti
    Commented Jan 18, 2020 at 2:08
1

Suppose you have the following struct:

struct Object {
    int field;
}

In C, there is no good way to write object.Add(1) and make Add use/change fields of object. You have two options:

a) Abandon the wish to write object.Add(1) and write idiomatic C code instead. Trying to find a way to write object.Add(1) in C using some tricks will just make the code more complex, more bug prone, and is not worth it.

The equivalent way to do it in C is to have a separate function accept a pointer to the object:

void Add(struct Object *object, int amount) {
    object->field += amount;
}

And call this function as follows:

struct Object object;
Add(&object, 1);

b) Use another programming language. For example, in C++, you can define a method:

struct Object {
    int field;
    void Add(int amount) {
        field += amount;
    }
}

and use it in your code:

Object object;
object.add(1);

The C++ compiler will make it work on your behalf. In fact, as long as inheritance is not involved, the C++ code will be equivalent to the function definition and its invocation mentioned in (a).

-1

You can pass the struct pointer to function as function argument. It called pass by reference.

If you modify something inside that pointer, the others will be updated to. Try like this:

typedef struct client_t client_t, *pno;
struct client_t
{
        pid_t pid;
        char password[TAM_MAX]; // -> 50 chars
        pno next;
};

pno AddClient(client_t *client)
{
        /* this will change the original client value */
        client.password = "secret";
}

int main()
{
    client_t client;

    //code ..

    AddClient(&client);
}

2
  • 1
    There is no pass by reference is C. You are passing a copy of the pointer, the references of the pointer are the same, but the pointers itself no.
    – sgtcortez
    Commented Dec 25, 2020 at 22:58
  • Thankyou for your correction. I have read C pass by reference tutorial from Tutorialspoint. Is this different from what i written above? Commented Dec 26, 2020 at 15:18

Not the answer you're looking for? Browse other questions tagged or ask your own question.