C Pointer plus
补充回顾
标量:这个名称似乎是《C和指针》的特有名词,他是为了区分数组而产生,数组可以通过下标或者数组名间接访问,可以理解为向量,而标量则与其相反,例如结构体,但是在某些博客上,则认为标量是指枚举类型、字符类型以及整数类型
int i; int *pi; int **pii; pi=&a; pii=π //通过以上赋值语句,可以使下列三条赋值语句等价 i='a'; *pi='a'; **pii='a';
int *f;//将*f声明为整数,因此f为指向整型的指针 int *f();//函数调用操作符的优先级高于间接访问操作符,所以先执行函数调用,因此返回值为int* int (*f)()//函数指针,先执行小括号,函数指针的基本类型为 void (*变量名)() int* (*f)()//依然为函数指针,只不过上一个返回类型为int,而这一个为int* int f[ ]//整型数组f int *f[ ]//数组名为f,数组类型为指向整型的指针 int f()[ ]//这个声明是不合法,因为f会与()相结合形成函数,那么其返回类型为整型数组,是非法的 int f[ ]()//[ ]和()的结合性从左向右,所以这是一个数组,类型为返回类型为整型的函数,但是这个声明是非法,因为数组元素的长度 必须相等,而不同的函数的长度显然不相等 int (*f[ ])()//这个与上一个的目的类似,但是说这一次的数组类型为函数指针,尽管不同函数的长度可能不同,但是其指针长度是一致的 int* (*f[ ])()//这个与上一个函数的目的类似,只是这一函数采用了返回指向整数的指针的方法,也就是说这一次的数组类型为函数指针 //展示两个例子 int (*f)(int,float) int* (*g[])(int,float)
int f(int); int (*pf)(int); pf=&f;//实际上这里的&操作符是可选的,因为f函数名的本质是函数指针,指针指向函数代码区域的起始部分 //以下是三种调用方式 f(1);//直接调用 (*pf)(1);//间接访问 pf(1);//实际上函数调用时,函数名会隐式转换为函数指针,而此表达式则将此过程显式表达了
回调函数
回调函数是一种技巧,用户将函数指针作为函数参数传入,函数反过来调用用户的函数,回调函数主要应用于两种情况,一种是所编写的函数需要在不同时刻执行不同类型的工作,另一种则是只能执行函数调用者定义的工作
回调函数的使用
#include<stdio.h> #include<stdlib.h> #include<string.h> #include<malloc.h> typedef struct node{ int a; struct node* b; }node; //不使用函数回调的情况 //node* search_(node* a1, int const val) //{ // while (a1 != NULL) // { // if (a1->a == val) // break; // a1 = a1->b; // } // return a1; //} //使用函数回调的情况 int compare(void const* a, void const* b) { if (*(int*)a == *(int*)b) return 0; else return 1; } node* search_(node* a1, int const val,int (*abx)(void const*a1,void const *val)) { while (a1 != NULL) { if (abx(&a1->a,&val)==0) break; a1 = a1->b; } return a1; } int main() { node c, d, e, f, g; node* head; c.a = 1; d.a = 2; e.a = 3; f.a = 4; g.a = 5; head = &c; c.b = &d; d.b = &e; e.b = &f; f.b = &g; node* phead = head; int (*abx)(void const*, void const*) = &compare; phead = search_(head, 3,abx); printf("%d", phead->a); return 0; }
回调函数的好处
回调似乎只是函数间的调用,和普通函数调用没啥区别,但仔细一看,可以发现两者之间的一个关键的不同:在回调中,主程序把回调函数像参数一样传入库函数。这样一来,只要我们改变传进库函数的参数,就可以实现不同的功能,这样有没有觉得很灵活?并且丝毫不需要修改库函数的实现,这就是解耦。再仔细看看,主函数和回调函数是在同一层的,而库函数在另外一层,想一想,如果库函数对我们不可见,我们修改不了库函数的实现,也就是说不能通过修改库函数让库函数调用普通函数那样实现,那我们就只能通过传入不同的回调函数了,这也就是在日常工作中常见的情况。
转移表
回调函数的好处
回调似乎只是函数间的调用,和普通函数调用没啥区别,但仔细一看,可以发现两者之间的一个关键的不同:在回调中,主程序把回调函数像参数一样传入库函数。这样一来,只要我们改变传进库函数的参数,就可以实现不同的功能,这样有没有觉得很灵活?并且丝毫不需要修改库函数的实现,这就是解耦。再仔细看看,主函数和回调函数是在同一层的,而库函数在另外一层,想一想,如果库函数对我们不可见,我们修改不了库函数的实现,也就是说不能通过修改库函数让库函数调用普通函数那样实现,那我们就只能通过传入不同的回调函数了,这也就是在日常工作中常见的情况。
转移表
命令行参数
C语言程序main函数有两个参数argc和argv,argc表示命令行参数数目,argv用来存储一组参数值,因为argc的数目没有内在限制,所以argv指向这组参数值的首元素(本质上是数组)的第一个元素,这些元素的每一个都是指向一个参数文本的指针,main在声明时就要加上一些参数:int main(int argc,char**argv)
这里的char**argv就相当于char *argv[ ]
字符串常量
当一个字符串常量出现在表达式中时,其值为指针常量,指针指向字符串的第一个字符,,编译器将字符串的一份副本存储在内存的某个位置,并保留了一份指向第一份字符的指针
"xyz"+1//y *"xyz"//x "xyz"[2]//z putchar("0123456789ABCDEF"[values%16])//简洁实现二进制变字符,values为源数
优先级 | 运算符 | 名称或含义 | 使用形式 | 结合方向 | 说明 |
---|---|---|---|---|---|
1 | [] | 数组下标 | 数组名[常量表达式] | 左到右 | |
() | 圆括号 | (表达式) 函数名(形参表) | |||
. | 成员选择(对象) | 对象.成员名 | |||
-> | 成员选择(指针) | 对象指针->成员名 | |||
2 | - | 负号运算符 | -表达式 | 右到左 | 单目运算符 |
(类型) | 强制类型转换 | (数据类型)表达式 | |||
++ | 自增运算符 | ++变量名 变量名++ | 单目运算符 | ||
-- | 自减运算符 | --变量名 变量名-- | 单目运算符 | ||
* | 取值运算符 | *指针变量 | 单目运算符 | ||
& | 取地址运算符 | &变量名 | 单目运算符 | ||
! | 逻辑非运算符 | !表达式 | 单目运算符 | ||
~ | 按位取反运算符 | ~表达式 | 单目运算符 | ||
sizeof | 长度运算符 | sizeof(表达式) | |||
3 | / | 除 | 表达式 / 表达式 | 左到右 | 双目运算符 |
* | 乘 | 表达式*表达式 | 双目运算符 | ||
% | 余数(取模) | 整型表达式%整型表达式 | 双目运算符 | ||
4 | + | 加 | 表达式+表达式 | 左到右 | 双目运算符 |
- | 减 | 表达式-表达式 | 双目运算符 | ||
5 | << | 左移 | 变量<<表达式 | 左到右 | 双目运算符 |
>> | 右移 | 变量>>表达式 | 双目运算符 | ||
6 | > | 大于 | 表达式>表达式 | 左到右 | 双目运算符 |
>= | 大于等于 | 表达式>=表达式 | 双目运算符 | ||
< | 小于 | 表达式<表达式 | 双目运算符 | ||
<= | 小于等于 | 表达式<=表达式 | 双目运算符 | ||
7 | == | 等于 | 表达式==表达式 | 左到右 | 双目运算符 |
!= | 不等于 | 表达式!= 表达式 | 双目运算符 | ||
8 | & | 按位与 | 表达式&表达式 | 左到右 | 双目运算符 |
9 | ^ | 按位异或 | 表达式^表达式 | 左到右 | 双目运算符 |
10 | | | 按位或 | 表达式|表达式 | 左到右 | 双目运算符 |
11 | && | 逻辑与 | 表达式&&表达式 | 左到右 | 双目运算符 |
12 | || | 逻辑或 | 表达式||表达式 | 左到右 | 双目运算符 |
13 | ?: | 条件运算符 | 表达式1? 表达式2: 表达式3 | 右到左 | 三目运算符 |
14 | = | 赋值运算符 | 变量=表达式 | 右到左 | |
/= | 除后赋值 | 变量/=表达式 | |||
*= | 乘后赋值 | 变量*=表达式 | |||
%= | 取模后赋值 | 变量%=表达式 | |||
+= | 加后赋值 | 变量+=表达式 | |||
-= | 减后赋值 | 变量-=表达式 | |||
<<= | 左移后赋值 | 变量<<=表达式 | |||
>>= | 右移后赋值 | 变量>>=表达式 | |||
&= | 按位与后赋值 | 变量&=表达式 | |||
^= | 按位异或后赋值 | 变量^=表达式 | |||
|= | 按位或后赋值 | 变量|=表达式 | |||
15 | , | 逗号运算符 | 表达式,表达式,… | 左到右 |