Generate UUID in C
libuuid is the POSIX standard for UUID generation in C — part of util-linux on Linux. uuid_generate_random() always produces a v4 UUID backed by the OS CSPRNG.
Quick Reference
| Function | Version | Notes | Use Case |
|---|---|---|---|
| uuid_generate_random() | v4 | Always random | Recommended — always uses CSPRNG |
| uuid_generate() | v4 or v1 | Falls back to v1 | May use v1 if /dev/random unavailable |
| uuid_unparse_lower() | any | Lowercase output | Standard lowercase hyphenated string |
| uuid_unparse_upper() | any | Uppercase output | Uppercase hyphenated string |
Primary Implementation
#include <uuid/uuid.h>
#include <stdio.h>
int main(void) {
uuid_t id;
char id_str[37]; /* 36 chars + null terminator */
/* Always use uuid_generate_random for guaranteed v4 */
uuid_generate_random(id);
/* Lowercase output (recommended) */
uuid_unparse_lower(id, id_str);
printf("%s\n", id_str);
/* → f47ac10b-58cc-4372-a567-0e02b2c3d479 */
/* Uppercase output */
char id_upper[37];
uuid_unparse_upper(id, id_upper);
printf("%s\n", id_upper);
/* → F47AC10B-58CC-4372-A567-0E02B2C3D479 */
/* Parse an existing UUID string */
uuid_t parsed;
if (uuid_parse("f47ac10b-58cc-4372-a567-0e02b2c3d479", parsed) == 0) {
printf("Valid UUID\n");
} else {
printf("Invalid UUID\n");
}
/* Compare two UUIDs */
uuid_t id2;
uuid_generate_random(id2);
if (uuid_compare(id, id2) == 0) {
printf("UUIDs are equal\n");
}
return 0;
}
All UUID Versions
UUID v4 — Random (always use this)
#include <uuid/uuid.h>
uuid_t id;
char str[37];
/* uuid_generate_random always uses /dev/urandom — guaranteed v4 */
uuid_generate_random(id);
uuid_unparse_lower(id, str);
printf("%s\n", str); /* → "550e8400-e29b-41d4-a716-446655440000" */
UUID v1 — Time-based (legacy)
#include <uuid/uuid.h>
uuid_t id;
char str[37];
/* v1: timestamp + MAC address — leaks host info, use only for legacy compat */
uuid_generate_time(id);
uuid_unparse_lower(id, str);
printf("%s\n", str);
Null UUID check
#include <uuid/uuid.h>
uuid_t id;
uuid_generate_random(id);
/* Check if UUID is all zeros (nil UUID) */
if (uuid_is_null(id)) {
fprintf(stderr, "UUID generation failed\n");
}
/* Clear a UUID (set to nil) */
uuid_clear(id);
Real-World Use Cases
1. Linux system daemon — unique instance ID
#include <uuid/uuid.h>
#include <stdio.h>
#include <stdlib.h>
/* Generate a unique instance ID for this daemon run */
char *create_instance_id(void) {
uuid_t id;
char *str = malloc(37);
if (!str) return NULL;
uuid_generate_random(id);
uuid_unparse_lower(id, str);
return str; /* caller must free() */
}
int main(void) {
char *instance_id = create_instance_id();
if (!instance_id) {
fprintf(stderr, "Failed to allocate UUID\n");
return 1;
}
printf("Daemon instance: %s\n", instance_id);
free(instance_id);
return 0;
}
2. Embedded C service — unique message ID
#include <uuid/uuid.h>
#include <string.h>
typedef struct {
char message_id[37];
char payload[256];
int payload_len;
} Message;
void init_message(Message *msg, const char *data, int len) {
uuid_t id;
uuid_generate_random(id);
uuid_unparse_lower(id, msg->message_id);
memcpy(msg->payload, data, len);
msg->payload_len = len;
}
3. Database client — generate PK before insert
#include <uuid/uuid.h>
#include <stdio.h>
/* Generate UUID and format as SQL literal */
void uuid_to_sql(char *out, size_t out_len) {
uuid_t id;
char str[37];
uuid_generate_random(id);
uuid_unparse_lower(id, str);
snprintf(out, out_len, "'%s'", str);
}
int main(void) {
char sql_uuid[41]; /* 37 + 2 quotes + null */
uuid_to_sql(sql_uuid, sizeof(sql_uuid));
printf("INSERT INTO orders (id) VALUES (%s);\n", sql_uuid);
return 0;
}
Common Mistakes
Not allocating enough space for the output buffer
uuid_unparse() writes exactly 36 characters plus a null terminator. Always allocate char str[37] — a buffer of 36 will cause a buffer overflow.
Using uuid_generate() instead of uuid_generate_random()
uuid_generate() may fall back to a time-based v1 UUID if /dev/random is unavailable. Use uuid_generate_random() to guarantee a v4 UUID from /dev/urandom.
Using rand() to build a UUID manually
rand() is not CSPRNG-backed and only provides 15–31 bits of entropy. A manually constructed UUID using rand() is not RFC 4122 compliant and has a high collision probability.
How It Works
uuid_generate_random() reads 16 bytes from /dev/urandom (Linux) or the platform CSPRNG, then sets the version bits (4) and variant bits (RFC 4122) in the uuid_t array.
uuid_t is defined as unsigned char[16] — a 16-byte array on the stack. uuid_unparse_lower() formats it as a 36-character lowercase hyphenated string.
Output Formats
uuid_unparse_lower(id, str)
f47ac10b-58cc-4372-a567-0e02b2c3d479
uuid_unparse_upper(id, str)
F47AC10B-58CC-4372-A567-0E02B2C3D479
uuid_t — raw bytes
unsigned char[16]
Best Practices
Always use uuid_generate_random() — never uuid_generate() for security-sensitive IDs.
Always allocate char str[37] for the output buffer — 36 chars + null terminator.
Use uuid_unparse_lower() for consistent lowercase output.
Performance
libuuid generates roughly 1–5 million UUIDs/second. The bottleneck is the /dev/urandom read syscall.
uuid_t is a 16-byte stack-allocated array — zero heap allocation per UUID. The string formatting (uuid_unparse) writes to a caller-provided buffer.
Installation
# Debian/Ubuntu
apt install uuid-dev
# RHEL/CentOS/Fedora
yum install libuuid-devel
# Compile with -luuid
gcc myapp.c -luuid -o myapp
On macOS, libuuid is part of the system SDK — no install needed. Link with -framework CoreFoundation or use the POSIX-compatible header.
Security
Entropy source: /dev/urandom on Linux/macOS. uuid_generate_random() always uses the CSPRNG — cryptographically secure.
Suitable for session tokens and security-sensitive IDs. Avoid uuid_generate_time() for security use — it embeds the MAC address.