C语言1.作业指针(二)2021_8_03:数组指针、函数指针数组、qsort()使用和qsort模拟()、字符串左旋全部可能性判断

函数指针题

请添加图片描述
首先,一说函数指针,联想最简单的:
int (*p)(int, int):它是个指针因为括号星p, (*p),名字外左是返回类型,右边是参数,所以描述:函数指针指向的函数有两个int形参,且返回int类型数据
如果函数指针有两个int形参,且返回函数指针:
(*p)(int, int) 它的返回 是函数指针: 所以把他名字部分括号起来:
(*(*p)(int, int)) ,然后左边是外层函数指针左边返回int,右边是参数int

  1. 声明一个指向含有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 *) :指向函数指针数组的指针

  1. 函数形参合适的是:
    下面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,它将找不到结束。
调用test给arr[0]
↑ 普通情况下,%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

  1. 下面代码中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

  1. 程序结果:
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,小于返回<0strcmp(((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语言直接用两个字符串函数:

  1. 拼接: strncat(str2, str2, strlen(str2))
  2. 直接用子串函数strstr()判断:
    strstr(str2, str1);
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值