第六章 【C语言篇:结构体&位运算】 结构体、位运算全面解析

目录

[1. 结构体与共用体](#1. 结构体与共用体)

[1.1 结构体的定义](#1.1 结构体的定义)

[1.1.1 结构体的基本概念](#1.1.1 结构体的基本概念)

[1.1.2 结构体变量的声明](#1.1.2 结构体变量的声明)

[1.2 结构体成员的访问](#1.2 结构体成员的访问)

[1.2.1 结构体变量成员的表示方法](#1.2.1 结构体变量成员的表示方法)

[1.2.2 结构体变量的赋值和初始化](#1.2.2 结构体变量的赋值和初始化)

[1.3 结构体数组](#1.3 结构体数组)

[1.3.1 结构体数组的定义](#1.3.1 结构体数组的定义)

[1.4 结构体指针](#1.4 结构体指针)

[1.4.1 指向结构体变量的指针](#1.4.1 指向结构体变量的指针)

[1.4.2 指向结构体数组的指针](#1.4.2 指向结构体数组的指针)

[1.4.3 结构体指针作为函数参数](#1.4.3 结构体指针作为函数参数)

[1.4.4 动态存储分配与链表](#1.4.4 动态存储分配与链表)

[1.5 共用体(联合体)](#1.5 共用体(联合体))

[1.5.1 共用体的基本概念](#1.5.1 共用体的基本概念)

[1.5.2 共用体的应用场景](#1.5.2 共用体的应用场景)

[1.6 枚举类型](#1.6 枚举类型)

[1.6.1 枚举类型的定义和使用](#1.6.1 枚举类型的定义和使用)

[1.6.2 枚举与结构体结合使用](#1.6.2 枚举与结构体结合使用)

[1.7 类型定义符 typedef](#1.7 类型定义符 typedef)

[2. 位运算](#2. 位运算)

[2.1 位运算符](#2.1 位运算符)

[2.1.1 按位与运算(&)](#2.1.1 按位与运算(&))

[2.1.2 按位或运算(|)](#2.1.2 按位或运算(|))

[2.1.3 按位异或运算(^)](#2.1.3 按位异或运算(^))

[2.1.4 按位取反运算(~)](#2.1.4 按位取反运算(~))

[2.1.5 左移运算(<<)](#2.1.5 左移运算(<<))

[2.1.6 右移运算(>>)](#2.1.6 右移运算(>>))

[2.2 位域(位段)](#2.2 位域(位段))

[2.2.1 位域的基本概念](#2.2.1 位域的基本概念)

[2.2.2 位域的注意事项](#2.2.2 位域的注意事项)

[2.3 本章小结](#2.3 本章小结)

结构体与共用体总结

位运算总结

综合应用建议


摘要:

本文系统介绍了C语言中结构体、共用体和位运算的核心知识点。结构体部分详细讲解了定义方式、成员访问、数组与指针操作、动态内存分配及链表实现;共用体部分阐述了其共享内存特性及应用场景;位运算部分全面解析了6种位运算符的功能与典型应用,并介绍了位域的概念与注意事项。此外,还涵盖了枚举类型和typedef的用法。这些内容为C语言中复杂数据结构的构建、内存优化和底层操作提供了关键技术手段,是系统编程和嵌入式开发的重要基础。

1. 结构体与共用体

1.1 结构体的定义

1.1.1 结构体的基本概念

结构体(struct)是一种用户自定义的数据类型,用于将不同类型的数据组合成一个整体。

定义结构体的一般形式

cpp 复制代码
struct 结构体标签 {
    数据类型 成员1;
    数据类型 成员2;
    // ... 更多成员
};

示例

cpp 复制代码
// 定义一个学生结构体
struct Student {
    int id;           // 学号
    char name[50];    // 姓名
    int age;          // 年龄
    float score;      // 成绩
};

1.1.2 结构体变量的声明

方式1:先定义结构体类型,再声明变量

cpp 复制代码
struct Student {
    int id;
    char name[50];
    float score;
};

// 声明变量
struct Student stu1, stu2;

方式2:定义结构体类型的同时声明变量

cpp 复制代码
struct Student {
    int id;
    char name[50];
    float score;
} stu1, stu2;  // 直接声明变量

方式3:使用匿名结构体

cpp 复制代码
// 匿名结构体(没有标签)
struct {
    int id;
    char name[50];
    float score;
} stu1, stu2;  // 只能在这里声明变量

方式4:使用typedef创建别名

cpp 复制代码
typedef struct Student {
    int id;
    char name[50];
    float score;
} Student;  // Student 是 struct Student 的别名

// 现在可以直接使用 Student 声明变量
Student stu1, stu2;

1.2 结构体成员的访问

1.2.1 结构体变量成员的表示方法

使用.运算符访问结构体成员。

示例

cpp 复制代码
#include <stdio.h>
#include <string.h>

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

int main() {
    struct Person p1;
    
    // 访问结构体成员并赋值
    strcpy(p1.name, "张三");
    p1.age = 25;
    p1.height = 175.5;
    
    // 读取结构体成员
    printf("姓名: %s\n", p1.name);
    printf("年龄: %d\n", p1.age);
    printf("身高: %.1f cm\n", p1.height);
    
    // 可以直接赋值
    struct Person p2 = p1;  // 结构体可以直接赋值(浅拷贝)
    printf("\n复制后的姓名: %s\n", p2.name);
    
    return 0;
}

1.2.2 结构体变量的赋值和初始化

初始化方式

cpp 复制代码
#include <stdio.h>

struct Date {
    int year;
    int month;
    int day;
};

struct Book {
    char title[100];
    char author[50];
    float price;
    struct Date publish_date;  // 结构体嵌套
};

int main() {
    // 方式1:按顺序初始化所有成员
    struct Date d1 = {2023, 10, 15};
    
    // 方式2:按顺序初始化部分成员(剩余成员自动初始化为0)
    struct Date d2 = {2023};  // month=0, day=0
    
    // 方式3:指定成员初始化(C99标准)
    struct Date d3 = {
        .year = 2023,
        .month = 10,
        .day = 15
    };
    
    // 方式4:结构体嵌套初始化
    struct Book b1 = {
        "C语言程序设计",
        "李四",
        45.5,
        {2022, 8, 20}  // 嵌套结构体初始化
    };
    
    // 方式5:指定成员的嵌套初始化
    struct Book b2 = {
        .title = "数据结构",
        .author = "王五",
        .price = 38.0,
        .publish_date = {
            .year = 2021,
            .month = 5,
            .day = 10
        }
    };
    
    // 输出验证
    printf("书籍信息:\n");
    printf("书名: %s\n", b1.title);
    printf("作者: %s\n", b1.author);
    printf("价格: %.2f\n", b1.price);
    printf("出版日期: %d年%d月%d日\n", 
           b1.publish_date.year,
           b1.publish_date.month,
           b1.publish_date.day);
    
    return 0;
}

1.3 结构体数组

1.3.1 结构体数组的定义

结构体数组是每个元素都是结构体类型的数组。

定义方式

cpp 复制代码
struct Student {
    int id;
    char name[50];
    float score;
};

// 定义结构体数组
struct Student class[3];  // 包含3个学生的数组

示例

cpp 复制代码
#include <stdio.h>
#include <string.h>

struct Employee {
    int id;
    char name[50];
    char department[30];
    float salary;
};

int main() {
    // 定义并初始化结构体数组
    struct Employee employees[5] = {
        {1001, "张三", "技术部", 8000.0},
        {1002, "李四", "销售部", 7000.0},
        {1003, "王五", "市场部", 7500.0},
        {1004, "赵六", "技术部", 8500.0},
        {1005, "孙七", "人事部", 6500.0}
    };
    
    int count = 5;
    
    // 遍历结构体数组
    printf("员工信息表:\n");
    printf("ID\t姓名\t部门\t\t薪资\n");
    printf("--------------------------------------\n");
    
    for (int i = 0; i < count; i++) {
        printf("%d\t%s\t%s\t%.2f\n",
               employees[i].id,
               employees[i].name,
               employees[i].department,
               employees[i].salary);
    }
    
    // 计算平均工资
    float total_salary = 0;
    for (int i = 0; i < count; i++) {
        total_salary += employees[i].salary;
    }
    printf("\n平均工资: %.2f\n", total_salary / count);
    
    // 查找最高工资的员工
    int max_index = 0;
    for (int i = 1; i < count; i++) {
        if (employees[i].salary > employees[max_index].salary) {
            max_index = i;
        }
    }
    printf("\n最高工资员工:\n");
    printf("ID: %d, 姓名: %s, 部门: %s, 薪资: %.2f\n",
           employees[max_index].id,
           employees[max_index].name,
           employees[max_index].department,
           employees[max_index].salary);
    
    return 0;
}

1.4 结构体指针

1.4.1 指向结构体变量的指针

使用指针访问结构体成员有两种方式:

  1. 使用->运算符

  2. 使用*解引用然后使用.运算符

示例

cpp 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

typedef struct {
    char title[100];
    char author[50];
    int pages;
    float price;
} Book;

int main() {
    // 创建结构体变量
    Book book1;
    strcpy(book1.title, "C Programming");
    strcpy(book1.author, "K&R");
    book1.pages = 274;
    book1.price = 35.5;
    
    // 创建指向结构体的指针
    Book *ptr = &book1;
    
    printf("访问结构体成员的不同方式:\n\n");
    
    // 方式1:直接访问
    printf("直接访问:\n");
    printf("书名: %s\n", book1.title);
    printf("作者: %s\n", book1.author);
    printf("页数: %d\n", book1.pages);
    printf("价格: %.2f\n\n", book1.price);
    
    // 方式2:使用指针和 -> 运算符
    printf("使用指针和 -> 运算符:\n");
    printf("书名: %s\n", ptr->title);
    printf("作者: %s\n", ptr->author);
    printf("页数: %d\n", ptr->pages);
    printf("价格: %.2f\n\n", ptr->price);
    
    // 方式3:使用指针和 * 运算符
    printf("使用指针和 * 运算符:\n");
    printf("书名: %s\n", (*ptr).title);
    printf("作者: %s\n", (*ptr).author);
    printf("页数: %d\n", (*ptr).pages);
    printf("价格: %.2f\n\n", (*ptr).price);
    
    // 通过指针修改结构体成员
    ptr->price = 38.0;  // 修改价格
    strcpy(ptr->title, "C Programming Language");
    
    printf("修改后的信息:\n");
    printf("书名: %s\n", book1.title);
    printf("价格: %.2f\n", book1.price);
    
    return 0;
}

1.4.2 指向结构体数组的指针

示例

cpp 复制代码
#include <stdio.h>

typedef struct {
    int id;
    char name[20];
    int score;
} Student;

int main() {
    // 定义并初始化结构体数组
    Student students[] = {
        {1001, "Alice", 85},
        {1002, "Bob", 92},
        {1003, "Charlie", 78},
        {1004, "David", 95},
        {1005, "Eve", 88}
    };
    
    int count = sizeof(students) / sizeof(students[0]);
    
    // 创建指向结构体数组的指针
    Student *ptr = students;  // 指向第一个元素
    
    printf("使用指针遍历结构体数组:\n");
    printf("ID\t姓名\t成绩\n");
    printf("--------------------\n");
    
    // 方法1:使用指针遍历
    for (int i = 0; i < count; i++) {
        printf("%d\t%s\t%d\n", 
               ptr->id,     // 等价于 (*ptr).id
               ptr->name,   // 等价于 (*ptr).name
               ptr->score); // 等价于 (*ptr).score
        ptr++;  // 移动到下一个结构体
    }
    
    printf("\n计算平均成绩:\n");
    
    // 重置指针
    ptr = students;
    
    // 方法2:使用指针运算
    float total = 0;
    Student *end_ptr = students + count;  // 指向数组末尾
    
    while (ptr < end_ptr) {
        total += ptr->score;
        ptr++;
    }
    
    printf("平均成绩: %.2f\n", total / count);
    
    return 0;
}

1.4.3 结构体指针作为函数参数

传递结构体指针比传递整个结构体更高效。

示例

cpp 复制代码
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

typedef struct {
    char name[50];
    int age;
    float salary;
} Employee;

// 函数1:通过值传递结构体(会复制整个结构体)
void print_employee_by_value(Employee emp) {
    printf("值传递方式:\n");
    printf("姓名: %s\n", emp.name);
    printf("年龄: %d\n", emp.age);
    printf("薪资: %.2f\n\n", emp.salary);
    
    // 修改不会影响原结构体
    emp.salary = 9999.99;  // 不影响原数据
}

// 函数2:通过指针传递结构体(只传递地址)
void print_employee_by_pointer(const Employee *emp) {
    printf("指针传递方式:\n");
    printf("姓名: %s\n", emp->name);
    printf("年龄: %d\n", emp->age);
    printf("薪资: %.2f\n\n", emp->salary);
}

// 函数3:通过指针修改结构体
void increase_salary(Employee *emp, float percentage) {
    emp->salary += emp->salary * percentage / 100;
    printf("给 %s 涨薪 %.1f%% 后,新薪资: %.2f\n", 
           emp->name, percentage, emp->salary);
}

// 函数4:动态创建结构体并返回指针
Employee* create_employee(const char *name, int age, float salary) {
    Employee *new_emp = (Employee*)malloc(sizeof(Employee));
    if (new_emp == NULL) {
        printf("内存分配失败\n");
        return NULL;
    }
    
    strcpy(new_emp->name, name);
    new_emp->age = age;
    new_emp->salary = salary;
    
    return new_emp;
}

// 函数5:比较两个员工薪资
int compare_salary(const Employee *emp1, const Employee *emp2) {
    if (emp1->salary > emp2->salary) return 1;
    if (emp1->salary < emp2->salary) return -1;
    return 0;
}

int main() {
    // 创建员工结构体
    Employee emp1 = {"张三", 30, 8000.0};
    
    printf("=== 结构体指针作为函数参数示例 ===\n\n");
    
    // 测试值传递
    print_employee_by_value(emp1);
    printf("原薪资未改变: %.2f\n\n", emp1.salary);
    
    // 测试指针传递
    print_employee_by_pointer(&emp1);
    
    // 测试修改函数
    increase_salary(&emp1, 10.0);
    printf("修改后原薪资: %.2f\n\n", emp1.salary);
    
    // 测试动态创建
    Employee *emp2 = create_employee("李四", 28, 7500.0);
    if (emp2 != NULL) {
        printf("动态创建的员工:\n");
        print_employee_by_pointer(emp2);
        
        // 测试比较函数
        int result = compare_salary(&emp1, emp2);
        if (result > 0) {
            printf("%s 的薪资比 %s 高\n", emp1.name, emp2->name);
        } else if (result < 0) {
            printf("%s 的薪资比 %s 低\n", emp1.name, emp2->name);
        } else {
            printf("%s 和 %s 薪资相同\n", emp1.name, emp2->name);
        }
        
        // 释放动态分配的内存
        free(emp2);
    }
    
    // 结构体指针数组示例
    printf("\n=== 结构体指针数组示例 ===\n");
    
    Employee employees[] = {
        {"王五", 35, 9000.0},
        {"赵六", 32, 8500.0},
        {"孙七", 29, 7800.0}
    };
    
    int count = sizeof(employees) / sizeof(employees[0]);
    
    // 创建结构体指针数组
    Employee *emp_ptrs[3];
    for (int i = 0; i < count; i++) {
        emp_ptrs[i] = &employees[i];
    }
    
    // 使用指针数组排序(按薪资降序)
    for (int i = 0; i < count - 1; i++) {
        for (int j = 0; j < count - i - 1; j++) {
            if (emp_ptrs[j]->salary < emp_ptrs[j+1]->salary) {
                // 交换指针
                Employee *temp = emp_ptrs[j];
                emp_ptrs[j] = emp_ptrs[j+1];
                emp_ptrs[j+1] = temp;
            }
        }
    }
    
    printf("按薪资降序排列:\n");
    printf("姓名\t年龄\t薪资\n");
    printf("----------------------\n");
    for (int i = 0; i < count; i++) {
        printf("%s\t%d\t%.2f\n", 
               emp_ptrs[i]->name,
               emp_ptrs[i]->age,
               emp_ptrs[i]->salary);
    }
    
    return 0;
}

1.4.4 动态存储分配与链表

示例:单向链表的实现

cpp 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

// 定义链表节点结构体
typedef struct Node {
    int id;
    char data[50];
    struct Node *next;  // 指向下一个节点的指针
} ListNode;

// 函数声明
ListNode* create_node(int id, const char *data);
void insert_at_begin(ListNode **head, int id, const char *data);
void insert_at_end(ListNode **head, int id, const char *data);
void delete_node(ListNode **head, int id);
void print_list(ListNode *head);
ListNode* search_node(ListNode *head, int id);
void free_list(ListNode **head);

int main() {
    ListNode *head = NULL;  // 链表头指针
    
    printf("=== 单向链表操作示例 ===\n\n");
    
    // 插入节点
    insert_at_end(&head, 1, "第一项");
    insert_at_end(&head, 2, "第二项");
    insert_at_begin(&head, 3, "第三项(在开头)");
    insert_at_end(&head, 4, "第四项");
    
    // 打印链表
    printf("当前链表内容:\n");
    print_list(head);
    
    // 搜索节点
    printf("\n搜索节点:\n");
    ListNode *found = search_node(head, 2);
    if (found) {
        printf("找到节点: ID=%d, Data=%s\n", found->id, found->data);
    }
    
    // 删除节点
    printf("\n删除ID=2的节点后:\n");
    delete_node(&head, 2);
    print_list(head);
    
    // 释放链表内存
    free_list(&head);
    
    return 0;
}

// 创建新节点
ListNode* create_node(int id, const char *data) {
    ListNode *new_node = (ListNode*)malloc(sizeof(ListNode));
    if (new_node == NULL) {
        printf("内存分配失败\n");
        return NULL;
    }
    
    new_node->id = id;
    strcpy(new_node->data, data);
    new_node->next = NULL;
    
    return new_node;
}

// 在链表开头插入节点
void insert_at_begin(ListNode **head, int id, const char *data) {
    ListNode *new_node = create_node(id, data);
    if (new_node == NULL) return;
    
    new_node->next = *head;
    *head = new_node;
}

// 在链表末尾插入节点
void insert_at_end(ListNode **head, int id, const char *data) {
    ListNode *new_node = create_node(id, data);
    if (new_node == NULL) return;
    
    // 如果链表为空
    if (*head == NULL) {
        *head = new_node;
        return;
    }
    
    // 找到最后一个节点
    ListNode *current = *head;
    while (current->next != NULL) {
        current = current->next;
    }
    
    current->next = new_node;
}

// 删除指定ID的节点
void delete_node(ListNode **head, int id) {
    if (*head == NULL) {
        printf("链表为空\n");
        return;
    }
    
    // 如果要删除的是头节点
    if ((*head)->id == id) {
        ListNode *temp = *head;
        *head = (*head)->next;
        free(temp);
        return;
    }
    
    // 查找要删除的节点
    ListNode *current = *head;
    ListNode *prev = NULL;
    
    while (current != NULL && current->id != id) {
        prev = current;
        current = current->next;
    }
    
    if (current == NULL) {
        printf("未找到ID=%d的节点\n", id);
        return;
    }
    
    // 删除节点
    prev->next = current->next;
    free(current);
}

// 打印链表
void print_list(ListNode *head) {
    if (head == NULL) {
        printf("链表为空\n");
        return;
    }
    
    ListNode *current = head;
    while (current != NULL) {
        printf("ID: %d, Data: %s\n", current->id, current->data);
        current = current->next;
    }
}

// 搜索节点
ListNode* search_node(ListNode *head, int id) {
    ListNode *current = head;
    
    while (current != NULL) {
        if (current->id == id) {
            return current;
        }
        current = current->next;
    }
    
    return NULL;  // 未找到
}

// 释放链表内存
void free_list(ListNode **head) {
    ListNode *current = *head;
    ListNode *next;
    
    while (current != NULL) {
        next = current->next;
        free(current);
        current = next;
    }
    
    *head = NULL;  // 避免野指针
    printf("\n链表内存已释放\n");
}

1.5 共用体(联合体)

1.5.1 共用体的基本概念

共用体(union)允许在相同的内存位置存储不同的数据类型,但任何时候只能存储其中一种类型。

定义共用体

cpp 复制代码
union Data {
    int i;
    float f;
    char str[20];
};

共用体与结构体的区别

  • 结构体:每个成员有自己的内存空间

  • 共用体:所有成员共享同一块内存空间

示例

cpp 复制代码
#include <stdio.h>
#include <string.h>

union Data {
    int i;
    float f;
    char str[20];
};

int main() {
    union Data data;
    
    printf("共用体大小: %zu 字节\n", sizeof(data));
    printf("int 大小: %zu 字节\n", sizeof(int));
    printf("float 大小: %zu 字节\n", sizeof(float));
    printf("char[20] 大小: %zu 字节\n\n", sizeof(char[20]));
    
    // 使用 int 成员
    data.i = 10;
    printf("data.i = %d\n", data.i);
    
    // 使用 float 成员(会覆盖 int)
    data.f = 220.5;
    printf("data.f = %.2f\n", data.f);
    printf("data.i 现在 = %d(无意义)\n\n", data.i);  // 被覆盖了
    
    // 使用 char 数组成员
    strcpy(data.str, "Hello");
    printf("data.str = %s\n", data.str);
    printf("data.f 现在 = %.2f(无意义)\n", data.f);  // 被覆盖了
    
    // 同时只能正确访问最后赋值的成员
    printf("\n正确使用方式:\n");
    union Data d1, d2, d3;
    
    d1.i = 100;
    printf("d1.i = %d\n", d1.i);
    
    d2.f = 3.14;
    printf("d2.f = %.2f\n", d2.f);
    
    strcpy(d3.str, "Test");
    printf("d3.str = %s\n", d3.str);
    
    return 0;
}

1.5.2 共用体的应用场景

示例1:节省内存空间

cpp 复制代码
#include <stdio.h>

// 产品类型枚举
typedef enum {
    BOOK,
    ELECTRONIC,
    CLOTHING
} ProductType;

// 产品信息共用体
union ProductInfo {
    struct {
        int pages;
        char author[50];
    } book;
    
    struct {
        float weight;
        char model[50];
    } electronic;
    
    struct {
        char size[10];
        char material[30];
    } clothing;
};

// 完整产品结构体
typedef struct {
    int id;
    char name[100];
    float price;
    ProductType type;
    union ProductInfo info;  // 共用体,根据类型存储不同信息
} Product;

void print_product(const Product *p) {
    printf("产品ID: %d\n", p->id);
    printf("产品名称: %s\n", p->name);
    printf("价格: %.2f\n", p->price);
    printf("类型: ");
    
    switch (p->type) {
        case BOOK:
            printf("书籍\n");
            printf("页数: %d\n", p->info.book.pages);
            printf("作者: %s\n", p->info.book.author);
            break;
            
        case ELECTRONIC:
            printf("电子产品\n");
            printf("重量: %.2f kg\n", p->info.electronic.weight);
            printf("型号: %s\n", p->info.electronic.model);
            break;
            
        case CLOTHING:
            printf("服装\n");
            printf("尺码: %s\n", p->info.clothing.size);
            printf("材质: %s\n", p->info.clothing.material);
            break;
    }
    printf("---\n");
}

int main() {
    Product p1 = {
        .id = 1,
        .name = "C语言程序设计",
        .price = 45.5,
        .type = BOOK,
        .info.book = {
            .pages = 320,
            .author = "谭浩强"
        }
    };
    
    Product p2 = {
        .id = 2,
        .name = "智能手机",
        .price = 2999.0,
        .type = ELECTRONIC,
        .info.electronic = {
            .weight = 0.2,
            .model = "XYZ-2023"
        }
    };
    
    Product p3 = {
        .id = 3,
        .name = "T恤衫",
        .price = 89.9,
        .type = CLOTHING,
        .info.clothing = {
            .size = "L",
            .material = "纯棉"
        }
    };
    
    printf("产品信息:\n\n");
    print_product(&p1);
    print_product(&p2);
    print_product(&p3);
    
    // 显示内存节省效果
    printf("\n内存占用分析:\n");
    printf("Product 结构体大小: %zu 字节\n", sizeof(Product));
    printf("共用体 ProductInfo 大小: %zu 字节\n", sizeof(union ProductInfo));
    
    // 如果不使用共用体,结构体会更大
    struct WithoutUnion {
        int id;
        char name[100];
        float price;
        ProductType type;
        
        // 所有信息都存储
        int book_pages;
        char book_author[50];
        float electronic_weight;
        char electronic_model[50];
        char clothing_size[10];
        char clothing_material[30];
    };
    
    printf("不使用共用体的大小: %zu 字节\n", sizeof(struct WithoutUnion));
    printf("节省了 %zu 字节\n", 
           sizeof(struct WithoutUnion) - sizeof(Product));
    
    return 0;
}

示例2:类型转换和位操作

cpp 复制代码
#include <stdio.h>

// 将浮点数按位解释为整数
union FloatInt {
    float f;
    int i;
};

// 将32位整数分解为4个字节
union IntBytes {
    int value;
    unsigned char bytes[4];
};

// 检查系统字节序(大端/小端)
void check_endianness() {
    union {
        int i;
        char c[4];
    } test = {0x12345678};
    
    printf("测试字节序:\n");
    printf("整数值: 0x%x\n", test.i);
    printf("字节内容: ");
    
    for (int i = 0; i < 4; i++) {
        printf("[%d]: 0x%02x ", i, (unsigned char)test.c[i]);
    }
    printf("\n");
    
    if (test.c[0] == 0x78) {
        printf("这是小端字节序(低位在前)\n");
    } else {
        printf("这是大端字节序(高位在前)\n");
    }
}

int main() {
    // 示例1:浮点数到整数的按位转换
    union FloatInt fi;
    fi.f = 3.14159;
    
    printf("浮点数: %f\n", fi.f);
    printf("按整数解释: 0x%x\n", fi.i);
    printf("二进制: ");
    
    // 打印二进制表示
    for (int i = 31; i >= 0; i--) {
        printf("%d", (fi.i >> i) & 1);
        if (i % 8 == 0) printf(" ");
    }
    printf("\n\n");
    
    // 示例2:分解整数为字节
    union IntBytes ib;
    ib.value = 0x12345678;
    
    printf("整数: 0x%x\n", ib.value);
    printf("分解为字节: ");
    
    for (int i = 0; i < 4; i++) {
        printf("[%d]: 0x%02x ", i, ib.bytes[i]);
    }
    printf("\n\n");
    
    // 示例3:字节序检查
    check_endianness();
    
    return 0;
}

1.6 枚举类型

1.6.1 枚举类型的定义和使用

定义枚举

cpp 复制代码
enum 枚举标签 {
    标识符1,
    标识符2,
    // ...
};

示例

cpp 复制代码
#include <stdio.h>

// 定义枚举类型
enum Weekday {
    MONDAY,     // 0
    TUESDAY,    // 1
    WEDNESDAY,  // 2
    THURSDAY,   // 3
    FRIDAY,     // 4
    SATURDAY,   // 5
    SUNDAY      // 6
};

// 可以指定枚举值
enum Color {
    RED = 1,    // 1
    GREEN,      // 2
    BLUE = 5,   // 5
    YELLOW,     // 6
    WHITE = 10  // 10
};

// 使用typedef创建别名
typedef enum {
    JANUARY = 1,
    FEBRUARY,
    MARCH,
    APRIL,
    MAY,
    JUNE,
    JULY,
    AUGUST,
    SEPTEMBER,
    OCTOBER,
    NOVEMBER,
    DECEMBER
} Month;

int main() {
    // 声明枚举变量
    enum Weekday today = WEDNESDAY;
    enum Color my_color = BLUE;
    Month current_month = OCTOBER;
    
    printf("枚举类型示例:\n\n");
    
    // 使用枚举
    printf("今天是星期几?\n");
    switch (today) {
        case MONDAY:
            printf("星期一\n");
            break;
        case TUESDAY:
            printf("星期二\n");
            break;
        case WEDNESDAY:
            printf("星期三\n");
            break;
        case THURSDAY:
            printf("星期四\n");
            break;
        case FRIDAY:
            printf("星期五\n");
            break;
        case SATURDAY:
            printf("星期六\n");
            break;
        case SUNDAY:
            printf("星期日\n");
            break;
    }
    
    printf("\n颜色值: %d\n", my_color);
    printf("当前月份: %d月\n", current_month);
    
    // 枚举值实际上是整数
    printf("\n枚举值的整数值:\n");
    printf("MONDAY = %d\n", MONDAY);
    printf("TUESDAY = %d\n", TUESDAY);
    printf("WEDNESDAY = %d\n", WEDNESDAY);
    
    printf("\n颜色枚举值:\n");
    printf("RED = %d\n", RED);
    printf("GREEN = %d\n", GREEN);
    printf("BLUE = %d\n", BLUE);
    printf("YELLOW = %d\n", YELLOW);
    printf("WHITE = %d\n", WHITE);
    
    // 枚举可以进行整数运算
    enum Weekday tomorrow = (enum Weekday)(today + 1);
    printf("\n明天是星期: %d\n", tomorrow);
    
    // 遍历枚举
    printf("\n所有月份:\n");
    for (Month m = JANUARY; m <= DECEMBER; m++) {
        printf("月份 %d\n", m);
    }
    
    return 0;
}

1.6.2 枚举与结构体结合使用

示例

cpp 复制代码
#include <stdio.h>
#include <string.h>

// 枚举类型
typedef enum {
    MALE,
    FEMALE,
    OTHER
} Gender;

typedef enum {
    FRESHMAN,
    SOPHOMORE,
    JUNIOR,
    SENIOR,
    GRADUATE
} GradeLevel;

// 结构体类型
typedef struct {
    int id;
    char name[50];
    int age;
    Gender gender;
    GradeLevel grade;
    float gpa;
} Student;

// 打印性别字符串
const char* gender_to_string(Gender g) {
    switch (g) {
        case MALE: return "男";
        case FEMALE: return "女";
        case OTHER: return "其他";
        default: return "未知";
    }
}

// 打印年级字符串
const char* grade_to_string(GradeLevel g) {
    switch (g) {
        case FRESHMAN: return "大一";
        case SOPHOMORE: return "大二";
        case JUNIOR: return "大三";
        case SENIOR: return "大四";
        case GRADUATE: return "研究生";
        default: return "未知";
    }
}

// 打印学生信息
void print_student(const Student *s) {
    printf("学号: %d\n", s->id);
    printf("姓名: %s\n", s->name);
    printf("年龄: %d\n", s->age);
    printf("性别: %s\n", gender_to_string(s->gender));
    printf("年级: %s\n", grade_to_string(s->grade));
    printf("GPA: %.2f\n", s->gpa);
    printf("---\n");
}

int main() {
    // 创建学生数组
    Student students[] = {
        {1001, "张三", 20, MALE, SOPHOMORE, 3.5},
        {1002, "李四", 21, FEMALE, JUNIOR, 3.8},
        {1003, "王五", 22, MALE, SENIOR, 3.2},
        {1004, "赵六", 19, FEMALE, FRESHMAN, 3.9},
        {1005, "孙七", 24, MALE, GRADUATE, 3.7}
    };
    
    int count = sizeof(students) / sizeof(students[0]);
    
    printf("学生信息列表:\n\n");
    
    // 打印所有学生
    for (int i = 0; i < count; i++) {
        print_student(&students[i]);
    }
    
    // 统计各年级人数
    printf("各年级人数统计:\n");
    
    int grade_count[5] = {0};  // 对应5个年级
    
    for (int i = 0; i < count; i++) {
        grade_count[students[i].grade]++;
    }
    
    for (GradeLevel g = FRESHMAN; g <= GRADUATE; g++) {
        printf("%s: %d人\n", grade_to_string(g), grade_count[g]);
    }
    
    // 查找GPA最高的学生
    int max_index = 0;
    for (int i = 1; i < count; i++) {
        if (students[i].gpa > students[max_index].gpa) {
            max_index = i;
        }
    }
    
    printf("\nGPA最高的学生:\n");
    print_student(&students[max_index]);
    
    return 0;
}

1.7 类型定义符 typedef

typedef 用于为已有的数据类型创建别名,提高代码的可读性和可移植性。

基本用法

cpp 复制代码
typedef 已有类型 新类型名;

示例

cpp 复制代码
#include <stdio.h>
#include <string.h>

// 1. 为基本类型创建别名
typedef int INTEGER;
typedef float REAL;
typedef char CHAR;

// 2. 为数组类型创建别名
typedef int INT_ARRAY_10[10];
typedef char STRING[100];

// 3. 为指针类型创建别名
typedef int* INT_PTR;
typedef char* STR_PTR;

// 4. 为结构体创建别名
typedef struct {
    int x;
    int y;
} Point;

// 5. 为函数指针创建别名
typedef int (*COMPARE_FUNC)(int, int);

// 使用别名
int add(int a, int b) {
    return a + b;
}

int subtract(int a, int b) {
    return a - b;
}

int main() {
    printf("typedef 示例:\n\n");
    
    // 使用基本类型别名
    INTEGER num1 = 10;
    REAL num2 = 3.14;
    CHAR ch = 'A';
    
    printf("INTEGER: %d\n", num1);
    printf("REAL: %.2f\n", num2);
    printf("CHAR: %c\n\n", ch);
    
    // 使用数组别名
    INT_ARRAY_10 arr;
    for (int i = 0; i < 10; i++) {
        arr[i] = i * 2;
    }
    
    STRING name;
    strcpy(name, "张三");
    printf("姓名: %s\n\n", name);
    
    // 使用指针别名
    INT_PTR p1 = &num1;
    STR_PTR p2 = name;
    
    printf("p1 指向的值: %d\n", *p1);
    printf("p2 指向的字符串: %s\n\n", p2);
    
    // 使用结构体别名
    Point p = {10, 20};
    printf("点坐标: (%d, %d)\n\n", p.x, p.y);
    
    // 使用函数指针别名
    COMPARE_FUNC func_ptr;
    
    func_ptr = add;
    printf("10 + 5 = %d\n", func_ptr(10, 5));
    
    func_ptr = subtract;
    printf("10 - 5 = %d\n", func_ptr(10, 5));
    
    // 复杂的typedef示例
    typedef struct Node* NodePtr;
    typedef NodePtr List;
    
    struct Node {
        int data;
        NodePtr next;
    };
    
    // 创建链表
    struct Node node1 = {10, NULL};
    struct Node node2 = {20, NULL};
    struct Node node3 = {30, NULL};
    
    node1.next = &node2;
    node2.next = &node3;
    
    List head = &node1;
    
    printf("\n链表遍历:\n");
    NodePtr current = head;
    while (current != NULL) {
        printf("%d ", current->data);
        current = current->next;
    }
    printf("\n");
    
    return 0;
}

typedef 与 #define 的区别

cpp 复制代码
#include <stdio.h>

// 使用 #define
#define INT_PTR_DEF int*
#define INT_PAIR_DEF struct {int a; int b;}

// 使用 typedef
typedef int* INT_PTR_TYP;
typedef struct {int a; int b;} INT_PAIR_TYP;

int main() {
    printf("#define 与 typedef 的区别:\n\n");
    
    // 示例1:指针声明的区别
    INT_PTR_DEF p1, p2;  // 展开为: int* p1, p2;
    // p1 是指针,p2 是普通int
    
    INT_PTR_TYP p3, p4;  // 两个都是指针
    // p3 和 p4 都是 int*
    
    int a = 10, b = 20;
    p1 = &a;
    p2 = b;      // p2 是 int,不是指针
    
    p3 = &a;
    p4 = &b;     // p4 是指针
    
    printf("p1=%p, *p1=%d\n", (void*)p1, *p1);
    printf("p2=%d (不是指针)\n", p2);
    printf("p3=%p, *p3=%d\n", (void*)p3, *p3);
    printf("p4=%p, *p4=%d\n\n", (void*)p4, *p4);
    
    // 示例2:结构体的区别
    INT_PAIR_DEF pair1 = {1, 2};  // 每次使用都要写完整的 struct
    INT_PAIR_DEF pair2 = {3, 4};  // 这是不同的类型!
    
    INT_PAIR_TYP pair3 = {5, 6};
    INT_PAIR_TYP pair4 = {7, 8};  // 这是相同的类型
    
    // 可以赋值
    pair4 = pair3;  // 正确
    
    // 但这对 pair1 和 pair2 不适用,因为它们是不同的类型
    // pair2 = pair1;  // 错误:类型不匹配
    
    printf("pair1: a=%d, b=%d\n", pair1.a, pair1.b);
    printf("pair3: a=%d, b=%d\n", pair3.a, pair3.b);
    printf("pair4: a=%d, b=%d\n", pair4.a, pair4.b);
    
    return 0;
}

2. 位运算

2.1 位运算符

C语言提供了6种位运算符,用于对整数的二进制位进行操作。

2.1.1 按位与运算(&)

规则:两个位都为1时,结果为1,否则为0。

真值表

cpp 复制代码
0 & 0 = 0
0 & 1 = 0
1 & 0 = 0
1 & 1 = 1

示例

cpp 复制代码
#include <stdio.h>

void print_binary(int num) {
    printf("二进制: ");
    for (int i = 31; i >= 0; i--) {
        printf("%d", (num >> i) & 1);
        if (i % 8 == 0) printf(" ");
    }
    printf("\n");
}

int main() {
    int a = 12;    // 二进制: 0000 1100
    int b = 25;    // 二进制: 0001 1001
    int result;
    
    printf("=== 按位与运算(&) ===\n\n");
    
    printf("a = %d\n", a);
    print_binary(a);
    
    printf("b = %d\n", b);
    print_binary(b);
    
    result = a & b;
    printf("\na & b = %d\n", result);
    print_binary(result);
    
    // 应用示例1:检查奇偶性
    printf("\n应用1:检查奇偶性\n");
    for (int i = 1; i <= 5; i++) {
        if (i & 1) {
            printf("%d 是奇数\n", i);
        } else {
            printf("%d 是偶数\n", i);
        }
    }
    
    // 应用示例2:清零特定位
    printf("\n应用2:清零特定位\n");
    int flags = 0b11111111;  // 8位全为1
    int mask = 0b11110111;   // 要清零第4位(从0开始)
    int cleared = flags & mask;
    
    printf("原始标志位: ");
    for (int i = 7; i >= 0; i--) printf("%d", (flags >> i) & 1);
    printf("\n");
    
    printf("掩码:       ");
    for (int i = 7; i >= 0; i--) printf("%d", (mask >> i) & 1);
    printf("\n");
    
    printf("结果:       ");
    for (int i = 7; i >= 0; i--) printf("%d", (cleared >> i) & 1);
    printf("\n");
    
    // 应用示例3:提取特定位
    printf("\n应用3:提取特定位\n");
    int data = 0b11011010;
    int bit_mask = 0b00000100;  // 提取第2位
    
    int extracted = (data & bit_mask) >> 2;
    printf("数据: %d (二进制: ", data);
    for (int i = 7; i >= 0; i--) printf("%d", (data >> i) & 1);
    printf(")\n");
    
    printf("第2位的值: %d\n", extracted);
    
    return 0;
}

2.1.2 按位或运算(|)

规则:两个位中只要有一个为1,结果为1,否则为0。

真值表

cpp 复制代码
0 | 0 = 0
0 | 1 = 1
1 | 0 = 1
1 | 1 = 1

示例

cpp 复制代码
#include <stdio.h>

int main() {
    int a = 12;    // 二进制: 0000 1100
    int b = 25;    // 二进制: 0001 1001
    
    printf("=== 按位或运算(|) ===\n\n");
    
    printf("a = %d (二进制: ", a);
    for (int i = 7; i >= 0; i--) printf("%d", (a >> i) & 1);
    printf(")\n");
    
    printf("b = %d (二进制: ", b);
    for (int i = 7; i >= 0; i--) printf("%d", (b >> i) & 1);
    printf(")\n");
    
    int result = a | b;
    printf("\na | b = %d (二进制: ", result);
    for (int i = 7; i >= 0; i--) printf("%d", (result >> i) & 1);
    printf(")\n");
    
    // 应用示例1:设置特定位
    printf("\n应用1:设置特定位\n");
    int permissions = 0b00000100;  // 只有读取权限
    int write_permission = 0b00000010;  // 写入权限位
    int execute_permission = 0b00000001;  // 执行权限位
    
    // 添加写入权限
    permissions = permissions | write_permission;
    printf("添加写入权限后: ");
    for (int i = 7; i >= 0; i--) printf("%d", (permissions >> i) & 1);
    printf("\n");
    
    // 添加执行权限
    permissions = permissions | execute_permission;
    printf("添加执行权限后: ");
    for (int i = 7; i >= 0; i--) printf("%d", (permissions >> i) & 1);
    printf("\n");
    
    // 应用示例2:合并标志位
    printf("\n应用2:合并标志位\n");
    int flag1 = 0b00100000;  // 标志1
    int flag2 = 0b00001000;  // 标志2
    int flag3 = 0b00000010;  // 标志3
    
    int all_flags = flag1 | flag2 | flag3;
    printf("合并所有标志: ");
    for (int i = 7; i >= 0; i--) printf("%d", (all_flags >> i) & 1);
    printf("\n");
    
    return 0;
}

2.1.3 按位异或运算(^)

规则:两个位不同时,结果为1,相同时为0。

真值表

cpp 复制代码
0 ^ 0 = 0
0 ^ 1 = 1
1 ^ 0 = 1
1 ^ 1 = 0

示例

cpp 复制代码
#include <stdio.h>

int main() {
    int a = 12;    // 二进制: 0000 1100
    int b = 25;    // 二进制: 0001 1001
    
    printf("=== 按位异或运算(^) ===\n\n");
    
    printf("a = %d (二进制: ", a);
    for (int i = 7; i >= 0; i--) printf("%d", (a >> i) & 1);
    printf(")\n");
    
    printf("b = %d (二进制: ", b);
    for (int i = 7; i >= 0; i--) printf("%d", (b >> i) & 1);
    printf(")\n");
    
    int result = a ^ b;
    printf("\na ^ b = %d (二进制: ", result);
    for (int i = 7; i >= 0; i--) printf("%d", (result >> i) & 1);
    printf(")\n");
    
    // 异或的性质
    printf("\n异或运算性质:\n");
    printf("1. a ^ a = 0\n");
    printf("   %d ^ %d = %d\n", a, a, a ^ a);
    
    printf("2. a ^ 0 = a\n");
    printf("   %d ^ 0 = %d\n", a, a ^ 0);
    
    printf("3. 交换律: a ^ b = b ^ a\n");
    printf("   %d ^ %d = %d, %d ^ %d = %d\n", 
           a, b, a ^ b, b, a, b ^ a);
    
    printf("4. 结合律: (a ^ b) ^ c = a ^ (b ^ c)\n");
    int c = 7;
    printf("   (%d ^ %d) ^ %d = %d ^ %d = %d\n",
           a, b, c, a ^ b, c, (a ^ b) ^ c);
    printf("   %d ^ (%d ^ %d) = %d ^ %d = %d\n",
           a, b, c, a, b ^ c, a ^ (b ^ c));
    
    // 应用示例1:交换两个变量的值(不使用临时变量)
    printf("\n应用1:交换两个变量的值\n");
    int x = 10, y = 20;
    printf("交换前: x = %d, y = %d\n", x, y);
    
    x = x ^ y;
    y = x ^ y;  // y = (x ^ y) ^ y = x ^ (y ^ y) = x ^ 0 = x
    x = x ^ y;  // x = (x ^ y) ^ x = y ^ (x ^ x) = y ^ 0 = y
    
    printf("交换后: x = %d, y = %d\n", x, y);
    
    // 应用示例2:数据加密/解密
    printf("\n应用2:简单的数据加密/解密\n");
    char plain_text[] = "HELLO";
    char key = 0b10101010;  // 加密密钥
    
    printf("明文: %s\n", plain_text);
    printf("密钥: ");
    for (int i = 7; i >= 0; i--) printf("%d", (key >> i) & 1);
    printf("\n");
    
    // 加密
    printf("加密后的密文: ");
    for (int i = 0; plain_text[i] != '\0'; i++) {
        char encrypted = plain_text[i] ^ key;
        printf("%02X ", (unsigned char)encrypted);
    }
    printf("\n");
    
    // 解密
    char encrypted[] = {0xCA, 0xCF, 0xCC, 0xCC, 0xC9};  // "HELLO"加密后的值
    printf("解密后的明文: ");
    for (int i = 0; i < 5; i++) {
        char decrypted = encrypted[i] ^ key;
        printf("%c", decrypted);
    }
    printf("\n");
    
    // 应用示例3:找出唯一不重复的数字
    printf("\n应用3:找出唯一不重复的数字\n");
    int nums[] = {1, 2, 3, 4, 5, 4, 3, 2, 1};
    int size = sizeof(nums) / sizeof(nums[0]);
    
    int unique = 0;
    for (int i = 0; i < size; i++) {
        unique ^= nums[i];
    }
    
    printf("数组: ");
    for (int i = 0; i < size; i++) {
        printf("%d ", nums[i]);
    }
    printf("\n唯一不重复的数字: %d\n", unique);
    
    return 0;
}

2.1.4 按位取反运算(~)

规则:对操作数的每一位取反,0变1,1变0。

示例

cpp 复制代码
#include <stdio.h>

int main() {
    unsigned char a = 12;  // 二进制: 0000 1100
    
    printf("=== 按位取反运算(~) ===\n\n");
    
    printf("a = %d (二进制: ", a);
    for (int i = 7; i >= 0; i--) printf("%d", (a >> i) & 1);
    printf(")\n");
    
    unsigned char result = ~a;
    printf("\n~a = %u (二进制: ", result);
    for (int i = 7; i >= 0; i--) printf("%d", (result >> i) & 1);
    printf(")\n");
    
    // 注意:有符号整数取反的陷阱
    printf("\n有符号整数取反的注意事项:\n");
    char signed_a = 12;
    char signed_result = ~signed_a;
    
    printf("有符号 char: %d\n", signed_a);
    printf("~%d = %d (注意:这不是 -13)\n", signed_a, signed_result);
    
    // 应用示例1:创建掩码
    printf("\n应用1:创建掩码\n");
    unsigned int mask = ~((1 << 4) - 1);  // 清除低4位的掩码
    printf("清除低4位的掩码: %08X\n", mask);
    printf("二进制: ");
    for (int i = 31; i >= 0; i--) printf("%d", (mask >> i) & 1);
    printf("\n");
    
    // 应用示例2:求补码(取反加1)
    printf("\n应用2:求补码\n");
    int num = 42;
    int complement = ~num + 1;  // 补码
    printf("%d 的补码是: %d\n", num, complement);
    printf("验证: %d + %d = %d\n", num, complement, num + complement);
    
    // 应用示例3:切换特定位
    printf("\n应用3:切换特定位\n");
    unsigned char flags = 0b10101010;
    unsigned char toggle_mask = 0b00001111;  // 切换低4位
    
    printf("原始标志位: ");
    for (int i = 7; i >= 0; i--) printf("%d", (flags >> i) & 1);
    printf("\n");
    
    unsigned char toggled = flags ^ toggle_mask;  // 使用异或切换
    printf("切换后标志位: ");
    for (int i = 7; i >= 0; i--) printf("%d", (toggled >> i) & 1);
    printf("\n");
    
    return 0;
}

2.1.5 左移运算(<<)

规则:将操作数的所有位向左移动指定的位数,右边空出的位用0填充。

示例

cpp 复制代码
#include <stdio.h>

int main() {
    int a = 5;  // 二进制: 0000 0101
    
    printf("=== 左移运算(<<) ===\n\n");
    
    printf("a = %d (二进制: ", a);
    for (int i = 7; i >= 0; i--) printf("%d", (a >> i) & 1);
    printf(")\n\n");
    
    // 左移1位
    for (int shift = 1; shift <= 4; shift++) {
        int result = a << shift;
        printf("a << %d = %d (二进制: ", shift, result);
        for (int i = 7; i >= 0; i--) printf("%d", (result >> i) & 1);
        printf(")\n");
    }
    
    // 左移的性质:相当于乘以2的n次方
    printf("\n左移运算的性质:\n");
    printf("a << n 相当于 a × 2^n\n");
    
    for (int i = 0; i <= 4; i++) {
        printf("%d << %d = %d, %d × 2^%d = %d\n", 
               a, i, a << i, a, i, a * (1 << i));
    }
    
    // 应用示例1:快速计算2的幂
    printf("\n应用1:快速计算2的幂\n");
    for (int i = 0; i <= 10; i++) {
        printf("2^%d = %d\n", i, 1 << i);
    }
    
    // 应用示例2:设置特定位
    printf("\n应用2:设置特定位\n");
    unsigned int flag = 0;
    
    // 设置第3位(从0开始)
    flag = flag | (1 << 3);
    printf("设置第3位后: %08X\n", flag);
    
    // 设置第5位
    flag = flag | (1 << 5);
    printf("设置第5位后: %08X\n", flag);
    
    // 应用示例3:RGB颜色组合
    printf("\n应用3:RGB颜色组合\n");
    unsigned char red = 255;     // 0-255
    unsigned char green = 128;   // 0-255
    unsigned char blue = 64;     // 0-255
    
    // 将RGB组合成一个32位整数(ARGB格式,A=255表示不透明)
    unsigned int color = (255 << 24) | (red << 16) | (green << 8) | blue;
    
    printf("红色分量: %d (0x%02X)\n", red, red);
    printf("绿色分量: %d (0x%02X)\n", green, green);
    printf("蓝色分量: %d (0x%02X)\n", blue, blue);
    printf("组合颜色: 0x%08X\n", color);
    
    // 从组合颜色中提取分量
    unsigned char extracted_red = (color >> 16) & 0xFF;
    unsigned char extracted_green = (color >> 8) & 0xFF;
    unsigned char extracted_blue = color & 0xFF;
    
    printf("提取的红色: %d\n", extracted_red);
    printf("提取的绿色: %d\n", extracted_green);
    printf("提取的蓝色: %d\n", extracted_blue);
    
    return 0;
}

2.1.6 右移运算(>>)

规则:将操作数的所有位向右移动指定的位数。

注意:对于有符号数,右移时左边空出的位用符号位填充(算术右移);对于无符号数,用0填充(逻辑右移)。

示例

cpp 复制代码
#include <stdio.h>

int main() {
    printf("=== 右移运算(>>) ===\n\n");
    
    // 无符号数的右移
    printf("无符号数的右移(逻辑右移):\n");
    unsigned int u = 0b10101100;  // 172
    
    printf("u = %u (二进制: ", u);
    for (int i = 7; i >= 0; i--) printf("%d", (u >> i) & 1);
    printf(")\n\n");
    
    for (int shift = 1; shift <= 4; shift++) {
        unsigned int result = u >> shift;
        printf("u >> %d = %u (二进制: ", shift, result);
        for (int i = 7; i >= 0; i--) printf("%d", (result >> i) & 1);
        printf(")\n");
    }
    
    // 有符号数的右移
    printf("\n有符号数的右移(算术右移):\n");
    int s = -8;  // 二进制补码: 1111 1000
    
    printf("s = %d (二进制: ", s);
    for (int i = 7; i >= 0; i--) printf("%d", (s >> i) & 1);
    printf(")\n\n");
    
    for (int shift = 1; shift <= 4; shift++) {
        int result = s >> shift;
        printf("s >> %d = %d (二进制: ", shift, result);
        for (int i = 7; i >= 0; i--) printf("%d", (result >> i) & 1);
        printf(")\n");
    }
    
    // 右移的性质:相当于除以2的n次方(向下取整)
    printf("\n右移运算的性质:\n");
    printf("对于正整数:a >> n 相当于 a ÷ 2^n\n");
    printf("对于负整数:a >> n 相当于 a ÷ 2^n(向下取整)\n\n");
    
    int positives[] = {16, 32, 64, 128};
    int negatives[] = {-16, -32, -64, -128};
    
    printf("正整数右移:\n");
    for (int i = 0; i < 4; i++) {
        printf("%d >> 1 = %d, %d / 2 = %d\n",
               positives[i], positives[i] >> 1,
               positives[i], positives[i] / 2);
    }
    
    printf("\n负整数右移:\n");
    for (int i = 0; i < 4; i++) {
        printf("%d >> 1 = %d, %d / 2 = %d\n",
               negatives[i], negatives[i] >> 1,
               negatives[i], negatives[i] / 2);
    }
    
    // 应用示例1:提取特定位段
    printf("\n应用1:提取特定位段\n");
    unsigned int data = 0x12345678;
    
    printf("原始数据: 0x%08X\n", data);
    
    // 提取高16位
    unsigned int high = data >> 16;
    printf("高16位: 0x%04X\n", high);
    
    // 提取低16位
    unsigned int low = data & 0xFFFF;
    printf("低16位: 0x%04X\n", low);
    
    // 提取中间的8位(从第8位开始)
    unsigned int middle = (data >> 8) & 0xFF;
    printf("中间8位(8-15位): 0x%02X\n", middle);
    
    // 应用示例2:快速除以2的幂
    printf("\n应用2:快速除以2的幂\n");
    int numbers[] = {100, 1000, 10000, 100000};
    
    for (int i = 0; i < 4; i++) {
        printf("%d / 8 = %d, %d >> 3 = %d\n",
               numbers[i], numbers[i] / 8,
               numbers[i], numbers[i] >> 3);
    }
    
    // 应用示例3:分离RGB颜色分量
    printf("\n应用3:分离RGB颜色分量\n");
    unsigned int rgb_color = 0xFF336699;  // ARGB格式
    
    printf("颜色值: 0x%08X\n", rgb_color);
    
    unsigned char alpha = (rgb_color >> 24) & 0xFF;
    unsigned char red = (rgb_color >> 16) & 0xFF;
    unsigned char green = (rgb_color >> 8) & 0xFF;
    unsigned char blue = rgb_color & 0xFF;
    
    printf("Alpha通道: %d (0x%02X)\n", alpha, alpha);
    printf("红色分量: %d (0x%02X)\n", red, red);
    printf("绿色分量: %d (0x%02X)\n", green, green);
    printf("蓝色分量: %d (0x%02X)\n", blue, blue);
    
    return 0;
}

2.2 位域(位段)

2.2.1 位域的基本概念

位域允许在结构体中定义占用特定位数的成员,用于节省内存空间。

定义语法

cpp 复制代码
struct {
    类型 成员名 : 位数;
    类型 成员名 : 位数;
    // ...
};

示例

cpp 复制代码
#include <stdio.h>
#include <stdbool.h>

int main() {
    printf("=== 位域(位段)示例 ===\n\n");
    
    // 示例1:简单的位域结构
    struct {
        unsigned int flag1 : 1;  // 1位
        unsigned int flag2 : 1;  // 1位
        unsigned int flag3 : 1;  // 1位
        unsigned int flag4 : 1;  // 1位
        unsigned int value : 4;  // 4位(0-15)
    } bitfield1;
    
    printf("示例1:简单的位域结构\n");
    printf("结构体大小: %zu 字节\n", sizeof(bitfield1));
    
    bitfield1.flag1 = 1;
    bitfield1.flag2 = 0;
    bitfield1.flag3 = 1;
    bitfield1.flag4 = 0;
    bitfield1.value = 12;
    
    printf("flag1: %u\n", bitfield1.flag1);
    printf("flag2: %u\n", bitfield1.flag2);
    printf("flag3: %u\n", bitfield1.flag3);
    printf("flag4: %u\n", bitfield1.flag4);
    printf("value: %u\n", bitfield1.value);
    
    // 示例2:混合位域和普通成员
    struct Mixed {
        char name[20];           // 20字节
        unsigned int age : 7;    // 7位(0-127岁)
        unsigned int gender : 1; // 1位(0=男,1=女)
        unsigned int grade : 4;  // 4位(0-15年级)
        unsigned int : 4;        // 4位未命名填充
        float salary;            // 4字节
    };
    
    printf("\n示例2:混合位域和普通成员\n");
    printf("结构体大小: %zu 字节\n", sizeof(struct Mixed));
    
    struct Mixed person = {"张三", 30, 0, 3, 8000.0};
    printf("姓名: %s\n", person.name);
    printf("年龄: %u\n", person.age);
    printf("性别: %s\n", person.gender ? "女" : "男");
    printf("年级: %u\n", person.grade);
    printf("薪资: %.2f\n", person.salary);
    
    // 示例3:位域的实际应用(网络协议头)
    printf("\n示例3:IP数据包头结构(简化)\n");
    
    // 简化的IP头结构(仅用于演示)
    struct IPHeader {
        unsigned int version : 4;      // 版本号(4位)
        unsigned int ihl : 4;          // 头部长度(4位)
        unsigned int dscp : 6;         // 差分服务代码点(6位)
        unsigned int ecn : 2;          // 显式拥塞通知(2位)
        unsigned int total_length : 16; // 总长度(16位)
        unsigned int identification : 16; // 标识(16位)
        unsigned int flags : 3;        // 标志(3位)
        unsigned int fragment_offset : 13; // 分片偏移(13位)
        unsigned int ttl : 8;          // 生存时间(8位)
        unsigned int protocol : 8;     // 协议(8位)
        unsigned int checksum : 16;    // 头部校验和(16位)
        unsigned int source_ip;        // 源IP地址(32位)
        unsigned int dest_ip;          // 目的IP地址(32位)
    };
    
    struct IPHeader ip_packet = {
        .version = 4,
        .ihl = 5,
        .dscp = 0,
        .ecn = 0,
        .total_length = 1500,
        .identification = 12345,
        .flags = 2,
        .fragment_offset = 0,
        .ttl = 64,
        .protocol = 6,  // TCP
        .checksum = 0xABCD,
        .source_ip = 0xC0A80101,  // 192.168.1.1
        .dest_ip = 0xC0A80164     // 192.168.1.100
    };
    
    printf("IP头大小: %zu 字节\n", sizeof(struct IPHeader));
    printf("版本: %u\n", ip_packet.version);
    printf("头部长度: %u 字(%u 字节)\n", ip_packet.ihl, ip_packet.ihl * 4);
    printf("总长度: %u 字节\n", ip_packet.total_length);
    printf("TTL: %u\n", ip_packet.ttl);
    printf("协议: %u\n", ip_packet.protocol);
    printf("源IP: %08X\n", ip_packet.source_ip);
    printf("目的IP: %08X\n", ip_packet.dest_ip);
    
    // 示例4:位域的边界对齐
    printf("\n示例4:位域的边界对齐\n");
    
    struct BitFieldTest1 {
        unsigned int a : 5;
        unsigned int b : 8;
        unsigned int c : 19;
    } test1;
    
    struct BitFieldTest2 {
        unsigned int a : 5;
        unsigned int   : 0;  // 强制对齐到下一个边界
        unsigned int b : 8;
        unsigned int c : 19;
    } test2;
    
    struct BitFieldTest3 {
        unsigned int a : 5;
        unsigned int b : 8;
        unsigned int   : 0;  // 无名位域,填充剩余位
        unsigned int c : 19;
    } test3;
    
    printf("test1 大小: %zu 字节\n", sizeof(test1));
    printf("test2 大小: %zu 字节\n", sizeof(test2));
    printf("test3 大小: %zu 字节\n", sizeof(test3));
    
    // 示例5:位域与联合体结合
    printf("\n示例5:位域与联合体结合\n");
    
    union StatusRegister {
        unsigned int raw_value;
        struct {
            unsigned int ready : 1;      // 设备就绪
            unsigned int busy : 1;       // 设备忙
            unsigned int error : 1;      // 错误标志
            unsigned int timeout : 1;    // 超时标志
            unsigned int data_ready : 1; // 数据就绪
            unsigned int : 3;           // 保留位
            unsigned int error_code : 8; // 错误代码
            unsigned int data_size : 16; // 数据大小
        } bits;
    };
    
    union StatusRegister status;
    status.raw_value = 0xABCD1234;
    
    printf("原始值: 0x%08X\n", status.raw_value);
    printf("ready: %u\n", status.bits.ready);
    printf("busy: %u\n", status.bits.busy);
    printf("error: %u\n", status.bits.error);
    printf("timeout: %u\n", status.bits.timeout);
    printf("data_ready: %u\n", status.bits.data_ready);
    printf("error_code: %u (0x%02X)\n", status.bits.error_code, status.bits.error_code);
    printf("data_size: %u\n", status.bits.data_size);
    
    // 修改位域
    status.bits.error = 1;
    status.bits.error_code = 0x42;
    
    printf("修改后的原始值: 0x%08X\n", status.raw_value);
    
    return 0;
}

2.2.2 位域的注意事项

cpp 复制代码
#include <stdio.h>

int main() {
    printf("=== 位域的注意事项 ===\n\n");
    
    // 1. 位域不能取地址
    struct Test {
        unsigned int a : 4;
        unsigned int b : 4;
        int normal;  // 普通成员
    } t;
    
    // &t.a;  // 错误:不能对位域成员取地址
    &t.normal;  // 正确:可以对普通成员取地址
    
    // 2. 位域不能是数组
    struct BadExample {
        // unsigned int arr[10] : 4;  // 错误:位域不能是数组
    };
    
    // 3. 位域的类型限制
    struct TypeExample {
        unsigned int u1 : 4;    // 正确
        int s1 : 4;            // 正确(但有符号)
        // float f1 : 4;       // 错误:不能是浮点型
        // double d1 : 4;      // 错误:不能是双精度型
    };
    
    // 4. 位域的跨字节边界
    struct CrossBoundary {
        unsigned int a : 10;  // 10位,可能跨字节
        unsigned int b : 10;  // 10位,可能跨字节
        unsigned int c : 12;  // 12位
    } cb;
    
    printf("跨字节边界的位域大小: %zu 字节\n", sizeof(cb));
    
    // 5. 位域的符号问题
    struct SignedBitField {
        int a : 3;   // 3位有符号,范围:-4到3
        int b : 4;   // 4位有符号,范围:-8到7
        int c : 5;   // 5位有符号,范围:-16到15
    } sbf;
    
    sbf.a = 3;   // 正确
    sbf.b = -5;  // 正确
    // sbf.c = 20; // 错误:超出范围(可能被截断)
    sbf.c = 15;  // 正确
    
    printf("\n有符号位域示例:\n");
    printf("a = %d\n", sbf.a);
    printf("b = %d\n", sbf.b);
    printf("c = %d\n", sbf.c);
    
    // 6. 零长度位域的特殊含义
    struct ZeroWidth {
        unsigned int a : 5;
        unsigned int   : 0;  // 强制对齐到下一个int边界
        unsigned int b : 7;
    } zw;
    
    printf("\n零长度位域示例:\n");
    printf("结构体大小: %zu 字节\n", sizeof(zw));
    
    // 7. 位域的存储顺序(依赖于系统)
    struct BitOrder {
        unsigned int a : 1;
        unsigned int b : 1;
        unsigned int c : 1;
        unsigned int d : 1;
    } bo;
    
    bo.a = 1;
    bo.b = 0;
    bo.c = 1;
    bo.d = 0;
    
    printf("\n位域存储顺序示例:\n");
    printf("位域值: a=%d, b=%d, c=%d, d=%d\n", bo.a, bo.b, bo.c, bo.d);
    
    // 8. 位域的实际应用:设备寄存器
    printf("\n实际应用:设备控制寄存器\n");
    
    struct DeviceControl {
        unsigned int enable : 1;      // 设备使能
        unsigned int mode : 2;        // 工作模式(00,01,10,11)
        unsigned int speed : 3;       // 速度(0-7)
        unsigned int reserved : 2;    // 保留位
        unsigned int status : 4;      // 状态位
        unsigned int error_code : 8;  // 错误代码
        unsigned int data : 12;       // 数据
    };
    
    struct DeviceControl device = {
        .enable = 1,
        .mode = 2,
        .speed = 5,
        .status = 3,
        .error_code = 0,
        .data = 2047
    };
    
    printf("控制寄存器内容:\n");
    printf("使能: %s\n", device.enable ? "是" : "否");
    printf("模式: %u\n", device.mode);
    printf("速度: %u\n", device.speed);
    printf("状态: %u\n", device.status);
    printf("错误代码: %u\n", device.error_code);
    printf("数据: %u\n", device.data);
    
    return 0;
}

2.3 本章小结

结构体与共用体总结

  1. 结构体(struct)

    • 用于组合不同类型的数据

    • 支持嵌套、数组、指针

    • 可以使用.运算符访问成员

  2. 共用体(union)

    • 所有成员共享同一块内存

    • 用于节省内存或实现类型转换

    • 同时只能有效存储一个成员

  3. 枚举(enum)

    • 定义命名的整数常量集合

    • 提高代码可读性

    • 可进行整数运算

  4. typedef

    • 为已有类型创建别名

    • 提高代码可读性和可移植性

  5. 结构体指针

    • 使用->运算符访问成员

    • 动态内存分配创建结构体

    • 链表等数据结构的实现基础

位运算总结

  1. 基本位运算符

    • &:按位与,用于屏蔽位

    • |:按位或,用于设置位

    • ^:按位异或,用于切换位

    • ~:按位取反

    • <<:左移,相当于乘以2的幂

    • >>:右移,相当于除以2的幂

  2. 位域(位段)

    • 在结构体中定义特定位数的成员

    • 节省内存空间

    • 用于硬件寄存器映射

  3. 常见应用

    • 权限管理系统

    • 图像处理

    • 数据加密

    • 网络协议解析

    • 硬件控制

  4. 注意事项

    • 注意有符号数的右移行为

    • 位域不能取地址

    • 位运算的优先级较低,建议使用括号

    • 注意整数溢出问题

综合应用建议

  1. 结构体与指针结合

    • 使用指针传递大结构体提高效率

    • 动态创建结构体数组

    • 实现链表、树等数据结构

  2. 位运算优化技巧

    • 使用位运算代替乘除法

    • 使用掩码操作特定位

    • 使用异或交换变量值

  3. 内存对齐考虑

    • 结构体成员顺序影响内存占用

    • 位域的存储顺序依赖系统

    • 使用#pragma pack控制对齐

  4. 可移植性

    • 避免依赖特定字节序

    • 使用固定宽度整数类型(如uint32_t

    • 为位域使用无符号类型

相关推荐
iAkuya3 小时前
(leetcode)力扣100 61分割回文串(回溯,动归)
算法·leetcode·职场和发展
梵刹古音3 小时前
【C语言】 指针与数据结构操作
c语言·数据结构·算法
VT.馒头3 小时前
【力扣】2695. 包装数组
前端·javascript·算法·leetcode·职场和发展·typescript
刘琦沛在进步4 小时前
【C / C++】引用和函数重载的介绍
c语言·开发语言·c++
CoderCodingNo5 小时前
【GESP】C++五级练习题 luogu-P1865 A % B Problem
开发语言·c++·算法
大闲在人5 小时前
7. 供应链与制造过程术语:“周期时间”
算法·供应链管理·智能制造·工业工程
VekiSon5 小时前
Linux内核驱动——杂项设备驱动与内核模块编译
linux·c语言·arm开发·嵌入式硬件
小熳芋5 小时前
443. 压缩字符串-python-双指针
算法
Charlie_lll5 小时前
力扣解题-移动零
后端·算法·leetcode