【27】C语言 | 指针进阶
创始人
2024-05-20 02:22:51
0

目录

一、指针概念

二、字符指针

三、指针数组

四、数组指针

五、数组参数、指针参数

六、函数指针

七、函数指针数组

八、回调函数


一、指针概念

  • 1.指针就是个变量,用来存放地址,地址唯一标识一块内存空间。
  • 2.指针的大小是固定的4/8个字节(32位平台/64位平台)。
  • 3.指针是有类型,指针的类型决定了指针的+-整数的步长,指针解引用操作的时候的权限

二、字符指针

在指针的类型中我们知道有一种指针类型为字符指针 char*;

一般使用:

int main()
{char ch = 'q';    char* cp = &ch;    //* 说明cp是一个指针变量,char 说明指针指向的数据类型是charreturn;
}

 字符指针不仅指向字符,还可以指向一个字符串

int main()
{char arr[] = "hello bit"; //这里是把“hello bit”全部放到arr字符串中了char* ps = "hello bit";//本质上是把“hello bit”这个字符窜的首字符的地址存储在了ps中printf("%c\n",*ps);  //按字符打印:输出hprintf("%s\n",ps);   //按字符串打印:输出hello bitprintf("%s\n",arr);  //按字符串打印:输出hello bitreturn 0;
}

案例:下面输出结果是什么 

int main()
{char strl[] = "hello bit.";char str2[] = "hello bit.";char* str3 = "hello bit.";char* str4 = "hello bit.";if (strl == str2)printf("strl and str2 are same\n");elseprintf("strl and str2 are not same\n");if (str3 == str4)printf("str3 and str4 are same\n");elseprintf("str3 and str4 are not same\n");return 0;
}

输出为: 

strl and str2 are not same
str3 and str4 are same
//因为当char strl[] = "hello bit.";char str2[] = "hello bit.";和的时候
//内存开辟了两份空间比较strl1和strl2,他们的地址不同//char* str3 = "hello bit.";时候,"hello bit."属于常量字符串,内存中只存一份
//所以比较他们的指针变量指向的地址时是相同的

三、指针数组

在《指针》章节我们也学了指针数组,指针数组是一个存放指针的数组

int main()
{//指针数组//数组 - 数组中存放的是指针(地址)//int* arr[3];int a = 10;int b = 20;int c = 30;int i = 0;int* arr[3] = {&a,&b,&c};for(i=0; i<3; i++){printf("%d ",*(arr[i]));}return 0;
}

再看一个案例:

int main()
{int a[6] = {1,2,3,4,5,6};int b[] = {2,3,4,5,6,7};int c[] = {3,4,5,6,7,8,};int i = 0;int* arr[3] = {a,b,c};for(i=0; i<3; i++){int j = 0;for(j=0; j<6; j++){printf("%d ",*(arr[i]+j));}printf("\n");}return 0;
}

和上面是一样的

int main()
{int a[6] = {1,2,3,4,5,6};int b[] = {2,3,4,5,6,7};int c[] = {3,4,5,6,7,8,};int i = 0;int* arr[3] = {a,b,c};for(i=0; i<3; i++){int j = 0;for(j=0; j<6; j++){//printf("%d ",*(arr[i]+j));printf("%d ",arr[i][j]);}printf("\n");}return 0;
}

四、数组指针

int main()
{int a = 10;int* pa = &a;  //返回的类型为整形,所以是整形指针char ch = 'w';char* pc = &ch;  //返回的类型为字符类型,所以是字符指针int arr[10] = {1,2,3,4,5,};//arr - 数组名是首元素的地址 - arr[0]的地址int (*parr)[10] = &arr;//取出的是数组的地址//parr就是一个数组指针,*parr说明是一个指针,int加上[10]说明指针指向的类型是一个数组,里面放的是整形//parr存放的是数组的地址     return 0;
}

下列输出什么:

int main()
{int arr[10] = {0};int* p1 = arr;int (*p2)[10] = &arr;printf("%p\n",p1);printf("%p\n",p1+1);printf("%p\n",p2);printf("%p\n",p2+1);return 0;
}

数组名是数组首元素的地址

但是有2个例外:

sizeof(数组名) - 数组名表示整个数组,计算的是整个数组大小,单位是字节

&数组名 - 数组名表示整个数组,取出的是整个数组的地址

 下面用整形指针输出数组每个元素的地址

int main()
{int arr[10] = {1,2,3,4,5,6,7,8,9,10};int *p = arr;int i = 0;for(i=0; i<10; i++){printf("%d ",*p+i);}return 0;
}

然后再用数组指针输出数组每个元素的地址

int main()
{int arr[10] = {1,2,3,4,5,6,7,8,9,10};int (*pa)[10] = &arr;int i = 0;for(i=0; i<10; i++){printf("%d ",*((*pa)+i)); //这里的*pa相当于arr(数组名的地址)}                                //i相当于下标为i的地址return 0;
}

以前的打印一个二维数组:

void print(int arr[3][5],int r,int c)
{int  i =0;for(i=0; i{1,2,3,4,5,},{2,3,4,5,6},{4,5,6,7,8}};print(arr,3,5);return 0;
}

理解下列:

int main()
{ int arr[5];      //整形数组int *parr1[10];  //整形指针的数组,//数组里有10个元素,每个元素的返回类型是int*int (*parr2)[10]; //数组指针,该指针能够指向一个数组,//数组10个元素,每个元素的类型是intint (*parr3[10])[5];  //是一个存放数组指针的数组//该数组能够存放10个数组指针//每个数组指针能够指向一个数组,数组5个元素,每个元素是int类型return 0;
}

五、数组参数、指针参数

在写代码的时候难免要把[数组]或者[指针]传给函数,那函数的参数该如何设计呢?

void test(int arr[])//数组传参,数组接受
{}
void test(int arr[10])//和上面一样的,这里的10没有任何意义
{}
void test(int *arr)//实参arr是首元素的地址,是int,这里也是int,用指针来接收
{}
void test2(int *arr[20])//实参arr2是一个整形指针数组,存放int*的数组,正好指针的数组接收
{}
void test2(int **arr)//实参arr2传过来是第一个int*的地址,实际上是取了一个一级指针的地址,所以可以用二级指针接收
{}
int main()
{int arr[10] = {0};int *arr2[20] = {0};test(arr);test2(arr2);return 0;
}

 一维数组传参

void print(int* p,int sz)
{int i = 0;for(i=0; i

二级指针传参

void test(int** p2)
{**p2 = 20;
}
int main()
{int a = 0;int* pa = &a;  //pa是一级指针int ** ppa = &pa;  //ppa是二级指针test(ppa);test(&pa); //传一级指针的变量的地址printf("%d\n",a);return 0;
}

六、函数指针

  • 函数指针:指向函数的指针,存放函数地址的指针~
  • 数组名 != &数组名 数组名是数组首元素地址,&数组名是整个数组的地址
  • 函数名 == &函数名
int Add(int x,int y)
{return x + y;
}
int main()
{int a = 0;int* pa = &a;char ch = 'w';char* pc = &ch;int arr[10] = {0};int (*parr)[10] = &arr;//函数指针 - 存放函数地址的指针int (*pf)(int, int) = &Add;printf("%p\n",&Add);return 0;
}
int Add(int x,int y)
{return x + y;
}
int main()
{//函数指针 - 存放函数地址的指针//int (*pf)(int, int) = &Add;int (*pf)(int, int) = Add;int ret = (*pf)(3,5);int ret = pf(3,5);printf("%d\n",ret);return 0;
}

七、函数指针数组

存放函数指针的数组

int Add(int x,int y)
{return x + y;
}
int Sub(int x,int y)
{return x - y;
}
int main()
{int (*pf1)(int ,int) = Add;int (*pf2)(int ,int) = Sub;int (*pfarr[2])(int, int) = {Add,Sub}; //pfarr:函数指针数组return 0;
}

举例:

首先按以前的方法写一个加减乘除的计算器

void menu()
{printf("****************************\n");printf("**  1.add     2.sub  *******\n");printf("**  3.mul     4.div  *******\n");printf("*******  0.exit  ***********\n");printf("****************************\n");printf("****************************\n");
}
int Add(int x, int y)
{return x + y;
}
int Sub(int x, int y)
{return x - y;
}
int Mul(int x, int y)
{return x * y;
}
int Div(int x, int y)
{return x / y;
}int main()
{int x = 0;int y = 0;int input = 0;do{menu();printf("请选择>:\n");scanf("%d",&input);switch(input){case 1:printf("请输出2个操作符>:\n");scanf("%d %d",&x,&y);printf("%d\n",Add(x,y));break;case 2:printf("请输出2个操作符>:\n");scanf("%d %d",&x,&y);printf("%d\n",Sub(x,y));break;case 3:printf("请输出2个操作符>:\n");scanf("%d %d",&x,&y);printf("%d\n",Mul(x,y));break;case 4:printf("请输出2个操作符>:\n");scanf("%d %d",&x,&y);printf("%d\n",Div(x,y));break;case 0:printf("退出程序\n");break;default:printf("输入错误,重新输出\n");break;}}while(input);return 0;
}

利用指针数组

void menu()
{printf("****************************\n");printf("**  1.add     2.sub  *******\n");printf("**  3.mul     4.div  *******\n");printf("*******  0.exit  ***********\n");printf("****************************\n");printf("****************************\n");
}
int Add(int x, int y)
{return x + y;
}
int Sub(int x, int y)
{return x - y;
}
int Mul(int x, int y)
{return x * y;
}
int Div(int x, int y)
{return x / y;
}
int main()
{int input = 0;do{menu();int x = 0;int y = 0;int (*pfarr[5])(int,int) = {NULL,Add,Sub,Mul,Div};printf("请选择>:\n");scanf("%d",&input);if(input>=1 && input<=4){printf("请输入2个操作符>:\n");scanf("%d %d",&x,&y);printf("%d\n",(pfarr[input](x,y)));}else if(input == 0){printf("退出程序\n");break;}else{printf("输入错误\n");}}while(input);return 0;    
}
int arr[5];         //整形数组
int (*p1)[5] = &arr;//*p1说明这是一个指针,(*p1)[5]:一个指向数组的指针,这个数组有5个元素,每个元素的返回类型是intint* arr[5];  //整形指针的数组,arr[5]:这是一个数组,数组有5个元素,每个元素的返回类型是int*
int* (*p2)[5] = &arr;// *p2:说明是一个指针,这个指针指向的是一个数组,这个数组有5个元素,每个元素的返回类型是int*//也可以说成,它是一个指向【整形指针数组】的指针//函数指针数组
//&函数指针数组
int(*p)(int,int);  //函数指针
int(*p2[4])(int,int); //它是一个数组,数组有4个元素,每个元素的返回类型是函数指针,所以它是要给函数指针数组
int(*(*p3)[4])(int,int) = &p2;//它是一个指针,指向一个数组,这个数组有4个元素,每个元素是一个函数指针数组//p3是一个指向【函数指针数组】的指针

八、回调函数

回调函数就是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。回调函数不是由该函数的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进行响应。

例如:

void menu()
{printf("****************************\n");printf("**  1.add     2.sub  *******\n");printf("**  3.mul     4.div  *******\n");printf("*******  0.exit  ***********\n");printf("****************************\n");printf("****************************\n");
}
int Add(int x, int y)
{return x + y;
}
int Sub(int x, int y)
{return x - y;
}
int Mul(int x, int y)
{return x * y;
}
int Div(int x, int y)
{return x / y;
}
int Calc(int (*pf)(int,int ))
{int x = 0;int y = 0;printf("请输出2个操作符>:\n");scanf("%d %d",&x,&y);return pf(x,y);
}int main()
{int input = 0;do{menu();printf("请选择>:\n");scanf("%d",&input);switch(input){case 1:printf("%d\n",Calc(Add));break;case 2:printf("%d\n",Calc(Sub));break;case 3:printf("%d\n",Calc(Mul));break;case 4:printf("%d\n",Calc(Div));break;case 0:printf("退出程序\n");break;default:printf("输入错误,重新输出\n");break;}}while(input);return 0;
}

再写一个冒泡排序

void bubble_sort(int arr[],int sz)
{int i = 0;for(i=0;i<=sz-1;i++){int j = 0;for(j=0; j arr[j+1]){int tmp = arr[j];arr[j] = arr[j+1];arr[j+1] = tmp;}}}
}
void print(int arr[],int sz)
{int i = 0;for(i=0; i

相关内容

热门资讯

每日一句精美句子 每日一句精美句子  一、什么是句子  句子是语言运用的基本单位,它由词、词组(短语)构成,能表达一个...
心累了的感情句子 心累了的感情句子(精选175句)  无论是在学校还是在社会中,大家都经常接触到句子吧,不同的句子类型...
描写红花木的句子精选53句 描写红花木的句子 精选47句1. 再看饱胀得裂了的小口子,刚刚绽开花苞的山茶花,犹如一个个朝天的铃铛...
十句以环境来描写心情好的句子... 十句以环境来描写心情好的句子 精选110句1. 我说人生啊,如果尝过一回痛快淋漓的风景,写过一篇杜鹃...
最难的时候靠自己的句子精选1... 最难的时候靠自己的句子 精选66句1. 有苦自己吃,有伤自己养,有泪自己擦,没有人爱你,那就自己爱自...
四川方言版句子精选239句 四川方言版句子 精选67句1. 灾难的乌云终有一天会消散,因为人本心中都有一缕光辉人与人之间的相连天...
挪威的森林英文句子精选59句 挪威的森林英文句子 精选59句1. The so-called effort refers to a...
你就像阳光一样的句子精选31... 你就像阳光一样的句子 精选98句1. 青春是有限的,不能在犹豫和观望中度过。2. 太阳一年操劳到头,...
美景句子经典 美景句子经典大全  在平凡的学习、工作、生活中,大家总少不了接触一些耳熟能详的句子吧,句子的组成部分...
慢羊羊的句子 关于慢羊羊的句子精选  1. 慢羊羊:我是怕你们不堪重负,所以重的东西就由我来承担!  2. 慢羊羊...
形容做业务辛苦的幽默句子精选... 形容做业务辛苦的幽默句子 精选139句1. 凡我放不下的,必是因为我拥有不了的。2. 虽然我喜欢这样...
关于安全感的句子精选235句 关于安全感的句子 精选62句1. 安全感是什么 我好像从来没拥有过2. 爱情生活里,比找不到安全感更...
夜晚黑的句子精选452句 夜晚黑的句子 精选94句1. 没有什么不同,天黑时我们仰望同一片星空。2. 黑黑的夜慢轻轻地拉开,群...
祝福的句子 有关祝福的句子大全  句子是语言运用的基本单位,它由词、词组(短语)构成,能表达一个完整的意思,如告...
关于赞扬最美逆行者的句子精选... 关于赞扬最美逆行者的句子 精选114句1. 天耀中华!致敬奋斗在一线的白衣天使!不出门,就是最好的配...
描写同床异梦的句子精选314... 描写同床异梦的句子 精选79句1. 后来,我再也没有说不要走这种好笑的话。2. 谎言听的多了后,你就...
真相堕落精彩句子精选189句 真相堕落精彩句子 精选35句1. 很多我们以为一辈子都不会忘记的事情,就在我们念念不忘的日子里,被我...
北海句子精选155句 北海句子 精选134句1. 过渡:王维是怎样表达自己思念家人的感情的呢?好,让我们一起学习第四自然段...
表达心里介意的句子精选172... 表达心里介意的句子 精选121句1. NG,我认为我一定会比上次好,而且不会两次演法一样。要NG,便...
描写元宵节的句子 描写元宵节的句子精选  元宵节的.句子(一)  《元夜踏灯》  (清)董舜民  百枝火树千金屧,宝马...