目录
前言
关键字 - volatile
关于const与volatile
关键字-struct
空结构体多大
struct的内存对齐
柔性数组
struct与class的区别
关键字-union
union和大小端
关键字-enum
enum 与 #define 的区别
关键字-typedef
typedef 与 #define的区别
前言
本章节主要讲解:
- 深入关键字volatile
- 深入关键字struct
- 深入关键字union
- 深入关键字enum
- 深入关键字typedef
关键字 - volatile
- 结论:
volatile是一个类型修饰符,作用是作为指令关键字,一般都是和const对应 用来确保本条指令不会被编译器的优化而忽略(既保持内存可见性)
- 示例:
- 不加volatile
int main()
{
int i = 10;
int a = i;
printf("i=%dn ", i);
//下面汇编语句的作用就是改变内存中i的值,但是又不让编译器知道
__asm
{
mov dword ptr[ebp - 4], 20h
}
int b = i;
printf("i=%dn", b);
return 0;
}
在debug(调试)版本模式运行程序,输出结果如下: i = 10 i = 32 然后在release版本模式运行下,输出结果如下: i = 10 i = 10
- 加上volatile
int main()
{
volatile int i = 10;
int a = i;
printf("i=%dn ", i);
//下面汇编语句的作用就是改变内存中i的值,但是又不让编译器知道
__asm
{
mov dword ptr[ebp - 4], 20h
}
int b = i;
printf("i=%dn", b);
return 0;
}
分别在debug和release版本运行结果都是如下: i = 10 i = 32
关于const与volatile
const要求你不要进行写入;volatile意思是你读取时,每次都要从内存读,两者并不冲突 所以,一个参数既可以是const还可以是volatile
- 例如只读的状态寄存器:
它是volatile因为它可能被意想不到地改变 它是const因为程序不应该试图去修改它
关键字-struct
空结构体多大
- 示例:
struct student
{
}stu;
int main (void)
{
printf ("sizeof (stu) = %dn", sizeof (stu));
return 0;
}
代码语言:javascript复制输出结果:
在C中, sizeof (stu) = 0
在C 中, sizeof (stu) = 1
结论:对于空结构体不同编译器理解不同,所以大小不一(可能0或者1)
struct的内存对齐
定义:struct中的各成员变量的存储地址有一套对齐的机制(让CPU能够更舒服地访问变量) struct内存空间的分配是粗放的,不管用不用,全分配
- 规律:
- 每个成员变量的首地址,必须是它的类型的所占字节数的整数倍,如果不满足,它与前一个成员变量之间要填充(padding)一些无意义的字节来满足;
- 整个struct的大小,必须是该struct中所有成员的类型中占字节最大者的整数倍,如果不满足,在最后一个成员后面填充
- 示例:
struct student{
char sex;
int score;
};
- 现象:
第一个char类型成员与第二个int类型成员之间会填充数据,(要求1); 最大长度为整型占用4个字节的空间,所以其占用的空间为4的倍数,这样s占用的空间就是8个字节(要求2)
注意:数据成员的书写顺序会影响结构体占用的空间的大小,尽量将相同数据类型的变量连续书写
柔性数组
- 定义:
C99 中,结构中的最后一个元素允许是未知大小的数组,这就叫做柔性数组成员,但结构中的柔性数组成员前面必须至少一个其他成员
- 使用:
sizeof 返回的这种结构大小不包括柔性数组的内存 用malloc()函数进行内存动态分配,分配的内存应该大于结构的大小,以适应柔性数组的预期大小 用malloc函数分配了内存,肯定就需要用free函数来释放内存
- 示例:
typedef struct data
{
int len; //一般用来表示字符数组的字符个数
char name[];//空间大小为0
}S;
int main(void)
{
S s;
printf("sizeof(s)=%dn",sizeof(s));//输出为4,即是int类型大小
int len = 10; //申请空间
struct data *p =(struct data*)malloc(sizeof(s) sizeof(char)*len);
//判断是否申请成功&请空处理
p->len = len;
strcpy(p->name,"xxxxxx"); //字符串赋值需要用strcpy
printf("%sn",p->name);
//释放指针p
free(p);
return 0;
}
struct与class的区别
在C 里struct关键字与class关键字一般可以通用 只有一个很小的区别:struct的成员默认情况下属性是public的,而class成员却是private的
关键字-union
- 定义:
联合(union)是一种节省空间的特殊的类,一个 union 可以有多个数据成员,但是在任意时刻只有一个数据成员可以有值,当某个成员被赋值后其他成员变为未定义状态
- 使用:
union 主要用来压缩空间:如果一些数据不可能在同一时间同时被用到,则可以使用union
union和大小端
代码语言:javascript复制#include<stdio.h>
union var{
char c[4];
int i;
};
int main(){
union var data;
data.c[0] = 0x04;//因为是char类型,值对应ascii
data.c[1] = 0x03;//16进制便于直接与内存中的值对比
data.c[2] = 0x02;
data.c[3] = 0x01;
//数组先使用低地址再使用高地址,内存内容依次为:04,03,02,11(共四字节)
//而把四个字节作为一个整体,对于小端来说:低地址放在低权位
//读取出来则是:0x01020304
//反之则是大端存储模式
printf("%xn",data.i);//共用空间
}
关键字-enum
- 定义:
枚举型是一个集合,其元素(枚举成员)是一些命名的整型常量(元素之间用逗号隔开)
- 使用:
在程序中,可能需要为某些整数定义一个别名
我们可以利用预处理指令#define来完成这项工作:
代码语言:javascript复制#define MON 1
#define TUE 2
#define WED 3
#define THU 4
#define FRI 5
#define SAT 6
#define SUN 7
枚举类型能完成同样的工作(更加简洁便捷):
代码语言:javascript复制enum DAY
{
MON=1, TUE, WED, THU, FRI, SAT, SUN
};
注:第一个枚举成员的默认值为整型的0,后续枚举成员的值在前一个成员上加1 可以人为设定枚举成员的值,从而自定义某个范围内的整数
enum 与 #define 的区别
- define是在预处理阶段直接进行替换,并且不进行类型检查,存储在代码段
- 枚举则是在程序运行之后才起作用(作用的时期不同),枚举常量存储在数据段的静态存储区里
- 枚举变量的大小只能为整型数据(例如:0、1、2…),宏则不是
- enum当我们不主动对它进行赋值时,第一个枚举成员的默认值为整型的0,后续枚举成员的值在前一个成员上加1;#define则不会
- 枚举可以一次定义大量相关的常量,而#define宏一次只能定义一个
- 一般在编译器里,可以调试枚举常量,但是不能调试宏常量
- 枚举量具有类型,宏没有类型,枚举变量具有与普通变量相同的性质(如作用域等)而宏没有
- 枚举常量属于常量,宏定义不是常量
关键字-typedef
- 定义:
作用是为一种数据类型(内置数据类型/自定义(struct))定义一个新名字
typedef 与 #define的区别
- #define 进行简单的进行字符串替换, #define 宏定义可以使用 #ifdef、#ifndef 等来进行逻辑判断,还可以使用 #undef 来取消定义
- typedef 是为一个类型起新名字,typedef 符合(C语言)范围规则,使用 typedef 定义的变量类型,其作用范围限制在所定义的函数或者文件内(取决于此变量定义的位置)
- 示例:
typedef char* pStr1;
#define pStr2 char*
pStr1 s1, s2;
pStr2 s3, s4;
- #define效果:
char* s3, s4;//实际上s4类型是char(与定义前一样)
- typedef效果:
char *s3, *s4;//类型都是char*