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 likepthread_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:
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.
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_FAILURE);
}
}
pthread_exit();
}
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
-
The compiler reads
#include <pthread.h>.
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.
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;
*name;
} Task;
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
Comparisons
| Concept | What it does | Example problem | Typical fix |
|---|---|---|---|
| Header include | Gives declarations to the compiler | implicit declaration of function | Add the right #include |
| Linking a library | Provides compiled implementations | undefined reference to pthread_create | Add -pthread |
-lpthread | Links pthread library only | May work, but less complete | Prefer -pthread |
-pthread | Sets thread options and links pthreads | Recommended for GCC on Linux |
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 referenceusually 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
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 can keep the process alive. In real programs, is often clearer because it explicitly waits for threads.
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.
Keep learning
Related questions
Array-to-Pointer Conversion in C and C++ Explained
Learn what array-to-pointer conversion means in C and C++, how array decay works, and how it differs from a pointer to an array.
Building More Fault-Tolerant Embedded C++ Applications for Radiation-Prone ARM Systems
Learn practical C++ and compile-time techniques to reduce soft-error damage in embedded ARM systems exposed to radiation.
C Pointer to Array vs Array of Pointers: How to Read Complex Declarations
Learn the difference between pointer-to-array and array-of-pointers in C, plus a simple rule for reading complex declarations correctly.