【C语言】struct结构体

这篇具有很好参考价值的文章主要介绍了【C语言】struct结构体。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

一. 结构体简述

具有相同或不同类型元素的集合叫做结构体。定义一个结构体,本质是在制作一个类型:

// 声明一个学生信息结构体
struct Student
{
	char name[20];
	int age;
};

int main()
{
	// 定义出两个学生变量
	struct Student s1 = { "张三", 18};
	struct Student s2 = { "李四", 20};
	return 0;
}

二. 结构体的声明和定义

1、简单地声明一个结构体和定义结构体变量

在C中,结构体内只能存放各种类型的变量,不能存函数:
c语言struct,C语言,c语言

像上面这样就是声明了一个结构体struct Student,此时的 struct Student 相当于一个类型名。

然后我们可以用这个自己声明的结构体类型去定义变量:
c语言struct,C语言,c语言

补充:C 和 C++ 中定义结构体变量的区别

  • 在 C 中使用结构体去定义变量时,需要在结构体名称前加上 struct 关键字。
  • 在 C++ 中使用结构体去定义变量时,可以不加 struct 关键字

c语言struct,C语言,c语言

2、声明结构体的同时也定义结构体变量

c语言struct,C语言,c语言
也许初期看不习惯容易困惑,其实这就相当于两步合并一步:先定义结构体 struct Student,再定义变量 s1 和 s2:

3、匿名结构体

使用方式:声明结构体的时候缺失结构体名,同时定义出一个或n个结构体变量:
c语言struct,C语言,c语言

这种形式只能使用在声明结构体的同时也定义出结构体变量,由于没有结构体名,因此后续不可以再定义新的结构体变量。

4、配合typedef,声明结构体的同时为结构体取别名

c语言struct,C语言,c语言

前面说过,使用结构体去定义结构体变量时,C 需要加 struct,C++ 不需要。那么使用结构体的别名去定义变量呢?

答:使用结构体别名去定义结构体变量时,C 和 C++ 都不需要加 struct,加了反而都会报错,因为取别名时把struct连同结构体名称一起包含进去了。

5、在声明匿名结构体时,使用typedef给这个匿名结构体取别名

c语言struct,C语言,c语言

这种形式声明了一个缺失结构体名的结构体,但同时使用 typedef 为结构体设置了别名,所以之后我们可以使用这个别名,去定义结构体变量。

三. 结构体变量的初始化

先弄清楚变量初始化和赋值的区别:

struct Student
{
	char name[20];
	int age;
};

int main()
{
	// 变量刚开始创建时给值,这个叫初始化
	struct Student s1 = {"nick", 18};
	// 变量创建后,再对它的值进行操作这个叫赋值
	strcpy(s1.name, "tony");
	s1.age = 24;

	return 0;
}

结构体只能被整体初始化,不能被整体赋值,想要赋值的话只能把成员逐个地取出来再赋值。
c语言struct,C语言,c语言

补充:数组也是一样的道理:只能整体初始化,不能整体赋值。如果是字符数组想要整体赋值的话,可以使用 strcpy 函数:
c语言struct,C语言,c语言

本人推测结构体和数组不能被整体赋值的原因是:它们内部空间在逻辑上是独立一块块的,所以我们只能对这些独立的空间逐个赋值,而不能整体赋值。

四. 结构体成员的访问方法

我们可以通过变量或变量的地址去访问结构体的成员。

struct Student
{
	char name[20];
	int age;
};

int main()
{
	// 1、通过变量访问结构体成员
	struct Student s;
	strcpy(s.name, "张三");
	s.age = 18;
	// 2、通过指针访问结构体成员
	struct Student* p = &s;
	printf("%s\n", p->name);
	printf("%d\n", p->age);

	return 0;
}

--------结果如下--------
张三
18

为什么结构体会有两种访问方式?

在函数传参(传值、传址)时,会生成临时变量,如果要传的结构体变量太大的话,传值拷贝出来的临时对象也会很大,如果用传地址的方式来传结构体变量地址的话,可以很好的节省空间。

c语言struct,C语言,c语言
当然如果可以直接拿到结构体变量的话,使用变量来访问结构体成员会更直观点。

五. 结构体大小的计算

1. 计算方法

结构体的大小不是结构体元素单纯相加就行的,因为我们现在主流的计算机使用的都是 64 位字长的 CPU,对这类型的 CPU 取 8 个字节的数要比取一个字节要高效,也更方便。所以在结构体中每个成员的首地址都是8的整数倍的话,取数据元素时就会相对更高效,这就是内存对齐的由来。每个特定平台上的编译器都有自己的默认“对齐系数”(也叫对齐模数)。程序员可以通过预编译命令#pragma pack(n)来改变这一系数,其中的 n 就是你要指定的“对齐系数”。

PS:VS 中的默认对齐数为 8,不是所有编译器都有默认对齐数,当编译器没有默认对齐数的时候,成员变量的大小就是该成员的对齐数。

第一步:结构体中成员对齐
保证每个成员都放在一个对齐的地址上,对齐数 = min(该成员类型大小, 对其系数)

  • 如果可以被对齐数整除,则放在了对齐的地址上
  • 如果不可以整除,则需要补一些字节让其可以整除

第二步:结构体整体对齐
保证结构体整体放在一个对齐的地址上,对齐数 = min(最大类型成员所长字节数,对其系数),看能否被第一步算出来的结构体的大小整除

  • 如果可以整除,则在对齐的位置上
  • 如果不可以整除,则需要补一些字节让其可以整除

2. 普通结构体

第一步:找出每个成员变量的大小将其与编译器的默认对齐数相比较,取其较小值为该成员变量的对齐数
c语言struct,C语言,c语言
PS:这里使用的是VS编译器,故默认对齐数为8。

第二步:根据每个成员对应的对齐数画出它们在内存中的相对位置
c语言struct,C语言,c语言
第三步:通过最大对齐数决定最终该结构体的大小

通过图我们可以知道,绿色部分(double d成员占用)+红色部分(char c成员占用)+紫色部分(int i成员占用)+红色与紫色之间的白色部分(浪费掉了)总共占用了16个字节的内存空间。

我们需要将它们总共占用的内存空间(16)与结构体成员的最大对齐数(8)相比较,结构体的总大小为最大对齐数的整数倍,此时16正好是8的整数倍,所以该结构体在VS编译器下的大小就16个字节。即创建一个该类型的结构体变量,内存需为其开辟16个字节的内存空间。

PS:大多数情况下,成员变量已经占用的总字节个数并不一定正好为其成员变量中的最大对齐数的整数倍,这时我们需要将其扩大为最大对齐数的整数倍。

3. 包含数组成员的结构体

数组应拆开来看,不能看做一个整体

struct S
{
	char a; //对齐数为1。占1个字节
    char c[5]; //对齐数为1。可看成5个char占5个字节
    int b; //对齐数为4。占4个字节,因为前面所有成员占6个字节,不是4
       	   //个字节的整数倍,所以在第二个成员和第三个成员
       	   //之间要补2个字节
} //所以该结构体的大小为1+5+2(补)+4=12个字节

4. 成员包含结构体的结构体

1)如果结构体成员只是说明而没有定义变量,则这个结构体成员不占内存空间。

struct S
{
     char a; //对齐数为1。占1个字节
     struct s
     {
             int c;
             char d;
     }; //此处结构体只声明,没有定义结构体变量,所以该声明
        //的结构体在地址空间中并不占位置
     int f; //对齐数为4。占4个字节
     double b; //对齐数为8,
}; //该结构体的大小为1+3(补)+4+8=16个字节

2)如果内部定义并申明了其他结构体变量,这时需要把这个结构体看成一个整体,大小要独立计算,至于对齐数取其内部最大成员的对齐数。

struct t
{
    char a;  //对齐数1
    struct s //对齐数4
    {
            int c; //对齐数4
            char d;//对齐数1
    }g;//此处定义并申明了结构体变量,在这里需要把结构体
       //看成一个整体,独立计算这个结构体的大小为8字节
       //结构体整体的对齐数是内部最大成员的对齐数
       //之后把这个结构体看出对齐数为4,大小为8的成员
    char f; //对齐数1
    int b;  //对齐数4
}; //所以该结构体的大小为1+3(补)+8+1+3(补)+4=20个字节

5. 成员包含联合体的结构体

联合体的大小等同于联合体里面最大成员的大小,所以可以把联合体等效成一个变量,这个变量就是联合体里面最大的那个成员。

和前文所说的结构体一样,如果只声明联合体,没定义联合体变量,则联合体就当成不存在。

struct t
{
    char a;
    union s
    {
            int c;
            char d;
            double h;
    }g; 
    int f;
    double b;
};//所以该结构体的大小为1+7(补)+8+4+4(补)+8=32个字节

6. 空结构体的大小

1)在 VS2017 下测试
c语言struct,C语言,c语言

2)在 Centos7 下测试
c语言struct,C语言,c语言

六. 柔性数组

1. 介绍

在 c99 中有明确的规定允许结构体中最后一个数组大小是未知的。

  • 数组作为结构体的最后一个成员
  • 数组元素可以不写或写成0
  • 结构体中至少包含一个以上处数组外的其他类型的成员
struct T
{
	int a;
	char b;

	int arr[];//或者int arr[0];

};

int main()
{
	struct T t;
	// sizeof 求结构体大小时所求出的大小没有包括柔性数组的大小
	printf("%lu\n", sizeof(struct T));
	return 0;
}

--------结果如下--------
8

2. 使用方法

包含柔性数组的结构体,可以把整个结构体看成是变长的。文章来源地址https://www.toymoban.com/news/detail-769635.html

#include<stdio.h>   
#include<stdlib.h>                                          
#include<stdlib.h>     

struct d
{                                                                                    
  int nb;  
  int nn;                                 
  
  int arr[];                 
};                         
                                 
int main()                      
{
	//分别给结构体中其他类型的成员和柔性数组申请空间
    struct d *p=(struct d*)malloc(sizeof(struct d)+5*sizeof(int));
    p->nb=100;
    p->nn=50;  
    
    for(int i=0;i<5;i++)
    {
      p->arr[i]=i;//赋值
      printf("%d ",p->arr[i]);
    }     
    
    //重新调整所申请的空间,将柔性数组调整为40。
    struct d *pp=(struct d*)realloc(p,48); 
 
    if(pp!=NULL)
    {
       p=pp;

       for(int i=5;i<10;i++)
       {
         p->arr[i]=i;//赋值
         printf("%d ",p->arr[i]);    
       } 

       free(p);
       p=NULL;
    }                                                                                                    
    return 0;                                                                           
}       

--------结果如下--------
0 1 2 3 4 5 6 7 8 9                                                                                                                                                                                                       

3. 柔性数组的特点

  1. 柔性数组只需在 malloc 创建时要独立于结构体申请空间,此后的 realloc 再分配空间和 free 释放都只需对一个结构体指针操作即可。
  2. 柔性数组申请的内存更加集中,有利于查找使用和减少内存碎片。
  3. sizeof 求结构体大小时所求出的大小没有包括柔性数组的大小。

七. C++ 中 struct 与 class 的区别

  • class 成员的默认权限为 private,struct 成员的默认权限为 public。
  • class 的继承默认是 private 继承,struct 的继承默认是 public 继承。
  • class 可以作为一个关键字定义模板参数(与 typename 作用一样),而struct 不可以。

八. C 和 C++ 结构体的区别

  1. C++ 结构体内部可以有成员变量和成员函数,而 C 中结构体只能有成员变量。
  2. C 结构体的成员变量不能在声明时给初值,而 C++ 中可以
    c语言struct,C语言,c语言
  3. C++ 中定义结构变量时,可以不在名称前面加上 struct 关键字,而 C 一定要
  4. C 结构体内不能有静态成员,而 C++ 可以。
  5. C 结构没有访问修饰限定符,而 C++ 有。

到了这里,关于【C语言】struct结构体的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • C#: 结构体 struct 应用笔记

    说明:记录 在 C# 中的一些结构体相关的应用         在下面的示例中,在struct中定义了一个固定长度为 8 的 byte 数组 data 。通过使用 MarshalAsAttribute 的 UnmanagedType.ByValArray 参数,指定了数组的类型为值类型数组,并使用 SizeConst 字段指定了数组的大小为 8。请注意,使用固

    2024年01月17日
    浏览(47)
  • Golang struct 结构体指针类型 / 结构体值类型

    结构体变量之间的赋值是值拷贝。 当需要通过变量去修改另外一个结构体内容的时候,可以通过传递地址的方式来达到这个效果。 这样使用肯定是不对的,因为.的运算优先级要高,所以先会去执行p2.age,之后又取地址,所以这个就不行。 在代码执行的时候先执行.的操作,

    2024年02月13日
    浏览(42)
  • Rust 数据类型 之 结构体(Struct)

    目录 结构体(Struct) 定义与声明 结构体定义 结构体实例 结构体分类 单元结构体(Unit Struct) 元组结构体(Tuple Struct) 具名结构体(Named Struct) 结构体嵌套 结构体方法 例1:结构体转换为字符串描述 例2:矩形的周长和面积 例3:结构体字段的更新与输出 关联函数 结构体

    2024年02月16日
    浏览(48)
  • Golang空结构体struct{}的作用?

    在平时做项目得时候,经常会看到很多包里面定义了结构体。 e.g. 在 context 包里面 Context 接口中的 Done() 方法, Done() 返回一个是以空结构体定义数据的通道 chan struct{} ,那这里他是有什么特殊用意吗?我们接下来分析 struct{} 的作用。 struct{} 空结构是没有字段的结构类型。下

    2024年02月11日
    浏览(42)
  • Rust学习笔记005:结构体 struct

            在 Rust 中, struct 是一种用于创建自定义数据类型的,它允许你定义和组织数据的结构。 struct 可以包含多个不同类型的字段(fields),每个字段都有一个名称和一个类型。         下面是一个简单的例子,展示了如何定义和使用 struct : 元组结构体(

    2024年02月03日
    浏览(39)
  • go struct结构体之间的转换

    原文链接:https://www.zhoubotong.site/post/94.html 说下背景吧,大家在开发中可能在不同的目录(package)下定义了相同的struct(属性参数完全一样如名字、个数和类型),在方法调用传参数的时候,可能是用到了其中某一个struct的引用。 那么这里就牵扯到相互间的转换:直接上demo: 显然

    2023年04月17日
    浏览(47)
  • go 语言(九)----struct

    定义一个结构体 结构体使用 结构体传参

    2024年01月20日
    浏览(39)
  • golang 结构体struct转map实践

      1、反射 type sign struct {     Name string `json:\\\"name,omitempty\\\"`     Age  int    `json:\\\"age,omitempty\\\"` }   var s sign s.Name = \\\"csdn\\\" s.Age = 18     //方式1 反射 var data = make(map[string]interface{})   t := reflect.TypeOf(s) v := reflect.ValueOf(s) for i := 0; i t.NumField(); i++ {     data[t.Field(i).Name] = v.Field(i).Interfa

    2024年02月12日
    浏览(40)
  • Golang空结构体struct{}的作用是什么?

    在Go语言中,空结构体 struct{}是一种特殊的数据类型,它不占用任何内存空间。空结构体没有任何字段,也没有任何方法。尽管它看起来没有实际的用途,但在编程中确实有一些常见的应用场景。 以下是空结构体 struct{}的几个常见用途: 空结构体可以用作占位符,表示某个位

    2024年02月14日
    浏览(33)
  • C++的结构体(struct)和类(class)对比

    在C++中,结构体(struct)和类(class)都是用户自定义的数据类型,用于封装数据和方法。结构体通常用于定义一些简单的数据结构,而类则更多的用于面向对象的编程。 两者的不同之处如下: 结构体 (struct)是一种简单的数据集合,它可以包含不同类型的成员变量,但默

    2024年02月15日
    浏览(43)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包