C Typedef — Type Aliases, Struct Typedef, and Function Pointer Typedefs
In this tutorial, you will learn about C Typedef. We cover key concepts, practical examples, and best practices to help you master this topic.
C typedef creates aliases for existing types, making code more readable and portable by hiding complex type declarations behind simple, meaningful names.
Why It Matters
Typedef simplifies complex type declarations, reduces code verbosity, and improves maintainability. It is especially valuable for function pointers, struct types, and platform-dependent types where the underlying type may change across architectures.
Real-World Use
The standard library defines size_t as a typedef for an unsigned integer type that can hold any array size. POSIX defines pid_t, uid_t, and off_t as typedefs. Libraries use typedef to create opaque types that hide implementation details.
What You Will Learn
- Creating typedefs for primitive types
- Simplifying struct declarations with typedef
- Typedef for function pointers
- Using typedef for platform portability
- Opaque types and information hiding
Learning Path
flowchart LR A[Bit Fields] --> B[Typedef
You are here] B --> C[Void Pointers] C --> D[File I/O] D --> E[Error Handling] style B fill:#f90,color:#fff
Basic Typedef Syntax
Typedef creates an alias that can be used anywhere the original type is accepted:
#include <stdio.h>
// Create aliases for primitive types
typedef int Integer;
typedef unsigned long ulong;
typedef char* string;
int main() {
Integer x = 42; // Same as int x = 42
ulong y = 1000000UL; // Same as unsigned long y = ...
string msg = "Hello"; // Same as char *msg = "Hello"
printf("x = %d\n", x);
printf("y = %lu\n", y);
printf("msg = %s\n", msg);
return 0;
}
Expected output:
x = 42
y = 1000000
msg = Hello
Typedef with Structs
The most common use of typedef is simplifying struct declarations:
#include <stdio.h>
#include <string.h>
// Without typedef
struct Point {
int x;
int y;
};
// With typedef
typedef struct {
char name[50];
int age;
} Person;
int main() {
// Without typedef -- must use 'struct' keyword
struct Point p1 = {10, 20};
// With typedef -- no 'struct' needed
Person p2;
strcpy(p2.name, "Alice");
p2.age = 30;
printf("Point: (%d, %d)\n", p1.x, p1.y);
printf("Person: %s, %d\n", p2.name, p2.age);
// You can still use the struct name with typedef
typedef struct Book {
char title[100];
char author[50];
} Book;
Book b = {"C Programming", "Ritchie"};
printf("Book: '%s' by %s\n", b.title, b.author);
return 0;
}
Expected output:
Point: (10, 20)
Person: Alice, 30
Book: 'C Programming' by Ritchie
Typedef for Function Pointers
Function pointer syntax is notoriously complex. Typedef makes it readable:
#include <stdio.h>
// Without typedef (raw syntax)
int add(int a, int b) { return a + b; }
int subtract(int a, int b) { return a - b; }
int multiply(int a, int b) { return a * b; }
// With typedef
typedef int (*operation_t)(int, int);
// Function that takes a callback
int apply(operation_t op, int a, int b) {
return op(a, b);
}
// Array of function pointers
operation_t operations[] = {add, subtract, multiply};
int main() {
// Using typedef
operation_t op = add;
printf("add(10, 5): %d\n", op(10, 5));
// Using function parameter
printf("apply(subtract, 10, 5): %d\n", apply(subtract, 10, 5));
// Using array of function pointers
const char *names[] = {"add", "subtract", "multiply"};
for (int i = 0; i < 3; i++) {
printf("%s(10, 5): %d\n", names[i], operations[i](10, 5));
}
return 0;
}
Expected output:
add(10, 5): 15
apply(subtract, 10, 5): 5
add(10, 5): 15
subtract(10, 5): 5
multiply(10, 5): 50
Function Pointer Typedef Anatomy
// Declaration: typedef return_type (*name)(parameter_types);
typedef int (*callback)(int, int);
// ^ ^
// name parameters
// Usage:
callback my_cb = &some_function;
int result = my_cb(10, 20);
Typedef for Complex Declarations
Typedef can simplify any type declaration:
#include <stdio.h>
#include <stdlib.h>
// Pointer to array of 10 ints
typedef int (*array10_ptr)[10];
// Array of 5 function pointers
typedef int (*func_arr[5])(int);
// Pointer to function returning pointer to int
typedef int* (*func_ptr)(void);
void demo_func_ptr() {
printf("Function pointer works\n");
}
typedef void (*void_func)(void);
int main() {
// Use typedef for array pointer
int arr[10] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
array10_ptr p = &arr;
printf("arr[5] = %d\n", (*p)[5]);
// Use typedef for function pointer
void_func vf = demo_func_ptr;
vf();
return 0;
}
Typedef for Portability
Typedef is essential for creating portable code across platforms:
#include <stdio.h>
#include <stdint.h>
// Portable integer types with explicit sizes
typedef int32_t i32;
typedef uint32_t u32;
typedef int64_t i64;
typedef uint64_t u64;
// Platform-dependent type (abstracted)
#if defined(__linux__)
typedef long ssize_t;
#elif defined(_WIN32)
typedef long long ssize_t;
#endif
int main() {
i32 x = -100;
u32 y = 300;
i64 large = 1000000000000LL;
printf("i32: %d\n", x);
printf("u32: %u\n", y);
printf("i64: %lld\n", large);
printf("Size of i32: %zu\n", sizeof(i32));
printf("Size of i64: %zu\n", sizeof(i64));
return 0;
}
Opaque Types with Typedef
Opaque types hide implementation details from the user:
#include <stdio.h>
#include <stdlib.h>
// In header file: forward declaration
typedef struct Database Database;
// Functions that operate on opaque type
Database* db_open(const char *path);
void db_close(Database *db);
int db_query(Database *db, const char *query);
// In implementation file
struct Database {
int handle;
char path[256];
};
Database* db_open(const char *path) {
Database *db = malloc(sizeof(Database));
if (db) {
// Initialize (simplified)
db->handle = 1;
snprintf(db->path, sizeof(db->path), "%s", path);
}
return db;
}
void db_close(Database *db) {
if (db) {
printf("Closing database: %s\n", db->path);
free(db);
}
}
int db_query(Database *db, const char *query) {
printf("Querying '%s' on handle %d\n", query, db->handle);
return 0;
}
int main() {
Database *db = db_open("/data/mydb");
db_query(db, "SELECT * FROM users");
db_close(db);
// User cannot access struct members directly:
// db->handle = 5; // ERROR: incomplete type
return 0;
}
Common Mistakes
1. Confusing Typedef with Macro
typedef char* string; // OK
#define STRING char* // Bug: STRING a, b; expands to char *a, b;
Typedef is type-safe and handles multiple declarations correctly. Macros do text substitution and can cause unexpected results.
2. Typedef Inside Header (Multiple Inclusion)
typedef int myint; // If included twice, compiler error
Use header guards to prevent multiple typedef definitions.
3. Typedef for Array Types
typedef int arr5[5];
arr5 arr = {1, 2, 3, 4, 5};
// arr is int[5], not a pointer
Array typedefs preserve array semantics (sizeof works, cannot reassign).
4. Overusing Typedef for Readability
typedef int X;
typedef int Y;
X add(X a, Y b) { return a + b; } // Confusing, not helpful
Use typedef for complexity reduction, not just renaming.
5. Not Using Typedef for Structs with Self-References
typedef struct Node {
int data;
struct Node *next; // Must use 'struct Node' here
} Node; // 'Node' is not available yet
Inside the struct definition, you must use struct Node because the typedef name is not yet declared.
Practice Questions
What does typedef do in C? It creates an alias for an existing type. It does not create a new type, just a new name.
How do you create a typedef for a function pointer?
typedef return_type (*name)(parameter_types);What is the advantage of typedef with structs? It eliminates the need for the
structkeyword in declarations, making code cleaner and less verbose.How does typedef differ from #define? typedef is a compile-time type alias with full Type Checking. #define is a preprocessor text substitution with no type checking.
Challenge: Use typedef to create a portable set of fixed-width integer types and a function pointer type for comparison callbacks.
Mini Project: Generic Comparator
#include <stdio.h>
#include <stdlib.h>
typedef int (*comparator_t)(const void*, const void*);
int int_compare(const void *a, const void *b) {
return *(int*)a - *(int*)b;
}
int reverse_int_compare(const void *a, const void *b) {
return *(int*)b - *(int*)a;
}
void sort_and_print(int *arr, int n, comparator_t cmp) {
qsort(arr, n, sizeof(int), cmp);
for (int i = 0; i < n; i++) {
printf("%d ", arr[i]);
}
printf("\n");
}
int main() {
int numbers[] = {5, 2, 8, 1, 9, 3, 7, 4, 6};
int n = sizeof(numbers) / sizeof(numbers[0]);
printf("Ascending: ");
sort_and_print(numbers, n, int_compare);
printf("Descending: ");
sort_and_print(numbers, n, reverse_int_compare);
return 0;
}
FAQ
What is Next
Now that you understand typedef, proceed to Void Pointers to learn about generic pointers and functions like qsort and bsearch.