⭐️ 关键字深度剖析 ⭐️第七章(关键字volatilestructunionenumtypedef)

2022-11-30 08:10:02 浏览数 (1)

目录

前言

关键字 - volatile

关于const与volatile

关键字-struct

空结构体多大

struct的内存对齐

柔性数组

struct与class的区别

关键字-union

union和大小端

关键字-enum

enum 与 #define 的区别

关键字-typedef

typedef 与 #define的区别


前言


本章节主要讲解:

  • 深入关键字volatile
  • 深入关键字struct
  • 深入关键字union
  • 深入关键字enum
  • 深入关键字typedef

关键字 - volatile


  • 结论:

volatile是一个类型修饰符,作用是作为指令关键字,一般都是和const对应 用来确保本条指令不会被编译器的优化而忽略(既保持内存可见性)

  • 示例:
  • 不加volatile
代码语言:javascript复制
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
代码语言:javascript复制
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


空结构体多大

  • 示例:
代码语言:javascript复制
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内存空间的分配是粗放的,不管用不用,全分配

  • 规律:

  1. 每个成员变量的首地址,必须是它的类型的所占字节数的整数倍,如果不满足,它与前一个成员变量之间要填充(padding)一些无意义的字节来满足;
  2. 整个struct的大小,必须是该struct中所有成员的类型中占字节最大者的整数倍,如果不满足,在最后一个成员后面填充
  • 示例:
代码语言:javascript复制
struct student{
    char sex;
    int score;
};
  • 现象:

第一个char类型成员与第二个int类型成员之间会填充数据,(要求1); 最大长度为整型占用4个字节的空间,所以其占用的空间为4的倍数,这样s占用的空间就是8个字节(要求2)

注意:数据成员的书写顺序会影响结构体占用的空间的大小,尽量将相同数据类型的变量连续书写

柔性数组

  • 定义:

C99 中,结构中的最后一个元素允许是未知大小的数组,这就叫做柔性数组成员,但结构中的柔性数组成员前面必须至少一个其他成员

  • 使用:

sizeof 返回的这种结构大小不包括柔性数组的内存 用malloc()函数进行内存动态分配,分配的内存应该大于结构的大小,以适应柔性数组的预期大小 用malloc函数分配了内存,肯定就需要用free函数来释放内存

  • 示例:
代码语言:javascript复制
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 的区别

  1. define是在预处理阶段直接进行替换,并且不进行类型检查,存储在代码段
  2. 枚举则是在程序运行之后才起作用(作用的时期不同),枚举常量存储在数据段的静态存储区里
  3. 枚举变量的大小只能为整型数据(例如:0、1、2…),宏则不是
  4. enum当我们不主动对它进行赋值时,第一个枚举成员的默认值为整型的0,后续枚举成员的值在前一个成员上加1;#define则不会
  5. 枚举可以一次定义大量相关的常量,而#define宏一次只能定义一个
  6. 一般在编译器里,可以调试枚举常量,但是不能调试宏常量
  7. 枚举量具有类型,宏没有类型,枚举变量具有与普通变量相同的性质(如作用域等)而宏没有
  8. 枚举常量属于常量,宏定义不是常量

关键字-typedef


  • 定义:

作用是为一种数据类型(内置数据类型/自定义(struct))定义一个新名字

typedef #define的区别

  1. #define 进行简单的进行字符串替换, #define 宏定义可以使用 #ifdef、#ifndef 等来进行逻辑判断,还可以使用 #undef 来取消定义
  2. typedef 是为一个类型起新名字,typedef 符合(C语言)范围规则,使用 typedef 定义的变量类型,其作用范围限制在所定义的函数或者文件内(取决于此变量定义的位置)
  • 示例:
代码语言:javascript复制
typedef char* pStr1;
#define pStr2 char* 
pStr1 s1, s2;
pStr2 s3, s4;
  •  #define效果:
代码语言:javascript复制
char* s3, s4;//实际上s4类型是char(与定义前一样)
  • typedef效果:
代码语言:javascript复制
char *s3, *s4;//类型都是char*

0 人点赞