Skip to main content

Searching...

Tools
Articles
View All Results

Developer Lab · C

Generate UUID in C

libuuid is the de facto 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

Production Ready
c
#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)

c
#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)

c
#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

c
#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

c
#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

c
#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

c
#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, Performance, and Security

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.

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.

Installation

libuuid (uuid_generate)

bash
# Debian/Ubuntu
apt install uuid-dev

# RHEL/CentOS/Fedora
yum install libuuid-devel
bash
# Compile with -luuid
gcc myapp.c -luuid -o myapp

On macOS, the uuid/uuid.h functions ship with the system C library - no install and no extra linker flags needed.

Frequently Asked Questions

How do I generate a UUID in C?

libuuid is the de facto standard for UUID generation in C, part of util-linux on Linux. Include the uuid/uuid.h header, then uuid_generate_random() fills a uuid_t with a v4 UUID from the OS CSPRNG and uuid_unparse() converts it to a string. Link against -luuid when compiling.

Is uuid_generate() cryptographically secure?

Yes. uuid_generate() uses the platform CSPRNG (operating system secure random source), suitable for session tokens, API keys, and idempotency keys. Do not use non-cryptographic random sources for security-sensitive identifiers.

What is the difference between UUID v4 and v7 in C?

UUID v4 (uuid_generate() or equivalent) is fully random and not sortable. UUID v7 embeds a millisecond timestamp for chronological sorting (RFC 9562). Use v4 for general-purpose IDs; use v7 for database primary keys at scale.

Do I need to install a package for UUID generation in C?

A package or library install may be required. See the Installation section for the recommended approach in C.

How do I validate a UUID string in C?

Use the platform's UUID parse/validation function, or test against the RFC 4122 regex: /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i. Always validate external input at API boundaries.

Should I use UUIDs as database primary keys in C applications?

UUIDs work well as primary keys for distributed systems. Prefer native UUID/BINARY(16) column types over VARCHAR(36). For very large tables, consider UUID v7 for better B-tree insert locality.

Can I generate UUIDs in C without a network connection?

Yes. UUID generation uses local OS entropy sources and does not require network access. Each call is independent and thread-safe on modern platforms.

What output formats are available in C?

The standard hyphenated lowercase string (36 chars) is the default. Most APIs also support 32-char hex (no hyphens) and 16-byte binary formats. Use string format for APIs and binary for database storage.

What RFC standards apply to C UUID generation?

Version 4 UUIDs follow RFC 4122. UUID v7 follows RFC 9562 (May 2024). Ensure your chosen method produces compliant version and variant bits.

When should I avoid UUID v1?

Avoid UUID v1 in security-sensitive contexts - it embeds MAC address and timestamp information. Prefer v4 (uuid_generate() or equivalent) unless you need legacy Cassandra timeuuid compatibility.

Key definitions

UUID
128-bit universally unique identifier, usually shown as 36 hex characters with hyphens.
CSPRNG
Cryptographically secure pseudo-random number generator - the entropy source behind secure UUID generation.
RFC 4122
IETF standard defining UUID versions 1 through 5. Version 4 is random.
RFC 9562
IETF standard adding UUID versions 6, 7, and 8. Version 7 is time-ordered.