一般的两种用法 1.指针指向字符地址char*
int main()
{
char ch='w';
char *pc=&ch;
*pc='w';
return 0;
}
2.指针调用字符数组(字符串)
int main()
{
char str[]="hello";
char*pstr1=str;
printf("%sn",str); //%s以' '未结束标志,否则会有乱码
char*pstr2="hello";
printf("%sn",pstr2);
//pstr2="hi";
return 0;
}
示例pstr1常规使用
pstr2指向的是字符串常量"hello"的地址,指向位置未知;且字符常量,为静态(static),且为常量,无法修改!
#include指针进阶int main() { char str1[] = "hello bit."; char str2[] = "hello bit."; char *str3 = "hello bit."; char *str4 = "hello bit."; if(str1 ==str2) //==比较的是地址 printf("str1 and str2 are samen"); else printf("str1 and str2 are not samen"); if(str3 ==str4) printf("str3 and str4 are samen"); else printf("str3 and str4 are not samen"); return 0; }
int为例
数组概念回顾
数组名代表整个数组的两种情况
sizeof(数组名)
&数组名
int main()
{
int arr[3]={1,2,3};
printf("%pn",arr[0]);
printf("%pn",arr);
printf("%pn",&arr);
printf("%pn",arr+1);
printf("%pn",&arr+1);
return 0;
}
指针数组
是数组
[]优先级高于*
数组中每个元素是指针
int *a[5];
数组指针a数组有五个元素每个元素是int型指针(指向int的指针)
是指针
[]优先级高于*
指向数组的指针
int (*a)[5]; //[]值不能省略
//使用
//1.0
int arr[5]={1,2,3,4,5};
a=&arr;
//2.0
int arr[5][5];
a=arr;
二维数组
示例第二个[]值不能省略
每个元素,都是一个数组。
int arr[3][3]={1,2,3, arr[0]
4,5,6, arr[1]
7,8,9}; arr[2]
#includevoid print_arr1(int arr[3][5], int row, int col) { int i = 0; for(i=0; i
数组参数&指针参数a是指向数组的指针,该数组有五个元素每个元素是int型
一维数组传参数组传参会退化为指针
void test (int arr[]) {} void test(int *p) {} void test2(int **p) {} int main() { int arr[10]={0}; int *arr2[10]={0}; test(arr); test2(arr2); }二维数组传参void test1(int arr[3][5]) {} void test2(int arr[][5]) //arr[]会退化为指针 {} void test3(int (*p_arr)[5]) //标准写法 {} //no,二维数组传参只能省略第一个[]的数字 //对于一个二维数组可以不知道行,必须知道一行有多少个元素 //void test(int arr[][]) //{} int main() { int arr[3][5]; test1(arr); test2(arr); test3(arr); return 0; }一级指针void test(int *p) {} int main() { int a; int arr[10]; test(&a); test(arr); return 0; }二级指针可以接收int型数据地址(传址操作)
接受一维数组
指向指针的指针
void test(int **p) {} int main() { int *arr[10]; test(arr); }函数指针二级指针指向数组首元素地址,而指针数组的每个元素都为指针
是指针
()优先级高于*
指向函数的指针
int (*a)(int);所指函数返回值,(指针),(所指函数参数)
a是指向函数的指针,该函数有一个int类型的形参,返回值类型为int
#includevoid test() { printf("heihein"); } int main() { printf("%pn",test); printf("%pn",&test); return 0; } 示例输出结果,两地址相同。
为test函数地址
函数名(同数组名含义)是函数的入口地址
#include指针函数void test() { printf("helln"); } int test1(int a,int b) { return a+b; } int main() { void(*pfun1)()=&test; //1.0 (*pfun1)(); void(*pfun11)()=test; //2.0 常用 pfun11(); int(*pfun2)(int,int)=&test1; (*pfun2)(1,2); int(*pfun2)(int,int)=test1; pfun22(1,2); } 是函数
()优先级高于*
函数的返回值为指针
int * a(int);a函数有一个int形参,返回int指针
int*fun(int a,int b) { static int v=a+b; return &v; }多个组合练习 定义右左法则
未知变量名开始先右,遇括号就掉头
int (*a[10])[10];a是一个指针数组,数组有十个指针元素,每个指针指向一个数组,该数组有10个整型元素
int(*(*a)[10])(int);a是指针,指向数组,数组有十个元素,每个元素是指针,该指针指向一个函数,函数有一个整型参数,返回值类型是int型
int*(*(*a)())[10];大小a是指针,指向函数,该函数无参,返回一个指针,该指针指向数组,数组有10个元素,每个元素为int指针
int (*ar)[10][10]; printf("%d",sizeof(ar)); //?是指针,4字节
short (*ar[10])[10];简化定义是指针数组,40字节
typedef简化
typedef int(*pArray)[10]; typedef int(*pFun)(int*); void main() { pArray pa; pFun pf; }示例定义该类数组指针类型名,pArray
定义该类函数指针类型名,pFun
#includetypedef int(*pFun)(int*); typedef pFun(*pArray)[5]; int main() { //int (*(*func)[5])(int *) //pFun (*func)[5]; pArray func; } 解读(c陷阱与缺陷charpter2)
(*(void(*)())0)();调用首地址为0的子程序(函数)
类型强转:()
将0强转为函数指针类型,该类型指针指向一个无参无返回值的函数;
再调用0指向的函数;
简化
typedef void(*pFun)(); (*(pFun)0)();转移表(函数指针数组)示例数组是一个存放相同类型数据的存储空间,通过函数指针数组(转移表),实现代码的简化!
本质还是数组,使用方法就是数组的使用方法。
计算器的简易实现
#includeint add(int a, int b) { return a + b; } int sub(int a, int b) { return a - b; } int mul(int a, int b) { return a*b; } int div(int a, int b) { return a / b; } 函数结构统一。
typedef enmu{ADD,SUB,MUL,DIV,QUIT}; int main() { int x, y; int input = 1; int ret = 0; do { printf( "*************************n" ); printf( " 1:add 2:sub n" ); printf( " 3:mul 4:div n" ); printf( "*************************n" ); printf( "请选择:" ); scanf( "%d", &input); printf("输入操作数:" ); scanf( "%d %d", &x, &y); switch (input) { case 1://ADD //魔鬼数字,数字含义不清晰!使用宏定义,或enum枚举 ret = add(x, y); printf( "ret = %dn", ret); break; case 2: //SUB ret = sub(x, y); printf( "ret = %dn", ret); break; case 3: //MUL ret = mul(x, y); printf( "ret = %dn", ret); break; case 4:DIV ret = div(x, y); printf( "ret = %dn", ret); break; case 0://QUIT printf("退出程序n"); break; default: printf( "选择错误n" ); break; } } while (input); return 0; }switch语句可以通过转移表化简。
//简化 //typedef int (*pFun)(int,int); //pFun p[5]; int(*p[])(int x,int y) = {NULL,add,sub,mul,div}; //转移表定义 int main() { int x,y; int input = 1; int ret = 0; while(input) { printf("************************n"); printf("* 1.add 2.sub *n"); printf("* 3.mul 4.div *n"); printf("************************n"); scanf("%d",&input); if((input<=4 && input >= 1)) { printf("输入操作数:"); scanf("%d %d",&x,&y); ret = (*p[input])(x,y); // ret = p[input](x,y); } else printf("输入有误!n"); printf("ret = %d",ret); } return 0; }指向函数指针数组的指针
void test(const char *str) { printf("%sn",str); } int main() { //函数指针pFun void(*pfun)(const char*) = test; //函数指针的数组pFunArr void (*pfunArr[5])(const char*str); pfunArr[0]=test; //指向函数指针数组pfunArr的指针ppfunArr void (*(*ppfunArr)[10])(const char*) = &pfunArr; return 0; }回调函数回调函数就是一个通过函数指针调用的函数。
将一个函数的指针(地址)作为参数传递给另一个函数,这个指针被用来调用所指向的函数时,我们说这是回调函数。
void类型
只能定义指针
无法直接对指针进行操作
可以通过类型强转实现操作(C语言——指针)
int main() { void *p_void; int *p_int; int a=1; p_int = &a; p_void = p_int; }示例#include#include #define _CRT_SECURE_NO_WARNINGS //通过冒泡函数实现qsort()函数的模拟 //qsort()快速排序 //*可以排序各种能定义的类型数据 //用户定义比较函数,根据返回值,判断大小,是否交换数据 // 本例使用int比较 //*返回值为int型 //*传入两个参数,通过指针操作(方便操作各个类型数据) //*可以通过相反数,来实现从大到小,还是由小到大 int cmp_int(const void* p1, const void* p2) { return (*(int*)p1 - *(int*)p2); //<0 =0 >0 } //交换数值,可以交换各种类型数据 //*传入为void指针,可以交换各种类型数据 //*指针类型不明确,强转void为(char*),步长为1字节,方便操作各类型数据 //*根据size确定指针移动步长,确保交换完整数据 //--例如 // 交换为int型,4字节,即完整数据为4字节 // 每次交换1字节,共交换4次 void _swap(void* p1, void* p2, int size) { for (int i = 0; i < size; i++) { *((char*)p1 + i) ^= *((char*)p2 + i); *((char*)p2 + i) ^= *((char*)p1 + i); *((char*)p1 + i) ^= *((char*)p2 + i); } } //冒泡排序,排序所有类型数组数据 //*四个参数:排序元素首地址,排序元素个数,元素类型大小,指向比较函数的函数指针 //*无返回值 void BubbleSort(void* base, int num, int size, int (*cmp)(const void* p1,const void* p2)) { for (int i = 0; i < num - 1; i++) { for (int j = 0; j < num - i - 1; j++) { //比较 if (cmp((char*)base + j * size, (char*)base + (j + 1) * size) > 0) { //交换数值 _swap((char*)base + j * size, (char*)base + (j + 1) * size,size); } } } } int main() { int arr[] = { 80,20,1,3,46,96,8,9,85,79,81 }; int n = sizeof(arr) / sizeof(arr[0]); BubbleSort(arr, n, sizeof(int), cmp_int); return 0; } 主函数main的参数可以自己尝试实现char、double、float、struct的cmp
命令行参数
//参数名称无所谓 //argc:命令行参数个数,初始为1 //argv:命令行参数值,以字符串指针数组定义;初始为该文件所在目录 //"远古时代"程序的调用在命令行,查找到文件位置并调用 int main(int argc, char* argv[]) { printf("%dn", argc); for (int i = 0; i < argc; i++) { printf("%sn", argv[i]); } return 0; }命令行参数传值1.vs2019 项目->属性->调试
2.通过命令行传值
cd 文件 :进入目录
dir :显示目录
最难不过坚持!



