函数指针题
首先,一说函数指针,联想最简单的:
int (*p)(int, int):它是个指针因为括号星p, (*p),名字外左是返回类型,右边是参数,所以描述:函数指针指向的函数有两个int形参,且返回int类型数据
如果函数指针有两个int形参,且返回函数指针:
(*p)(int, int) 它的返回 是函数指针: 所以把他名字部分括号起来:
(*(*p)(int, int)) ,然后左边是外层函数指针左边返回int,右边是参数int
- 声明一个指向含有10个元素的数组的指针,其中每个元素是一个函数指针,该函数的返回值是int,参数是int*,正确的是( )
A.(int p[10])(int)
B.int [10]*p(int )
C.int ((*p)[10])(int *)
D.int ((int *)[10])*p
分析:录播视频22(指针三,2.02分)
分析:首先是数组指针,函数指针也一样,因为是指针,所以要星和名先结合,(*p),所以A错了,然后B中p不知道和谁结合,B也错了,然后是数组,(*p)[10] 左侧加数组内容,数组内容是函数指针,先再考虑最简单的数组指针:int (*p)[4] , 数组指针去掉星名和大小,应该显示指向内容,它指向函数,所以必须剩余 : 类型(*)(参)的结构,所以写出来C。且C中,给(*p)[10]加括号再左边放星也一样,优先级原因,不需要括号。D很乱,直接错
又或
直接看C:解读顺序: (*p)是指针,右边是【10】,说明指向了数组,此时,拿走已解读过内容,剩下为数组所存内容: int(*)(int *) ,相当于拿走了名,星不和int结合,星和拿走的内容结合,剩下的是函数指针,左侧说明函数返回int,右侧是int*参数。
指向数组的指针(指针它指向数组):
指针数组(数组中存多个指针):int * a[10];
函数指针(指向函数):int (*pf)(int ,int) = ;返回值、参数
函数指针数组(存函数指针的数组,也叫转移表):它是个数组,所以先和[]结合,所以函数指针数组:比如:
int(*pf[4])(int, int) = {Add, Sub......};
内部 pf[4],数组大小为4,外面有左边挨着*,说明内部存着指针,出了括号,看外面左边和右边,说明内部存的指针是函数指针,返回为int且参数两个int。所以显然是A
指向函数指针数组的指针:(简单理解,它是个数组的指针)写一下:
-
先写一个函数指针:int (*pf)(int, int)
-
变函数指针为函数指针数组:int (*pf[10])(int, int) ; 因为优先结合【】,它成了函数指针数组
-
取数组地址: 是一个函数指针数组的地址 , 我左边必用指针接收
问题转为,如何定义一个指针类型,把上一步拷下来改,*pf[10]我不希望 它是数组,希望它是指针,所以 让星先结合,再给外面加星, (*(*pf)[10]),再从内往外看,它是个指针,内容是(存着)10个指针,(向外看)而指针指向的是函数,返回int,参数是(int, int)
总之,给函数指针数组名字之前加一个星,并括起来,即可。pf_arr = &pf;
分析一下C:
C.int (*(*p)[10])(int *) :指向函数指针数组的指针
- 函数形参合适的是:
下面test函数设计正确的是:( )
char* arr[5] = {“hello”, “bit”};
test(arr);
A.void test(char* arr);
B.void test(char** arr);
C.void test(char arr[5]);
D.void test(char* arr[5]);
分析:
本身 char* arr[5] = {“hello”, “bit”};
字符指针数组,大小为5,它里面存的应该是5个字符的地址,但它给了一些常字符串,所以每个位置存的是常字符串的首地址。arr[0]存h,arr[1]存b。
A:char* arr, 只是一个字符指针,只能接收一个字符地址,所以它应该是只拿到了"hello"的h,因为arr[5]本来第一个存的也只是h的位置。
调用test函数,必须给arr[0],因为这样给的是h的地址,而arr,它什么也拿不到。
按a走,我在test中,如果打印%s , *arr,它将找不到结束。
↑ 普通情况下,%s + arr[0],即%s+ 字符串首地址 可以完整打印常字符串。
%c,打印单个地址,需要解引用,上面没有解引用,就错了,arr[0]是个地址。
%s打印字符串,给它常量首地址arr就行了,它会自己往后找,而打印单个字符%c,要对地址解引用。即, %c + *arr[0]。
test中,%c搭配 *arr , %s 搭配arr
给test传的是arr[0],但arr没有显示,如下面
**给test要传arr[0],而传arr不可以,猜测是test内部%c,往后走太多了,走一个数组大小 **
先留下问题:传arr给的是数组地址,传arr[0]给的是字符串地址
分析:arr和arr[0],发现加&,它俩一样,不加,它俩不一样
↓最关键,给test arr给的是数组arr的地址,而不是arr[0]中hello的地址
所以,我明白了,test要接收arr中字符,需要是二维地拿,&arr[0]和arr都是数组arr的地址,而arr[0] 是hello的地址,是数组arr中存的元素。除了arr[0]不一样,其它几个值都一样。所以要用 二维指针或数组,拿arr内部的东西
B:
test(char** arr):
用char **arr接收到arr二维数组的地址,然后一个个拿它内部元素
%s输出字符串,给首地址即可,arr[0]就是地址,它会顺着给后面找,
%s给地址就行, %s给地址就行,%s给地址就行 如下代码中arr[0], arr[1]都是地址
printf(“%s”, arr[0]);输出 hello
printf(“%s”, arr[1]);有输出 bit
所以B设计的很合适。
C:
void test(char arr[5]);
C是一个数组字符数组arr,肯定不行
D:
void test(char* arr[5]);
D:arr和[5]结合,前面看是char* ,指针数组,存5个指针。
int main()
{
int aa[2][5] = {10,9,8,7,6,5,4,3,2,1};
int *ptr1 = (int *)(&aa + 1);
int *ptr2 = (int *)(*(aa + 1));
printf( "%d,%d", *(ptr1 - 1), *(ptr2 - 1));
return 0;
}
aa本身代表首元素地址,直接对aa+1,会挪动一个数组元素大小
&aa代表数组,&aa+1会挪动整个数组大小,比如这里是10个,所以到了数组尾巴完,所以*(ptr-1)是1, ptr2是5,-1是6
- 下面代码中print_arr函数参数设计哪个是正确的?( )
int arr[3][5] = {1,2,3,4,5,6,7,8,9,10};
print_arr(arr, 3, 5);
A.void print_arr(int arr[][],int row, int col);
B.void print_arr(int* arr, int row, int col);
C.void print_arr(int (*arr)[5], int row, int col);
D.void print_arr(int (*arr)[3], int row, int col);
用数组指针接收二维或一维数组 ,即
int (*arr) [size]** ,就行
A. 缺少了二维的大小,
B. 是指针数组,这里要接收二维数组,或者一维数组,需要的是数组指针
数组指针的写法: int (*arr)[size]
C. 是数组指针,size是5 int ()
数组指针的写法:int (*arr) [size] ,一维就大小写一维,二维就写每行多少列,一维可以不给,赋值给整个数组的地址,&arr,或者arr,视频21,指针(二)
D. 错误在给了3
- 程序结果:
int main()
{
int a[5] = {5, 4, 3, 2, 1};
int *ptr = (int *)(&a + 1);
printf( "%d,%d", *(a + 1), *(ptr - 1));
return 0;
}
对数组取地址,&a+1,位置应该到了1后,而a+1,是首元素+1,值为4,
ptr-1回到了1
设有以下函数void fun(int n,char *s){……},则下面对函数指针的定义和赋值均是正确的是:( )
A.void (*pf)(int,char); pf=&fun;
B.void (*pf)(int n,char *s); pf=fun;
C.void *pf(); *pf=fun;
D.void *pf(); pf=fun;
分析:
一说函数指针 : (*pf) , 左边是返回值类型,右边是参数
所以选B,赋值]直接给fun
qsort模拟实现(指针进阶最后)
前言:我们写好一个排序函数,类型已经固定了,比如下面这个,只能排int,而面对float就不行
这就可以利用qsort
stdlib.h
cmp_int(const void* )
sqort():
sz就用sizeof()/sizeof()
大小就:sizeof(arr[0])
width:比较每个数据的字节大小
- int(* compare)(const void *e1, const void *e2) : 接收函数,这个函数的参数是两个要比较的元素的地址
- void* e1 void* e2 不能直接解引用,因为void是无类型的指针
且大于,两个元素按你给的方式做比较,大于就返回大于0的数,小于就返回小于0的数,如下面代码中return e1 - e2;
// *e1不能直接解引用,因为指针类型是void,无类型,不能解引用,因为解引用要知道解开的大小
void cmp_int(const void* e1, const void* e2)
{
// 如果是比较int,给void指针转为int* ,转了之后,也还是指针,但是带了类型,所以需还要解引用
return *(int*)e1 - *(int*)e2;
}
void print_arr(int arr[], int sz)
{
int i = 0;
for (i = 0; i < sz; i++)
{
printf("%d", arr[i]);
}
printf("\n");
}
int main()
{
int arr[] = { 9, 8, 1, 3, 4, 2, 6 };
int sz = sizeof(arr) / sizeof(arr[0]);
// qsort:参数4 :int(* compare)(const void *e1, const void *e2):函数指针 返回int , e1、e2:比较的两个元素地址
// arr, sz, width是元素大小, int, 自写函数,
print_arr(arr, sz);
qsort(arr, sz, 4, cmp_int);
print_arr(arr, sz);
return 0;
}
所以,使用qsrot()我们需要写比较返回大小,且里面要对比较类型做解引用,工作量不大
接下来,换个类型去写:
结构体排序
发现右边arr,用年龄排好了
结构体指针注意:
((struct stu*)e1)->age
先强制转化怒,再括号,再指向age
且使用qsort()传入大小为 sizeof(arr[0])
- 结构体 按名字比
字典序比较字符串,用strcmp() ,且strcmp()函数正好大于返回>0,小于返回<0
strcmp(((struct S*)e1)->name, ((struct S*)e2)->name )
struct S
{
char name[20];
int age;
};
int cmp_stu_by_name(const void* e1, const void* e2)
{
return strcmp(((struct S*)e1)->name, ((struct S*)e2)->name );
}
int main()
{
struct S arr[] = { {"zhangsan", 20}, {"lisi", 12},{"wangwu", 88}};
int sz = sizeof(arr) / sizeof(arr[0]);
//qsort(arr, sz, sizeof(arr[0]), cmp_stu_by_age);
qsort(arr, sz, sizeof(arr[0]), cmp_stu_by_name);
return 0;
}
发现,已经按照name排好了
写个qsort()
分析:起始为base、比较的数组大小、每个元素字节大小
它能接收各种类型的数据,所以base是 void * ,不能解引用
void * point,无具体类型指针,可以接收任意数据类型的地址
没有加k,一直交换的是第一个位置
if (cmp_fum((char*)base+j*width, (char*)base+(j+1)*width)>0)
{
// 交换,依次把元素的每个字节的内容交换,没有临时变量
int k = 0;
for (k = 0; k < width; k++)
{
// 一个字节是一个char, 两个元素中的char一个个交换,所以拿char 接收
// 分别是两个元素第一个字节的地址:(char*)base + j*w , (char*) base + (j+1)*w
// 值换了1个字节,再用上k,把所有的字节都换
char t = *((char*)base + j * width); // 解引用*(对上面第一个字节的地址)
// *((char*)base + j * width) *((char*)base+(j+1)*width);
*((char*)base + j * width) = *((char*)base + (j + 1) * width);
*((char*)base + (j + 1) * width) = t;
; }
}
if (cmp_fum((char*)base+j*width, (char*)base+(j+1)*width)>0)
{
// 交换,依次把元素的每个字节的内容交换,没有临时变量
int k = 0;
for (k = 0; k < width; k++)
{
// 一个字节是一个char, 两个元素中的char一个个交换,所以拿char 接收
// 分别是两个元素第一个字节的地址:(char*)base + j*w , (char*) base + (j+1)*w
// 值换了1个字节,再用上k,把所有的字节都换
char t = *((char*)base + j * width+k); // 解引用*(对上面第一个字节的地址)
// *((char*)base + j * width) *((char*)base+(j+1)*width);
*((char*)base + j * width+k) = *((char*)base + (j + 1) * width+k+1);
*((char*)base + (j + 1) * width+k+1) = t;
; }
}
中间比较有点复杂,封装swap,且* 太复杂了,所以单独写swap
void _Swap(char * buf1, char*buf2, int width)
{
int i = 0;
for (i = 0; i < width; i++)
{
char tmp = *(buf1 + i);
*(buf1 + i) = *(buf2 + i);
*(buf2 + i) = tmp;
}
}
// 重写qsort() : 参数base,接收各种类型数据, sz, 每个元素大小, 函数指针, 必须返回int,大于就返回》0的数
void my_qsort_bubble(void* base, int sz, int width, int(*cmp_fun)(const void* e1, const void* e2))
{
// 1. 先搭好冒泡架子
// 2. 考虑 if中比较的类型:比较base和base+4,但是base是void*,需要强制转换,
// (int*)base+4 一下子跳过16个字节 ,即跳过4个int* 长度,不可以
// 用(char*)base+width:这样跳过的就是一个完整元素长度的内存,因为width会和char*同类型转换
// 注意base不能直接+-,要先强制类型转换
for (int i = 0; i< sz - 1 ; i++)
{
for (int j = 0; j< sz - 1-i ; j++)
{
if (cmp_fun((char*)base+j*width, (char*)base+(j+1)*width)>0)
{
// 交换
_Swap((char*)base + j*width, (char*)base+(j+1)*width, width);
}
}
}
}
void print_arr(int arr[], int sz)
{
int i = 0;
for (i = 0; i < sz; i++)
{
printf("%d", arr[i]);
}
printf("\n");
}
int main()
{
int arr[] = { 9, 8, 1, 3, 4, 2, 6 };
int sz = sizeof(arr) / sizeof(arr[0]);
// qsort:参数4 :int(* compare)(const void *e1, const void *e2):函数指针 返回int , e1、e2:比较的两个元素地址
// arr, sz, width是元素大小, int, 自写函数,
print_arr(arr, sz);
//qsort(arr, sz, 4, cmp_int);
// cmp_int:有问题、之前int也不对,是int,写完qsort传cmp吗
// 换用自己写的
my_qsort_bubble(arr, sz, 4, cmp_int);
print_arr(arr, sz);
return 0;
}
字符串左旋全部情况
犯错:函数内的数组char* new大小不需要给,直接new[i]赋值。传入sz,不用给右指针,new是从外部传进的,大胆给大小,101,所以调用函数的内部直接可以new[i]
- 先写输入左旋k个字符的代码
void zxuan(char* head, char* new,int sz, int k)
{
// 先接收前k个
int len = 0;
// 左旋k从下标k开始
for (int i = k; i<= sz-1; i++)
{
new[len++] = *(head + i);
}
// 从0到k
for (int i = 0; i <= k - 1; i++)
{
new[len++] = *(head+i);
}
}
// 左旋字符:有几个,左旋几次
int main()
{
char s[10] = { 0 };
gets(s);
// gets可拿带空格的字符串,用strlen()求长度
int sz = strlen(s);
char* new[101] = {0};
// 要返回一个新的数组,所以改一个
int k = 0;
printf("输入左旋k:");
scanf("%d", &k);
zxuan(s, new, sz, k);
printf("长度%d\n", sz);
printf("新的:%s", new);
return 0;
}
- 改左旋k为左旋从1~sz个,然后看输入在不在范围:
int zxuan(char* head, char* new,int sz, int k, char* find)
{
// 先接收前k个
int len = 0;
char* flag = head;
int res = 1;
for (int k = 0; k <= sz - 1; k++)
{
// 每次交换都要初始化len、head、res
len = 0;
head = flag;
res = 1;
// 左旋k从下标k开始
for (int i = k; i <= sz - 1; i++)
{
new[len++] = *(head + i);
}
// 从0到k
for (int i = 0; i <= k - 1; i++)
{
new[len++] = *(head + i);
}
// 判断是否是当前
printf("现在比得是:%s\n", new);
printf("现在找得是:%s\n", find);
for (int i = 0; i < sz; i++)
{
if (new[i] != find[i])
{
res = 0;
break;
}
else
{
continue;
}
}
// 当前转得和find相等
if (res == 1)
{
break;
}
}
return res;
}
// 左旋字符:有几个,左旋几次
int main()
{
char s[10] = { 0 };
gets(s);
char find[10] = {0};
gets(find);
printf("原始: %s\n", s);
printf("寻找: %s\n", find);
// gets可拿带空格的字符串,用strlen()求长度
int sz = strlen(s);
char new[101] = {0};
// 要返回一个新的数组,所以改一个
int res = zxuan(s, new, sz, sz, find);
printf("存在吗:%d\n", res);
return 0;
}
判断字符串是否是字符串左旋后的结果(最简单思路)
C语言直接用两个字符串函数:
- 拼接: strncat(str2, str2, strlen(str2))
- 直接用子串函数strstr()判断:
strstr(str2, str1);