Question

Fixing "Undefined Reference to pthread_create" in C on Linux

clinuxmultithreadingpthreads

Question

I am compiling this POSIX threads example in C on Ubuntu Linux:

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

#define NUM_THREADS 5

void *PrintHello(void *threadid)
{
    long tid = (long)threadid;
    printf("Hello World! It's me, thread #%ld!\n", tid);
    pthread_exit(NULL);
}

int main(int argc, char *argv[])
{
    pthread_t threads[NUM_THREADS];
    int rc;
    long t;

    for (t = 0; t < NUM_THREADS; t++) {
        printf("In main: creating thread %ld\n", t);
        rc = pthread_create(&threads[t], NULL, PrintHello, (void *)t);
        if (rc) {
            printf("ERROR: return code from pthread_create() is %d\n", rc);
            exit(-1);
        }
    }

    pthread_exit(NULL);
}

But when I compile it with:

gcc -o term term.c

I get this error:

warning: incompatible implicit declaration of built-in function ‘exit’
undefined reference to `pthread_create`
collect2: ld returned 1 exit status

Why does this happen even though pthread.h is included, and how should I compile this program correctly on Linux?

Short Answer

By the end of this page, you will understand the difference between including a header file and linking a library, why pthread_create can produce an "undefined reference" error, and how to compile POSIX thread programs correctly in C on Linux using gcc and the -pthread flag.

Concept

In C, #include and linking solve two different problems:

  • #include <pthread.h> gives the compiler the declaration of functions like pthread_create.
  • Linking tells the system where the actual compiled function implementation lives.

That is why including pthread.h is not enough by itself.

What "undefined reference" means

When GCC compiles your source file, it first checks syntax and function declarations. Because pthread.h is included, the compiler knows that pthread_create exists and what arguments it expects.

But after compilation, the linker tries to build the final executable. At that stage, it must find the compiled implementation of pthread_create inside the pthread library. If you do not link that library, the linker reports:

undefined reference to `pthread_create`

Why -pthread matters

On Linux, POSIX thread programs are usually compiled with:

gcc file.c -pthread

The -pthread option is important because it does two things:

  • enables thread-related compiler settings
  • links the pthread library correctly

Many beginners try -lpthread, which may work in some cases, but -pthread is the recommended and more complete option.

Why you also saw the exit warning

Your program calls exit(-1), but exit is declared in:

#include <stdlib.h>

Without that header, the compiler warns that exit was used without a proper declaration.

So there are really two issues in the original program:

  1. Missing #include <stdlib.h> for exit
  2. Missing -pthread during compilation/linking

Mental Model

Think of C compilation like building a movie set:

  • A header file is the script telling actors what lines exist.
  • A library is the actual actor showing up on set.

Including pthread.h means your code has read the script and knows pthread_create is a real function.

But if you do not link pthreads, the actor never arrives. When the linker tries to assemble the final program, it says:

  • "I know this character name exists..."
  • "...but I cannot find the actual person to play it."

That is the meaning of undefined reference.

Take Quiz

Syntax and Examples

The normal way to compile a pthread program with GCC on Linux is:

gcc program.c -o program -pthread

Corrected example

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

#define NUM_THREADS 5

void *print_hello(void *threadid)
{
    long tid = (long)threadid;
    printf("Hello World! It's me, thread #%ld!\n", tid);
    return NULL;
}

int main(void)
{
    pthread_t threads[NUM_THREADS];
    int rc;

    for (long t = 0; t < NUM_THREADS; t++) {
        printf("In main: creating thread %ld\n", t);
        rc = pthread_create(&threads[t], NULL, print_hello, (void *)t);
        if (rc != 0) {
            printf("ERROR: return code from pthread_create() is %d\n", rc);
            exit(EXIT_FAILURE);
        }
    }

    pthread_exit(NULL);
}

Compile it with:

gcc term.c -o term -pthread

What each part does

  • #include <pthread.h> declares thread functions and types.
  • pthread_t stores thread handles.
  • pthread_create(...) starts a new thread.
  • pthread_exit(NULL) keeps the process alive until threads finish.
  • -pthread tells GCC to compile and link for POSIX threads.

Alternative

You may also see:

gcc term.c -o term -lpthread

This can link the pthread library, but -pthread is the preferred option on Linux because it also sets compiler options appropriately.

Step by Step Execution

Consider this smaller example:

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

void *worker(void *arg)
{
    printf("Worker thread running\n");
    return NULL;
}

int main(void)
{
    pthread_t thread;

    if (pthread_create(&thread, NULL, worker, NULL) != 0) {
        printf("Could not create thread\n");
        exit(EXIT_FAILURE);
    }

    pthread_join(thread, NULL);
    printf("Main thread finished\n");
    return 0;
}

Compile:

gcc main.c -o main -pthread

Step by step

  1. The compiler reads #include <pthread.h>.

    • It learns the declaration of pthread_create and pthread_join.
  2. The compiler compiles the source into object code.

    • Calls to pthread_create remain as references to external library code.
  3. The linker runs.

    • Because -pthread was provided, GCC links the pthread library.
    • The linker finds the real implementation of pthread_create.
  4. The executable is created successfully.

  5. At runtime, main() calls pthread_create.

    • A new thread starts and runs worker.
  6. pthread_join(thread, NULL) waits for that thread to finish.

  7. The program prints:

Worker thread running
Main thread finished

What happens without -pthread

Steps 1 and 2 still work.

The failure happens in step 3, when the linker cannot find the real implementation of pthread_create. That is why the error is a linker error, not a syntax error.

Real World Use Cases

POSIX threads are used in many kinds of Linux C programs.

Common uses

  • Servers: handle multiple client connections at once
  • Background workers: process jobs while the main program keeps responding
  • File processing: read, transform, or compress multiple files in parallel
  • Monitoring tools: one thread collects metrics while another handles output
  • GUI or event-driven apps: keep the interface responsive while work runs in the background

Example scenarios

Web server

A thread can handle each request or pick tasks from a shared queue.

Log processing tool

One thread reads logs, another parses them, and another writes summaries.

Download manager

Multiple threads can download different files at the same time.

In all of these, simply including pthread.h is still not enough. The final executable must be linked with pthread support.

Take Quiz

Real Codebase Usage

In real projects, developers rarely stop at just calling pthread_create. They also use patterns that make threaded code safer and easier to maintain.

Common patterns

Error checking

Always check the return value of pthread functions.

int rc = pthread_create(&thread, NULL, worker, NULL);
if (rc != 0) {
    fprintf(stderr, "pthread_create failed: %d\n", rc);
    return EXIT_FAILURE;
}

Joining threads

If the main thread exits too early, worker threads may not finish. pthread_join is often clearer than calling pthread_exit in main.

pthread_join(thread, NULL);

Passing structured data

In real code, developers usually pass a pointer to a struct instead of casting integers to void *.

typedef struct {
    int id;
    const char *name;
} Task;

Guard clauses

Thread creation often appears behind validation or setup checks.

if (config.thread_count <= 0) {
    return EXIT_FAILURE;
}

Build system integration

In Makefiles or CMake projects, -pthread is added to compiler and linker settings so every relevant target builds correctly.

Practical advice

If a real codebase uses pthreads, treat -pthread as part of the build configuration, not something to remember manually each time.

Common Mistakes

1. Including the header but forgetting to link the library

This is the exact problem from the question.

Broken

gcc term.c -o term

Correct

gcc term.c -o term -pthread

2. Forgetting the correct header for exit

Broken

#include <stdio.h>

int main(void) {
    exit(1);
}

This can trigger a warning because exit is declared in <stdlib.h>.

Correct

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

3. Using -lpthread when -pthread is the better choice

-lpthread may link successfully on some systems, but -pthread is the safer and recommended option with GCC on Linux.

4. Passing integers through void * carelessly

This appears in many simple tutorials:

pthread_create(&threads[t], NULL, worker, (void *)t);

It may work for demonstration code, but in production code it is better to pass a pointer to real data.

Better approach

typedef struct {
    long id;
} ThreadData;

5. Not waiting for threads to finish

If main returns immediately, your process can end before worker threads complete.

Better options

  • use pthread_join for each thread
  • or use pthread_exit(NULL) in main when appropriate

6. Confusing compile-time and link-time errors

  • Compile-time: syntax errors, wrong types, missing declarations
  • Link-time: missing library implementations like pthread_create

This distinction helps diagnose errors much faster.

Comparisons

ConceptWhat it doesExample problemTypical fix
Header includeGives declarations to the compilerimplicit declaration of functionAdd the right #include
Linking a libraryProvides compiled implementationsundefined reference to pthread_createAdd -pthread
-lpthreadLinks pthread library onlyMay work, but less completePrefer -pthread
-pthreadSets thread options and links pthreadsRecommended for GCC on LinuxUse for compile and link

pthread_exit vs return in main

ApproachEffect
return 0; from mainProcess exits; other threads may be terminated if still running
pthread_exit(NULL); in mainMain thread ends, process stays alive until other threads finish
pthread_join(...)Waits for specific thread(s) and is often clearer

Declaration vs definition

TermMeaning
DeclarationTells the compiler a function exists
Definition/implementationThe actual compiled code for the function

pthread.h provides declarations. The pthread library provides implementations.

Take Quiz

Cheat Sheet

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

Compile command

gcc file.c -o file -pthread

Key rule

  • Header files declare functions.
  • Libraries provide implementations.
  • undefined reference usually means a linking problem.

Common pthread functions

pthread_create(&thread, NULL, worker, arg);
pthread_join(thread, NULL);
pthread_exit(NULL);

If you see this error

undefined reference to `pthread_create`

Check:

  • Did you compile with -pthread?
  • Are you using gcc source.c -o app -pthread?
  • Did you spell the function correctly?
  • Are you actually on a system with pthread support?

If you see this warning

implicit declaration of function 'exit'

Add:

#include <stdlib.h>

Best practice

  • Prefer -pthread over -lpthread
  • Check return values
  • Use pthread_join when you need to wait for threads
  • Pass structs or allocated data to threads in real programs

FAQ

Why does pthread.h not solve the error by itself?

Because headers only declare functions. The linker still needs the actual pthread library implementation.

What is the correct GCC command for pthreads on Linux?

Usually:

gcc program.c -o program -pthread

Is -lpthread the same as -pthread?

Not exactly. -lpthread mainly links the library. -pthread is the recommended option because it also applies thread-related compiler settings.

Why did I get a warning about exit?

Because exit is declared in <stdlib.h>, and that header was missing.

Is pthread_create part of standard C?

No. It is part of POSIX threads, commonly available on Unix-like systems such as Linux.

Should I use pthread_exit(NULL) or pthread_join()?

For simple demos, pthread_exit(NULL) in main can keep the process alive. In real programs, pthread_join() is often clearer because it explicitly waits for threads.

Can this error happen with other libraries too?

Yes. Any time you include a header but forget to link its library, you can get an undefined reference error.

Take Quiz

Mini Project

Description

Build a small C program that creates several worker threads to process numbered tasks. This project demonstrates the full workflow: including the correct headers, compiling with pthread support, creating threads safely, and waiting for them to finish.

Goal

Create and run a multi-threaded C program on Linux without linker errors, using pthread_create and pthread_join correctly.

Requirements

  • Create a C program that starts 3 worker threads.
  • Each thread should print its own task ID.
  • The main thread must wait for all worker threads to finish.
  • Include all required headers.
  • Compile the program with the correct GCC pthread flag.
Take Quiz

Keep learning

Related questions

View more