exit() 的含义 :提前结束程序 .c 文件
return 的含义 :提前结束函数,其所在行以下,整体大函数底花括号以上,2者之间的所有语句都不会被执行到,用它来提前结束程序。
break 的含义 :提前结束其所在的本层循环,即break所在行的上层for,switch,while
continue 的含义 :提前结束其所在的本次循环
Static与Const的区别
1、static 局部变量 将一个变量声明为函数的局部变量,那么这个局部变量在函数执行完成之后不会被释放,而是继续保留在内存中,其值是可随时变得,只是内存空间不释放,下次在用此内部变量时其值为上次结束时的值,可以理解为一个作用域限制于函数体内的全局变量。
2、static 全局变量 表示一个变量在当前文件的全局内可访问,值可变。
3、static 函数 表示一个函数只能在当前文件中被访问
4、static 类成员变量 表示这个成员为全类所共有
5、static 类成员函数 表示这个函数为全类所共有,而且只能访问静态成员变量
static 关键字的作用:
1、函数体内static变量的作用范围为该函数体,该变量的内存只被分配一次,因此其值在下次调用时仍维持上次的值(值可变);
2、在模块内的static全局变量和函数可以被模块内的函数访问,但不能被模块外其它函数访问;
3、在类中的static成员变量属于整个类所拥有,对类的所有对象只有一份拷贝;
4、在类中的static成员函数属于整个类所拥有,这个函数不接收this指针,因而只能访问类的static成员变量。
enum 关键字的作用:
1、不同枚举中的名字必须互不相同, 同一枚举中不同的名字可以有相同的值.
2、枚举类型:指一个被命名的整型常数的集合。即枚举类型,本质上是一组常数的集合体,只是这些常数有各自的命名。枚举类型,是一种用户自定义数据类型。
3、枚举变量:由枚举类型定义的变量。枚举变量的大小,即枚举类型所占内存的大小。由于枚举变量的赋值,一次只能存放枚举结构中的某个常数。所以枚举变量的大小,实质是常数所占内存空间的大小(常数为int类型,当前主流的编译器中一般是32位机器和64位机器中int型都是4个字节),枚举类型所占内存大小也是这样。
const 关键字的作用:
1、const 常量:定义时就初始化,以后不能更改。
2、const 形参:func(const int a){}; 该形参在函数里不能改变
3、const 非指针类型的全局/局部变量,其值固定不可变; Const int a ,a的值不可变; const int *a, a值可变。
4、const char * :const在 * 前边,所以叫常量指针,即一个指向不可变的常量的指针,一个指向char类型的指针且不能更改指向地址上的值。 (const char *p 等价 char const *p) 即不能通过p去修改p指向地址内的值
5、char * const p : * 在 const 前边,所以叫指针常量,即一个值是固定值的指针变量,即不能更改指向的地址,但可以通过p去修改p指向地址内的值。
const 和 #define 的区别
1、const定义的常量时带类型, define不带类型。
2、const是在编译、运行的时候起作用:而define是在编译的预处理阶段起作用。
3、define只是简单的替换,没有类型检查。简单的字符串替换会导致边界效应。
4、const常量可以进行调试的, define是不能进行调试的,主要是预编译阶段就已经替换掉了,调试的时候就没它了。
5、const不能重定义,不可以定义两个一样的,而define通过undef取消某个符号的定义,再重新定义。
6、由于const常量一旦被创建后其值就不能再改变,所以const常量必须在定义的同时赋值(初始化),后面的任何赋值行为都将引发错误。初始化const常量可以使用任意形式的表达式,既可以在运行时初始化也可以在编译时初始化。
举例: int n = 90;
const int MaxNum1 = getNum(); //运行时初始化
const int MaxNum2 = n; //运行时初始化
const int MaxNum3 = 80; //编译时初始化
define 关键字的作用:
1、通常宏定义出现在 .h头文件之中,也就是说在其他文件中只要你包含了这个头文件,你就可以用此宏定义了;如果是定义在 .c文件之中的,那就只能在该c文件中使用了。
2、#if !defined symbol 等价 #ifndef symbol (如果没定义symbol则…)
3、宏定义加括号可使其为一整体,不用考虑宏带入后的优先级错误。
eg:若 Define a 2+3 则 2*a == 7;若 Define a (2+3) 则 2*a == 10
define 和 typedef 的区别:
1、typedef是给已存在的重命名,而define可在任何条件下重命名。
2、typedef int hh ->hh a; 等同于 int a;
3、typedef char hhh[6] ->hhh a; 等同于 char a[6];
4、typedef char(hhh)[6] -> hhh b 等同于 char b[6];
5、typedef (*ccc)(char c) -> ccc是一个 只有一个char形参的函数指针,调用函数指针执行具体的函数时,函数指针前加不加*都可以。
6、define 之后第一个连续的字符串(由字母数字下划线构成,\拼接不起效) 等同于后面的一堆 (\拼接起效)
7、typedef 最后一个连续的字符串 等同于中间的一堆(不完全准确,有些可以这样理解)。
8、没有赋值的宏在预处理时会被删掉, 比如 #define FUCK if(1==FUCK)就会变为if(1==)会报错。但是作为#if define FUCK的判断依旧会成立
9、typedef struct _type
{
uint16_t aa;
uint8_t payload[ ];
}type1, *type2,frame_t; sizeof(frame_t) == 2, uint8_t payload[ ] 或 uint8_t payload[ 0 ] 的形式都不占内存空间,这种书写形式叫柔性数组
{
uint16_t aa;
uint8_t payload[ ];
}type1, *type2,frame_t; sizeof(frame_t) == 2, uint8_t payload[ ] 或 uint8_t payload[ 0 ] 的形式都不占内存空间,这种书写形式叫柔性数组
而第2行如果写成 uint8_t *payload,则sizeof(frame_t) == 6 ; 这里一共定义了 3 种新的数据类型,分别是type1, type2, frame_t; 其中类型type1和frame_t等同于类型(struct _type),而类型type2等同于类型(struct _type *)
10、typedef 理解, 只要有typedef必然是在定义自定义类型名
typedef int(*p)(int);
定义了一个类型名为 p 的新类型,这种类型用于定义函数指针变量。 p fun; 定义了一个p类型的变量fun,p不占内存,fun占内存, fun指向返回值为int型并且有一个int型参数的函数。
int (*p)(int);
当没有typedef时,表示既定义了一个类型为 int(*)(int)的新类型,同时也定义了一个此类型的变量p,此时p占内存,一般这种形式出现在结构体中。此类型是一个指针类型,用于指向返回值为int型并且有一个int型参数的函数。
void (*fun)(void*)
fun既不是函数名也不是指针变量名而是个类型名,此类型指向一个参数是viod*,且返回值是void的函数。(函数指针)
void *fun(void*)
fun是个函数名,这个函数的参数是void*,函数返回一个viod*的指针;等同于(void*)fun(void*)。(指针函数)
int (*fun())(int)
注意结尾无;因为变量名fun()中带(),所以这里定义一个名为fun的函数,函数的参数为(void), 返回值为int (*)(int)类型,可理解为 int(*)(int) fun(void) { 函数体 } 如果结尾有; 就是对此函数的声明。
void (*signal(int sig, void (*func)(int)))(int)
定义了一个返回类型为 void(*)(int), 一个参数类型为int,一个参数类型为 void(*)(int) 的函数signal
可理解为 void(*)(int) signal (int sig, void (*func)(int)) { } 这样的形式.
typedef void target_t;
这个定义创建了一个名为 target_t 的类型,等同于 #define target_t void
typedef void *target_t;
这个定义创建了一个名为 target_t 的类型,等同于 #define target_t (void *)
typedef void target_t();
这个定义创建了一个名为 target_t 的函数类型,该函数不接收任何参数,并返回 void(即不返回任何值)。这个类型可以用来声明函数,但不是用来声明函数指针。 eg: target_t my_function; //声明一个函数
typedef void (*target_t)();
这个定义创建了一个名为 target_t 的指针类型,该指针指向一个返回 void 并且不接收任何参数的函数。这个类型可以用来声明指向函数的指针。typedef void target_t(); 定义了一个函数的类型,而typedef void (*target_t)(); 定义了一个指向函数的指针的类型。
eg: target_t pfun; //声明一个指向函数的指针
void my_function(void) { ; }
pfun = my_function; //将函数的地址赋给指针
#include 或 #define 中 ‘#’ 的作用:
1、表示编译预处理,#开始的语句是在编译器开始编译之前由编译预处理程序先进行处理,比如#include 就是将include后面的头文件内容插入到include 语句处
2、#用在预编译语句里面可以把预编译函数的变量直接格式成字符串;注意,不能直接在其它非预编译函数直接使用#a的形式,假如main函数里面出现printf("the square of " #x " is %d.\n",(x)*(x))是不能通过编译的.而#define Func1(x) printf("the square of "#x" is %d.\n",(x)*(x))是对的
3、##是宏连接符,作变量链接; #用来把参数转化为字符串。 eg:#define HHH(n) int##n 则HHH(32)就等同于int32
inline 关键字的作用:
1、inline函数调用时,不需要繁琐的入栈、出栈过程,这一点相对于普通函数执行效率会高很多;所以如果不希望函数调用发生栈的操作,则把被调函数申明为inline。
2、inline函数在编译时,会将函数体指令码直接插入到调用点,这样函数的调用更加简单,但是如果调用点多的话,会增加代码体积;相比于宏,inline函数是函数,可以单步调试,宏是在预编译过程中直接替换文本;
3、虽说inline函数执行时效率高,但是不能把所有的函数都写成inline函数,一般inline函数里面不能有循环语句,一般是把频繁调用且简短的代码写成inline函数。
4、可以将函数体的内容用宏封装后 等效替换 内联inline
extern 关键字的作用:
1、函数本身不加任何修饰的话就是extern的,加上extern的修饰会加速程序的预处理,而全局变量在定义文件外引用就需要此修饰变量。
2、在函数使用的地方,extern函数名可以不用管函数具体定义位置,也不用包含相应头文件,即可使用。
sizeof 关键字的作用:sizeof是运算符,不是函数名。
uint8_t *Number = "ASDASD"; sizeof(Number)==4。 strlen(Number)==6。
uint8_t Number[] = "ASDASD"; sizeof(Number)==7。 strlen(Number)==6。
uint8_t Number[10] = "ASD"; sizeof(Number)==10。 strlen(Number)==3。
uint8_t Number[]="0X36,0X16,0X0D,0X0A"; sizeof(Number)==5,strlen(Number)==4。
uint16_t a[10]; sizeof(a)==20。uint32_t a[10]; sizeof(a)==40。
sizeof(char) = 1; sizeof(char *) = 4; sizeof(int) = 4;
int a sizeof(a) = sizeof(int) = 4;
char a[] = hello; char *p = a; sizeof(p) = 4;
sizeof(*p) = sizeof('h') = 1;
所有字符数组在定义时,长度必须比实际需要的长1,以自动补充一个‘\0‘,方便字符操作函数strcat,strlen等的处理。后补的字符串会自动覆盖前面字符串的'\0'。 结构体的大小只能用sizeof,而不能用strlen
void 关键字的使用规则:
1、如果函数没有返回值,那么应声明为void类型;
2、如果函数无参数,那么应声明其参数为void;
3、如果函数的参数可以是任意类型指针,那么应声明其参数为void * ;void*的指针变量是指此指针变量可以被赋予任意类型的指针值,而不是说void*的指针变量可以指向任何数据类型。viod*只传递地址值,不具有指向功能。所以形参是void*的函数,被调时会被赋予一个指向具体数据类型的指针,但在调用函数中不能直接引用此形参指针,须在被调函数中定义个与实参同类型的指针,然后将形参指针的值赋予被调函数中定义的指针,然后操作此指针。如果形参是指向具体类型的指针就不必再定义同类型指针传递,而是直接操作此行参即可。void *变量的加减等同于uint32数的加减,不同于确定类型的sizeof(类型)整数倍的加减
4、void不能代表一个真实的变量;
void 的含义:
1、void的字面意思是“无类型”,void *则为“无类型指针”,void *可以指向任何类型的数据。
2、void几乎只有“注释”和限制程序的作用,因为从来没有人会定义一个void变量;void真正发挥的作用在于对函数返回的限定 和 对函数参数的限定。
Struct 关键字的使用规则:
结构体内的成员在内存中是连续存储的,类似数组;但如果结构体内有指针,则指针的位置只占4字节,它只是指向另外一片连续的空间,而不是把这个空间整体放到指针的位置。对于任意的结构体变量a,可以通过&a来获取首地址,等同于结构体内第一个元素的首地址。结构体类型变量既可以作为参数值传入也可以作为返回值返回,这不符合c99标准,但现在的编译器都支持.
1、大量数据通过 a.txt 文件引入程序的方式:
1、a.txt中存入格式 ("hello word"); 必须有()和 " "
程序中: print 此行不需要 ' \ '
#include a.txt 等同于print(“hello word”);
若文件中没有;号,则在代码下行写入;号
2、a.txt中存入“hello word”则引用字符串方式为:
const char *p = // 必须单写一行
#include a.txt // 必须单写一行
; // 必须单写一行
print(“%s”, p); // 则输出 “hello word”
2、函数体的 { } 分别对应压栈和出栈操作,{ } 内定义的变量只能在 { } 内使用, 函数的形参属于局部变量也只能在 { } 内起效,多个形参从右向左压入栈。 形参对应的实参是在主调函数体内定义的栈变量,而不是在被调函数中定义的。
3、三种循环体的区别:
while 会先执行()内的条件语句,如果为假或值为0,则不会进入函数体,直接退出;这样就有可能导致函数体的语句完全没有被执行。
do while 会先执行一次函数体,之后再执行()内的条件语句,根据条件语句的真假,决定函数体是否再次执行;
goto标签可以用 do while() 实现,eg:
label_xx: do
{ ... { ...
} 等同于 }
goto label_xx; while (goto label_xx的条件);
while 与 do while()内的条件语句如果是多条 && 的形式,在前面的条件为假时,后面的语句不会执行。
for 会先执行第一个;前的所有语句,之后执行第二个;前的条件语句,根据条件语句的真假,决定是否执行函数体,函数体执行完毕后会执行第二个;后的语句,之后再次执行第二个;前的条件语句,并根据条件语句的真假,决定是否执行函数体,实现循环。
for 中的 continue 会执行第二个;后的语句,但for中的 break 则不会执行第二个;后的语句。 因此如果将第二个;后的语句从第二个;后移动到continue所在行下,则continue时就不会执行那条语句。
4、一个 uint32 * 类型的指针变量,其值必然是 4 的整数倍;因为其指向一个个的 uint32 数,而 uint32 的数占用 4 字节宽度;
同理 uint8 * 类型的指针变量,其值必然是 1 的整数倍;uint16 * 类型的指针变量,其值必然是 2 的整数倍;一个包含2个 uint32 成员的结构体变量指针,其值必然是 8 的整数倍;
一个包含1个 uint32 成员和1个 uint8 成员的结构体变量指针,如果定义结构体时声明 4 字节对齐则其值必然是 8 的整数倍,若定义结构体时声明 1 字节对齐则其值必然是 5 的整数倍。
5、 *p++ 和 (*p)++ 的区别是:*p++ 示取p所指单元的值,p指向下一单元,即p值加1,*p++ 是指下一个地址; 但是 (*p)++ 是指将 *p 所指的数据的值加一,p值不变;
*p++,*(p)++, *(p++) 3者等价
示例: int x,y,a[5]={1,2,3,4,5},*p=a,*q=a;
x=*p++;//执行这一句后x=a[0]=1,p=a+1=&a[1]
y=(*q)++;//执行这一句后,y=1, a[0]=2,q仍然=a
6、
![]()

7、对于一维数组 buf[N] 而言 buf == &buf[0] == &buf == 这片连续N字节内存的起始地址值;
对于二维数组 buf[M][N]而言,buf == &buf[0] == &buf == buf + 0 == buf[0] == 这片连续M*N字节内存的起始地址值;
因为 a[1] 是一维数组某个成员值,a是一维数组起始地址,故 a[1][1]是二维数组某个成员值,a[1] == a + 1是某一行一维数组起始地址(即最右侧[ ]前的一堆为一维数组的起始地址),
而且对于数组a[ ]又有 a == &a == &a[0],因此二维数组某一行存在 a[n] == a + n == &a[n][0]
浙公网安备 33010602011771号