Skip to content

C 标准库 - <stddef.h>

概述

<stddef.h> 头文件定义了几个标准类型和宏,这些定义在多个标准库头文件中都会用到。它提供了与指针、数组大小和偏移量相关的基本类型和宏。

标准类型

size_t

无符号整数类型,用于表示对象的大小。

c
#include <stdio.h>
#include <stddef.h>

int main() {
    size_t size = sizeof(int);
    printf("int 的大小: %zu\n", size);
    return 0;
}

ptrdiff_t

有符号整数类型,用于表示两个指针之间的差值。

c
#include <stdio.h>
#include <stddef.h>

int main() {
    int arr[10];
    int* ptr1 = &arr[0];
    int* ptr2 = &arr[5];
    
    ptrdiff_t diff = ptr2 - ptr1;
    printf("指针差值: %td\n", diff);
    
    return 0;
}

wchar_t

宽字符类型,用于处理多字节字符集。

c
#include <stdio.h>
#include <stddef.h>
#include <wchar.h>

int main() {
    wchar_t wc = L'A';
    printf("宽字符: %lc\n", wc);
    
    return 0;
}

标准宏

NULL

空指针常量。

c
#include <stdio.h>
#include <stddef.h>

int main() {
    int* ptr = NULL;
    
    if (ptr == NULL) {
        printf("指针是 NULL\n");
    }
    
    return 0;
}

offsetof(type, member)

计算结构体成员的偏移量。

c
#include <stdio.h>
#include <stddef.h>

struct Person {
    char name[50];
    int age;
    double height;
};

int main() {
    printf("name 的偏移量: %zu\n", offsetof(struct Person, name));
    printf("age 的偏移量: %zu\n", offsetof(struct Person, age));
    printf("height 的偏移量: %zu\n", offsetof(struct Person, height));
    
    return 0;
}

实际应用示例

1. 内存分配

c
#include <stdio.h>
#include <stddef.h>
#include <stdlib.h>
#include <string.h>

void* safe_malloc(size_t size) {
    void* ptr = malloc(size);
    if (ptr == NULL) {
        fprintf(stderr, "内存分配失败\n");
        exit(EXIT_FAILURE);
    }
    return ptr;
}

int main() {
    size_t size = 100;
    char* buffer = safe_malloc(size);
    
    strcpy(buffer, "Hello, World!");
    printf("%s\n", buffer);
    
    free(buffer);
    return 0;
}

2. 数组遍历

c
#include <stdio.h>
#include <stddef.h>

void print_array(int* arr, size_t size) {
    for (size_t i = 0; i < size; i++) {
        printf("%d ", arr[i]);
    }
    printf("\n");
}

int main() {
    int arr[] = {1, 2, 3, 4, 5};
    size_t size = sizeof(arr) / sizeof(arr[0]);
    
    print_array(arr, size);
    return 0;
}

3. 指针运算

c
#include <stdio.h>
#include <stddef.h>

void pointer_arithmetic() {
    int arr[] = {10, 20, 30, 40, 50};
    int* ptr1 = &arr[1];
    int* ptr2 = &arr[4];
    
    ptrdiff_t diff = ptr2 - ptr1;
    printf("ptr2 - ptr1 = %td\n", diff);
    printf("ptr1 指向的值: %d\n", *ptr1);
    printf("ptr2 指向的值: %d\n", *ptr2);
}

int main() {
    pointer_arithmetic();
    return 0;
}

4. 结构体偏移量

c
#include <stdio.h>
#include <stddef.h>
#include <string.h>

struct Student {
    int id;
    char name[50];
    double score;
};

void print_student_info(struct Student* student) {
    printf("结构体大小: %zu\n", sizeof(struct Student));
    printf("id 偏移量: %zu\n", offsetof(struct Student, id));
    printf("name 偏移量: %zu\n", offsetof(struct Student, name));
    printf("score 偏移量: %zu\n", offsetof(struct Student, score));
    
    printf("\n学生信息:\n");
    printf("ID: %d\n", student->id);
    printf("姓名: %s\n", student->name);
    printf("分数: %.2f\n", student->score);
}

int main() {
    struct Student student = {1, "张三", 95.5};
    print_student_info(&student);
    
    return 0;
}

5. 动态数组

c
#include <stdio.h>
#include <stddef.h>
#include <stdlib.h>

typedef struct {
    int* data;
    size_t size;
    size_t capacity;
} DynamicArray;

DynamicArray* create_array(size_t initial_capacity) {
    DynamicArray* array = malloc(sizeof(DynamicArray));
    if (array == NULL) {
        return NULL;
    }
    
    array->data = malloc(initial_capacity * sizeof(int));
    if (array->data == NULL) {
        free(array);
        return NULL;
    }
    
    array->size = 0;
    array->capacity = initial_capacity;
    return array;
}

void push_back(DynamicArray* array, int value) {
    if (array->size >= array->capacity) {
        array->capacity *= 2;
        array->data = realloc(array->data, array->capacity * sizeof(int));
    }
    
    array->data[array->size++] = value;
}

void print_array(DynamicArray* array) {
    for (size_t i = 0; i < array->size; i++) {
        printf("%d ", array->data[i]);
    }
    printf("\n");
}

void free_array(DynamicArray* array) {
    free(array->data);
    free(array);
}

int main() {
    DynamicArray* array = create_array(5);
    if (array == NULL) {
        return 1;
    }
    
    for (int i = 1; i <= 10; i++) {
        push_back(array, i);
    }
    
    printf("数组内容: ");
    print_array(array);
    printf("大小: %zu\n", array->size);
    printf("容量: %zu\n", array->capacity);
    
    free_array(array);
    return 0;
}

6. 字符串长度

c
#include <stdio.h>
#include <stddef.h>

size_t my_strlen(const char* str) {
    size_t len = 0;
    while (str[len] != '\0') {
        len++;
    }
    return len;
}

int main() {
    const char* str = "Hello, World!";
    size_t len = my_strlen(str);
    
    printf("字符串: %s\n", str);
    printf("长度: %zu\n", len);
    
    return 0;
}

7. 内存复制

c
#include <stdio.h>
#include <stddef.h>
#include <string.h>

void* my_memcpy(void* dest, const void* src, size_t n) {
    unsigned char* d = (unsigned char*)dest;
    const unsigned char* s = (const unsigned char*)src;
    
    for (size_t i = 0; i < n; i++) {
        d[i] = s[i];
    }
    
    return dest;
}

int main() {
    char src[] = "Hello, World!";
    char dest[20];
    
    my_memcpy(dest, src, sizeof(src));
    
    printf("源字符串: %s\n", src);
    printf("目标字符串: %s\n", dest);
    
    return 0;
}

8. 结构体对齐

c
#include <stdio.h>
#include <stddef.h>

struct Aligned {
    char c;
    int i;
    double d;
};

void print_alignment_info() {
    printf("结构体对齐信息:\n");
    printf("sizeof(struct Aligned): %zu\n", sizeof(struct Aligned));
    printf("offsetof(c): %zu\n", offsetof(struct Aligned, c));
    printf("offsetof(i): %zu\n", offsetof(struct Aligned, i));
    printf("offsetof(d): %zu\n", offsetof(struct Aligned, d));
}

int main() {
    print_alignment_info();
    return 0;
}

9. 指针比较

c
#include <stdio.h>
#include <stddef.h>

void compare_pointers() {
    int arr[] = {10, 20, 30, 40, 50};
    int* ptr1 = &arr[0];
    int* ptr2 = &arr[2];
    
    ptrdiff_t diff = ptr2 - ptr1;
    
    printf("ptr1: %p\n", (void*)ptr1);
    printf("ptr2: %p\n", (void*)ptr2);
    printf("ptr2 - ptr1: %td\n", diff);
    
    if (ptr1 < ptr2) {
        printf("ptr1 在 ptr2 之前\n");
    } else if (ptr1 > ptr2) {
        printf("ptr1 在 ptr2 之后\n");
    } else {
        printf("ptr1 和 ptr2 相同\n");
    }
}

int main() {
    compare_pointers();
    return 0;
}

10. 链表节点

c
#include <stdio.h>
#include <stddef.h>
#include <stdlib.h>

typedef struct Node {
    int data;
    struct Node* next;
} Node;

Node* create_node(int data) {
    Node* node = malloc(sizeof(Node));
    if (node == NULL) {
        return NULL;
    }
    
    node->data = data;
    node->next = NULL;
    return node;
}

void print_list(Node* head) {
    Node* current = head;
    while (current != NULL) {
        printf("%d -> ", current->data);
        current = current->next;
    }
    printf("NULL\n");
}

void free_list(Node* head) {
    Node* current = head;
    while (current != NULL) {
        Node* next = current->next;
        free(current);
        current = next;
    }
}

int main() {
    Node* head = create_node(1);
    head->next = create_node(2);
    head->next->next = create_node(3);
    head->next->next->next = create_node(4);
    head->next->next->next->next = create_node(5);
    
    printf("链表: ");
    print_list(head);
    
    free_list(head);
    
    return 0;
}

注意事项

1. size_t 的使用

size_t 是无符号类型,避免与有符号类型混用。

c
#include <stdio.h>
#include <stddef.h>

void good_usage() {
    size_t size = 10;
    for (size_t i = 0; i < size; i++) {
        printf("%zu ", i);
    }
    printf("\n");
}

void bad_usage() {
    size_t size = 10;
    for (int i = 0; i < size; i++) {
        printf("%d ", i);
    }
    printf("\n");
}

int main() {
    good_usage();
    bad_usage();
    return 0;
}

2. NULL 的使用

使用 NULL 而不是 0 来表示空指针。

c
#include <stdio.h>
#include <stddef.h>

void good_usage() {
    int* ptr = NULL;
    if (ptr == NULL) {
        printf("指针是 NULL\n");
    }
}

void bad_usage() {
    int* ptr = 0;
    if (ptr == 0) {
        printf("指针是 0\n");
    }
}

int main() {
    good_usage();
    bad_usage();
    return 0;
}

3. offsetof 的限制

offsetof 只能用于标准布局的结构体。

c
#include <stdio.h>
#include <stddef.h>

struct StandardLayout {
    int a;
    char b;
};

struct NonStandardLayout {
    int a;
    char b;
    virtual void foo();
};

int main() {
    printf("标准布局偏移量: %zu\n", offsetof(struct StandardLayout, b));
    return 0;
}

4. ptrdiff_t 的范围

ptrdiff_t 可能无法表示非常大的指针差值。

c
#include <stdio.h>
#include <stddef.h>
#include <stdlib.h>
#include <limits.h>

void check_ptrdiff_range() {
    size_t huge_size = SIZE_MAX / 2;
    int* huge_array = malloc(huge_size * sizeof(int));
    
    if (huge_array != NULL) {
        int* ptr1 = &huge_array[0];
        int* ptr2 = &huge_array[huge_size - 1];
        
        ptrdiff_t diff = ptr2 - ptr1;
        
        if (diff < 0) {
            printf("指针差值溢出\n");
        } else {
            printf("指针差值: %td\n", diff);
        }
        
        free(huge_array);
    } else {
        printf("无法分配大数组\n");
    }
}

int main() {
    check_ptrdiff_range();
    return 0;
}

总结

<stddef.h> 提供的基本类型和宏是 C 语言编程的基础:

  1. size_t - 用于表示对象大小和数组索引
  2. ptrdiff_t - 用于表示指针之间的差值
  3. NULL - 表示空指针常量
  4. offsetof - 计算结构体成员的偏移量

记住:

  • 使用 size_t 表示大小和索引
  • 使用 NULL 表示空指针
  • ptrdiff_t 可能有范围限制
  • offsetof 只适用于标准布局结构体