C语言32个关键字结合代码详解(超详细)

C语音32个关键字

分类

C语音关键字一共32个,可以分成四类。

数据类型:12个
控制语句:12个
存储类型:4个
其他关键字:4个

数据类型关键字

在这里插入图片描述

char:声明变量或者函数。占一个字节(八位)。分为有符号和无符号两种,有符号范围是 -128 ~127(-2 ^7 ---- 2 ^7-1)。无符号的范围是0 ~ 255(0----2 ^8-1)。
double:声明双精度变量或者函数。双精度浮点数。16 位编译器下,double 占 8 个字节(64位);32 位编译器下,double 占 8 个字节(64位);64 位编译器下,double 占 8 个字节(64位)。 1bit(符号位) 11bits(指数位) 52bits(尾数位)。double的范围为-2 ^1024 ~ 2 ^1024(-1.79E+308 ~ +1.79E+308)。默认保留15位小数。
float:声明浮点型变量或函数。单精度浮点数。占四个字节(32位)。1bit(符号位) 8bits(指数位) 23bits(尾数位)。 float的范围为-2 ^ 128 ~ +2^128(-3.40E+38 ~ +3.40E+38)。默认保留6位小数。
int:声明整型变量或函数。数据类型占内存的位数与操作系统的位数以及编译器有关,16位系统中,keil只占两个字节(16位)。但是这种情况一般不会出现。现在基本使用没有16位系统。在keil编译器中int 类型无论在 32 位或 64 位系统中都是 4 个字节(32位)。分为有符号和无符号两种,有符号范围是 -2147483648 ~ 2147483647 (-2 ^ -31 ---------- 2 ^ 31-1) ,无符号的范围是 0 ~ 4,294,967,295(0 ---- 2 ^32)。
short:声明短整型变量或函数。short 一般占两个字节(16位)。分为有符号和无符号两种,有符号范围是 -32,768~32,767(-2 ^15 ---- 2 ^15-1),无符号的范围是 0 ~ 65,535(0 ---- 2 ^16-1)。
long:声明长整型变量或函数。分为有符号和无符号两种。long在64位系统中,占8字节(64字节)。有符号范围是 (-2 ^ 63 — 2^63-1),无符号的范围是 (0 ---- 2 ^64)。在16位和32位占四个字节(32位)。有符号范围是 -2147483648 ~ 2147483647 (-2 ^ -31 ---------- 2 ^ 31-1) ,无符号的范围是 0 ~ 4,294,967,295(0 ---- 2 ^32)。
unsigned:声明无符号类型变量或函数。整型的每一种都有无符号( unsigned )和有符号( signed )两种类型( float 和 double 总是带符号的) 。在默认情况下声明的整型变量都是有符号的类型( char默认是无符号的)。如果需声明无符号类型的话就需要在类型前加上 unsigned 。

为什么char默认无符号,其他整型默认有符号?

其他整型默认为有符号是因为在计算机中,数的表示方式有两种:有符号和无符号。有符号整数可以表示正、负数和零,而无符号整数只能表示非负数(即正数和零)。对于其他整型,默认为有符号是因为在实际应用中,有符号整数更常见且更易于理解和处理。

而char类型默认为无符号是因为在C语言的char类型既可以表示字符也可以表示整数。为了保持兼容性,char类型被默认定义为无符号,以确保字符的值始终是非负的。这样做的好处是可以直接将char类型的值与ASCII码相对应,方便进行字符处理。

注意:C++标准并没有规定char类型的默认符号性质,具体实现可能会有所不同。因此,在编写代码时,最好明确指定char类型的符号性质,以避免潜在的问题。

signed:声明有符号类型变量或函数。signed 是默认的,被修饰的这个变量是有符号的,可以存储整数和负数。
struct:声明结构体变量或函数 。在C语言中,可以使用结构体( Struct )来存放一组不同类型的数据。对齐方式是按结构体中最大类型的字节数对齐。所以有时候成员换一下位置,结构体大小就改变了。
union:声明共用体(联合)数据类型。C语言中的 union 是联合体,就是一个多个变量的结构同时使用一块内存区域,区域的取值大小为该结构中长度最大的变量的值。修改其中一个共用体变量的值,其他的也会被改变,因为它们所用的空间是一样的。
void:声明函数无返回值或无参数,声明无类型指针。void 被翻译为无类型,相应的 void * 为无类型指针。
enum:声明枚举类型。枚举定义了一些符号,这些符号的本质就是int类型的常量,每个符号和一个常量绑定。这个符号就表示一个自定义的一个识别码,编译器对枚举的认知就是符号常量所绑定的那个int类型的数字。默认从0开始依次增加。如果用户自己给首成员定义了一个值,则从那个值开始往后依次增加。或者用户不按顺序赋值也可以。

控制语句关键字

1、循环语句类型关键字

在这里插入图片描述

for:一种循环语句。主要用来控制循环语句的执行 。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>

typedef unsigned char    u8;
typedef unsigned short   u16;
typedef unsigned int     u32;


int main(void)
{
   u16 i=0,sum=0;  //养成编程好习惯,局部非静态变量初始化赋值。赋值随机值容易出bug
   for(i=0;i<100;i++) //(循环变量赋初值;循环条件;循环变量增值)
   {
     sum+=i;  //循环体
   }
   printf("sum=%hu\r\n",sum);
}
	注意:for(;;) for(;一直为真;)//是死循环
do:循环语句的循环体。C语言中 do 是执行某代码块的意思,do 关键字不能单独使用,通常用在 do…while循环中。do…while 循环是在循环的尾部检查它的条件,do…while 循环与 while 循环类似,但是 do…while 循环不管真假都至少执行一次循环。
int main(void)
{
   do
	 {
      //循环体
	 }
	
 while(条件表达式); //先执行一次再判断条件,真继续循环,假往下走。
}

注意:while()的 英文分号 必须要加,不加会报错。


while:循环语句的循环条件,while 语句创建了一个循环,重复执行直到条件表达式为假。while 语句是一种入口条件循环,也就是说,在执行多次循环之前已决定是否执行循环。因此,循环有可能不被执行。也就是说可以执行0次到无数次。循环体可以是简单语句,也可以是复合语句。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>

typedef unsigned char    u8;
typedef unsigned short   u16;
typedef unsigned int     u32;


int main(void)
{
   u8 a=2,b=2;
   u16 i=0,sum=0;  

   while(0)  //不满足条件,循环体执行0次
   {
      sum = 10;
      printf("sum=%hu\r\n",sum);
   }
   while(a==b) //满足条件后改变条件后不满足,循环体执行1次
   {
      a=5;
      sum = 20;
      printf("sum=%hu\r\n",sum);
   }
   while(1)  //死循环
   {
      sum = 30;
      printf("sum=%hu\r\n",sum);
   }
  
  while(0);  //条件不成立,往下走 括号里面不是循环体
   {
      sum = 10;  //执行
      printf("sum=%hu\r\n",sum);
   }
   while(a==b); //条件成立,卡死,不会在往下走,括号里面不是循环体
   {
      a=5;
      sum = 20;
      printf("sum=%hu\r\n",sum);
   }
   while(1);  //条件成立,卡死,不会在往下走,括号里面不是循环体
   {
      sum = 30;
      printf("sum=%hu\r\n",sum);
   } 
   
}
注意:while 不带分号时while 与后面大括号内的循环体构成一个整体。满足条件才会执行{ }内容。不满足直接跳过。当while后紧跟分号时,while这一行自成一个空循环,{} 里面的代码不是循环体,一般用于卡程序不让它结束或者等待状态改变。满足条件时卡死在while,不满足条件往下走,括号里面的代码执行一次往下走。
break:跳出当前循环。break用于跳出一个循环体。多个嵌套时,只能跳出当前循环。如果想结束多层嵌套循环,直接在最外层循环加break,就能跳出全部循环。break可以跳出当前任何循环,包括死循环。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>

typedef unsigned char    u8;
typedef unsigned short   u16;
typedef unsigned int     u32;


int main(void)
{
   u8 a=2,b=2;
   u16 i=0,j=0,sum=0;  

   while(1)
   {
      sum = 30;
      printf("sum=%hu\r\n",sum);
      break;  //跳出while死循环往下执行
   }
   for(i=0;i<10;i++)
   {
      //如果这里加break,直接跳出所有循环
      for(j=0;j<10;j++)
     {
        break;  //只能跳出j循环,循环会继续。
        printf("j:i=%d,j=%d\r\n",i,j);
       
     }
     
     printf("i:i=%d,j=%d\r\n",i,j);
   }
   
   printf("i=%d,j=%d\r\n",i,j);
}
	
while(1)
{
  //这里加break,直接跳出所有循环
  switch(Num)
  {
    case 1: break; 
    case 2: break;
    case 3: break;
    case 4: break;
    default:break;  //跳出Switch,没有跳出while
  }
}
continue:跳过本次循环,开始下一次循环。continue 语句只结束本次循环,而不终止整个循环的执行。如果条件一直成立,则会一直循环。而 break 语句则是结束整个循环过程,不再判断执行循环的条件是否成立。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>

typedef unsigned char    u8;
typedef unsigned short   u16;
typedef unsigned int     u32;


int main(void)
{
   u8 a=2,b=2;
   u16 i=0,j=0,sum=0;  

   while(1)
   {
      continue; //不会执行后面的语句但是不会跳出while循环
      sum = 30;
      printf("sum=%hu\r\n",sum);
     
   }
   for(i=0;i<10;i++)
   {
    
      for(j=0;j<10;j++)
     {
        continue;//不会执行后面的语句但是不会跳出j循环,j++会执行,条件依然判断。
        printf("j:i=%d,j=%d\r\n",i,j);
       
     }
     
     printf("i:i=%d,j=%d\r\n",i,j);
   }
   
   printf("i=%d,j=%d\r\n",i,j);
}
2、条件语句类型关键字

在这里插入图片描述

if:条件语句。if (表达式) {语句;}。表达式成立(为真),则语句执行,若表达式不成立(为假),则语句不执行。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>

typedef unsigned char    u8;
typedef unsigned short   u16;
typedef unsigned int     u32;


int main(void)
{
   u8 a=2,b=2;
   u16 i=0,j=0,sum=0;  

   if(0) //表达式为假,不执行
   {
      sum = 30;
      printf("sum=%hu\r\n",sum);
     
   }
   if(1)//表达式为真,不执行
   {

      sum = 60;
      printf("sum=%hu\r\n",sum);
     
   }
   
   
}
else:条件语句否定分支 ,与if 连用,不能单独使用。if (表达式) {语句;} else {语句;} 在C语言中 else 是与 if 一起使用的一个关键字,表示如果满足if条件则不执行 else ,否则执行else 。if 不执行,则else一定执行。if执行,则else 一定不执行。两者互斥。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>

typedef unsigned char    u8;
typedef unsigned short   u16;
typedef unsigned int     u32;


int main(void)
{
   u8 a=2,b=2;
   u16 i=0,j=0,sum=0;  

   if(0) //表达式为假,不执行
   {

      sum = 30;
      printf("sum=%hu\r\n",sum);
     
   }
  else  //if 不执行,则else一定执行。if执行,则else 一定不执行。两者互斥
   {
      sum = 60;
      printf("sum=%hu\r\n",sum);
     
   }
   
   
}
	
goto:无条件跳转语句。goto 语句可以使程序在没有任何条件的情况下跳转到指定的位置,所以 goto 语句又被称为是无条件跳转语句。使用 goto 语句只能 goto 到同一函数内,而不能从一个函数里 goto 到另外一个函数里。goto在同一函数里面可以随便跳转,可以跳出死循环,可以跳出多层嵌套循环。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>

typedef unsigned char    u8;
typedef unsigned short   u16;
typedef unsigned int     u32;


int main(void)
{
   u8 a=2,b=2;
   u16 i=0,j=0,k=0,sum=0;  

Start:
   printf("start\r\n");
   for(i=0;i<10;i++)
   {
       for(j=0;j<10;j++)
     {
       for(k=0;k<10;k++)
       {
        printf("i=%d,j=%d\r\n",i,j);
        goto while1;  //直接跳出三层循环去到goto位置
       }
     }
   }
while1:
   while(1)
   {
      printf("while\r\n");
      goto Stop; //直接跳出死循环去到goto位置
   } 
Stop:
   printf("stop\r\n");   
   
}
3、开关语句类型关键字

在这里插入图片描述

switch:开关语句。switch 语句也是一种分支语句,常常用于多分支、多种状态的情况。也就是常说的状态机。必须与case一起使用。
case:开关语句分支。case 常量表达式只是起语句标号作用,并不是该处进行条件判断。在执行 switch 语句时,根据 switch 的表达式,找到与之匹配的 case 语句,就从此 case 子句执行下去,不在进行判断,直到碰到 break 或函数结束为止。必须与switch 一起使用。
default:开关语句中的“其他”分支。default 的作用就是switch语句里所有的 case 都不成立时所要执行的语句。default 关键字用来标记switch语句中的默认分支。必须与switch一起使用。
int main(void)
{
   u8 a=7,b=2;
   u16 i=0,j=0,k=0,sum=0;  

   switch(a)
   {
    case 1: printf("1\r\n");break;  //执行语句多时每个分支可以加{},看起来更清晰
    case 2: printf("2\r\n");break;
    case 3: printf("3\r\n");break;
    case 4: printf("4\r\n");break;
    default:printf("D\r\n");break;  
   }
   
}
4、返回语句关键字
return:终止该函数。并返回一个指定的值。可以是任何类型的数据,可以是变量,也可以是表达式。当函数执行return后,函数体将被赋值为函数的返回值,由 return 后面的参数指定。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>

typedef unsigned char    u8;
typedef unsigned short   u16;
typedef unsigned int     u32;


int Ret(void)
{
  u8 a=7,b=2;
  return a+b; //结束函数并返回表达式
  printf("Ret\r\n");//上面函数已经结束,不会在执行该语句
}

int main(void)
{
   int ret=0;
   u16 i=0,j=0,k=0,sum=0;  

   switch(3)
   {
    case 1: printf("1\r\n");break; 
    case 2: printf("2\r\n");break;
    case 3: printf("3\r\n");break;
    case 4: printf("4\r\n");break;
    default:printf("D\r\n");break;  //跳出Switch,没有跳出while
   }
   ret = Ret();
   printf("ret=%d\r\n",ret);
}
	

存储类型关键字

在这里插入图片描述

auto:声明自动变量。auto的原理就是根据后面的值,来推测前面的类型 。可以简化变量的初始化。

auto 声明的变量尽量初始化赋值,不然可能没办法判断变量是什么类型。
auto 不能直接声明数组。
auto是一个占位符,并不是一个他自己的类型,因此不能用于类型转换或其他一些操作,如sizeof会报错。

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

typedef unsigned char    u8;
typedef unsigned short   u16;
typedef unsigned int     u32;


auto buff[10]={1,2,3}; //不能直接定义数组
int main(void)
{
   auto b = 5;
   auto a;
   auto* p;
  

   printf("sizeof(auto)=%d\r\n",sizeof(auto)); //auto不是数据类型,不能进行sizeof

   printf("a=%d\r\n",a); //默认int型,不赋值随机值
   printf("p=%d\r\n",p); //没有指向型地址,野指针随机乱指,会出错
   printf("*p=%d\r\n",*p); //没有指向型地址,野指针随机乱指,值也是随机变化
   p = &b;  //指向变量b的地址
   printf("p-&b=%d\r\n",*p); //取变量b的值
}
	
extern:用在变量或函数的声明前,用来说明“此变量/函数是在别处定义的,要在此处引用。

对于变量:a.c文件中定义的变量如果需要去b.c文件中使用有两种方式:
1.在a.h中: extern 变量;然后在b.c中包含a.h即可使用。
2.直接在b.c中: extern 变量;即可使用。

对于函数: a.c文件中定义的函数需要去b.c文件中调用也有两种方式:
1.在a.h 中 :函数名;在b.c中包含a.h即可使用。
2.直接b.c中: extern 函数名;即可使用。

/*******************a.h**************/
#inlcude "b.h" //可以把b.h放到a.h中,a.c只用包含a.h就可以。也可以直接把b.h放入a.c中


/*******************a.c**************/
#include "a.h"
#include "b.h" //可以把b.h放到a.h中,a.c只用包含a.h就可以。也可以直接把b.h放入a.c中2extern u8 flag; //直接在a.c中 extern 变量也可以使用b.c中的变量2extern void fun(void); //直接在a.c中 extern 函数也可以使用b.c中的函数
int main(void)
{
   fun();
}
	
/***********************b.h*************************/1extern u8 flag;  //在b.h中extern 变量;在a.c中包含b.h。a.c就可使用该变量;1void fun(void);  //在b.h中声明函数;在a.c中包含b.h。a.c就可使用该函数;

/***********************b.c*************************/

#include "b.h"	

u8 flag;


void fun(void)
{



}
	
register:用 register 声明的变量是寄存器变量,是存放在CPU的寄存器里的,让该变量的访问速度达到最快。平时声明的变量是存放在内存中的。虽说内存的速度已经很快了,不过跟寄存器比起来还是差得远。这有效地提高了效率。

CPU操作的每一个操作结果,都由寄存器来暂时保存,最后才写入内存或从内存中读出。也就是说,变量的值通常保存在内存中,CPU对变量进行读取是先将变量的值从内存中读取到寄存器中,再进行运算,运算完后将结果写回内存中。
C语言允许使用寄存器保存变量的值,很明显这样能大大提高程序的执行速度。但是,寄存器的个数是十分有限的,我们不可能将全部的变量都声明为寄存器变量,因为其他代码也需要使用寄存器,同样,我们声明的寄存器变量也不一定直接保存在寄存器中,因为寄存器可能全部都被其他代码占用。编译器只能尽量把变量安排在寄存器中。

所以在使用寄存器变量时,请注意:
(1)待声明为寄存器变量的类型应该是CPU寄存器所能接受的类型,寄存器变量是单个变量,变量长度应该小于等于寄存器长度。
(2)不能对寄存器变量使用取地址符“&”,因为该变量没有内存地址。
(3)尽量在大量、频繁操作时使用寄存器变量,且声明的变量个数应该尽量少。

static:static 不仅可以用来修饰变量,还可以用来修饰函数。在使用 static 关键字修饰变量时,我们称此变量为静态变量。静态变量的存储方式与全局变量一样,都是静态存储方式。

修饰变量时:
1.局部变量:被static修饰的局部变量其作用时间发生改变,生命周期变长。跟全局变量有点像。存储从栈区变为静态区,默认初始值为0,且该函数结束后,变量不会被回收,仍然存在。只有主函数结束时才回收。且多次调用该函数,变量也只初始化一次。不像未被修饰的局部变量每次调用函数都会回收再分配,每次都重新初始化。
2.全局变量:被static修饰的全局变量其作用域发生改变,作用范围变小。只能在本文件中使用,不能在通过外部声明去其他文件中使用。所以其他文件里面可以出现相同的变量名。被static修饰的全局变量不需要担心在其他文件里面是否有变量重名。对于其他文件,被修饰的全局变量已经自动隐藏。

修饰函数:被static修饰的函数其作用域发生改变,作用范围变小。只能在本文件中使用,不能在通过外部声明去其他文件中调用该函数。所以在其他文件中可以出现同函数名的函数。被static修饰的函数不需要担心在其他文件是否重名。对于其他文件里面,被修饰的函数已经自动隐藏。

#include "main.h"
static u8 b; //被修饰的全局变量不能去其他文件中使用,其他文件里面可以定义相同名的全局变量

int main(void)
{
  return 0; 
} 

static void fun(void) //被修饰的函数不能去其他文件里面使用,其他文件里面可以定义相同名的函数名
{
  static u8 a;  //修饰后默认值为0
  u8 b = 0;   //未修饰不赋值是随机值
  
}

其他关键字

在这里插入图片描述

const:const修饰变量、指针、参数、返回值等,被修饰后变为只读。即一旦赋初值就不能被修改。保护被修饰的内容,防止意外修改,增强程序的健壮性。可以看成常量。const修饰时,一定要赋值,不然后面没办法在赋值了。编译器不为只读变量分配内存,这使得它的效率也很高。

1、修饰变量

u8 const a = 2;
const u8 a = 2; //两种写法是一样的,都是把a变为只读,不能修改其值。

2、修饰指针

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

typedef unsigned char    u8;
typedef unsigned short   u16;
typedef unsigned int     u32;


int main(void)
{
   int a = 3,b = 4,c = 5,d = 6,e = 2;
   
   const int *p1 = &a;  //修饰指针指向的内容,即指向的内容不能改变,但是指向的方向可以改变
   int* const p2 = &a;  //修饰指针的指向,即指向的内容可以改变,但是指向的方向不能改变
   int const *p3 = &a;  //修饰指针指向的内容,即指向的内容不能改变,但是指向的方向可以改变
   const int* const p4 =&a; //修饰指针指向的内容和指向,即指向的内容不能改变,指向的方向不能改变
   const int const *p5 =&a; //修饰指针指向的内容,即指向的内容不能改变,但是指向的方向可以改变
   p1 = &b;
   printf("*p1 = %d\r\n",*p1);
   *p1 = 8; //报错
   printf("*p1 = %d\r\n",*p1);
   
   p2 = &c; //报错
   printf("*p2 = %d\r\n",*p2);
   *p2 = 9;
   printf("*p2 = %d\r\n",*p2);
   
   p3 = &d;
   printf("*p3 = %d\r\n",*p3);
   *p3 = 10; //报错
   printf("*p3 = %d\r\n",*p3);
   
   p4 = &e; //报错
   printf("*p4 = %d\r\n",*p4);
   *p4 = 11;//报错
   printf("*p4 = %d\r\n",*p4);
  
   p5 = &e;
   printf("*p4 = %d\r\n",*p5);
   *p5 = 11; //报错
   printf("*p4 = %d\r\n",*p5);
}
	

总结:只要const 后面不是紧跟指针名(P),其指针指向性就能改变。只要 *号没有被修饰,也就是const在 *号后面,其指针指向的内容就能改变。

3、修饰参数

当需要把一个内容的地址复制到其他一个或多个地址时,且需要经常用到该功能,封装成函数是最方便的。但是又怕操作过程中把源地址内容改了,所有在传参时候可以把源地址用const修饰变为只读就不怕被修改。

void CopyFunction(char *Dest, const char *Srce); //把源地址的内容复制到目标地址

4、修饰返回值
如果给以“指针传递”方式的函数返回值加const 修饰,那么函数返回值(即指针)的内容不能被修改,该返回值只能被赋给加const 修饰的同类型指针。在值传递的过程种,将返回值的属性指定为const是没有任何意义的。只有在返回值是指针类型的时候,才会用到const。

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

typedef unsigned char    u8;
typedef unsigned short   u16;
typedef unsigned int     u32;

const int *fun(void)
{
    
    u8 i =0;
    int buff[5]={0};
    const int *p = buff;
    for(i=0;i<5;i++)
    {
      buff[i] = i;
    }
   return buff; //直接返回buff,会出现问题
   return p;   //直接返回buff,会出现问题,需要一个同类型指针指向地址后再返回
   
}

int main(void)
{
   u8 i =0;
   int *p1 = NULL;
   const int *p2 = &i;
   
   p1 = fun();  //不用const修饰的指针虽然报警告,但是可以正常接收
   p2 = fun();  //可以改变指针方向不能改变内容
   const int *p3  = fun(); //相同类型接收
   p1[2] = 5; //把buff[2]的值改变了。所以接收返回值需要使用相同类型接收,不然容易出问题
   p2[2] = 5; //报错,不能改变其内容
   for(i=0;i<5;i++)
   {
     printf("p1[%d]=%d\r\n",i,p1[i]);
     printf("p2[%d]=%d\r\n",i,p2[i]);
     printf("p3[%d]=%d\r\n",i,p3[i]);
   }
   printf("HHH\r\n");
}
	

sizeof:sizeof 的作用就是返回一个对象或者类型所占的内存字节数。后面跟一对括号,但它不是函数而是关键字

ret = sizeof(char);  //返回其所占字节大小
ret = sizeof(int);   //返回其所占字节大小
ret = sizeof(结构体); //返回其所占字节大小

typedef:在C语言中,除系统定义的标准类型和用户自定义的结构体、共用体等类型之外,还可以使用类型说明语句 typedef 定义新的类型来代替已有的类型。 意思是给一个已经存在的数据类型取一个别名,而不是定义新的数据类型。

我们平时用的u8,u16,u32等都是给现有类型取的别名而已不是新的类型。

typedef unsigned char    u8;
typedef unsigned short   u16;
typedef unsigned int     u32;

typedef struct 
{
  u8 a;
  u16 b;
  u32 c;
  u8 *p;
}DefName; //该结构体新名字,可以去定义结构体使用

DefName DefStruct1,DefStruct2;

typedef enum{
	INIT_DATA_TRAN_BY_TRAN,  //不赋值默认从0开始,赋值后面的就依次加1
	WAIT_ANCHOR_DATA_MSG,
	SEND_COMMAND_TO_DATA_TRAN,
	CHECK_ANCHOR_AND_TAG_MODE,
	ENSURE_ANCHOR_AND_TAG_IN_TRAN_MODE,
	TRAN_SEND_MSG,
	TRAN_SEND_UART_MSG,
	TRAN_WAIT_TAG_MSG,
	SEND_COMMAND_TAG_TO_LOCATE_MODE,
	LISTEN_TAG_MSG,
	ENSURE_TAG_IN_LOCATE_MODE,
	SEND_COMMAND_ANCHOR_TO_LOCATE_MODE,
	LISTEN_ANCHOR_RESP,
	ENSURE_ANCHOR_IN_LOCATE_MODE,

}TRAN_DATA_BY_TRAN_TRAN_STEP;  //可以直接使用也可以定义更多该枚举

TRAN_DATA_BY_TRAN_TRAN_STEP DefEnum1,DefEnum2;

volatile:被修饰的内容会防止编译器优化。一般修饰经常变化和使用的变量。如果在很多地方都改变,那读取的变量的值可能是在寄存器的值(寄存器没有更新最新值),而不是变化后最新的值。被volatile修饰后,可以确保每次都能读到最新的值。

当读取一个变量时候,为了提高读取速度,编译器进行优化时会先把变量读取到一个寄存器中,以后,当再次读取变量时,就直接从寄存器中读取,当变量值在本线程里改变时,会同时把变量的新值copy到寄存器中,以保持一致,当变量因别的线程发生改变,寄存器的值可能没有及时改变,从而造成应用程序读取的值和实际的变量值不一致,当寄存器因为别的线程改变了值,原来的变量可能不会改变,也会造成应用程序读取的值和实际的变量值不一致

 int a =23,b = 25;
 int Value1 = 0;
 volatile int Value2 = 0;
void fun1(void)
{
   Value1 = (a+b)*a;
   Value2 = (a+b)*a;
}
void fun2(void)
{
   Value1 = (a+b)*b;
   Value2 = (a+b)*b;
}
void fun3(void)
{
   Value1 = (a+b)*a*b;
   Value2 = (a+b)*a*b;
}
int main(void)
{
  while(1)
  {
      fun1();
      fun2();
      fun3();
      printf("Value1=%d\r\n",Value1);//每次提取的值不一定是最新的值,可能是上次或上几次在寄存器没有更新的值
      printf("Value2=%d\r\n",Value2); //保证每次都是最新的值   
     
  }
}
  • 8
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值