C语言进阶(指针2.函数指针和指针函数,二级指针,指针数组和数组指针,void*指针)
1.指针函数和函数指针
1. 1指针函数
是函数,函数的返回值是指针 不能返回局部变量的地址
指针函数返回的地址可以作为下一个函数调用的参数
封装函数实现strlen功能:
#include <stdio.h>int mylen(char *pstr){int i = 0;while(*pstr != '\0'){ pstr++;i++;}return i;}int main(void){char str[32] = {0};int len = 0;gets(str);len = mylen(str);printf("len = %d",len);return 0;}
封装函数实现strcpy功能:
#include <stdio.h>char *mystrcpy(char *pdst , char *psrc){char *pret = pdst;while(*psrc != '\0'){*pdst = *psrc;pdst++;psrc++;}*pdst = '\0';return pret;}int main(void){char src[50] = {0};char dst[50] = {0};gets(src);mystrcpy(dst,src);printf("src = %s\n",src);printf(" dst = %s\n", dst); return 0;}
封装函数实现strcmp功能:
#include <stdio.h>int mystrcmp(char *pdst , char *psrc){while(*pdst == *psrc&&*pdst != '\0'){pdst++;psrc++;}return *pdst - *psrc; }int main(void){char src[50] = {0};char dst[50] = {0};int res = 0;gets(dst);gets(src);res = mystrcmp(dst,src);printf("%d", res);return 0;}
封装函数实现strcat功能:
#include <stdio.h>void mystrcat(char *pstr1 , char *pstr2){while(*pstr1 != '\0'){pstr1++;}while(*pstr2 != '\0'){*pstr1 = *pstr2;pstr1++;pstr2++;}*(pstr1+1) ='\0'; }int main(void){char str1 [500] = {0};char str2 [50] = {0};gets(str1);gets(str2);mystrcat(str1,str2);printf("str1 = %s\nstr2 = %s\n",str1,str2);return 0;}
封装函数实现倒置功能:
#include <stdio.h>char *mysweap(char *psrc){char *pret = psrc;//这里把pret指向psrc的首地址.int i = 0;int j = 0;char tmp = '0';while(*psrc != '\0'){psrc++;i++;}j = i;psrc = pret;//由于psrc进行了自加操作,因此需要重新指向首地址才能交换for(i = 0;i <j/2 ;i++){tmp = pret[i];pret[i] = pret[j - 1 - i];pret[j - 1 -i] = tmp;}return pret; }int main(void){char src[50] = {0};gets(src);char *res = NULL;res = mysweap(src);printf("%s", res);return 0;}
1.2.函数指针
是指针,指针指向一个函数。
其形式为:
int (*p)(int,int);
函数指针,对加减乘除函数的调用:
#include <stdio.h>int jia(int a ,int b){return a + b;}int jian(int a ,int b){return a - b;}int cheng(int a ,int b){return a * b;}int chu(int a ,int b){return a / b;}int jisuanqi(int x,int y,int (*p)(int a ,int b)){return p( x, y);}int main(void){int (*p)(int a , int b) = NULL;int num1 = 0;int num2 = 0;char op = 0;scanf("%d %c %d",&num1,&op,&num2); int ret = 0;switch(op){case '+':p = jia;break;case '-':p = jian;break;case '*':p = cheng;break;case '/':p = chu;break;}printf("ret = %d",jisuanqi(num1 ,num2,p));return 0;}
如下代码会根据jisuanqi函数调用时的不同参数p进行不同的操作运算:(函数指针,调用操作方法)
#include <stdio.h>#include <stdlib.h>int InputArray(int *parray, int len){int i = 0;for (i = 0; i < len; i++){scanf("%d", &parray[i]);}return 0;}int OutputArray(int *parray, int len){int i = 0;for (i = 0; i < len; i++){printf("%d ", parray[i]);}printf("\n");return 0;}int Desc(int num1, int num2){if (num1 < num2){return 1;}return 0;}int Asc(int num1, int num2){if (num1 > num2){return 1;}return 0;}int AbsAsc(int num1, int num2){if (abs(num1) > abs(num2)){return 1;}return 0;}int AbsDesc(int num1,int num2){if(abs(num1) < abs(num2)){return 1;}return 0;}int SortArray(int *parray, int len, int (*pfun)(int, int)){int i = 0;int j = 0;int tmp = 0;for (j = 0; j < len-1; j++){for (i = 0; i < len-1-j; i++){if (pfun(parray[i], parray[i+1])){tmp = parray[i];parray[i] = parray[i+1];parray[i+1] = tmp;}}}return 0;}int main(void){int a[5] = {0};InputArray(a, 5);SortArray(a, 5, AbsDesc);OutputArray(a, 5);return 0;}
2.二级指针
二级指针是指向一级指针变量的指针。
int **q;定义一个指针变量q,占8个字节空间,指向一个指针变量空间,即指向一级指针变量的指针,也就是
二级指针
使用场景:
1. 函数体内部想修改函数体外部指针变量值的时候,需要传指针变量的地址即二级指针
2. 指针数组传参,数组的数组名是指向数组第一个元素的指针,第一个元素是指针,所以数组名 为指向指针的指针即二级指针。
二级指针的原理:指向指一级针变量的指针。
#include <stdio.h>int main(void){int num = 100;int *p = NULL;int **q = NULL;p = #q = &p;printf("&num = %p\n",&num);printf("&p = %p\n",&p);printf("&q = %p\n",&q);printf("-----------------------------------\n"); printf("p = %p\n",p);printf("q = %p\n",q);printf("num = %d\n",num);printf("-----------------------------------\n");printf("*p = %d\n",*p);printf("*q = %p\n",*q);printf("-----------------------------------\n");printf("**q = %d\n",**q);return 0;}
函数体内部想修改函数体外部指针变量的值,可以使用二级指针。
#include <stdio.h>void fun(char **pptmp){*pptmp = "hello world"; }int main(void){char *p = NULL;fun (&p);printf("p = %s",p);return 0;}
3.指针数组和数组指针
3.1指针数组
指针数组是数组,数组的每个元素都是指针,将字符串的首地址存放在各个指针构成的数组中其形式为:
int *a[5];
定义一个数组,占40个字节,数组名为a,数组中每个元素都是int *型的指针
存放字符串使用字符型数组,操作字符串使用指针。
存放字符串数组使用字符型二维数组,操作字符串数组使用指针数组。
使用指针数组(每个元素都是指针,因此都是八个字节)代替二维数(元素字节长度不固定)组进行排序,前者相对后者,其元素字节大小相等,而后者字节大小不固定,容易产生越界访问。
对于不同字符串采用冒泡排序:
#include <stdio.h>#include <string.h>int main (void){char str [5][50] = {0};char *pstr [5] = {str[0],str[1],str[2],str[3],str[4]};int i = 0;int j = 0;char *ptmp = NULL;for(i = 0;i < 5;i++){gets(str[i]);}for(j = 0;j <4;j++){for(i = 0;i < 5-j-1;i++){if(strcmp (pstr[i],pstr[i+1])>0)ptmp = pstr[i];pstr[i] = pstr[i+1];pstr[i + 1] = ptmp;}}for(i = 0;i < 5;i++){printf("pstr[%d] = %s\n",i, pstr [i] );}for(i = 0;i < 5;i++){printf("str[%d] = %s\n",i,str[i]); }return 0;}
避免了对数据交换,只对地址进行交换
3.2数组指针
数组指针是指针,指针指向整个数组,其形式为:
int (*a)[5];
定义一个指针,占8个字节,指针变量名为a,是指向数组20个字节空间的指针
对一维数组数组名&:值不变,类型升级为指向整个数组的指针(对一维数组数组名取地址不会得到二级指针**a,而是会将指针由指向a[0]首地址变为指向整个数组的首地址,两者相等,但是类型不同,一个是指向单个元素,一个是指向整个数组)。
对指针数组*:值不变,类型降级为指向数组第一个元素的指针。
int a[5] = {1, 2, 3, 4, 5};a == &a[0]a == int *
//这两种情况a不能理解为int*型。
//sizeof:获得数据类型、变量所占字节
//&: & int * -> int ** &a -> int (*p)[5]
数组函数的实质:
#include <stdio.h>#include <string.h>int main (void){int a[5] = {1,2,3,4,5};printf("a[0] = %p\n",&a[0]);printf("a[1] = %p\n",&a[1]);printf("a[2] = %p\n",&a[2]);printf("a[3] = %p\n",&a[3]);printf("a[4] = %p\n",&a[4]);printf("----------------------------\n");printf("a:%p\n",a);printf("a + 1 :%p\n",a + 1);printf("a + 2 : %p\n",a + 2);printf("----------------------------\n");printf("*a = %d\n",*a);printf("*(a+1) = %d\n",*(a+1));printf("*(a+2) = %d\n",*(a+2));printf("----------------------------\n");printf("&a = %p\n",&a);printf("&(a+1) = %p\n",&a+1);printf("----------------------------\n");printf("*&a = %p\n",*&a);printf("*&(a+1) = %p\n",*&a+1); printf("----------------------------\n");printf("**&a = %d\n",**&a);return 0;}
数组指针需要注意的事项:
如上代码,答案是3和5,第二个输出:(给a取地址,变成了指向整个数组的指针,字节大小为20,因此&a+1向后偏移20字节,而int*将其强制转化为整型类型,后续-1向前偏移四个字节,此时指针指向a[4]=5)。
数组指针的使用场景:
1)一维数组和指针的关系:数组的数组名是指向数组第一个元素的指针常量
int a[5] = {1, 2, 3, 4, 5};int *p = NULL;p = &a[0];p = a;a == &a[0];a[n] == *(a+n) == *(p+n) == p[n];
2)二维数组和指针的关系: 数组的数组名是指向数组第一行元素的数组指针
int a[2][3] = {1, 2, 3, 4, 5, 6};int *p = NULL;int (*q)[3] = NULL;p = &a[0][0];p = a[0];p = *a;q = a;a:指向数组第一行元素的数组指针 int (*)[3]a[0]:指向a[0][0]的指针 int *a[1]:指向a[1][0]的指针 int *
访问数组第m行第n列元素的方式:
a[m][n]*(a[m]+n)*(*(a+m)+n)*(p+m*N+n)*(*(q+m)+n)*(q[m]+n)q[m][n]
数组指针,其数组名指向第一排,数组名+1指向第二排,对其取星值不变,由指向数组的指针变为指向元素的指针:
3.3二维数组的传参
传参形式
int fun(int(*p)[3],int len);//形参为指针数组
int a[2][3]={1,2,3,4,5};
fun(a,2);//调用fun函数,传递数组名和行数
传递二维数组并打印:
#include <stdio.h> int OutputArray(int (*p)[3], int len){int i = 0;int j = 0;for (j = 0; j < len; j++){for (i = 0; i < 3; i++){printf("%d ", p[j][i]);}printf("\n");}return 0;}int InputArray(int (*p)[3],int len){int j = 0;int i = 0;for(j = 0;j < len; j++){for(i = 0;i < 3; i++){scanf("%d",&p[j][i]); }}}int main(void){int a[2][3] = {0};InputArray(a,2);OutputArray(a, 2);return 0;}
接收字符串将其排序后打印:(传递指针数组)
#include <stdio.h>
#include <string.h>
int OutputString(char (*p)[32], int len)
{ int i = 0;for (i = 0; i < len; i++){printf("%s\n", p[i]);}return 0;
}
int InputString(char (*p)[32], int len)
{int i = 0;for (i = 0; i < len; i++){gets(p[i]);}return 0;
}
int SortString(char (*p)[32], int len)
{int i = 0;int j = 0;char tmp[32] = {0};for (j = 0; j < len-1; j++){for (i = 0; i < len-1-j; i++){if (strcmp(p[i], p[i+1]) > 0){strcpy(tmp, p[i]);strcpy(p[i], p[i+1]);strcpy(p[i+1], tmp);}}}return 0;
}
int main(void)
{char str[5][32] = {0};InputString(str, 5);SortString(str, 5);OutputString(str, 5);return 0;
}
3.4指针数组的传参
传参形式
int fun(char **ppstr, int len);
char *pstr[5]= {NULL};
fun(pstr , 5);
接收字符串将其排序后打印:(传递数组指针)
#include <stdio.h>
#include <string.h>
int InputArray(char (*p)[32], int len)
{int i = 0;for (i = 0; i < len; i++){gets(p[i]);}return 0;
}
int SortArray(char **ppstr, int len)
{int j = 0;int i = 0;char *ptmp = NULL;for (j = 0; j < len-1; j++){for (i = 0; i < len-1-j; i++){if (strcmp(ppstr[i], ppstr[i+1]) > 0){ptmp = ppstr[i];ppstr[i] = ppstr[i+1];ppstr[i+1] = ptmp;}}}return 0;
}
int OutputArray(char **ppstr, int len)
{int i = 0;for (i = 0; i < len; i++){printf("%s\n", ppstr[i]);}return 0;
}
int OutputArray2(char (*p)[32], int len)
{int i = 0;for (i = 0; i < len; i++){printf("%s\n", p[i]);}return 0;
}
int main(void)
{char str[5][32] = {0};char *pstr[5] = {str[0], str[1], str[2], str[3], str[4]};InputArray(str, 5);SortArray(pstr, 5);OutputArray(pstr, 5);printf("=================\n");OutputArray2(str, 5);return 0;
}
4.void*指针
void *指针主要用来保存内存地址,其转化为char*,int*,double*时不需要类型转换,直接赋值就可以。char*,int*,double*转化为void*时需要强制类型转换。多用于函数传参和返回值,统一参数和返回类型。
void *p;
char *a;
int *b;
double *c;
p=(void *)a;