C语言数据类型分类
C语言数据类型分为内置类型和自定义类型
内置类型
内置类型是C语言自带的数据类型,整形,浮点型,字符型,指针,空类型等都属于内置类型,他们的意义比较单一,往往用来表示一个含义,比如整形,可以用来记录年龄,次数等数据,浮点类型可以用来存放身高,成绩等实数类型。
void(空类型)不做变量的定义,他的作用大部分场景在函数返回值类型(可以参考C语言函数),还有部分场景会使用到void*的指针。
他们在C语言中可以如下表示
int //整形数据类型
short int //短整型数据类型
char //字符型数据类型
int* //整形指针数据类型
double //双精度浮点型
float //单精度浮点型
void //空类型
自定义类型
自定义类型是描述复杂的含义,用单一的内置类型无法描述时,所使用的类型,是由用户自己来定义的,通俗一点说就是DIY。
结构体类型
我们需要描述一个人,他有身高,有体重,有性别,有年龄,用单纯的int或者char无法全部描述,因此我们可以进行如下操作
#include<stdio.h>
#include<string.h>
struct people{ //"struct people"为我们自定义的类型,下列name,height等成为成员变量
//下列name,height等成为成员变量
char name[10]; //姓名
float height; //身高
float weight; //体重
char sex; //性别
int age; //年龄
};//声明结构体类型
int main()
{
struct people pe; //定义结构体,
//为其成员变量赋值
strcpy(pe.name,"张三"); //字符串拷贝
pe.height = 183.5;
pe.weight = 130.5;
pe.sex = 'm';
pe.age= 18;
//打印信息
printf("姓名:%s 身高:%f 体重:%f 性别:%c 年龄:%d",pe.name,pe.height,pe.weight,pe.sex,pe.age);
return 0;
}
结构体类型可以将内置类型和自定义类型组合,构成一个类型,访问其成员时,可以进行如下操作
struct people pe;
pe.age = 18; //结构体直接访问
struct people* ppe = &pe;
ppe->age = 18; //指针间接访问
枚举类型
生活中一些可以一一举例的数据类型可以成为枚举类型
比如性别:男,女;季节:春,夏,秋,冬;
可以定义枚举类型
enum Season // 枚举类型 季节
{
Spring,
Summer,
Autumn,
Winter //最后一个没有逗号
};
enum Season now = Autumn; //定义枚举变量 now ,初始化为Autumn
可以理解为我们自己定义枚举常量,用这个常量来给我们的枚举变量赋值,类型是相对应的 。
枚举类型中的枚举常量都是有自己的值得,默认从0开始,依次递增,我们也可以给他赋初始值。
enum Season // 枚举类型 季节
{
Spring = 1,
Summer = 3,
Autumn = 4,
Winter = 5 //最后一个没有逗号
};
enum Season now = Autumn; //定义枚举变量 now ,初始化为Autumn, now的值为4
可以用枚举类型替换掉我们平时用的#define,有很多优点,可以一次定义多个常量,不是单纯的替换,可以避免很多问题,便于调试等。
联合体类型
联合体类型是一种特殊的自定义类型,类似于结构体,但是与结构体不同的是,联合体的成员变量共用一块空间,因此联合体也称做共用体
union Un //联合体的声明
{
int a;
char c;
};
union Un x; //联合体的定义
x.a = 5; //访问联合体成员
x.c = 'p';
C语言数据在内存中的存储
声明和定义的区别
内置类型
告诉编译器我有这个变量,而他的值未知,这是声明。
声明后给变量赋值,系统会为变量开辟空间来存储数据,这是定义。
也就是说,声明和定义的区别就是有没有分配内存。
变量的声明和定义方法如下
int age; // 数据类型 变量名称; 这一步是声明
age = 0; //分配内存空间,存储数据,这一步是定义
int cnt = 0; //声明的时候给他初始化,分配内存空间了,是定义
结构体类型
结构体类型声明和定义在介绍类型的时候已经展示,但也有一些特殊的声明和定义
//匿名结构体:省去struct后面的标签,并且直接定义x
struct
{
int a;
int b;
}x;
//匿名结构体定义时可以定义多个,也可以定义指针
struct
{
int a;
int b;
}x1,x2[10],*x;
*******************************************************************************************
//结构体自引用:在结构体中成员变量可以是自身的指针类型
struct Node
{
int a;
struct Node* next; //如果不是指针,就会递归定义,无休无止,所以定义成指针,
//就可以通过自身找到下一个结构体
}
stuct people
{
char name[10];
int age;
float height;
}p1; // 声明类型的同时定义结构体p1
struct people p2; // 定义结构体变量p2
struct people p3 = {"张三",21,180}; //定义p3的同时初始化p3
struct Node
{
int a;
struct people p; //结构体嵌套
struct Node* next; //结构体自引用
}p4 = {1, {"李四", 20, 175 }, &p3}; //结构体嵌套定义初始化
p5 = {2, {"王五", 20, 178 }, &p4}; //结构体嵌套定义初始化
数据存储
整数类型
在32位机器下,整数会分配4个字节的大小来存放数据,64位机器下是8个字节,每个字节是8个bit位,一个bit就是我们内存的最小单位,只有2种状态,我们可以用它存储2进制代码。
32位机器下,int类型的数据会有4个字节,也就是32个bit位来存放数据。存放时有符号位和数值位两部分。
符号位都是用0表示“正”,用1表示“负”;
原码
直接将整数按照正负数的形式翻译成二进制代码
反码
原码的符号位不变,其他位依次按位取反
补码
反码+1就是补码
正数的原码,补码,反码都是相同的。
在计算机中,整形存储都是存储补码
这样存储补码的原因是:使用补码可以统一处理符号位和数值位。可以统一处理加减法,补码与原码转换,其运算过程是相同的,不需要额外的硬件电路
浮点类型
浮点类型包括float,double,long double类型
存储规则
根据国际标准(IEEE)规定,任意一个二进制浮点数V可以表示成下面形式
其中
- 符号位(S):1位,用来表示这个数是正数还是负数。0表示正数,1表示负数。
- 指数位(E):8位,用来表示数值的范围。它存在一个偏移值,通常偏移量是127。
- 尾数位(M):23位,用来表示数值的精度。
例如,十进制20.0,写成二进制是10100.0 = 1.01 * 2^4. 则S = 0, M = 1.01, E = 4;
根据IEEE规定,32位浮点数最高位的1位存储符号位S,接着8位是指数E,剩下的23位是有效数字M。
对于64位,最高位为符号位,接着11位是指数E,剩下的52位为有效数字
对于M和E,有特殊规定
M:介于1到2之间,即1.***,默认第一个数总是1,因此可以将1省略,在内存中只存入小数部分,这样做的好处是存储有效数字的位数可以从23变到24
E:E是一个无符号的整数,8个bit位,意味着他的存储范围为0~255,但是科学计数法中E可以为负数,因此我们给E加上127存到内存里,就可以表示-127~128之间的数,对于11位的E,这个数是1023
例如,如果有一个32位的浮点数01000000 00000000 00000000 00000000
,那么:
- 符号位是0,表示这是一个正数。
- 指数位是
10000000
,转换为十进制是128,减去偏移量127,得到实际指数是1。 - 尾数位是
00000000 00000000 00000000 00000000
,表示小数部分是0。
所以这个数表示的是1 * 2^1 = 2
。
自定义类型
结构体在内存中存储是,其分配的空间不是各成员分配的空间之和,他还需遵守一个规则,结构体内存对齐
结构体内存对齐
对齐规则:
1.第一个成员在与结构体变量偏移量为0的位置
2.其他成员变量要对齐到某个数字(对齐数)的整数倍地址初,
3.结构体总大小为最大对齐数(每个成员对齐数的最大值)的整数倍。
4.如果有嵌套结构体的情况,成员结构体对齐到自己的最大对齐数整数倍,外结构体对齐到所有对齐数(包括成员结构体)的最大对齐数的整数倍
对齐数的计算
对齐数 = 编译器默认的对齐数 与 该成员大小的较小值,vs中默认对齐数是8
默认对齐数可以进行修改
#pragma pack(2) // 修改默认对齐数为2
struct Example {
char a; // 1 字节
int b; // 4 字节
char c; // 1 字节
};#pragma pack( ) // 恢复默认对齐数
举例
struct Example {
char a; // 1 字节
int b; // 4 字节
char c; // 1 字节
};
如果没有对齐,那他的内存分布是
根据对齐规则后,应该是
a是结构体的第一个成员,在偏移量为0的位置
b是int类型的数据,大小为4个字节,与默认对齐数8相比较小,所以b的对齐数是4,他的位置必须在4的整数倍
c类似,对齐数为1,位置在1的整数倍
整个结构体对齐数位成员对齐数1,4,1 的最大值4,所以他的大小为4的整数倍,所以他的总大小为12个字节
对齐的意义
编译器读取内存可能不是一位一位读取,是同时读取4个字节或者其他整数个字节,以4为例,如果不对齐的话,可能读取一个整数会需要访问两次,因为在他之前可能读过不是4的倍数大小的空间,而对齐后则是可以一次读取,尽管浪费了空间,但是换来了时间
标签:存储,struct,int,数据类型,C语言,pe,类型,对齐,定义 From: https://blog.csdn.net/2301_79969685/article/details/142533953