【C语言】结构体详解 -《探索C语言的 “小宇宙” 》

简介: 结构体通过`struct`关键字定义。定义结构体时,需要指定结构体的名称以及结构体内部的成员变量。

C语言结构体(struct)详解

结构体概览表

功能 描述
定义结构体 定义一个结构体类型
声明结构体变量 声明一个结构体变量
访问成员 使用点运算符(.)和箭头运算符(->)访问成员
初始化结构体 在声明时初始化结构体
计算大小 使用sizeof计算结构体的大小
作为函数参数 传递结构体或结构体指针作为函数参数
结构体嵌套 结构体中包含其他结构体
结构体与数组 结构体作为数组元素或包含数组的成员
内存对齐 结构体的内存对齐和填充
类型定义(typedef 使用typedef简化结构体声明
嵌入式应用 在嵌入式系统中使用结构体
拓展技巧 结构体指针运算和联合体比较

1. 结构体的基本概念

1.1 结构体定义

结构体通过struct关键字定义。定义结构体时,需要指定结构体的名称以及结构体内部的成员变量。

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

在上面的示例中,定义了一个Person结构体,其中包含三个成员:name(字符数组)、age(整数)和height(浮点数)。

1.2 结构体变量声明

定义结构体后,可以声明结构体变量来使用它。例如:

struct Person person1;

这里声明了一个Person结构体类型的变量person1

2. 结构体成员的访问

2.1 使用点运算符(.)访问成员

可以通过点运算符(.)访问结构体的成员变量。例如:

#include <stdio.h>

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

int main() {
   
    struct Person person1;

    // 初始化结构体成员
    person1.age = 25;
    person1.height = 175.5;
    snprintf(person1.name, sizeof(person1.name), "Alice");

    // 输出结构体成员
    printf("Name: %s\n", person1.name);
    printf("Age: %d\n", person1.age);
    printf("Height: %.2f\n", person1.height);

    return 0;
}

输出

Name: Alice
Age: 25
Height: 175.50

2.2 使用箭头运算符(->)访问成员

如果结构体变量是指针类型,则可以通过箭头运算符(->)访问其成员。例如:

#include <stdio.h>

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

void printPerson(struct Person *p) {
   
    printf("Name: %s\n", p->name);
    printf("Age: %d\n", p->age);
    printf("Height: %.2f\n", p->height);
}

int main() {
   
    struct Person person1 = {
   "Bob", 30, 180.0};
    struct Person *ptr = &person1;

    printPerson(ptr);

    return 0;
}

输出

Name: Bob
Age: 30
Height: 180.00

3. 结构体的初始化

3.1 结构体初始化

可以在定义结构体变量的同时进行初始化。例如:

#include <stdio.h>

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

int main() {
   
    struct Person person2 = {
   "Alice", 30, 160.0};

    printf("Name: %s\n", person2.name);
    printf("Age: %d\n", person2.age);
    printf("Height: %.2f\n", person2.height);

    return 0;
}

输出

Name: Alice
Age: 30
Height: 160.00

3.2 使用指定初始化器

C99标准引入了指定初始化器,可以按顺序或指定成员进行初始化。例如:

#include <stdio.h>

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

int main() {
   
    struct Person person3 = {
   .age = 40, .height = 180.0, .name = "Bob"};

    printf("Name: %s\n", person3.name);
    printf("Age: %d\n", person3.age);
    printf("Height: %.2f\n", person3.height);

    return 0;
}

输出

Name: Bob
Age: 40
Height: 180.00

4. 结构体的大小

结构体的大小取决于其成员的数量和类型,以及内存对齐的规则。可以使用sizeof运算符来获取结构体的大小。例如:

#include <stdio.h>

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

int main() {
   
    printf("Size of Person: %zu bytes\n", sizeof(struct Person));
    return 0;
}

输出

在不同平台上的输出可能不同,例如:

  • 32位系统Size of Person: 60 bytes
  • 64位系统Size of Person: 64 bytes

5. 结构体作为函数参数

5.1 传递结构体的副本

结构体可以作为函数参数传递。如果传递的是结构体的副本,则会创建结构体的一个副本,可能会影响性能。

#include <stdio.h>

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

void printPerson(struct Person p) {
   
    printf("Name: %s\n", p.name);
    printf("Age: %d\n", p.age);
    printf("Height: %.2f\n", p.height);
}

int main() {
   
    struct Person person1 = {
   "Alice", 30, 160.0};
    printPerson(person1);

    return 0;
}

输出

Name: Alice
Age: 30
Height: 160.00

5.2 传递结构体指针

为了提高效率,可以将结构体的指针传递给函数,这样只需传递指针而不是整个结构体。

#include <stdio.h>

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

void updateAge(struct Person *p, int newAge) {
   
    p->age = newAge;
}

int main() {
   
    struct Person person1 = {
   "Bob", 30, 180.0};
    updateAge(&person1, 35);

    printf("Updated Age: %d\n", person1.age);

    return 0;
}

输出

Updated Age: 35

6. 结构体的嵌套

结构体可以嵌套其他结构体。例如:

#include <stdio.h>

struct Address {
   
    char street[100];
    char city[50];
    int postalCode;
};

struct Person {
   
    char name[50];
    int age;
    struct Address address;  // 嵌套的结构体
};

int main() {
   
    struct Person person4 = {
   
        "John",
        28,
        {
   "123 Main St", "Metropolis", 12345}
    };

    printf("Name: %s\n", person4.name);
    printf("Address: %s, %s %d\n", person4.address.street, person4.address.city, person4.address.postalCode);

    return 0;
}

输出

Name: John
Address: 123 Main St, Metropolis 12345

7. 结构体与数组

结构体可以作为数组的元素,也可以包含数组作为成员。例如:

#include <stdio.h>

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

int main() {
   
    struct Person people[2] = {
   
        {
   "Alice", 30, 160.0},
        {
   "Bob", 40, 180.0}
    };

    for (int i = 0; i < 2; i++) {
   
        printf("Person %d: %s, %d, %.2f\n", i+1, people[i].name, people[i].age, people[i].height);
    }

    return 0;
}

输出

Person 1: Alice, 30, 160.00
Person 2: Bob, 40, 180.00

8. 结构体的内存对齐

结构体的内存对齐与填充是为了提高数据访问的效率。在C语言中,结构体的内存布局可能会受到对齐要求的影响,导致结构体的实际大小可能大于成员变量总和的大小。编译器通常会在成员之间插入填充字节,以确保每个成员的地址对齐。

8.1 对齐示例

#include <stdio.h>

struct Example {
   
    char c;       // 1 byte
    int i;        // 4 bytes, 3 bytes of padding
    short s;      // 2 bytes
};

int main() {
   
    printf("Size of Example: %zu bytes\n", sizeof(struct Example));
    return 0;
}

输出

Size of Example: 12 bytes

在这个例子中,Example结构体的大小是12字节。虽然char占1字节,int占4字节,short占2字节,成员变量的总和为7字节,但由于内存对齐要求,Example结构体实际上占用12字节。

8.2 结构体对齐与#pragma pack

在某些情况下,可以使用#pragma pack指令来控制结构体的对齐方式,从而减少内存占用。

#include <stdio.h>

#pragma pack(1)  // 设置结构体对齐为1字节

struct PackedExample {
   
    char c;
    int i;
    short s;
};

#pragma pack()  // 恢复默认对齐

int main() {
   
    printf("Size of PackedExample: %zu bytes\n", sizeof(struct PackedExample));
    return 0;
}

输出

Size of PackedExample: 7 bytes

使用#pragma pack(1)可以将PackedExample结构体的对齐方式设置为1字节,从而减少结构体的实际大小为7字节,但可能会影响访问效率。

9. 类型定义(typedef)简化结构体声明

使用typedef可以为结构体定义一个新的类型名,使得结构体的声明更加简洁。例如:

#include <stdio.h>

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

int main() {
   
    Person person1 = {
   "Charlie", 28, 175.0};

    printf("Name: %s\n", person1.name);
    printf("Age: %d\n", person1.age);
    printf("Height: %.2f\n", person1.height);

    return 0;
}

输出

Name: Charlie
Age: 28
Height: 175.00

10. 嵌入式系统中的应用

在嵌入式系统中,结构体用于管理硬件寄存器、配置参数以及存储设备状态等。结构体能够帮助开发者以更结构化的方式访问硬件资源,提高代码的可读性和维护性。

10.1 示例:硬件寄存器配置

#include <stdio.h>
#include <stdint.h>

// 定义硬件寄存器配置结构体
typedef struct {
   
    volatile uint32_t CONTROL;
    volatile uint32_t STATUS;
    volatile uint32_t DATA;
} UART_RegDef_t;

int main() {
   
    UART_RegDef_t UART1;  // 假设这是一个UART寄存器的实例

    // 设置UART寄存器
    UART1.CONTROL = 0x01;  // 启用UART
    UART1.STATUS = 0x00;   // 清除状态
    UART1.DATA = 0x55;     // 发送数据

    // 打印寄存器的配置
    printf("UART1 CONTROL: 0x%X\n", UART1.CONTROL);
    printf("UART1 STATUS: 0x%X\n", UART1.STATUS);
    printf("UART1 DATA: 0x%X\n", UART1.DATA);

    return 0;
}

输出

UART1 CONTROL: 0x1
UART1 STATUS: 0x0
UART1 DATA: 0x55

在这个示例中,结构体UART_RegDef_t用于表示UART寄存器的配置,包含CONTROLSTATUSDATA寄存器。通过这种方式,可以方便地设置和读取寄存器的值。

11. 拓展技巧

11.1 结构体指针的算术运算

可以对结构体指针进行算术运算,通常用于数组访问。例如:

#include <stdio.h>

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

int main() {
   
    struct Person people[3] = {
   
        {
   "Alice", 30, 160.0},
        {
   "Bob", 40, 180.0},
        {
   "Charlie", 25, 170.0}
    };

    struct Person *ptr = people;  // 指向结构体数组的指针

    for (int i = 0; i < 3; i++) {
   
        printf("Person %d: %s, %d, %.2f\n", i+1, (ptr+i)->name, (ptr+i)->age, (ptr+i)->height);
    }

    return 0;
}

输出

Person 1: Alice, 30, 160.00
Person 2: Bob, 40, 180.00
Person 3: Charlie, 25, 170.00

11.2 结构体与联合体(union)的比较

结构体和联合体都可以存储多个数据项,但结构体的每个成员都占有独立的内存空间,而联合体的所有成员共享同一块内存。使用结构体时每个成员都可用,而使用联合体时只有一个成员可以使用。

示例:结构体与联合体的比较

#include <stdio.h>

typedef union {
   
    int intValue;
    float floatValue;
    char charValue;
} UnionType;

typedef struct {
   
    int intValue;
    float floatValue;
    char charValue;
} StructType;

int main() {
   
    UnionType u;
    StructType s;

    u.intValue = 10;
    printf("Union intValue: %d\n", u.intValue);

    u.floatValue = 5.5;  // 修改联合体中的值
    printf("Union floatValue: %f\n", u.floatValue);
    // 注意:这时intValue的值是不确定的

    s.intValue = 10;
    s.floatValue = 5.5;
    s.charValue = 'A';
    printf("Struct intValue: %d\n", s.intValue);
    printf("Struct floatValue: %f\n", s.floatValue);
    printf("Struct charValue: %c\n", s.charValue);

    return 0;
}

输出

Union intValue: 10
Union floatValue: 5.500000
Struct intValue: 10
Struct floatValue: 5.500000
Struct charValue: A

7. 结束语

  1. 本节内容已经全部介绍完毕,希望通过这篇文章,大家对C语言中的结构体 struct 有了更深入的理解和认识。
  2. 感谢各位的阅读和支持,如果觉得这篇文章对你有帮助,请不要吝惜你的点赞和评论,这对我们非常重要。再次感谢大家的关注和支持
目录
相关文章
|
29天前
|
存储 网络协议 编译器
【C语言】深入解析C语言结构体:定义、声明与高级应用实践
通过根据需求合理选择结构体定义和声明的放置位置,并灵活结合动态内存分配、内存优化和数据结构设计,可以显著提高代码的可维护性和运行效率。在实际开发中,建议遵循以下原则: - **模块化设计**:尽可能封装实现细节,减少模块间的耦合。 - **内存管理**:明确动态分配与释放的责任,防止资源泄漏。 - **优化顺序**:合理排列结构体成员以减少内存占用。
129 14
|
2月前
|
存储 C语言
C语言如何使用结构体和指针来操作动态分配的内存
在C语言中,通过定义结构体并使用指向该结构体的指针,可以对动态分配的内存进行操作。首先利用 `malloc` 或 `calloc` 分配内存,然后通过指针访问和修改结构体成员,最后用 `free` 释放内存,实现资源的有效管理。
156 13
|
2月前
|
存储 数据建模 程序员
C 语言结构体 —— 数据封装的利器
C语言结构体是一种用户自定义的数据类型,用于将不同类型的数据组合在一起,形成一个整体。它支持数据封装,便于管理和传递复杂数据,是程序设计中的重要工具。
|
2月前
|
存储 编译器 数据处理
C 语言结构体与位域:高效数据组织与内存优化
C语言中的结构体与位域是实现高效数据组织和内存优化的重要工具。结构体允许将不同类型的数据组合成一个整体,而位域则进一步允许对结构体成员的位进行精细控制,以节省内存空间。两者结合使用,可在嵌入式系统等资源受限环境中发挥巨大作用。
71 11
|
2月前
|
存储 人工智能 算法
数据结构实验之C 语言的函数数组指针结构体知识
本实验旨在复习C语言中的函数、数组、指针、结构体与共用体等核心概念,并通过具体编程任务加深理解。任务包括输出100以内所有素数、逆序排列一维数组、查找二维数组中的鞍点、利用指针输出二维数组元素,以及使用结构体和共用体处理教师与学生信息。每个任务不仅强化了基本语法的应用,还涉及到了算法逻辑的设计与优化。实验结果显示,学生能够有效掌握并运用这些知识完成指定任务。
60 4
|
3月前
|
存储 C语言
如何在 C 语言中实现结构体的深拷贝
在C语言中实现结构体的深拷贝,需要手动分配内存并逐个复制成员变量,确保新结构体与原结构体完全独立,避免浅拷贝导致的数据共享问题。具体方法包括使用 `malloc` 分配内存和 `memcpy` 或手动赋值。
92 10
|
3月前
|
存储 大数据 编译器
C语言:结构体对齐规则
C语言中,结构体对齐规则是指编译器为了提高数据访问效率,会根据成员变量的类型对结构体中的成员进行内存对齐。通常遵循编译器默认的对齐方式或使用特定的对齐指令来优化结构体布局,以减少内存浪费并提升性能。
|
3月前
|
编译器 C语言
共用体和结构体在 C 语言中的优先级是怎样的
在C语言中,共用体(union)和结构体(struct)的优先级相同,它们都是用户自定义的数据类型,用于组合不同类型的数据。但是,共用体中的所有成员共享同一段内存,而结构体中的成员各自占用独立的内存空间。
|
3月前
|
存储 C语言
C语言:结构体与共用体的区别
C语言中,结构体(struct)和共用体(union)都用于组合不同类型的数据,但使用方式不同。结构体为每个成员分配独立的内存空间,而共用体的所有成员共享同一段内存,节省空间但需谨慎使用。
|
3月前
|
编译器 C语言 C++
C语言结构体
C语言结构体
36 5