Introduction to typedef
typedef is a keyword in C that allows you to create aliases for existing data types. It doesn't create new types but provides a way to give existing types more meaningful or convenient names. This is particularly useful for complex type declarations, improving code readability and maintainability.
typedef Syntax and Basic Usage
Basic Syntax
typedef existing_type new_type_name;
Simple Examples
#include <stdio.h>
// Basic typedef examples
typedef int integer;
typedef float real;
typedef char character;
typedef unsigned int uint;
int main() {
integer age = 25;
real salary = 45000.50;
character grade = 'A';
uint count = 100;
printf("Age: %d\n", age);
printf("Salary: %.2f\n", salary);
printf("Grade: %c\n", grade);
printf("Count: %u\n", count);
return 0;
}
typedef with Primitive Types
Creating Aliases for Primitive Types
#include <stdio.h>
// Platform-independent type definitions
typedef unsigned char byte;
typedef unsigned short word;
typedef unsigned int dword;
typedef unsigned long long qword;
// Fixed-size integer types
typedef signed char int8;
typedef unsigned char uint8;
typedef signed short int16;
typedef unsigned short uint16;
typedef signed int int32;
typedef unsigned int uint32;
typedef signed long long int64;
typedef unsigned long long uint64;
int main() {
byte b = 255;
word w = 65535;
dword dw = 4294967295U;
qword qw = 18446744073709551615ULL;
printf("byte (max): %u\n", b);
printf("word (max): %u\n", w);
printf("dword (max): %u\n", dw);
printf("qword (max): %llu\n", qw);
printf("\nSize of byte: %zu byte\n", sizeof(byte));
printf("Size of word: %zu bytes\n", sizeof(word));
printf("Size of dword: %zu bytes\n", sizeof(dword));
printf("Size of qword: %zu bytes\n", sizeof(qword));
return 0;
}
typedef with Structures
Basic Structure typedef
#include <stdio.h>
#include <string.h>
// Without typedef
struct Point {
int x;
int y;
};
// With typedef
typedef struct {
char name[50];
int age;
float salary;
} Employee;
// Combined declaration
typedef struct Student {
char name[50];
int id;
float gpa;
} Student;
int main() {
// Using struct without typedef
struct Point p1 = {10, 20};
// Using typedef'ed structure
Employee emp1;
strcpy(emp1.name, "John Doe");
emp1.age = 30;
emp1.salary = 50000.50;
// Using typedef with structure name
Student s1 = {"Alice Smith", 12345, 3.75};
printf("Point: (%d, %d)\n", p1.x, p1.y);
printf("Employee: %s, %d, %.2f\n", emp1.name, emp1.age, emp1.salary);
printf("Student: %s, %d, %.2f\n", s1.name, s1.id, s1.gpa);
return 0;
}
Self-Referential Structures
#include <stdio.h>
#include <stdlib.h>
// Linked list node using typedef
typedef struct Node {
int data;
struct Node *next; // Must use struct Node here
} Node;
// Another approach using forward declaration
typedef struct TreeNode TreeNode;
struct TreeNode {
int data;
TreeNode *left;
TreeNode *right;
};
int main() {
// Create linked list
Node *head = (Node*)malloc(sizeof(Node));
head->data = 10;
head->next = (Node*)malloc(sizeof(Node));
head->next->data = 20;
head->next->next = NULL;
// Create tree node
TreeNode *root = (TreeNode*)malloc(sizeof(TreeNode));
root->data = 100;
root->left = NULL;
root->right = NULL;
printf("Linked List: %d -> %d\n", head->data, head->next->data);
printf("Tree Node: %d\n", root->data);
free(head->next);
free(head);
free(root);
return 0;
}
typedef with Pointers
Pointer Type Aliases
#include <stdio.h>
#include <stdlib.h>
// Pointer typedefs
typedef int* IntPtr;
typedef char* String;
typedef void* Pointer;
// Function pointer typedef
typedef int (*Comparator)(const void*, const void*);
// Pointer to structure
typedef struct {
int x;
int y;
} Point, *PointPtr;
int compareInts(const void *a, const void *b) {
return *(int*)a - *(int*)b;
}
int main() {
// Using IntPtr
IntPtr p = (IntPtr)malloc(sizeof(int));
*p = 42;
printf("Value via IntPtr: %d\n", *p);
// Using String
String str = "Hello, typedef!";
printf("String: %s\n", str);
// Using Comparator
int arr[] = {5, 2, 8, 1, 9};
Comparator cmp = compareInts;
qsort(arr, 5, sizeof(int), cmp);
printf("Sorted array: ");
for (int i = 0; i < 5; i++) {
printf("%d ", arr[i]);
}
printf("\n");
// Using PointPtr
PointPtr pt = (PointPtr)malloc(sizeof(Point));
pt->x = 100;
pt->y = 200;
printf("Point: (%d, %d)\n", pt->x, pt->y);
free(p);
free(pt);
return 0;
}
typedef with Arrays
Array Type Aliases
#include <stdio.h>
// Array typedefs
typedef int Vector5[5];
typedef char String20[20];
typedef int Matrix3x3[3][3];
// Function returning array pointer (complex)
typedef int (*ArrayPtr)[5];
int main() {
// Using Vector5
Vector5 vec = {1, 2, 3, 4, 5};
printf("Vector: ");
for (int i = 0; i < 5; i++) {
printf("%d ", vec[i]);
}
printf("\n");
// Using String20
String20 str = "Hello, World!";
printf("String: %s\n", str);
// Using Matrix3x3
Matrix3x3 mat = {
{1, 2, 3},
{4, 5, 6},
{7, 8, 9}
};
printf("Matrix:\n");
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
printf("%d ", mat[i][j]);
}
printf("\n");
}
// Using ArrayPtr
int arr[5] = {10, 20, 30, 40, 50};
ArrayPtr ptr = &arr;
printf("\nArray via pointer: ");
for (int i = 0; i < 5; i++) {
printf("%d ", (*ptr)[i]);
}
printf("\n");
return 0;
}
typedef with Function Pointers
Function Pointer Aliases
#include <stdio.h>
// Simple function pointer typedefs
typedef int (*BinaryOp)(int, int);
typedef void (*PrintFunc)(int);
typedef int (*Comparator)(int, int);
// Complex function pointer typedefs
typedef void (*Callback)(const char*, void*);
typedef int* (*ArrayGenerator)(int size);
typedef void (*ErrorHandler)(const char* file, int line, const char* msg);
// Function implementations
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; }
int divide(int a, int b) { return (b != 0) ? a / b : 0; }
void printInt(int x) { printf("%d", x); }
void printIntNewline(int x) { printf("%d\n", x); }
int compare(int a, int b) { return a - b; }
void calculateAndPrint(BinaryOp op, int a, int b, PrintFunc print) {
int result = op(a, b);
print(result);
}
int main() {
BinaryOp operations[] = {add, subtract, multiply, divide};
PrintFunc printers[] = {printInt, printIntNewline};
const char *opNames[] = {"Add", "Subtract", "Multiply", "Divide"};
int x = 20, y = 5;
for (int i = 0; i < 4; i++) {
printf("%s: ", opNames[i]);
calculateAndPrint(operations[i], x, y, printers[0]);
printf(" = ");
calculateAndPrint(operations[i], x, y, printers[1]);
}
// Using Comparator
Comparator cmp = compare;
printf("\nComparison: %d\n", cmp(x, y));
return 0;
}
Callback System with typedef
#include <stdio.h>
#include <string.h>
// Callback type definitions
typedef void (*EventCallback)(const char* event, void* userData);
typedef int (*FilterFunction)(int value, void* context);
// Event system
typedef struct {
EventCallback onStart;
EventCallback onData;
EventCallback onError;
EventCallback onComplete;
void* userData;
} EventHandlers;
void processData(EventHandlers *handlers) {
if (handlers->onStart) {
handlers->onStart("Processing started", handlers->userData);
}
for (int i = 0; i < 5; i++) {
if (handlers->onData) {
char buffer[50];
sprintf(buffer, "Processing item %d", i);
handlers->onData(buffer, handlers->userData);
}
}
if (handlers->onComplete) {
handlers->onComplete("Processing complete", handlers->userData);
}
}
// Callback implementations
void logToConsole(const char* event, void* data) {
printf("[LOG] %s\n", event);
}
void logWithPrefix(const char* event, void* data) {
const char* prefix = (const char*)data;
printf("%s: %s\n", prefix, event);
}
int main() {
// Setup event handlers
EventHandlers handlers = {
.onStart = logToConsole,
.onData = logToConsole,
.onComplete = logToConsole,
.onError = logToConsole,
.userData = NULL
};
printf("=== Using console logger ===\n");
processData(&handlers);
printf("\n=== Using custom prefix ===\n");
handlers.onData = logWithPrefix;
handlers.userData = "DATA";
processData(&handlers);
return 0;
}
typedef with Unions
Union Type Aliases
#include <stdio.h>
// Simple union typedef
typedef union {
int i;
float f;
char c;
} Value;
// Tagged union (discriminated union)
typedef enum {
TYPE_INT,
TYPE_FLOAT,
TYPE_STRING
} ValueType;
typedef struct {
ValueType type;
union {
int intValue;
float floatValue;
char* stringValue;
} data;
} Variant;
// Color union with typedef
typedef union {
struct {
unsigned char r, g, b, a;
} rgba;
unsigned int value;
} Color;
void printVariant(Variant *v) {
switch (v->type) {
case TYPE_INT:
printf("Integer: %d\n", v->data.intValue);
break;
case TYPE_FLOAT:
printf("Float: %f\n", v->data.floatValue);
break;
case TYPE_STRING:
printf("String: %s\n", v->data.stringValue);
break;
}
}
int main() {
// Using Value union
Value val;
val.i = 42;
printf("As int: %d\n", val.i);
val.f = 3.14159;
printf("As float: %f\n", val.f);
val.c = 'A';
printf("As char: %c\n\n", val.c);
// Using Variant (tagged union)
Variant v1 = {.type = TYPE_INT, .data.intValue = 100};
Variant v2 = {.type = TYPE_FLOAT, .data.floatValue = 3.14};
Variant v3 = {.type = TYPE_STRING, .data.stringValue = "Hello"};
printVariant(&v1);
printVariant(&v2);
printVariant(&v3);
// Using Color union
Color c;
c.rgba.r = 255;
c.rgba.g = 128;
c.rgba.b = 64;
c.rgba.a = 255;
printf("\nColor: RGBA(%d,%d,%d,%d) = 0x%08X\n",
c.rgba.r, c.rgba.g, c.rgba.b, c.rgba.a, c.value);
return 0;
}
typedef with Enums
Enum Type Aliases
#include <stdio.h>
// Basic enum typedef
typedef enum {
MONDAY,
TUESDAY,
WEDNESDAY,
THURSDAY,
FRIDAY,
SATURDAY,
SUNDAY
} Weekday;
// Enum with explicit values
typedef enum {
STATUS_OK = 0,
STATUS_ERROR = -1,
STATUS_NOT_FOUND = 404,
STATUS_FORBIDDEN = 403,
STATUS_UNAUTHORIZED = 401
} StatusCode;
// Enum for flags (bitmask)
typedef enum {
FLAG_NONE = 0,
FLAG_READ = 1 << 0,
FLAG_WRITE = 1 << 1,
FLAG_EXECUTE = 1 << 2,
FLAG_DELETE = 1 << 3,
FLAG_ALL = FLAG_READ | FLAG_WRITE | FLAG_EXECUTE | FLAG_DELETE
} PermissionFlags;
const char* weekdayName(Weekday day) {
static const char* names[] = {
"Monday", "Tuesday", "Wednesday", "Thursday",
"Friday", "Saturday", "Sunday"
};
return names[day];
}
int main() {
// Using Weekday enum
Weekday today = WEDNESDAY;
printf("Today is %s\n", weekdayName(today));
// Using StatusCode
StatusCode code = STATUS_OK;
printf("Status code: %d\n", code);
// Using PermissionFlags
PermissionFlags userPerms = FLAG_READ | FLAG_WRITE;
if (userPerms & FLAG_READ) {
printf("User can read\n");
}
if (userPerms & FLAG_WRITE) {
printf("User can write\n");
}
if (userPerms & FLAG_EXECUTE) {
printf("User can execute\n");
} else {
printf("User cannot execute\n");
}
return 0;
}
Complex typedef Examples
Nested typedefs
#include <stdio.h>
#include <string.h>
// Forward declarations
typedef struct Object Object;
typedef void (*ObjectMethod)(Object*);
// Complex nested typedefs
typedef struct {
char name[50];
int age;
} Person;
typedef struct {
Person person;
int employeeId;
float salary;
} Employee;
typedef struct {
Employee *employees;
int count;
int capacity;
} Department;
typedef struct {
char brand[50];
char model[50];
int year;
} Car;
typedef struct {
Person owner;
Car cars[10];
int carCount;
} Garage;
// Self-referential with typedef
typedef struct TreeNode {
int data;
struct TreeNode *left;
struct TreeNode *right;
} TreeNode;
// Function pointer typedefs within struct
typedef struct MenuItem {
char label[50];
void (*action)(void);
struct MenuItem *subMenu;
} MenuItem;
void helloAction() { printf("Hello!"); }
void goodbyeAction() { printf("Goodbye!"); }
int main() {
// Employee example
Employee emp = {
.person = {"John Doe", 30},
.employeeId = 12345,
.salary = 50000.0
};
printf("Employee: %s (ID: %d, Age: %d, Salary: %.2f)\n",
emp.person.name, emp.employeeId, emp.person.age, emp.salary);
// Garage example
Garage garage;
strcpy(garage.owner.name, "Alice Smith");
garage.owner.age = 35;
garage.carCount = 2;
strcpy(garage.cars[0].brand, "Toyota");
strcpy(garage.cars[0].model, "Camry");
garage.cars[0].year = 2020;
strcpy(garage.cars[1].brand, "Honda");
strcpy(garage.cars[1].model, "Civic");
garage.cars[1].year = 2021;
printf("\n%s's Garage:\n", garage.owner.name);
for (int i = 0; i < garage.carCount; i++) {
printf(" %d. %s %s (%d)\n", i + 1,
garage.cars[i].brand,
garage.cars[i].model,
garage.cars[i].year);
}
// Menu example
MenuItem mainMenu[] = {
{"Hello", helloAction, NULL},
{"Goodbye", goodbyeAction, NULL},
{"Exit", NULL, NULL}
};
printf("\nMenu:\n");
for (int i = 0; i < 3; i++) {
printf("%d. %s\n", i + 1, mainMenu[i].label);
}
return 0;
}
Platform-Independent Types
Creating Portable Type Aliases
#include <stdio.h>
#include <stdint.h>
#include <limits.h>
// Platform-independent integer types
typedef signed char int8;
typedef unsigned char uint8;
typedef signed short int16;
typedef unsigned short uint16;
typedef signed int int32;
typedef unsigned int uint32;
typedef signed long long int64;
typedef unsigned long long uint64;
// Fast types (optimized for speed)
typedef int fast_int;
typedef unsigned int fast_uint;
// Small types (minimum size)
typedef char small_int;
typedef unsigned char small_uint;
// Pointer-sized integers
typedef intptr_t int_ptr;
typedef uintptr_t uint_ptr;
// Check sizes at compile time
#define STATIC_ASSERT(expr) typedef char static_assertion[(expr) ? 1 : -1]
// Verify sizes
STATIC_ASSERT(sizeof(int8) == 1);
STATIC_ASSERT(sizeof(int16) == 2);
STATIC_ASSERT(sizeof(int32) == 4);
STATIC_ASSERT(sizeof(int64) == 8);
int main() {
printf("=== Platform-Independent Types ===\n");
printf("int8 size: %zu byte\n", sizeof(int8));
printf("int16 size: %zu bytes\n", sizeof(int16));
printf("int32 size: %zu bytes\n", sizeof(int32));
printf("int64 size: %zu bytes\n\n", sizeof(int64));
printf("Min/max values:\n");
printf("int8 min: %d, max: %d\n", INT8_MIN, INT8_MAX);
printf("int16 min: %d, max: %d\n", INT16_MIN, INT16_MAX);
printf("int32 min: %d, max: %d\n", INT32_MIN, INT32_MAX);
printf("int64 min: %lld, max: %lld\n", (long long)INT64_MIN, (long long)INT64_MAX);
return 0;
}
typedef in Header Files
string_utils.h
#ifndef STRING_UTILS_H
#define STRING_UTILS_H
#include <stddef.h>
// Type definitions for string utilities
typedef char* String;
typedef const char* ConstString;
typedef size_t StringLength;
// Error codes
typedef enum {
STRING_OK = 0,
STRING_NULL_PTR = -1,
STRING_OUT_OF_MEMORY = -2,
STRING_INVALID_OPERATION = -3
} StringError;
// String builder structure
typedef struct {
char* buffer;
StringLength capacity;
StringLength length;
} StringBuilder;
// String view (non-owning reference)
typedef struct {
ConstString data;
StringLength length;
} StringView;
// Function prototypes
StringBuilder* string_builder_create(StringLength initialCapacity);
void string_builder_destroy(StringBuilder* sb);
StringError string_builder_append(StringBuilder* sb, ConstString str);
String string_builder_to_string(StringBuilder* sb);
StringView string_view_create(ConstString str);
int string_view_compare(StringView sv1, StringView sv2);
#endif
string_utils.c
#include "string_utils.h"
#include <stdlib.h>
#include <string.h>
StringBuilder* string_builder_create(StringLength initialCapacity) {
StringBuilder* sb = (StringBuilder*)malloc(sizeof(StringBuilder));
if (!sb) return NULL;
sb->buffer = (char*)malloc(initialCapacity);
if (!sb->buffer) {
free(sb);
return NULL;
}
sb->capacity = initialCapacity;
sb->length = 0;
sb->buffer[0] = '\0';
return sb;
}
void string_builder_destroy(StringBuilder* sb) {
if (sb) {
free(sb->buffer);
free(sb);
}
}
StringError string_builder_append(StringBuilder* sb, ConstString str) {
if (!sb || !str) return STRING_NULL_PTR;
StringLength strLen = strlen(str);
StringLength needed = sb->length + strLen + 1;
if (needed > sb->capacity) {
StringLength newCapacity = sb->capacity * 2;
while (newCapacity < needed) {
newCapacity *= 2;
}
char* newBuffer = (char*)realloc(sb->buffer, newCapacity);
if (!newBuffer) return STRING_OUT_OF_MEMORY;
sb->buffer = newBuffer;
sb->capacity = newCapacity;
}
strcpy(sb->buffer + sb->length, str);
sb->length += strLen;
return STRING_OK;
}
String string_builder_to_string(StringBuilder* sb) {
return sb ? strdup(sb->buffer) : NULL;
}
StringView string_view_create(ConstString str) {
StringView sv;
sv.data = str;
sv.length = str ? strlen(str) : 0;
return sv;
}
int string_view_compare(StringView sv1, StringView sv2) {
StringLength minLen = sv1.length < sv2.length ? sv1.length : sv2.length;
int result = memcmp(sv1.data, sv2.data, minLen);
if (result == 0) {
if (sv1.length < sv2.length) return -1;
if (sv1.length > sv2.length) return 1;
}
return result;
}
Advanced typedef Techniques
Opaque Pointers (Information Hiding)
#include <stdio.h>
#include <stdlib.h>
// Forward declaration of struct (opaque type)
typedef struct DatabaseConnection DatabaseConnection;
// Public API using opaque pointer
DatabaseConnection* db_connect(const char* connectionString);
void db_disconnect(DatabaseConnection* conn);
int db_query(DatabaseConnection* conn, const char* sql);
void db_close(DatabaseConnection* conn);
// Implementation (hidden from users)
struct DatabaseConnection {
char connectionString[256];
int socket;
int isConnected;
int lastError;
};
DatabaseConnection* db_connect(const char* connectionString) {
DatabaseConnection* conn = (DatabaseConnection*)malloc(sizeof(DatabaseConnection));
if (!conn) return NULL;
strcpy(conn->connectionString, connectionString);
conn->socket = 1234; // Simulated socket
conn->isConnected = 1;
conn->lastError = 0;
printf("Connected to database: %s\n", connectionString);
return conn;
}
void db_disconnect(DatabaseConnection* conn) {
if (conn && conn->isConnected) {
printf("Disconnected from database\n");
conn->isConnected = 0;
}
}
int db_query(DatabaseConnection* conn, const char* sql) {
if (!conn || !conn->isConnected) {
return -1;
}
printf("Executing query: %s\n", sql);
return 0;
}
void db_close(DatabaseConnection* conn) {
if (conn) {
db_disconnect(conn);
free(conn);
}
}
int main() {
DatabaseConnection* db = db_connect("mysql://localhost:3306/test");
if (db) {
db_query(db, "SELECT * FROM users");
db_query(db, "INSERT INTO logs VALUES ('test')");
db_close(db);
}
return 0;
}
Function Table (Virtual Table)
#include <stdio.h>
#include <stdlib.h>
// Shape interface using function pointers
typedef struct ShapeVTable ShapeVTable;
typedef struct Shape Shape;
struct ShapeVTable {
double (*area)(const Shape* shape);
double (*perimeter)(const Shape* shape);
void (*draw)(const Shape* shape);
void (*destroy)(Shape* shape);
};
struct Shape {
const ShapeVTable* vtable;
char name[50];
};
// Rectangle implementation
typedef struct {
Shape base;
double width;
double height;
} Rectangle;
double rectangle_area(const Shape* shape) {
const Rectangle* rect = (const Rectangle*)shape;
return rect->width * rect->height;
}
double rectangle_perimeter(const Shape* shape) {
const Rectangle* rect = (const Rectangle*)shape;
return 2 * (rect->width + rect->height);
}
void rectangle_draw(const Shape* shape) {
const Rectangle* rect = (const Rectangle*)shape;
printf("Rectangle %s: %.1f x %.1f\n", shape->name, rect->width, rect->height);
}
void rectangle_destroy(Shape* shape) {
free(shape);
}
const ShapeVTable rectangle_vtable = {
.area = rectangle_area,
.perimeter = rectangle_perimeter,
.draw = rectangle_draw,
.destroy = rectangle_destroy
};
Shape* rectangle_create(const char* name, double width, double height) {
Rectangle* rect = (Rectangle*)malloc(sizeof(Rectangle));
if (!rect) return NULL;
rect->base.vtable = &rectangle_vtable;
strcpy(rect->base.name, name);
rect->width = width;
rect->height = height;
return (Shape*)rect;
}
// Circle implementation
typedef struct {
Shape base;
double radius;
} Circle;
double circle_area(const Shape* shape) {
const Circle* circle = (const Circle*)shape;
return 3.14159 * circle->radius * circle->radius;
}
double circle_perimeter(const Shape* shape) {
const Circle* circle = (const Circle*)shape;
return 2 * 3.14159 * circle->radius;
}
void circle_draw(const Shape* shape) {
const Circle* circle = (const Circle*)shape;
printf("Circle %s: radius %.1f\n", shape->name, circle->radius);
}
void circle_destroy(Shape* shape) {
free(shape);
}
const ShapeVTable circle_vtable = {
.area = circle_area,
.perimeter = circle_perimeter,
.draw = circle_draw,
.destroy = circle_destroy
};
Shape* circle_create(const char* name, double radius) {
Circle* circle = (Circle*)malloc(sizeof(Circle));
if (!circle) return NULL;
circle->base.vtable = &circle_vtable;
strcpy(circle->base.name, name);
circle->radius = radius;
return (Shape*)circle;
}
void print_shape_info(const Shape* shape) {
printf("Shape: %s\n", shape->name);
printf(" Area: %.2f\n", shape->vtable->area(shape));
printf(" Perimeter: %.2f\n", shape->vtable->perimeter(shape));
shape->vtable->draw(shape);
printf("\n");
}
int main() {
Shape* shapes[4];
shapes[0] = rectangle_create("Rect1", 5.0, 3.0);
shapes[1] = rectangle_create("Rect2", 4.0, 4.0);
shapes[2] = circle_create("Circle1", 2.5);
shapes[3] = circle_create("Circle2", 1.5);
for (int i = 0; i < 4; i++) {
print_shape_info(shapes[i]);
}
// Cleanup
for (int i = 0; i < 4; i++) {
shapes[i]->vtable->destroy(shapes[i]);
}
return 0;
}
Common Pitfalls and Best Practices
1. typedef vs #define
#include <stdio.h>
// #define - simple text substitution
#define PINT int*
#define PCHAR char*
// typedef - type aliasing
typedef int* IntPtr;
typedef char* CharPtr;
int main() {
// Problem with #define
PINT p1, p2; // Expands to: int* p1, p2; (p2 is int, not int*)
IntPtr q1, q2; // Both q1 and q2 are int*
printf("Size of p1: %zu, p2: %zu\n", sizeof(p1), sizeof(p2));
printf("Size of q1: %zu, q2: %zu\n", sizeof(q1), sizeof(q2));
// With #define
// PCHAR s1, s2; // s2 would be char, not char*
// With typedef
CharPtr t1, t2; // Both char*
return 0;
}
2. Const Correctness
#include <stdio.h>
// Different const placements
typedef int* IntPtr;
typedef const int* ConstIntPtr; // pointer to const int
typedef int* const ConstPtr; // const pointer to int
typedef const int* const ConstBoth; // const pointer to const int
void demonstrate_const() {
int x = 10, y = 20;
IntPtr p1 = &x;
*p1 = 30; // OK
p1 = &y; // OK
ConstIntPtr p2 = &x;
// *p2 = 30; // Error: can't modify const int
p2 = &y; // OK: can change pointer
ConstPtr p3 = &x;
*p3 = 30; // OK: can modify int
// p3 = &y; // Error: can't change pointer
ConstBoth p4 = &x;
// *p4 = 30; // Error: can't modify int
// p4 = &y; // Error: can't change pointer
}
int main() {
demonstrate_const();
printf("Const correctness example compiled\n");
return 0;
}
3. Naming Conventions
// Good naming conventions
typedef struct {
int x;
int y;
} Point;
typedef struct {
Point topLeft;
Point bottomRight;
} Rectangle;
typedef enum {
COLOR_RED,
COLOR_GREEN,
COLOR_BLUE
} Color;
// Hungarian notation (optional)
typedef int i32; // 32-bit integer
typedef float f32; // 32-bit float
typedef double f64; // 64-bit float
typedef char* sz; // null-terminated string
// Common suffixes
typedef struct {
int id;
char name[50];
} User_t; // _t suffix for types
typedef enum {
STATE_IDLE,
STATE_BUSY,
STATE_ERROR
} State_e; // _e suffix for enums
int main() {
Point p = {10, 20};
Rectangle r = {{0, 0}, {100, 100}};
Color c = COLOR_RED;
i32 age = 25;
f32 temperature = 36.6f;
sz name = "John";
return 0;
}
Performance Considerations
typedef and Optimization
#include <stdio.h>
#include <time.h>
// Without typedef
void without_typedef() {
for (int i = 0; i < 1000000; i++) {
int* p = &i;
// Do something
}
}
// With typedef
typedef int* IntPtr;
void with_typedef() {
for (int i = 0; i < 1000000; i++) {
IntPtr p = &i;
// Do something
}
}
int main() {
clock_t start, end;
start = clock();
without_typedef();
end = clock();
printf("Without typedef: %ld ticks\n", end - start);
start = clock();
with_typedef();
end = clock();
printf("With typedef: %ld ticks\n", end - start);
// Note: Performance difference is negligible
// typedef is handled at compile time
return 0;
}
Summary Table
| Feature | Syntax | Use Case |
|---|---|---|
| Basic typedef | typedef int Integer; | Simple type aliases |
| Structure | typedef struct {...} Name; | Named structures |
| Pointer | typedef int* IntPtr; | Pointer aliases |
| Array | typedef int Vector[10]; | Array type aliases |
| Function pointer | typedef int (*Func)(int); | Callback types |
| Enum | typedef enum {...} Name; | Named enumerations |
| Union | typedef union {...} Name; | Named unions |
| Opaque pointer | typedef struct Name Name; | Information hiding |
Best Practices Summary
Do's and Don'ts
// DO: Use typedef for complex types
typedef struct {
int x;
int y;
} Point;
// DO: Use typedef for function pointers
typedef int (*CompareFunc)(const void*, const void*);
// DO: Use typedef for platform independence
typedef uint32_t DWORD;
typedef uint16_t WORD;
typedef uint8_t BYTE;
// DON'T: Hide pointer types unnecessarily
typedef int* PInt; // Can be confusing
// Better to use int* explicitly
// DON'T: Overuse typedef for simple types
typedef int my_int; // Unnecessary
// Just use int directly
// DO: Use consistent naming conventions
typedef struct {
int id;
char name[50];
} UserRecord;
// DO: Use typedef in header files for API stability
// mylibrary.h
typedef struct MyLibraryContext MyLibraryContext;
MyLibraryContext* mylib_create(void);
void mylib_destroy(MyLibraryContext* ctx);
Conclusion
typedef is a powerful feature in C that enhances code readability, maintainability, and portability:
Key Benefits
- Readability: Makes complex types easier to understand
- Maintainability: Centralized type definitions
- Portability: Platform-independent type aliases
- Abstraction: Hides implementation details
- Documentation: Self-documenting type names
When to Use typedef
- Complex structure types
- Function pointer types
- Platform-independent types
- API design (opaque pointers)
- Enumeration types
- Array types
When to Avoid typedef
- Simple, well-understood types
- When it hides important information (like pointers)
- In very small, local scopes
Mastering typedef is essential for professional C programming, especially when designing libraries, working with complex data structures, or writing portable code.