Frederico Oliveira Frederico Oliveira - 1 year ago 91
C Question

How to pass a struct by value to a pthread?

So I have a multithread C program in which will create N pthreads. I have to give the threads some arguments through a struct. In order to not have to allocate N structs, check if there was no malloc erros, pass them by reference to my threads and then free the struct array, I'd like to simply create a temporary struct and pass it by value. Here's a simple code that demonstrates the question:

#include <pthread.h>
#include <stdlib.h>
#include <stdio.h>

struct thread_arg {
int value1;
char value2;
float value3;

void *foo(void *arg);

int main(int argc, char *argv[])
int N = atoi(argv[1]);
pthread_t *thread = (pthread_t *) malloc(N * sizeof(pthread_t));

for (int i = 0; i < N; i++) {
struct thread_arg arg;
arg.value1 = i;
arg.value2 = 'f';
arg.value3 = i / 10;
pthread_create(&thread[i], NULL, foo, arg);


void *foo(void *arg)
struct thread_arg my_arg = (struct thread_arg) arg;
printf("%d%c%f\n", my_arg.value1, my_arg.value2, my_arg.value3);
return NULL;

I know it's perfectly normal to pass a struct by value to a function that expects it, but with threads and its NULL pointers I get errors no matter what type of cast I make.

Answer Source

I'd like to simply create a temporary struct and pass it [to a thread-start function] by value.

You can't. Thread-start functions accept one argument of type void *. You cannot convert the value of a struct to void * in any reasonable way. You might be able to hack around the problem if the size of a void * were at least as large as the size of your struct's representation, but that does not appear to be the case for you.

Instead, I suggest creating an automatic array of as many structs as you need, and then passing each thread a pointer to its own one of those. You can do that with a VLA even if you don't know before runtime how many you will need:

#define THREAD_LIMIT 50

int main(int argc, char *argv[])
    int N;

    if (argc < 2) {
        // handle too few arguments ...

    N = atoi(argv[1]);
    if (N < 0 || N > THREAD_LIMIT) {
        // handle invalid argument ...

    struct thread_arg args[N];
    pthread_t threads[N];

    for (int i = 0; i < N; i++) {
        args[i].value1 = i;
        args[i].value2 = 'f';
        args[i].value3 = i / 10;

        if (pthread_create(&threads[i], NULL, foo, &args[i])) {
            // handle thread creation failure ...

    // did you forget to pthread_join() your threads?
    for (int i = 0; i < N; i++) {
        pthread_join(threads[i], NULL);


Note that you can do the same thing for your array of pthread_t, as demonstrated above, and that releases you from the obligation to manually free memory.

If you want to be able to accommodate thread counts so large that the VLAs might exhaust the available stack space, or if you need this to work on a compiler that does not support C99 (e.g. MSVC++), then you can dynamically allocate the whole array of arg structs instead of allocating each struct individually, just as you do for your thread handles in your original code.

Whether you use a VLA or a dynamically allocated array or individual dynamically-allocated structs, it is of course necessary that the threads do not attempt to access the structs after the end of their lifetimes. The lifetime of a VLA ends when control leaves the innermost block enclosing its declaration; in the above example that would be when the main thread exits. The lifetime of a dynamically allocated object ends when it is freed, unless the program exits first.

If you intend for any of your threads to continue to run after the main thread exits, therefore, the VLA option is off the table, but dynamic allocation could still work if you never free the allocated memory, or if you are careful to orchestrate when it is freed. On the other hand, you could as easily keep the main thread around as long as needed by making at join all its child threads before it exits, as I added to the example.

Do note, by the way, that these approaches do not consume any more memory than passing your structs by value would do, if it were possible to do that, because by definition, passing the structs by value would make copies of them.

Additional notes:

  • Do check the program arguments, both number and value
  • Do check the return values of your function calls, at least where they make a difference. Thus, you should check pthread_create(), but perhaps you don't need to check pthread_join() in this case.
  • If it does join all the child threads then the main thread can perform an ordinary return or exit() or reach-the-ending-}, instead of a pthread_exit().