结构体是C语言中用于表示一组相关数据的自定义数据类型。结构体允许将多个不同类型的数据组组合在一起,以便更方便地管理和操作这些数据。
struct tag
{
member-list;
}variable-list;
以下代码,展示了如何定义一个简单的结构体以用来表示学生信息:
#include
// 定义一个结构体 Student
struct Student
{
char name[20];//名字
int age;//年龄
double score;//成绩
};
int main()
{
// 声明一个结构体变量并初始化
struct Student s1 = { "zzb", 20, 98};
// 访问结构体成员并打印信息
printf("学生姓名:%s\n", s1.name);
printf("学生年龄:%d\n", s1.age);
printf("学生成绩:%.2lf\n", s1.score);
return 0;
}
在C语言中,结构体变量访问其成员一共有如下两种方式:
结构体变量.成员名
例如,上面代码中有一个名为 s1 的结构体变量,并且结构体中有一个成员叫做 name,我们就可以通过以下方式访问该成员:
s1.name = "zzb";
结构体指针->成员名
例如,如果有一个名为 ps1 的指向结构体的指针,并且结构体中有一个成员叫做 age,我们就可以通过以下方式访问该成员:
ps1->age = 20;
在C语言中,我们可以创建匿名结构体,这是一种没有名字的结构体,结构体在声明的时候省略掉了结构体标签。该结构体通常用于简单的数据封装或临时数据组织,而不需要定义一个有具体名字的结构体类型。
其定义方式如下:
struct {
// 成员定义
} 结构体变量名;
结构体的自引用是指结构体中包含自身类型的成员。这种自引用的结构体通常用于创建复杂的数据结构,如树、链表和图等。在C语言中,要实现结构体的自引用,通常需要使用指针来解决。
以下代码,展示了如何创建一个简单的自引用结构体来表示链表节点:
#include
// 定义一个链表节点的结构体
struct ListNode
{
int data; // 节点数据
struct ListNode* next; // 指向下一个节点的指针
};
int main()
{
// 创建链表节点
struct ListNode node1, node2, node3;
node1.data = 1;
node2.data = 2;
node3.data = 3;
// 连接节点,形成链表
node1.next = &node2;
node2.next = &node3;
node3.next = NULL; // 链表结束
// 遍历链表并打印节点数据
struct ListNode *cur = &node1;
while (cur != NULL)
{
printf("%d ", cur->data);
cur = cur->next;
}
return 0;
}
在C语言中,结构体内存对齐是编译器为了提高内存访问效率而采取的一种优化策略。关于结构体内存对齐的详细介绍,这里可以参考之前写的文章:如何计算一个结构体的大小?(C语言)
这里就不再赘述了。
在C语言中,我们可以将结构体作为参数传递给函数。结构体传参的方式有两种:传值传递和传址传递(使用指针)。
在传值传递时,函数形参是实参的一份临时拷贝,这也就意味着在函数内部操作的是结构体的一份拷贝,不会影响实参结构体。这种方式适用于结构体较小且复制开销较小的情况。这是因为函数传参的时候,参数是需要压栈的。如果传递一个结构体对象的时候,结构体过大,参数压栈的的系统开销比较大,所以会导致性能的下降。
以下代码,展示了结构体传值传递的方式
#include
// 定义一个结构体
struct Point
{
int x;
int y;
};
// 以传值传递的方式
void modify_point(struct Point p)
{
p.x = 10;
p.y = 20;
}
int main()
{
struct Point p = {5, 5};
modify_point(p);
printf("x: %d, y: %d\n", p.x, p.y); // 输出仍为原始值
return 0;
}
在传址传递时,函数接收的是结构体的指针,可以通过指针修改实参结构体的内容。这种方式适用于当结构体较大且复制开销较大的情况。
#include
// 定义一个结构体
struct Point
{
int x;
int y;
};
// 以传址传递的方式
void modify_point(struct Point *p)
{
p->x = 10;
p->y = 20;
}
int main()
{
struct Point p = {5, 5};
modify_point(&p);
printf("x: %d, y: %d\n", p.x, p.y); // 输出已修改后的值
return 0;
}
位段是一种C语言的结构体成员,它可以在结构体定义时,指定某个成员变量所占用的二进制位数,这是因为有些数据在存储时并不需要占用一个完整的字节,而是需要占用一个或几个二进制位即可。
位段的主要目的是节省内存。
其声明语法如下:
struct
{
type member_name : width;
};
以下是一个位段的示例:
#include
// 定义一个包含位段的结构体
struct A
{
int _a:2;//2个比特位
int _b:5;//5个比特位
int _c:6;//10个比特位
int _d:8;//30个比特位
};
int main()
{
struct A a;
// 设置位段的值
a._a = 1;
a._b = 0;
a._c = 5;
a._d = 8;
printf("_a: %d\n", a._a);
printf("_b: %d\n", a._b);
printf("_c: %d\n", a._c);
printf("_d: %d\n", a._d);
return 0;
}
位段会按照其在结构体中的声明顺序依次分配内存。
我们通过下面的例子,探究以下位段在vs2013这个平台如何开辟空间的?
#include
struct S
{
char a:3;
char b:4;
char c:5;
char d:4;
};
int main()
{
struct S s = {0};
s.a = 10;
s.b = 12;
s.c = 3;
s.d = 4;
}
这里先假设在一个字节内部先使用低比特位且当一个字节剩余的bit位不足以存下下一个变量时,舍弃剩余的bit位,为下一个变量重新申请一个字节。
我们打开vs,进行调试,看下内存里面是如何存储的。
在vs下,具体的内存布局和我们假设的一致。
枚举是C语言中的一种自定义数据类型,用于定义一组命名的整数常量,这些常量通常用于表示一组相关的离散值。
其基本语法如下:
enum enum_name
{
a,
b,
c,
// ...
};
以下代码展示了枚举的使用:
#include
// 定义一个枚举类型
enum Color
{
RED, // 0
GREEN, // 1
BLUE // 2
};
int main() {
// 声明一个枚举类型的变量
enum Color col;
// 给枚举变量赋值
col = RED;
// 使用枚举常量
if (col == RED)
{
printf("The color is red.\n");
}
else if (col == GREEN)
{
printf("The color is green.\n");
}
else if (col == BLUE)
{
printf("The color is blue.\n");
}
return 0;
}
枚举常量在整个程序中具有全局作用域,可以用作整数值的符号名称,以提高代码的可读性。
联合也是C语言中的一种自定义的数据类型,与结构体类似,但与结构体不同的是:
下面代码的运行结果说明了其特性:
#include
//联合类型的声明
union Un
{
char c;
int i;
};
int main()
{
//联合变量的定义
union Un un;
// 下面输出的结果是一样的吗?
printf("&(un.i) = %d\n", &(un.i));
printf("&(un.c) = %d\n", &(un.c));
//下面输出的结果是什么?
un.i = 0x11223344;
un.c = 0x55;
printf("%x\n", un.i);
return 0;
}
分析如下:
联合的基本语法如下:
union union_name
{
// 成员定义
};
其大小的计算规则遵循以下两个规则:
思考一下,下面代码的运行结果是什么?
#include
union Un1
{
char c[5];
int i;
};
union Un2
{
short c[7];
int i;
};
int main()
{
//下面输出的结果是什么?
printf("%d\n", sizeof(union Un1));
printf("%d\n", sizeof(union Un2));
]
分析如下:
Un2的分析和上述分析一致,Un2的大为16。
运行结果如下:
这里需要注意的是,不同编译器和平台对于联合的对齐方式可能会有所不同,这可能会影响联合的总大小。但无论如何,联合的大小都不会小于最大成员的大小。
至此,本片文章就结束了,若本篇内容对您有所帮助,请三连点赞,关注,收藏支持下。
创作不易,白嫖不好,各位的支持和认可,就是我创作的最大动力,我们下篇文章见!
如果本篇博客有任何错误,请批评指教,不胜感激 !