C语言之结构体的总结

C语言之结构体的总结,第1张

C语言之结构体的总结

目录

一、什么是结构

二、结构的定义

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

四、结构的自引用

五、结构变量的使用

1.结构变量成员的引用

2.结构变量的整体赋值

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

六、结构指针

七、结构数组

八、结构体内存对齐

1.计算结构体大小

2.为什么内存的对齐

 九、修改默认对齐数


一、什么是结构

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

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

二、结构的定义
struct tag
{
     member_list; 
};

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

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

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

#include 
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 
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 
struct S 
{
 int data[1000];
 int num;
};
struct S s = {{1,2,3,4}, 1000};
void print1(struct S s) 
{
 printf("%dn", s.num);
}
void print2(struct S* ps) 
{
 printf("%dn", ps->num);
}
int main()
{
    print1(s);
    print2(&s);

    return 0;
}

print1和print2那个函数更好?

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

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

六、结构指针

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

#include 
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 
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("%dn",sizeof(struct S1));//等价于printf("%dn", sizeof(s));  12
    printf("%dn", sizeof(struct S2));//16
    printf("%dn", 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 
#pragma pack(1)//设置默认对齐数为1
struct S1
{
    char c1;
    int i;
    char c2;
};
//#pragma pack()//取消设置默认对齐数
int main()
{
    struct S1 s1 = { 0 };
    printf("%dn", sizeof(struct S1));//6 
    return 0;
}

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

size_t offsetof( structName, memberName );引用头文件

#include 
#include 
struct S1
{
    char c1;
    int i;
    char c2;
};
int main()
{
    printf("%un",offsetof(struct S1,c1));//0
    printf("%un", offsetof(struct S1, i));//4
    printf("%un", offsetof(struct S1, c2));//8

    return 0;
}

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/zaji/5711121.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-12-17
下一篇 2022-12-17

发表评论

登录后才能评论

评论列表(0条)

保存