朝花夕拾 · C语言 | 结构体篇

一.结构体的声明

struct 名称
{
        数据类型成员1;
        数据类型成员2;
        数据类型成员3;
}

struct date 
{
        int year,
        int mon,
        int day,
};
struct time

{
        int hour,
        int min,
        int sec,

};

struct datetime 
{
        struct date 
        {
                int year;
                int mon;
                int day;
        };
        struct time 
        {
                int hour;
                int min;
                int sec;
        };

};

注意:
1.声明结构体的时候,结构体后面的“;“不能省。
2.结构体成员之间用" ; "隔开。
3.struct 后面的“名称可以省略,省略后就是一个匿名结构体。

二.结构体变量定义

int  num;
结构体类型 变量名;

struct date mydate;
struct
{
        int year;
        int mon;
        int day;
}mydate;    //匿名结构体,由于没有定义变量名,所以为一次性

声明结构体类型的同时,定义了一个变量,变量的变量名mydate:

三.结构体初始化


全部初始化

struct date mydate = { 2026,8,6};
struct datetime 
{
        struct date mdate 
        struct time mtm;

};
struct datetime mydt = {{2026, 8, 6}, {14, 5, 23}};

部分初始化:

struct date mydate =
{
. year=2024,
. day = 6,
};

四.结构体数据成员的访问

  • 通过结构体变量访问结构体成员

结构变量变量名.成员1;
结构变量变量名.成员2;
结构变量变量名.成员3;
mydate.year:
mydate.mon;
mydate.day:

  • 通过结构体指针访问结构体成员

结构体指针->成员1;
结构体指针->成员2;
结构体指针->成员3;
结构变量变量名.成员1;表达式的结果取决成员的数据类型。

五.结构体的存储方式

原则:

1.结构体成员在内存中存储时,成员必须存储在自己字节大小的整数倍中;

2.结构体整体占的内存大小,是结构体中最大成员字节整数倍

注意:

1.在不同位系统下,相同结构体在内存中占的内存空间可能不一样

原因:64位系统long/指针类型占8字节;32位系统long/指针类型占4字节。

2.可以强制让结构体内存按指定字节数对齐

#pragma pack(1)    //强制结构体按1字节对齐
struct stu{
int a;
char b;
int c;
};
#pragma pack()    //取消结构体按1字节对齐

数组的对齐值等于其元素类型的对齐值,不等于整个数组的字节数。

六.结构体做为函数参数传递

  • 如果要在被调函数中修改主调函数中的结构体变量,则传递结构体变量的地址;
  • 如果在被调函数中使用主调函数中定义的结构体变量,可以值传递,也可以传地址。

七.结构体数组

struct  名称 数组名[元素个数];
struct  名称 数组名[元素个数]={{ },{ },{ }};

数组名 [下标];---》数据类型:“struct 名称” 类型

void print_stu_info(stu *p,int len)
{
    for(int i=0;i<len;i++)
	{    
            printf("%s %d %d\n",p[i].name,p[i].age,p[i].score);
            printf("%s %d %d\n",(p+i)->name,(p+i)->age,(p+i)->score);
            printf("%s %d %d\n",(*(p+i)).name,(*(p+i)).age,(*(p+i)).score);
    }
}

八.共用体/联合体

1.基本语法

声明共用体:

union  名称

{

        数据类型  成员1;

        数据类型  成员2;

        数据类型  成员3;        

};

定义共用体变量:

union  名称  变量名;

在使用共用体类型数据时要注意以下一些特点:

(1)同一个内存段可以用来存放几种不同类型的成员,但在每一瞬时只能存放其中一种,而不是同时存放几种。也就是说,每一瞬时只有一个成员起作用,其他的成员不起作用,即不是同时都存在和起作用。
(2)共用体变量中起作用的成员是最后一次存放的成员,在存入一个新的成员后原有的成员就失去作用。例如有以下赋值语句:

union
{int i;
char ch;
float f;
}a,b,c;

a.i=l;
a.c='a';
a.f=1.5;

在完成以上3个赋值运算以后,只有a.f是有效的,a.i和a.c已经无意义了。此用"printf("%d",a.i);"是不行的,而用“printf("%f",a.f);"是可以的,因为最后一次的赋值是向a.f赋值。因此在引用共用体变量时应十分注意当前存放在共用体变量中的究竟是哪个成员。
(3)共用体变量的地址和它的各成员的地址都是同一地址。例如,&a、&a.i、&a.c、&a.f都是同一地址值,其原因是显然的。
(4)不能对共用体变量名赋值,也不能企图引用变量名来得到一个值,又不能在定义共用体变量时对它初始化。例如,下面这些都是不对的:

union
{int i;
char ch;
float f;
}a=(1,'a',1.5); //不能初始化
a=l;            //不能对共用体变量赋值
m=a;            //不能引用共用体变量名以得到一个值

(5)不能把共用体变量作为函数参数,也不能使函数带回共用体变量,但可以使用指向共用体变量的指针(与结构体变量这种用法相仿)。

(6)共用体类型可以出现在结构体类型定义中,也可以定义共用体数组。反之,结构体也可以出现在共用体类型定义中,数组也可以作为共用体的成员。

2.内存大小端存储方式

小端:内存的高地址存储高字节位,内存的低地址存储低字节位;

大端:内存的高地址存储低字节位,内存的低地址存储高字节位;

//指针判断
int a=0x11223344;
char *p=(char*)&a;
printf("%d\n",*p);    //输出0x44,说明为小端

//共用体判断
union s
{
	int a;
	char b;
}p;

int main()
{
	p.a=1;
	printf("%d\n",p.b);    //输出1,说明为小端
}

九.枚举类型

  • 1.基本语法

枚举类型的声明:

enum 名称

{

        枚举常量1;//默认从0开始,枚举常量本质为int型数据

        枚举常量2;

        枚举常量3;

};

        

1.枚举里面的成员都是枚举常量,都是整型常量,所以不需要写数据类型。

2.枚举常量根据行业规范,一般写成大写方式,和普通变量区分;
3.枚举中的枚举常量,类似于整形符号常量,可以直接被引用;
4.枚举中的常量和常量之间用逗号隔开;
5.枚举中的枚举常量如果没有给定初始值,则默认从0开始;
如果给其中的某个枚举常量给定了初始值,则后面的常量以该值为基础自增1;

  • 2.枚举变量

enum 名称变量名;
枚举变量是枚举类型,但本质是一个整形,可以作为参数直接使用。
变量名=枚举常量;

// 定义一个名为 LogLevel 的枚举类型
typedef enum {
    LOG_ERROR = 1,    // 可以显式赋值,对应你作业里的 1
    LOG_WARNING = 2,
    LOG_INFO = 3
} LogLevel;
// 函数定义:接收一个 LogLevel 类型的参数
void write_log(LogLevel level, const char *message) {
    // 在函数内部,你可以直接拿 level 做判断
    if (level == LOG_ERROR) {
        // 处理错误...
    }
    // 或者用 switch 语句,这也是最推荐的方式
    switch (level) {
        case LOG_ERROR:
            printf("[ERROR] ");
            break;
        case LOG_WARNING:
            printf("[WARNING] ");
            break;
        case LOG_INFO:
            printf("[INFO] ");
            break;
        default:
            printf("[UNKNOWN] "); // 防御性编程,防止传入无效数字
            break;
    }
    printf("%s\n", message);
}

枚举类型作为参数,在C语言中本质上是按整数传递。它的用法非常直接,和传递 int 几乎一样。

在正式项目中,凡是涉及到“有限个固定选项”的参数(日志等级、传输协议类型、游戏难度等),都应该优先使用枚举来代替 #define 或 int。它不仅让函数签名更清晰,还能让你在调试时节省大量时间。

十.typedef

typedef struct stu{
char name[32];
int age;
int score;
}STU_S    //大写+_S表示struct
typedef unsigned int u32:
typedef unsigned char u8;
typedef unsigned short u16 ;
typedef unsigned long u64 ;
typedef signed int s32 ;
typedef struct
{
    int data;
    Node *pnext;  // ❌ typedef 的别名 Node 是在整个结构体定义结束之后才生效的。也就是说,当编译器解析到 Node *pnext; 这一行时,它还不知道 Node 是什么类型,所以报错。
}Node;


//方式一:给结构体加标签(最推荐)
typedef struct Node  // 加上标签 "Node"
{
    int data;
    struct Node *pnext;  // 使用 struct Node 而不是 Node
} Node;

//方式二:先声明后定义
typedef struct Node Node;  // 前向声明别名

struct Node                // 正式定义
{
    int data;
    Node *pnext;           // 现在 Node 已经声明过了,可以用了
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值