关于C结构体的问题
C语言中结构体变量之间可以直接进行赋值么
简单结构体的赋值
先说结论:一般来说,C语言中的结构体变量可以用另一个变量对其进行赋值或初始化。简单结构体(不包含指针成员)直接赋值没有问题。
我们先下面一段代码:
#include <stdio.h> #include <stdlib.h> struct MyStruct { int a; int b; char c[10]; }; int main() { struct MyStruct t1 = {1, 2, "hello"}; struct MyStruct t2 = {3, 4, "world"}; t2 = t1; //将t1赋值给t2 printf("MyStruct t1: %d, %d, %s\n", t1.a, t1.b, t1.c); printf("MyStruct t2: %d, %d, %s\n", t2.a, t2.b, t2.c); return 0; }
以上代码的输出为:
MyStruct t1: 1, 2, hello MyStruct t2: 1, 2, hello
以上用t1给t2进行初始化,结果也相同。可以看到简单的结构体(结构体的成员没有指针成员)变量之间直接赋值是没有问题的。
有指针成员的结构体赋值
而通常情况下,稍微复杂一点的结构体里面会有指针成员,那么以上的浅拷贝则会有问题了,我们假设MyStruct里面的成员c不是数组,而是字符指针,会有什么问题呢? 看如下代码:
#include <stdio.h> #include <stdlib.h> #include <string.h> struct MyStruct { int a; int b; char* c; }; int main() { struct MyStruct t1; t1.a = 1; t1.b = 2; // 为指针区域赋值 char *p = (char*)malloc(10*sizeof(char)); strcpy(p, "hello"); t1.c = p; struct MyStruct t2; t2 = t1; printf("MyStruct t1: %d, %d, %s\n", t1.a, t1.b, t1.c); // 释放了t1的内存 // free(p); printf("MyStruct t2: %d, %d, %s\n", t2.a, t2.b, t2.c); printf("t1 pointer addr: %p\n", t1.c); printf("t2 pointer addr: %p\n", t2.c); return 0; }
上面的输出结果为:
MyStruct t1: 1, 2, hello
MyStruct t2: 1, 2, hello
t1 pointer addr: 0x1829010
t2 pointer addr: 0x1829010
可以看到,赋值会直接将t1的指针变量赋值给t2.c,如果我们在赋值之后将t1所用的资源释放掉,那么使用t2的话则可能导致内存泄漏了。如果上面的代码,我们没有注释掉 free(p);,那么输出t2时结果这不确定了:
MyStruct t1: 1, 2, hello
MyStruct t2: 1, 2,
t1 pointer addr: 0x71e010
t2 pointer addr: 0x71e010
所以,如果struct中有指针成员,那么结构体赋值不能简单的直接复制了,而需要为指针成员另外分配内存,并将数据拷贝过去,当然我们可以将这些处理封装在单独的函数中来完成。 示例代码如下:
#include <stdio.h> #include <stdlib.h> #include <string.h> struct MyStruct { int a; int b; char* c; }; int main() { struct MyStruct t1; t1.a = 1; t1.b = 2; // 为指针区域赋值 char *p = (char*)malloc(10*sizeof(char)); strcpy(p, "hello"); t1.c = p; struct MyStruct t2; // 各个成员分别赋值,可以进行封装 t2.a = t1.a; t2.b = t1.b; char *p2 = (char*)malloc(10*sizeof(char)); strcpy(p2, t1.c); t2.c = p2; printf("MyStruct t1: %d, %d, %s\n", t1.a, t1.b, t1.c); // 释放了t1的内存 free(p); printf("MyStruct t2: %d, %d, %s\n", t2.a, t2.b, t2.c); // 释放了t2的内存 free(p2); printf("t1 pointer addr: %p\n", t1.c); printf("t2 pointer addr: %p\n", t2.c); return 0; }
以上代码输出结果为:
MyStruct t1: 1, 2, hello
MyStruct t2: 1, 2, hello
t1 pointer addr: 0x1046010
t2 pointer addr: 0x1046030
另外关于typedef在结构体内的用法
C 语言提供了 typedef 关键字,您可以使用它来为类型取一个新的名字。下面的实例为单字节数字定义了一个术语 BYTE:
typedef unsigned char BYTE;
在这个类型定义之后,标识符 BYTE 可作为类型 unsigned char 的缩写,例如:
BYTE b1, b2;
举例如下
#include<stdio.h> typedef struct Student { int age; char *code; int height; char *name; char *sex; }S2; int main() { S2 aa = {12,"sbc0001",145,"atp","male"}; printf("aa.age = %d\n", aa.age); }
结果
aa.age = 12
另一种实现方式
#include<stdio.h> struct Student { int age; char *code; int height; char *name; char *sex; }; typedef struct Student S1; int main() { S1 s = {12,"sbc0001",145,"atp","male"};; s.age = 13; printf("s.age =%d\n", s.age); }
sizeof结构体大小的问题
#include<stdio.h> typedef struct A { char a1; short int a2; int a3; double d; }A; typedef struct B { long int b2; short int b1; A a; }B; int main() { A a; printf("sizeof = %ld\n", sizeof(a)); B b; printf("sizeof = %ld\n", sizeof(b)); }
输出结果
sizeof = 16 sizeof = 32
结构体计算要遵循字节对齐原则。
结构体默认的字节对齐一般满足三个准则:
- 1) 结构体变量的首地址能够被其最宽基本类型成员的大小所整除;
- 2) 结构体每个成员相对于结构体首地址的偏移量(offset)都是成员大小的整数倍,如有需要编译器会在成员之间加上填充字节(internal adding);
- 3) 结构体的总大小为结构体最宽基本类型成员大小的整数倍,如有需要编译器会在最末一个成员之后加上填充字节(trailing padding)