C语言之结构体的总结

目录

一、什么是结构

二、结构的定义

三、结构体变量的定义和初始化

四、结构的自引用

五、结构变量的使用

1.结构变量成员的引用

2.结构变量的整体赋值

3.结构变量作为函数参数

六、结构指针

七、结构数组

八、结构体内存对齐

1.计算结构体大小

2.为什么内存的对齐

 九、修改默认对齐数


一、什么是结构

C语言c语言的内置类型:char,short,int,long,float,double;自定义类型:结构体,枚举,联合体

结构类型是一种允许程序员把一些数据分量聚合成一个整体的数据类型。一个结构中包含的每个数据分量都有名字,这些数据分量称为结构成员或者结构分量结构的成员可以是标量、数组、指针,甚至是其他结构体。结构与数组的区别在于:数组中所有元素的数据类型必须是相同的,而结构中各成员的数据类型可以不同。

二、结构的定义

struct tag
{
     member_list; 
};

struct是定义结构类型的关键字,在struct之后,自行命名一个结构名(一个合法C标识符),大括号内的内容是结构所包括的结构成员(又叫结构分量)。结构的声明以分号结束,这是因为C语言把结构的定义看成一条语句。

三、结构体变量的定义和初始化

结构体变量的定义:(3种方法)

#include <stdio.h>
struct Book
{
    char name[20];
    int price;
}b1,b2;//全局变量
       //结构体变量的混合定义
struct Book b3;//全局变量
int main()
{
    struct Book b4;//结构体变量的单独定义
                   //局部变量
    return 0;
}
//匿名结构体类型
struct
{
    int a;
    char b;
    float c; 
}x;//无类型名定义
struct
{
    int a;
    char b;
    float c; 
}*p;
int  main()
{
    struct s;//error,无类型名定义只能用一次
    *p = &x;//ok?

    return 0;
}

 *p = &x这条代码是否合法?非法。上面的两个匿名结构体,虽然成员变量相同,但是两个结构体类型不相同。

结构体变量的初始化:

struct Point
{
    int x;
    int y;
}p1 = { 5,6 }, p2 = { 7,8 };

struct Point p3 = { 1,2 };

struct S
{
    double d;
    struct Point p;//结构体的嵌套定义
    char name[20];
    int date[20];
};
int main()
{
    struct Point p4 = { 3,4 };
    struct S s = { 3.14,{1,5},"zhangsan" ,{1,2,3} };//结构体的嵌套初始化
    
    return 0;
}

注意:在定义嵌套的结构类型时,必须先定义成员的结构类型。在定义主机结构类型。

四、结构的自引用

结构的自引用:在结构中包含一个类型为该结构本身的成员
struct Node
{
    int data;
    struct Node n;
};//error,无限递归查找下去,无法在开辟空间,太大了

struct Node
{
    int data;//数据域
    struct Node* next;//指针域    结构的递归定义
};

 那么,这样写代码行吗?

typedef struct
{
    int data;
    Node* next; 
}Node;

当然不行,我们再对结构体重命名之前,结构体必须是可用的,但是我们看结构体中Node* next; ,它的Node是哪里来的?那个时候还没有重命名,所以Node是一个未定义的变量。我们可以这样解决:

typedef struct Node
{
 int data;
 struct Node* next; 
}Node;

五、结构变量的使用

1.结构变量成员的引用

在C语言中,使用结构成员操作符“.”来引用结构成员,格式为结构变量名.结构成员名

#include <stdio,h>
struct point
{
    double x;
    double y;
};
struct student
{
    int num;
    char name[20];
    struct point p;
};
int main()
{
    struct point p1 = {3.18 4.51};
    struct student s1 = {18, "John"};
 
    printf("%d %s",s1.num,s1.name);
    printf("%.2f %.2f",s1.p.x,s1.p.y);
 
    return 0;
}

对嵌套结构成员的访问,每个成员按从左到右,从外到内的方式引用。

2.结构变量的整体赋值

如果两个结构变量具有相同的类型,则允许将一个结构变量的值直接赋给另一个结构变量

3.结构变量作为函数参数

#include <stdio.h>
struct S 
{
 int data[1000];
 int num;
};
struct S s = {{1,2,3,4}, 1000};
void print1(struct S s) 
{
 printf("%d\n", s.num);
}
void print2(struct S* ps) 
{
 printf("%d\n", ps->num);
}
int main()
{
    print1(s);
    print2(&s);

    return 0;
}

print1和print2那个函数更好?

print2。函数传参的时候,参数是需要压栈的。如果传递一个结构体对象的时候,结构体过大,参数压栈的的系统开销比较大,所以会导致性能的下降。

结构变量作为函数参数的特点是,可以传递多个数据且参数形式较简单。但是,对于成员较多的大型结构,参数传递时所进行的结构数据复制使得效率较低。所以我们也可以把结构变量的地址传过去

六、结构指针

结构指针就是指向结构类型变量的指针,如:

#include <stdio.h>
struct student
{
    int num;
    char name[10];
    int computer,math,english;
}
int main()
{
    struct student s1 = {101, "zhu", 96, 92, 95};
    struct student *p;

    p = &s1;

    return 0;
}

 

 结构成员的访问:

(1)用*p访问:

如:(*p).num = 101;注意:(*p)中的括号必不可少,因为“.”的优先级高于“*”

(2)用指向运算符->访问:

p->num = 101;它与(*p).num = 101;等价

结构指针的值是结构变量的首地址,即第一个成员的地址。

七、结构数组

结构数组是结构与数组的结合体,与普通数组的不同之处在于每个数组元素都是一个结构类型的数据。

结构数组的定义:如:struct student students[50];//定义了一个结构数组studengts,它有50个元素,从students[0]到students[49],每个数组元素都是结构类型struct student.

结构数组的初始化:如:struct student students[50] = {{101, "zhu", 96, 92, 95},{102,“zhang”,83 86,93}};其存储形式如图:

 对结构数组元素成员的引用是通过使用数组下标与结构成员操作符“.”相结合的方式来实现,其一般格式为:

结构数组名[下标].结构成员名,如:studengts[i].num

八、结构体内存对齐

1.计算结构体大小

(1)第一个成员在与结构体变量偏移量为0的地址处。
(2)其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。 对齐数 = 编译器默认的一个对齐数与该成员大小的较小值
VS中默认的值为8
(3)结构体总大小为最大对齐数(每个成员变量都有一个对齐数)的整数倍。
(4)如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。
举个例子(VS编译器):
#include <stdio.h>
struct S1
{
    char c1;//1  8  的较小值为1,所以对齐数为1
    int a;//4  8的较小值为4,所以对齐数为4   a放在4的倍数处 
    char c2;//1  8  的较小值为1,所以对齐数为1   c2 放在1的倍数处
};
struct S2
{
    double d;//8   
    char c;//1
    int i;//4
};

struct S3
{
    char c1;
    struct S2 s2;//  S2中最大的对齐数为8
    double d;
};
int main()
{
    struct S1 s = { 'x',100,'y' };
 
    printf("%d\n",sizeof(struct S1));//等价于printf("%d\n", sizeof(s));  12
    printf("%d\n", sizeof(struct S2));//16
    printf("%d\n", sizeof(struct S3));//32

    return 0;
}

分析如下:

 如图:S1中一共用了9个空间,S1中最大对齐数为4,9不是4的倍数,下一个4的倍数为12,所以这个结构体的总大小为12个字节。

S2中一共用了15个空间,S2中最大对齐数为4,15不是4的倍数,下一个4的倍数为16,所以这个结构体的总大小为16个字节。

S3中一共用了31个空间,S3中最大对齐数为8,31不是8的倍数,下一个8的倍数为32,所以这个结构体的总大小为32个字节。

2.为什么内存的对齐

(1)平台原因 ( 移植原因 ) :不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能在某些地址处取某些特定类型的数据,否则抛出硬件异常。
(2) 性能原因 :数据结构( 尤其是栈 ) 应该尽可能地在自然边界上对齐。原因在于,为了访问未对齐的内存,处理器需要作两次内存访问;而对齐的内存访问仅需要一次访问。
总体来说:结构体的内存对齐是拿空间来换取时间的做法。
在设计结构体的时候,我们既要满足对齐,又要节省空间,可以让占用空间小的成员尽量集中在一起
如:
struct S1
{
    char c1;
    int i;
    char c2;
};//12
struct S2
{
   char c1;
   char c2;
   int i;
};//8

 

 九、修改默认对齐数

1.#pragma这个预处理指令可以用来修改默认对齐数

#include <stdio.h>
#pragma pack(1)//设置默认对齐数为1
struct S1
{
    char c1;
    int i;
    char c2;
};
//#pragma pack()//取消设置默认对齐数
int main()
{
    struct S1 s1 = { 0 };
    printf("%d\n", sizeof(struct S1));//6 
    return 0;
}

2.offsetof 宏可以计算结构体中某变量相对于首地址的偏移

size_t offsetof( structName, memberName );引用头文件<stddef.h>

#include <stdio.h>
#include <stddef.h>
struct S1
{
    char c1;
    int i;
    char c2;
};
int main()
{
    printf("%u\n",offsetof(struct S1,c1));//0
    printf("%u\n", offsetof(struct S1, i));//4
    printf("%u\n", offsetof(struct S1, c2));//8

    return 0;
}

<think>首先,用户的问题是:“我想了解C语言结构体的用法 请问C语言 结构体 定义 使用方法”。这是一个关于C语言结构体的查询,用户想知道如何定义和使用结构体。 在系统级指令中,我需要遵循几个规则: - 所有行内数学表达式必须使用$...$格式。 - 独立公式必须使用$$...$$格式并单独成段。 - 使用中文回答。 - 生成相关问题。 - 在回答中引用的段落末尾自然地添加引用标识,如[^1]。 - 回答结构清晰,帮助用户逐步解决问题,保证真实可靠。 参考的引用内容: - 引用[1]:讨论了结构体的定义方法,包括先定义类型再定义变量、混合定义等。 - 引用[2]:解释结构体是一种数据结构,是聚合数据类型,可以被声明为变量、指针或数组。 - 引用[3]:提供了结构体定义的基本方法,如先定义类型再定义变量。 这些引用是相关的,我应该基于这些信息构建回答,但确保回答真实可靠。 回答结构: - 由于用户的问题是关于结构体的定义和使用方法,我应该分步解释。 - 首先,介绍结构体的概念。 - 然后,解释如何定义结构体。 - 接着,说明如何使用结构体(如声明变量、访问成员)。 - 最后,生成相关问题。 关键点: - 定义结构体:使用`struct`关键字。 - 例子:如`struct student { int age; char name[20]; };` - 使用方法:声明变量、访问成员(如`s1.age = 20;`)。 数学表达式:用户的问题不涉及数学,但系统指令要求所有行内数学表达式使用$...$。在C语言结构体中,可能没有太多数学内容,但如果有变量或表达式,我需要用$...$。例如,如果提到数组索引,我可以写成$arr[i]$。 生成相关问题:在回答结束时,添加相关问题部分,如§§相关问题§§。 使用中文回答:整个回答必须用中文。 引用标识:在引用的段落末尾添加[^1]等。但引用是来自参考内容,我需要自然地融入。例如,当解释定义方法时,可以引用[^1]。 逐步构建回答: 1. **介绍结构体**:解释结构体是什么,基于引用[2]。 2. **定义方法**:详细说明几种定义方式,基于引用[1]和[3]。 - 先定义类型再定义变量。 - 混合定义(定义类型的同时定义变量)。 - 匿名结构体(可能)。 3. **使用方法**:如何声明变量、初始化、访问成员。 4. **例子**:提供代码示例。 5. **总结**:简要总结。 6. **相关问题**:生成相关问题。 相关问题示例: 1. 结构体在内存中是如何布局的? 2. 如何用结构体实现链表? 3. 结构体与联合体有什么区别? 确保回答清晰可靠:使用标准C语言知识,避免错误。 最终输出格式: - 回答主体。 - 相关问题部分。</think>### C语言结构体的定义与使用方法 在C语言中,结构体(struct)是一种用户自定义的聚合数据类型,用于组合多个不同类型的数据成员(如整数、字符、数组等)成为一个整体。它常用于表示复杂的数据结构,例如学生信息、坐标点等。结构体可以被声明为变量、指针或数组,便于管理和操作数据[^2]。下面我将逐步解释结构体的定义和使用方法,确保内容清晰可靠。 #### 1. **结构体的定义方法** 结构体的定义以关键字`struct`开头,后跟结构体名(可选)和花括号内的成员列表。定义后,可以创建该结构体的变量。常见定义方法有三种: - **方法一:先定义结构体类型,再定义变量(最标准方式)** 先声明结构体类型,然后单独定义变量。语法如下: ```c struct 结构体名 { 数据类型 成员1; 数据类型 成员2; // 更多成员... }; struct 结构体名 变量名1, 变量名2; // 定义变量 ``` **例子**:定义一个学生结构体,并创建变量。 ```c struct student { char name[20]; // 姓名 int age; // 年龄 float score; // 成绩 }; struct student s1, s2; // s1和s2是student类型的变量 ``` 此方法灵活,结构体名可复用[^1][^3]。 - **方法二:混合定义(定义类型的同时定义变量)** 在定义结构体类型时直接声明变量,省略了后续的`struct`关键字。语法: ```c struct 结构体名 { 数据类型 成员1; 数据类型 成员2; } 变量名1, 变量名2; // 直接定义变量 ``` **例子**: ```c struct point { int x; // x坐标 int y; // y坐标 } p1, p2; // p1和p2是point类型的变量 ``` 此方法简化代码,适用于一次性定义[^1]。 - **方法三:匿名结构体(省略结构体名)** 如果结构体只用于一次定义,可以省略结构体名(可选标志符)。语法: ```c struct { 数据类型 成员1; 数据类型 成员2; } 变量名1, 变量名2; // 直接定义变量 ``` **例子**: ```c struct { char id[10]; // ID号 int value; // 值 } item1, item2; // item1和item2是匿名结构体变量 ``` 此方法节省代码,但无法复用类型[^3]。 #### 2. **结构体的使用方法** 定义结构体变量后,可以通过点运算符`.`访问成员,或使用指针和箭头运算符`->`。关键操作包括声明、初始化、赋值和成员访问。 - **声明和初始化变量** 可以在定义时初始化,或后续赋值。 **例子**:初始化学生结构体。 ```c // 定义时初始化 struct student s1 = {"Alice", 20, 90.5}; // 后续赋值 struct student s2; strcpy(s2.name, "Bob"); // 字符串赋值需用strcpy s2.age = 22; s2.score = 85.0; ``` - **访问成员** 使用`.`运算符直接访问成员变量。 **例子**: ```c printf("姓名: %s, 年龄: %d\n", s1.name, s1.age); // 输出:姓名: Alice, 年龄: 20 s1.age = 21; // 修改成员值 ``` - **使用结构体指针** 结构体指针可通过`->`运算符访问成员。 **例子**: ```c struct student *ptr = &s1; // 指向s1的指针 printf("成绩: %.1f\n", ptr->score); // 输出:成绩: 90.5 ptr->score = 95.0; // 修改成员值 ``` - **结构体数组** 结构体可以用于定义数组,表示多个实体。 **例子**: ```c struct student class[3] = { {"Alice", 20, 90.5}, {"Bob", 22, 85.0}, {"Charlie", 21, 88.0} }; printf("第二名学生年龄: %d\n", class[1].age); // 输出:22 ``` #### 3. **注意事项** - **内存对齐**:结构体成员在内存中可能对齐(padding),影响大小。使用`sizeof(struct student)`可获取大小。 - **类型复用**:结构体名是可选的,但省略后无法在其他地方复用类型(如方法三)。 - **字符串处理**:字符数组成员需用`strcpy`赋值,避免直接赋值错误。 - **效率**:结构体适合组织相关数据,但频繁复制大结构体可能影响性能;建议用指针传递。 结构体是C语言中实现复杂数据的基础,广泛应用于文件处理、链表、树等数据结构中。掌握其定义和使用,能提升代码可读性和模块性[^2][^3]。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值