(四)结构体、共用体、枚举、字节对齐

本文详细介绍了C语言中的结构体、共用体和枚举类型等复合数据类型的定义、使用方法及注意事项,并探讨了地址对齐的概念。
一、结构体
1、概念:
结构体是复合的数据类型,是不同类型 数据的集合
在C语言中没有具体的结构体类型,但是C语言给出了定义结构体类型的语法格式,所以结构体也成为自定义类型
2、结构体类型定义的语法格式
struct  <结构体标签>{
成员类型  成员名;
成员类型  成员名;
....    ....
成员类型  成员名;
};
例如:定义一个学生的类型
struct student{
int sno;
char name[20];
int age;
float score;
};
3、结构体变量的定义
1》常规定义:
struct student{
int sno;
char name[20];
int age;
float score;
};
struct student  st1;  //st1为结构体变量
2》与类型同时定义
struct student{
int sno;
char name[20];
int age;
float score;
}st2; /st2为结构体变量
3》直接定义
struct {
int sno;
char name[20];
int age;
float score;
}st3; /st3为结构体变量
4、结构体变量的初始化
1》完全初始化

按顺序给结构体中的每个成员赋值,例如

struct student  st1 = {1001,"张飞",25,98.1}; 
2》部分初始化

按顺序给结构体中的前若干个成员赋值,例如:

struct student  st2 = {1001,"张飞"};  	//st2为结构体变量
3》指定成员初始化
不按顺序给结构体中某一些成员赋值,例如:

struct student  st3 = {.name="张飞",25,.sno = 1002};  	//st2为结构体变量
5、结构体变量的使用:
1》赋值:
除了初始化之外,不能像 .name="张飞",25,.sno = 1002 这样赋值,例如
struct student st;
st = {100,"abc",22,95.7};    //错误
但是:同类型的结构体变量之间可以相互赋值,例如:
struct student st;
//st = {1001,"张飞",25,98.1};   //错误的
st = st1;   //正确的
另外,也可以分别给每一个成员赋值,例如:
st.sno = 1001;
strcpy(st.name,"赵云");
st.age = 26;
st.score = 95.7;

2》打印:
不能整体打印,必须分别打印每一个成员,例如:
printf("st1:%d,%s,%d,%f\n",st1.sno,st1.name,st1.age,st1.score);
printf("st2:%d,%s,%d,%f\n",st2.sno,st2.name,st2.age,st2.score);
printf("st3:%d,%s,%d,%f\n",st3.sno,st3.name,st3.age,st3.score);
printf("st:%d,%s,%d,%f\n",st.sno,st.name,st.age,st.score);
3》传参:
1)值传递

void fun1(struct student st)
{
	printf("st:%d,%s,%d,%f\n",st.sno,st.name,st.age,st.score);
	st.sno = 1001;
	strcpy(st.name,"赵云");
	st.age = 26;
	st.score = 95.7;
}
2)地址传递

void fun2(struct student * pst)
{
	printf("in_fun2:%d,%s,%d,%f\n",(*pst).sno,(*pst).name,(*pst).age,(*pst).score);
	(*pst).sno = 1001;
	strcpy((*pst).name,"赵云");
	(*pst).age = 26;
	(*pst).score = 95.7;
}
int main(void)
{
	struct student  st1 = {1001,"张飞",25,98.1};  	//st2为结构体变量
	
#if 0
	fun1(st1);   //值传递
	printf("st1:%d,%s,%d,%f\n",st1.sno,st1.name,st1.age,st1.score);
#else
	fun2(&st1);  //地址传递
	printf("st1:%d,%s,%d,%f\n",st1.sno,st1.name,st1.age,st1.score);
#endif
	return 0;
}
6、结构体数组
元素为结构体类型的数组称为结构体数组,例如:
#include <stdio.h>
#include <string.h>

//结构体类型定义
struct student{
	int sno;
	char name[20];
	int age;
	float score;
};

int main(void)
{
	struct student st[50] = {{1001,"张三",25,99},{1002,"李四",22,95},{1003,"王五",26,97}};
	int i;

	for(i = 0; i < 3; i++)
		printf("%d,%s,%d,%f\n",st[i].sno,st[i].name,st[i].age,st[i].score);
	
	return 0;
}
7、结构体指针
指向结构体变量的指针称为结构体指针,例如:

#include <stdio.h>

//结构体类型定义
struct student{
	int sno;
	char name[20];
	int age;
	float score;
};

int main(void)
{
	struct student st = {1001,"张三",25,99};
	struct student *pst;

	pst = &st;
	printf("%d,%s,%d,%f\n",st.sno,st.name,st.age,st.score);
	printf("%d,%s,%d,%f\n",(*pst).sno,(*pst).name,(*pst).age,(*pst).score);
	printf("%d,%s,%d,%f\n",pst->sno,pst->name,pst->age,pst->score);
	
	return 0;
}
8、结构体成员类型:
1》基本类型:
struct A{
int a;
float b;
};
2》数组:
struct A{
int a[5];
float b;
};
3》结构体:
#include <stdio.h>

struct A{
	int a;
	float b;
};

#if 0
struct B{
	int x;
	struct A y;   //用已有的结构体类型定义成员变量
};
#else
struct B{
	int x;
	//定义一个新的结构体类型同时定义该结构体变量
	struct {
		int a;
		float b;
	}y;
};
#endif

int main(void)
{
	struct B st = {100,{123,34.56}};

	printf("%d,%d,%f\n",st.x,st.y.a,st.y.b);
	return 0;
}
4》指针:

#include <stdio.h>

struct A{
	int a;
	int *p;
};

int main(void)
{
	int x = 123;
	struct A st = {100,&x};
	printf("%d,%d\n",st.a,*st.p);
	return 0;
}
5》结构体指针:
例1:

#include <stdio.h>

struct A{
	int a;
	float b;
};

struct B{
	int x;
	struct A *pst;
};

int main(void)
{
	struct A sta = {123,3.5};
	struct B stb = {100,&sta};

	printf("%d\n",stb.x);
	printf("%d,%f\n",stb.pst->a,stb.pst->b);
	return 0;
}
例2:

#include <stdio.h>

typedef struct B{
	int x;
	struct B *pst;
}ST;

typedef struct node{
	int data;
	struct node *next;
}list,*plist;


int main(void)
{

	ST  st1 = {100,NULL};
	ST  st2 = {200,&st1};
	ST  st = {300,&st};
	list nd = {1000};
	plist pnd = &nd;

	printf("pnd->data = %d\n",pnd->data);
	printf("%d\n",st2.x);
	printf("%d\n",st2.pst->x);
	printf("%d,%d\n",st.x,st.pst->x);
	return 0;
}
二、共用体
也叫联合体,是一种复合数据类型,C语言中也没有该类型的具体定义,但是给出了定义该类型的语法格式
也是由不同类型数据组成,但是共用体中的多个数据共同使用同一块内存空间。
1、共用体类型的定义
union <共用体标签>{
成员类型  成员名;
成员类型  成员名;
....    ....
成员类型  成员名;
};
例如:定义一个共用体的类型
union student{
int sno;
char name[20];
int age;
float score;
};
2、共用体变量的定义
1》常规定义:
union student{
int sno;
char name[20];
int age;
float score;
};
union student  st1;  //st1为共用体变量
2》与类型同时定义
union student{
int sno;
char name[20];
int age;
float score;
}st2; /st2为共用体变量
3》直接定义
union {
int sno;
char name[20];
int age;
float score;
}st3; /st3为共用体变量


3、使用
1》赋值和打印与结构体类似,例如:

#include <stdio.h>
#include <string.h>

//共用体类型的定义
union A{
	short a;
	char b[3];
	char c[5];
};

int main(void)
{
	union A u1;
	printf("sizeof(union A) = %d\n",sizeof(union A));
	printf("sizeof(u1) = %d\n",sizeof(u1));
	printf("sizeof(u1.a) = %d\n",sizeof(u1.a));
	printf("sizeof(u1.b) = %d\n",sizeof(u1.b));
	printf("sizeof(u1.f) = %d\n",sizeof(u1.c));

#if 0
	/*************************************/
	u1.b = 12.34;
	strcpy(u1.c,"hello");
	u1.a = 123;
	printf("u1.a = %d\n",u1.a);
	printf("u1.b = %f\n",u1.b);
	printf("u1.c = %s\n",u1.c);
	/*************************************/
	strcpy(u1.c,"hello");
	u1.a = 123;
	u1.b = 12.34;
	printf("u1.a = %d\n",u1.a);
	printf("u1.b = %f\n",u1.b);
	printf("u1.c = %s\n",u1.c);
	/*************************************/
#endif
#if 0
	u1.a = 123;
	u1.b = 12.34;
	strcpy(u1.c,"hello");
	printf("u1.a = %d\n",u1.a);
	printf("u1.b = %f\n",u1.b);
	printf("u1.c = %s\n",u1.c);
#endif

	return 0;
}
总结:
1》共用体的成员共同使用同一块内存空间
2》在程序中,某一时刻只有一个成员的值有效,即最后被赋值的成员有效
实例:
判断机器的字节序?

#include <stdio.h>
#include <string.h>

union A{
	unsigned int word;
	unsigned char byte;
};

int main(void)
{
	union A u1;

	u1.word = 0x12345678;
	if(u1.byte == 0x12)
		printf("大端序!\n");
	else
		printf("小端序!\n");

	return 0;
}
三、枚举类型
该类型的本质是整型,为了提高程序额可读性,同时为了人为的程序中的一些数据规定一个取值的范围,而设计出这样的枚举类型,例如
在程序中表示一周的每一天,或者一年中的每个月都可以定义成枚举类型
1、枚举类型的定义
enum <枚举标签>{
枚举常量,
枚举常量,
.....
枚举常量
};
例如:
enum phone_color{
white, //枚举常量
black, //枚举常量
golden //枚举常量
};
默认情况下,枚举常量的值,从第一个开始依次是:0,1,2,3,....
enum phone_color   color;    //color为枚举变量,一般在程序中取枚举常量的值

#include <stdio.h>
#include <string.h>

enum phone_color{
	white,			//枚举常量
	black,			//枚举常量
	golden			//枚举常量
};

enum A{
	a1 = 100,
	a2,
	a3,
	a4 = 99,
	a5,
	a6	
};

struct phone{
	float value;
	enum phone_color color;
};

int main(void)
{
	printf("white : %d\n",white);
	printf("black: %d\n",black);
	printf("golden : %d\n",golden);
	
	printf("a1 = %d\n",a1);
	printf("a2 = %d\n",a2);
	printf("a3 = %d\n",a3);
	printf("a4 = %d\n",a4);
	printf("a5 = %d\n",a5);
	printf("a6 = %d\n",a6);
	
	struct phone  apple = {5000,white};
	return 0;
}
四、地址对齐
1、自然对齐
在数据分配空间时,如果该数据的起始地址能够被它的长度整除,则就是自然对齐
2、数据的M值
如果该数据的长度小于机器字长,则M值为该数据的长度
如果该数据的长度大于等于机器字长,则M值为机器字长。
3、适当对齐
在数据分配空间时,如果该数据的起始地址能够被它的M值整除,则就是适当对齐
4、不同类型数据的对齐
1》基本类型数据
按M值适当对齐
2》数组:
按元素的M值对齐
4》共用体:
按共用体变量的M值对齐,即:成员中M值最大的
5》结构体:
按结构体变量的M值对齐,即:成员中M值最大的

五、LinuxC语言中的一些特殊函数
1、递归函数
如果一个函数体内部,调用它自己,则该函数称为递归函数,例如:

#include <stdio.h>
#if 0
void fun(int x,int y)
{
	printf("%d\n",x+y);
}

void test(void)
{
	int a = 3,b = 5;
	fun(a,b);
}
#else
void test(int i)
{
	printf("i = %d\n",i++);
	if(i < 5)
		test(i);
}
#endif

int fun(int n)
{
	if(n == 1)
		return 1;
	else if(n > 1)
		return n*fun(n-1);
	else
		return 1;
}
int main(void)
{
	int n;
	scanf("%d",&n);
	printf("%d\n",fun(n));
	return 0;
}
2、变参函数
调用时,可以传不同类型的参数或者传不同个数的参数,这样的函数称为变参函数。
 int printf(const char *format, ...);
#include <stdio.h>
#include <stdarg.h>

void myprint(int n,...)
{
	va_list p;
	int i,t;

	va_start(p,n);  //对p初始化
	for(i = 0; i < n; i++){
		t = va_arg(p,int);
		printf("%d\t",t);
	}
	printf("\n");

	va_end(p);

}

int main(void)
{
	myprint(5,1,2,3,4,5);
	myprint(5,111,222,333,444,555);
	myprint(10,1,2,3,4,5,6,7,8,9,10);
	return 0;
}
3、回调函数
指的是函数调用的一种方式,简单来说:间接调用的函数称为回调函数,例如:


4、内联函数









<think> 我们被要求详细讲解结构体共用体枚举。根据用户提供的引用,我们可以总结如下: 1. 结构体(struct):将不同类型的数据组合成一个整体,每个成员拥有独立的内存空间。 2. 共用体(union):所有成员共享同一块内存空间,同一时间只能使用其中一个成员。 3. 枚举(enum):定义一组命名的整数常量,增加代码可读性。 下面我们按照这三个部分进行详细讲解。 ### 一、结构体(Struct) 结构体是一种用户自定义的数据类型,它允许将多个不同类型的变量组合在一起,形成一个新的数据类型。结构体的每个成员都拥有自己的内存空间。 #### 1.1 结构体的声明与定义 ```c // 声明结构体类型 struct Student { char name[20]; // 姓名 int age; // 年龄 float score; // 成绩 }; // 定义结构体变量 struct Student stu1; // 方式1:先声明类型再定义变量 // 定义结构体同时定义变量 struct Employee { char name[20]; int id; } emp1, emp2; // 方式2:同时定义变量 // 匿名结构体(不推荐,无法重用) struct { int x; int y; } point; ``` #### 1.2 结构体变量的初始化 ```c // 按顺序初始化 struct Student stu2 = {"Alice", 18, 90.5}; // 指定成员初始化(C99标准) struct Student stu3 = {.name="Bob", .age=19, .score=88.0}; ``` #### 1.3 结构体成员的访问 使用点操作符(.)访问成员: ```c strcpy(stu1.name, "Tom"); stu1.age = 20; stu1.score = 95.5; printf("Name: %s, Age: %d, Score: %.1f\n", stu1.name, stu1.age, stu1.score); ``` #### 1.4 结构体数组 ```c struct Student class[3] = { {"Alice", 18, 90.5}, {"Bob", 19, 88.0}, {"Cindy", 20, 92.5} }; for (int i=0; i<3; i++) { printf("Student %d: %s, %d, %.1f\n", i+1, class[i].name, class[i].age, class[i].score); } ``` #### 1.5 结构体指针 ```c struct Student *pStu = &stu1; // 通过指针访问成员:使用箭头操作符(->) pStu->age = 21; printf("Age via pointer: %d\n", pStu->age); ``` #### 1.6 结构体内存对齐 结构体的大小通常大于各成员大小之和,因为存在内存对齐。例如: ```c struct Example { char a; // 1字节 // 填充3字节(假设int为4字节,需要对齐到4的倍数) int b; // 4字节 char c; // 1字节 // 填充3字节(使结构体整体大小为4的倍数) }; // 在32位系统下,sizeof(struct Example) = 12(1+3+4+1+3=12)[^4] ``` ### 二、共用体(Union) 共用体联合体)是一种特殊的数据类型,它允许在相同的内存位置存储不同的数据类型。共用体中的所有成员共享同一块内存空间,因此同一时间只能有一个成员有效。 #### 2.1 共用体的声明与定义 ```c union Data { int i; float f; char str[20]; }; union Data data1; // 定义共用体变量 ``` #### 2.2 共用体的特点 - 所有成员共享同一块内存空间。 - 共用体的大小至少等于最大成员的大小(可能更大,因为内存对齐)。 - 修改一个成员会影响其他成员的值。 #### 2.3 共用体的使用 ```c data1.i = 10; printf("data1.i = %d\n", data1.i); data1.f = 220.5; printf("data1.f = %.2f\n", data1.f); // 此时data1.i的值被覆盖,变得无意义 strcpy(data1.str, "C Programming"); printf("data1.str = %s\n", data1.str); ``` #### 2.4 共用体的大小 共用体的大小由最大成员决定,同时考虑内存对齐: ```c union Un { char c[5]; // 5字节 int i; // 4字节 }; // 假设4字节对齐,则大小为8(5字节需要填充到4的倍数,即8)[^4] printf("Size of union Un: %d\n", sizeof(union Un)); // 输出8 ``` #### 2.5 共用体的应用场景 - 节省内存,特别是在嵌入式系统中。 - 同一内存区域需要存储不同类型的数据时(如协议解析)。 ### 三、枚举(Enum) 枚举是一种用户自定义的数据类型,用于定义一组命名的整数常量,使代码更易读。 #### 3.1 枚举的声明与定义 ```c enum Weekday { Monday, // 默认为0 Tuesday, // 1 Wednesday, // 2 Thursday, // 3 Friday, // 4 Saturday, // 5 Sunday // 6 }; enum Weekday today = Wednesday; // 定义枚举变量并赋值 ``` #### 3.2 指定枚举常量的值 ```c enum Color { RED = 1, // 指定值为1 GREEN = 2, // 指定值为2 BLUE = 4 // 指定值为4 }; enum Color c = GREEN; ``` #### 3.3 枚举的使用 ```c if (today == Wednesday) { printf("Today is Wednesday!\n"); } switch(c) { case RED: printf("Red\n"); break; case GREEN: printf("Green\n"); break; case BLUE: printf("Blue\n"); break; } ``` #### 3.4 枚举的特点 - 枚举常量是整型的,默认从0开始递增。 - 枚举类型的大小通常是4字节(取决于编译器)。 - 枚举可以提高代码的可读性和可维护性。 ### 、三者的对比 | 特性 | 结构体 (struct) | 共用体 (union) | 枚举 (enum) | |--------------|------------------------|--------------------------|-------------------| | **内存占用** | 各成员独立内存,总内存>=各成员之和 | 所有成员共享内存,大小为最大成员大小(考虑对齐) | 通常为4字节(整型) | | **存储方式** | 成员顺序存储,有填充 | 所有成员从同一地址开始 | 整型常量 | | **同时存储** | 可同时存储所有成员 | 同一时间只能存储一个成员 | 存储一个枚举值 | | **主要用途** | 组合不同类型数据 | 节省内存,多类型复用内存 | 定义一组命名常量 | ### 五、综合示例 ```c #include <stdio.h> #include <string.h> // 枚举:定义设备类型 enum DeviceType { MOUSE, KEYBOARD, MONITOR }; // 结构体:设备信息 struct Device { enum DeviceType type; // 设备类型 union { // 共用体:根据设备类型存储不同信息 int id; // 鼠标ID char model[20]; // 键盘型号 float size; // 显示器尺寸 } info; }; int main() { struct Device d1, d2; d1.type = MOUSE; d1.info.id = 1001; // 使用共用体中的id成员 d2.type = KEYBOARD; strcpy(d2.info.model, "Mechanical"); // 使用共用体中的model成员 printf("Device1: type=%d, id=%d\n", d1.type, d1.info.id); printf("Device2: type=%d, model=%s\n", d2.type, d2.info.model); return 0; } ``` §§ 相关问题 §§ 1. 结构体内存对齐的具体规则是什么? 2. 共用体在协议解析中的具体应用案例? 3. C语言中枚举与宏定义常量的区别? 4. 如何通过位域优化结构体内存占用? [^1]: 共用体看起来与结构体很相像,好像就只是关键字由struct变成了union。两者的区别为:结构体定义了一个由多个数据成员组成的特殊类型,而共用体定义了一块为所有数据成员共享的内存。 [^2]: C语言绝对值得一看的常识讲解:自定义数据类型——枚举结构体共用体篇。共用体类型是用户自定义数据类型,与基本类型具有相同的地位和作用。 [^3]: 共用体(union)。共用体也是一种用户自定义的复合数据类型,它与结构体类似,但所有成员都共享同一个内存空间。 [^4]: 结构体共用体枚举联合体的特点是所有成员共⽤同⼀块内存空间。所以联合体也叫:共⽤体。 [^5]: 结构体类型的初始化。 </think>### 结构体共用体枚举详解 #### 一、结构体(Struct) **定义**:将不同类型的数据组合成一个整体的复合数据类型[^5]。 **特点**: - 各成员拥有独立内存空间 - 总大小 = 各成员大小之和 + 内存对齐填充 - 支持嵌套定义(结构体包含结构体) ```c // 声明与初始化 struct Student { char name[20]; // 字符数组 int age; // 整型 float score; // 浮点型 }; // 初始化方式 struct Student s1 = {"Alice", 18, 90.5}; // 顺序初始化 struct Student s2 = {.age=19, .name="Bob"}; // 指定成员初始化(C99) ``` **内存布局**: ```mermaid graph LR A[结构体起始地址] --> B[name[20]] B --> C[age] C --> D[score] D --> E[填充字节] ``` **访问成员**: ```c printf("Name: %s, Age: %d", s1.name, s1.age); // 点运算符访问 struct Student *p = &s1; printf("Score: %.1f", p->score); // 箭头运算符访问指针 ``` #### 二、共用体(Union) **定义**:所有成员共享同一块内存空间的复合类型[^1][^3]。 **特点**: - 内存大小由最大成员决定 - 任一时刻只能使用一个成员 - 修改一个成员会覆盖其他成员 ```c union Data { int i; float f; char str[4]; }; union Data d; d.i = 65; printf("%c", d.str[0]); // 输出'A'(ASCII 65) d.f = 3.14; // 覆盖原int值 ``` **内存布局**: ```mermaid graph TD A[共用体内存地址] --> B[int i] A --> C[float f] A --> D[char str[4]] ``` **典型应用**: 1. 协议解析(同一内存区解析不同数据类型) 2. 节省内存空间(如嵌入式系统) 3. 类型转换技巧 **大小计算**: $$ \text{union size} = \max(\text{成员大小}) + \text{对齐填充} $$ ```c union Example { char arr[5]; // 5字节 int n; // 4字节 }; // sizeof(union Example) = 8(4字节对齐) ``` #### 三、枚举(Enum) **定义**:创建命名整数常量的用户自定义类型[^2]。 **特点**: - 增强代码可读性 - 自动分配整数值(默认从0开始) - 类型安全优于宏定义 ```c enum Color { RED, // 0 GREEN, // 1 BLUE = 5 }; enum Color c = GREEN; printf("%d", c); // 输出1 if(c == GREEN) { printf("绿色"); } ``` **使用规范**: 1. 常量名全大写(如 `MAX_SIZE`) 2. 显式赋值避免依赖隐式顺序 3. 配合switch语句提升可读性 #### 、三者的核心差异 | **特性** | 结构体(struct) | 共用体(union) | 枚举(enum) | |----------------|------------------------|------------------------|------------------| | **内存分配** | 成员独立内存 | 成员共享内存 | 不占用内存 | | **存储能力** | 同时存储所有成员 | 一次仅存储一个成员 | 存储单个整数值 | | **大小计算** | ∑成员大小+对齐填充 | max(成员大小)+对齐填充 | 固定为int大小 | | **主要用途** | 数据聚合 | 内存复用 | 状态码/选项集合 | | **赋值影响** | 不影响其他成员 | 覆盖其他成员 | 仅改变自身值 | #### 五、联合应用示例 ```c // 网络数据包解析 struct PacketHeader { enum { TEXT, IMAGE } type; // 枚举类型 union { // 共用体 struct { int len; char* txt; } text; struct { int w, h; unsigned char* pixels; } image; } content; }; struct PacketHeader pkt; pkt.type = TEXT; pkt.content.text.len = 256; // 仅text成员有效 ```
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值