首页 > 其他分享 >C语言自定义类型结构体详解

C语言自定义类型结构体详解

时间:2024-03-26 23:00:11浏览次数:20  
标签:char struct 自定义 int C语言 位段 详解 对齐 结构

C语言中,有内置类型,也就是C语言本身支持的现成的类型,例如int , char, double, float 等,当然,仅有内置类型是不够的,例如,我们想表示一个变量人,但是现有的类型不支持我们表示,因此,我们引入了自定义类型,自定义类型分为:

//结构体 struct
//枚举 - enum	
//联合体 - union

现在,我们介绍结构体的内容。

1.结构体类型的声明,创建和初始化

结构体声明的方式如下:

strcut tag    //tag是结构体的类型
{
	member - list //成员,一个或多个
}vaiable - list //变量名

例如,我们描述一个学生:

struct Stu
{
    char name[20];
    int age;
    char sex[5];
};

或者,我们可以描述一本书

struct Book
{
	char name[20];
	char author[20];
	float price;
	char id[13];
	//...
};

当我们想尝试创建变量时候,可以有哪些方式来创建呢?

(1)创建全局变量的方式

struct Book
{
	char name[20];
	char author[20];
	float price;
	char id[13];
	//...
}b3,b4; //全局变量

struct Book b2; //全部变量创建

这样进行创建时,得到的都是全局变量

(2)局部变量创建的方式

int main()
{
	struct Book b1 = {"hello world","zhangsan",18.8,"PG1001"}; //局部变量创建
	struct Book b6 = { .id = "PG1002",.author = "lisi",.name = "我叫李四",.price = 88.8 };
	struct Book b[5];//数组
	
	r

这里创建的b1和b6都是局部变量,甚至我们可以创建一个结构体的数组,当然,我们在进行变量初始化的时候,也可以有上面两种初始化的方式,第一个是按照顺序初始化,第二个是.+对应内容进行初始化

2.结构体的特殊声明

在结构体的声明时,可以出现不完全声明 - 匿名结构体类型,它的形式如下:

struct 
{
	char c;
	int i;
	double d;
}s;

这里,我们就创建了一个匿名结构体,后续我们使用的时候,直接用s即可

int main()
{
	printf("%c %d %f", s.c, s.i, s.d);

	return 0;
}

那么,我们如果进行这样的声明:

struct 
{
	char c;
	int i;
	double d;
}s;

struct
{
	char c;
	int i;
	double d;
}*ps;
int main()
{
	ps = &s; //编译器会把上面两个定义为不同的,因此非法

	return 0;
}

这样是不可以的,因为编译器会把以上的两个声明当成两个完全不同的类型,因此是非法的,一般来说,匿名结构体类型,如果没有对结构体重命名的话,基本只能使用一次。

3.结构体自引用的方式

数据结构是数据在内存中存储和组织的结构,有线性数据结构:顺序表,链表,栈,队列
有树形数据结构:二叉树,还有图等

我们可以定义链表的结点:

typedef struct 
{
	int data;
	struct Node* next; //下一个节点的地址,结构体指针
}Node;

这种书写方式,后续我们可以直接用Node来定义链表的结构体了

当然,也可以写成这样:

struct Node
{
	int data;
	struct Node* next; //下一个节点的地址,结构体指针
};
typedef struct Node Node;

请注意,匿名的结构体类型是不能实现结构体的自引用效果的

4.结构体内存对齐

现在我们来讨论结构体的大小,它是符合结构体的对齐规则的:

对齐规则:

1.结构体第一个成员对齐的偏移量为0
2.其他成员变量要对其到某个数字的整数倍的地址处
    对齐数 = 编译器默认的一个对其数与成员变量大小的较小值
        VS默认为8,Linux中gcc没有默认对齐数,对齐数就是成员自身大小
3.结构体总大小必须为最大的对齐数的整数倍
4.如果嵌套了结构体,嵌套的结构体对齐到自己成员中最大对齐数的整数倍,结构体整体大小就是所有最大对齐数整数倍

现在,我们来举出一个例子,来判断结构体的大小吧

struct S
{
	char c1;//1
	int i;//4
	char c2;//1
};

这个结构体是占据12个字节的

这个结构体

struct S
{
	char c1;//1
	char c2;//1
	int i;//4
};

它占据几个字节呢?

我们再看这个结构体

struct S
{
	double d;//8
	char c;//1
    int i;//4

};

现在,我们看这个结构体的大小

这是一个结构体中有另一个结构体

那么,为什么存在内存对齐呢?

事实上,这是拿空间换取时间的做法,

1. 平台原因(移植原因):不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能在某些地址处取某些特定 类型的数据,否则抛出硬件异常。

2. 性能原因: 数据结构(尤其是栈)应该尽可能地在⾃然边界上对⻬。原因在于,为了访问未对⻬的内存,处理器需要 作两次内存访问;⽽对⻬的内存访问仅需要⼀次访问。假设⼀个处理器总是从内存中取8个字节,则地 址必须是8的倍数。如果我们能保证将所有的double类型的数据的地址都对⻬成8的倍数,那么就可以 ⽤⼀个内存操作来读或者写值了。否则,我们可能需要执⾏两次内存访问,因为对象可能被分放在两 个8字节内存块中。

在设计结构体的时候,我们尽量让占用空间小的成员集中在一起,可以节省空间

修改默认对齐数

我们可以修改默认对齐数,方法如下:

#pragma pack(1) //设置默认对齐数
//修改默认对齐数
int main0()
{

	return 0;
}

#pragma pack() //取消

5.结构体的传参

在结构体中,我们传参时尽量传入结构体的地址,这样可以节省时间和空间

例如,我们定义了一个结构体

struct S1
{
	int arr[100];
	int n;
	double d;

};

现在我们想写一个函数,输出这个结构体的某些值

我们一般会这样写:

void print1(struct S tmp)	//空间和时间的双重浪费
{
	int i = 0;
	for (i = 0; i < 5; i++)
	{
		printf("%d ", tmp.arr[i]);
	}
	printf("%d ", tmp.n);
	peintf("%lf ", tmp.d);
}

但是,tmp会进行拷贝,这样不仅占用空间,也造成了时间的浪费,但如果我们传入的是结构体的地址呢?

void print2(const struct S* ps)
{
	int i = 0;
	for (i = 0; i < 5; i++)
	{
		printf("%d ", ps->arr[i]);

	}
	printf("%d ", ps->n);
	printf("%lf", ps->d);
}

这样,就可以了

6.结构体实现位段

什么是位段?

位段的声明和结构是类似的,有两个不同: 1. 位段的成员必须是 int、unsigned int 或signed int ,在C99中位段成员的类型也可以 选择其他类型。 2. 位段的成员名后边有⼀个冒号和⼀个数字。

例如,结构体的形式如下:

struct S
{
	int _a;	
	int _b;	
	int _c;
	int _d;
};

而位段的形式如下:

struct S
{
	int _a:2;	//只占两个比特位
	int _b:5;	//只占五个比特位
	int _c:10;
	int _d:30;
};

冒号后面的代表这个参数占据的空间大小

那现在,我们就来判断一下这个位段的大小吧!

但是,位段存在跨平台问题,原因如下:

1. int位段被当成有符号数还是⽆符号数是不确定的。 2. 位段中最⼤位的数⽬不能确定。(16位机器最⼤16,32位机器最⼤32,写成27,在16位机器会 出问题。 3. 位段中的成员在内存中从左向右分配,还是从右向左分配,标准尚未定义。 4. 当⼀个结构包含两个位段,第⼆个位段成员⽐较⼤,⽆法容纳于第⼀个位段剩余的位时,是舍弃 剩余的位还是利⽤,这是不确定的。

网络协议中,位段可以有很好的效果

在位段的使用时,我们应该注意,某些位段并不在字节初始的位置,而一个字节对应一个地址,因此我们不能直接使用scanf的方式给位段赋值,我们应该先创建并赋值一个变量,然后利用 = 来赋值

标签:char,struct,自定义,int,C语言,位段,详解,对齐,结构
From: https://blog.csdn.net/qq_73509351/article/details/137019953

相关文章

  • C语言经典例题(7) --- 计算三角形的周长和面积、球体的体积、变种水仙花数、时间转换
    文章目录1.计算三角形的周长和面积2.计算球体的体积3.变种水仙花数4.时间转换5.输出学生信息1.计算三角形的周长和面积题目描述:根据给出的三角形3条边a,b,c(0<a,b,c<100,000),计算三角形的周长和面积。输入描述:一行,三角形3条边(能构成三角形),中间用一个空......
  • C语言经典例题(8) --- 进制A+B、网购、及格分数、最高分数、计算一元二次方程
    文章目录1.进制A+B2.网购3.及格分数4.最高分数5.计算一元二次方程1.进制A+B题目描述:输入一个十六进制数a,和一个八进制数b,输出a+b的十进制结果(范围-231~231-1)。输入描述:一行,一个十六进制数a,和一个八进制数b,中间间隔一个空格。输出描述:一行,a+b的十进制结果......
  • 第二讲 C语言中的数据类型和变量
    1数据类型介绍C语言提供了丰富的数据类型来描述生活中的各种数据。使用整形类型来描述整数,使用字符类型来描述字符,使用浮点型类型来描述小数。下列是C语言提供的各种数据类型:1.1字符型char[signed]char //有符号的unsignedchar //无符号的1.2整型//短整型......
  • [引]ArkTS 自定义组件
    创建自定义组件-自定义组件-基本语法-学习ArkTS语言-入门-HarmonyOS应用开发HarmonyOSDeveloper > 文档 > 指南 > 入门创建自定义组件 更新时间:2024-01-1511:55分享添加收藏在ArkUI中,UI显示的内容均为组件,由框架直接提供的称为系统组件,由开......
  • Scrapy详解
    Scrapy分布式爬虫1、yield关键词主要用途:异步处理:Scrapy是基于Twisted框架构建的,Twisted是一个时间驱动的网络框架,它允许Scrapy进行非阻塞操作。使用yield可以暂停当前的爬虫处理,等待某些异步操作完成后再继续执行。延迟处理:当你需要处理大量数据或者需要等待某些耗时的I/O......
  • C++ 用户输入与数据类型详解:建立基本计算器及变量类型
    C++用户输入你已经学习了cout用于输出(打印)值。现在我们将使用cin来获取用户输入。cin是一个预定义变量,它使用提取运算符(>>)从键盘读取数据。在下面的示例中,用户可以输入一个数字,该数字存储在变量x中。然后我们打印x的值:示例intx;cout<<"Typeanumber:";//......
  • C++ 用户输入与数据类型详解:建立基本计算器及变量类型
    C++用户输入你已经学习了cout用于输出(打印)值。现在我们将使用cin来获取用户输入。cin是一个预定义变量,它使用提取运算符(>>)从键盘读取数据。在下面的示例中,用户可以输入一个数字,该数字存储在变量x中。然后我们打印x的值:示例intx;cout<<"Typeanumber:"......
  • C++文件类(文件流类)及用法详解
    open()fstreamifstreamofstream打开指定文件,使其与文件流对象相关联。is_open()检查指定文件是否已打开。close()关闭文件,切断和文件流对象的关联。swap()交换2个文件流对象。operator>>fstreamifstream重载>>运算符,用于从指定文件中读取数据。gcoun......
  • 别想宰我,怎么查看云厂商是否超卖?详解 cpu steal time
    据说有些云厂商会超卖,宿主有96个核心,结果卖出去100多个vCPU,如果这些虚机负载都不高,大家相安无事,如果这些虚机同时运行一些高负载的任务,相互之间就会抢占CPU,对应用程序有较大影响,我应该如何查看我的CPU是否被抢占了呢?什么是cpustealtime?如果你在物理机上查看这个......
  • duilib 自定义扩展列表
    目录前言一、扩展列表项1、基本数据结构列表项Item1)、ListContainerElement定义......