1

【C4】结构体

 2 years ago
source link: https://www.guofei.site/2021/12/04/c4.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

【C4】结构体

2021年12月04日    Author:Guofei

文章归类: 语言    文章编号: 12001


版权声明:本文作者是郭飞。转载随意,但需要标明原文链接,并通知本人
原文链接:https://www.guofei.site/2021/12/04/c4.html

Edit

结构体

struct student {
    char name[100];
    int age;
};

int main() {
    struct student st; // 定义一个变量 st,其类型是 student
    st.age = 20;
    strcpy(st.name, "张三"); // 不能用 st.name = "张三"

    // 如何使用:
    printf("name= %s, age = %d\n", st.name, st.age);


    // 定义2:定义+初始化
    struct student st1 = {"李四", 25};

    // 定义3:可以省略1个
    struct student st2 = {"王二"};

    // 定义4:所有字段都初始化为0
    struct student st3 = {0};

    // 定义5:指定字段,这样顺序可以打乱
    struct student st4 = {.age=26, .name="麻子"};
}

用等号赋值就是内存拷贝

struct student st1;
st1 = st;

结构体的内存布局

1

// 占用2个字节,没问题
struct A {
    char a1;
    char a2;
};

// 占用8个字节,也没问题
struct A {
    int a1;
    int a2;
};


// 占用8个Byte
struct A {
    char a1;
    int a2;
};
// 这是因为结构体的内存总是对齐的
// 按照占用最大内存的元素为基本单位,做对齐

内存中是这样的

char a1; int a2;

占用 8 Byte

2

// 这个也占8个 Byte
struct A {
    char a1;
    char a2;
    char a3;
    char a4;
    int i;
};
// 因为空闲的内存给补上了

内存中是这样的

char a4; 1Byte1Byte1Byte1Byte char a1;char a2;char a3; int a2;

占用 8 Byte

3

struct A {
    char a1;
    short a2;
    int a3;
};

内存中是这样的

1Byte1Byte1Byte1Byte char a1;short a2; int a2;

4

struct A {
    char a1;
    short a2;
    char a3;
    int i;
};

内存中是这样的

</table>

占用 12 个字节

所以,**你可以通过调整顺序,减少内存消耗**。但计算机 **不会自动帮你优化**

### 5:出现数组的情况

```c struct A { char a1[5]; int i; }; ```

占用 12 个字节

1Byte1Byte1Byte1Byte char a1;short a2;</th></tr> char a1; int a2;1Byte1Byte1Byte1Bytechar a[0];char a[1];char a[2];char a[3];char a[4];int a2;

按照每个元素对齐,而不是按照数组为单位

### 操作内存 ```c struct A { char a1[4]; int i; };

int main() {

struct A a; char *s = (char *) &a; s[0] = 'a'; s[1] = 'b'; s[5] = 'c'; //其实指向 i 的第1个Byte了

printf("%s\n", a.a1); printf("%d\n", a.i); return 0; } ```

所以 1. 可以像操作数组一样,用指针操作结构体 2. 可以用指针,把浪费的“空隙”用起来

### 指定bit

```c // 总共只占用1个Byte struct A { char a1: 2; // 占用2个bit char a2: 2; // 占用2个bit char a3: 4; //占用4个bit }; ```

可以用这个技巧把内存应用到极致,尤其是在简陋的设备上

但是不要用不同的类型做定义 ```c // 这是错的 struct A { char a1:1; int a2:1; }; // 实测在不同的机器上占用内存不一样。在内存中的堆叠方式也不一样 ```

## 结构体数组

```c struct student { char name[20]; unsigned char age; int sex; };

// 初始化方法1 struct student st[3];

strcpy(st[0].name, "张三"); st[0].age = 15; st[0].sex=1;

// 初始化方法2 struct student st2[3] = { {"张三", 16, 1}, {"李四", 18, 0} }; // 初始化方法3 struct student st3[] = { {"张三", 16, 1}, {"李四", 18, 0} }; ```

基于结构体数组的特点,元素交换可以直接用内存交换来做

```c

struct student { char name[20]; unsigned char age; int sex; };

int swap_std(struct student *a, struct student *b) { static struct student tmp; memcpy(&tmp, a, sizeof(tmp)); memcpy(a, b, sizeof(tmp)); memcpy(b, &tmp, sizeof(tmp)); }

int main() {

struct student st[] = { {"张三", 16, 1}, {"李四", 18, 0}, {"王二", 14, 0}, {"白展堂", 20, 0}, {"吕秀才", 19, 0} };

swap_std(&st[0],&st[1]);

//显示 for (int i = 0; i < sizeof(st) / sizeof(st[0]); i++) { printf("%s,%d,%d\n", st[i].name, st[i].age, st[i].sex); }

//其实结构体也可以直接赋值 int swap_std(struct student *a, struct student *b) { static struct student tmp; tmp = *a; *a = *b; *b = tmp; return 0; }

} ```

## 结构体嵌套

如何使用? ```c struct A { char a1; };

struct B{ struct A a; int b1; };

struct B b; b.a.a1;

```

### 内存

内存 case 1 ```c struct A { char a1; };

// B 占 2 个 Byte struct B{ struct A a; char b1; }; ```

内存 case 2 ```c struct A { char a1; };

// B 占 8 个 Byte struct B{ struct A a; int b1; }; ``` 似乎在内存中是紧凑的堆叠

```c struct A { int a1; char a2;

};

// 占用 16 个字节 struct B { struct A a; char b1; int b2; }; ```

1Byte1Byte1Byte1Byteint A.a1char A.a2char b1int b2

b1 不会用来补其它结构体的内存空隙

## 结构体与指针

指向结构体的指针 ```c struct student st; struct student *p = &st;

strcpy(p->name, "hello"); p->age = 20; ```

`p->age` 等价于 `(*p).age`

指向结构体数组的指针

```c struct student { char name[100]; int age; };

int main() { struct student st[3] = { {"张三", 25}, {"李四", 26}, {"王二", 29} }; struct student *p = st;

// 用指针赋值 p->age = 20; p++; p->age = 15;

// 用指针取值 p--; for (int i = 0; i < 3; i++) { printf("%s,%d\n", p[i].name, p[i].age); } } ```

结构体的成员是指针的情况

```c struct student { char *name; };

int main() { struct student st = {0}; st.name = calloc(20, sizeof(char)); // 因为是指针,因此需要分配内存 strcpy(st.name, "hello"); printf("%s", st.name); free(st.name); } ```

下面这个很好理解,浅拷贝 ```c struct student { char *name; };

int main() { struct student st = {0}; st.name = calloc(20, sizeof(char)); // 因为是指针,因此需要分配内存 struct student st1 = st; strcpy(st.name, "hello");

printf("%s", st1.name); free(st.name); } ```

## 堆中的结构体

```c struct student p1={0}; // 在栈里面 struct student *p = malloc(sizeof(struct student)); // p->name 在堆里面 free(p); ```

## 联合体 union

```c union A { int a1; short a2; char a3; };

int main() { union A a; printf("%ld\n", sizeof(union A)); printf("%p==%p==%p", &a.a1, &a.a2, &a.a3); } ```

联合体在形式上与结构体类似,但区别是联合体成员的内存是共用的,每个成员在内存中的开头都一样。

## 枚举类型 enum

```c enum spectrum { red, yellow, green, blue, white, black }; // 其实是定义了很多int型的常量(0, 1, 2, ...),提高代码可读性

int main() { enum spectrum flower_color = red; } ```

还可以自定义值 ```c enum spectrum { red = 100, yellow, //这里后面累加1 green, blue, white, black }; ```

## typedef

用途 - 增加代码的可读性 - 减少代码量 - 提高可维护性 - 提高跨平台能力

```c typedef struct student stu; // 就是定义 stu = struct student typedef unsigned char BYTE; // 就是定义了 BYTE = unsigned char int main() { stu st; // 等价于 struct student st BYTE a; // 等价于 unsigned char a sizeof(BYTE); } ```

**可维护性**:例如,你代码里面有很多 `short a1;` 你可以这样: ```c typedef short SHORT; SHORT a1; SHORT a2; ```

如果很多版本后,你想把那些变量改成 long long 类型,只需要改1行 `def long long SHORT`

**跨平台**: ```c #ifdef UNICODE typedef wchar_t TCHAR // 不同平台用的 char 类型不一样 #else typedef char TCHAR #endif

// 下面是主体代码: #define UNICODE // 靠这个来定义不同平台上的变量定义 TCHAR a; ```

您的支持将鼓励我继续创作!


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK