C语言进阶1.指针 + 旧的作业(换汽水问题、规范数组:奇数左偶数右)

旧作业:

若struct *p = &a;
*p.name输出name写法错误,因为点的优先级高于星
换汽水
20元,1元1瓶汽水,给20,能喝多少
int count = 0;
int value = 20,while value>=2 {除2次,加1次,count++;}
交换数组
int类型数组,arr[] ={};
当arr大小未定时,用sizeof()求
思路:操作int类型arr,需要传入长度sz,不用分很大情况,左边找偶数,右边找奇数,再做交换
move_fun(int arr[], int sz)
{
while(left<right)
left = 0;
right = sz-1;
// left找偶数
while(arr[left]%21){left++;}
// right找奇数
while(arr[right]%2
0){right–;}
// 下来时如果左、右指针还正常
if(left<right) {交换;}
}
以上写法,当全是奇时,会越界,下面找奇数数组已经越界了,所以在left处加判断left<right

数组遍历时一定要注意越界问题,c语言不查越界错误

指针进阶

回顾:

  1. 32位、64位下指针大小分别是4、8
  2. 指针类型决定了指针±移动步长,即解引用操作时的权限
    char:字符指针*
char ch = 'w';
char* pc = &ch;
char* pstr = "abcdef" ;// 常量字符串,不能修改
printf("%s", pstr);
*pstr = 'w';// 上面是常量字符串,不能修改会报错
=============上面会报错==============
char arr[10] = "abcd";
char*p2 = arr; // 这样的arr可以修改

char* pstr = “abcdef”; 不是把字符串放到指针变量里
pstr大小4字节,它只是存了字符串首地址,输出时会输出一串
且"abcdef"是常量字符串,以\0结尾
且用pstr修改字符串,会报错
但如果是字符串用char数组存,可以修改
总之,*char str = “abcd”, 不是存入了字符串,而是存了首地址&‘a’, 但读取%s能拿出全部,且不能修改 ,原始字符串用字符数组存才行

读代码:
请添加图片描述
我猜:两个数组不等,因为字符串比较用特定函数,第二个等,因为都是拿内存中值给了两个指针 (√)
解析

  1. 创建两个空间,arr1,arr2,内容都是"abcde",内存中两个数组,在两个不同位置,用数组名arr1、arr2比较,比较的是地址,必然不等
  2. 内存中存"abcedf",常量字符串–不能被修改,且没必要存两份,
    用两个指针指向的是同一个地方,比较指针,指针变量存的是地址,本身p1、p2值是地址,相等的地址。

指针数组:是个数组,存放指针

// 指针数组
int a=10;
int b=20;
int c= 30;
int* arr[3] = {&a, &b, &c};  //里面存变量地址
printf("%d", *(arr[i]));
// 下面这个也是指针数组
int arr1[] = {1, 2, 3};
int arr2[] = {3, 4, 5};
int arr3[] = {6, 7, 8};
int* arr[3] = {arr1, arr2, arr3};
访问arr1、arr2、arr3中的值
用arr[i][j]即可
===========字符指针============
字符指针:顾名思义,指向字符

** 数组指针:它是个指针,指向数组的指针**
数组指针 = 数组地址
int arr[10] = {1, 2, 3, 4…};
首先:右边取整个数组的地址,&arr
那么左边的指针考虑int *p[10];
但是这样显然是一个指针数组,不是指针
所以为了是区分是指针,(*p)
所以:int(*p)[10] = &arr; 表示是数组指针,该指针指向了一个数组,每个元素类型是int,

总之,它是一个指针,专门存数组地址
int(*p)[10],这个指针指向大小为10的数组

char* ch[5];
char* (*pc)[5] = &ch;
// 上面是数组指针,从中间读、再读右边,再读左
// 数组指针*pc,指向数组大小5,内部元素是char*
char* ch[5];
char (*pa)[5] = &arr;
// 数组指针*pa,指向数组大小5,数组内部元素是char

总之,数组指针,取的是整个数组地址,用&arr,而不是过去arr,或&arr[0],但是输出&arr和后面两个打印值一样,因为起始位置一样,但是数组指针+1,会跳过一个数组大小
打印指针用%p
请添加图片描述
这里的实验,它俩+1后地址差28,但这是16进制,16进制28大小为40字节。
所以&arr是整个数组的地址

数组指针的用法:多在二维指针
回顾:

  1. char* ps = “hello”; 常量字符串,这种写法不严谨,内容不能改变,运行会错,改进如下:
    const char* ps2 = “hello”;
  2. 指针数组:存放指针的数组:int* arr[] = {&a, &b, &c};

&arr:数组的地址
arr:数组的首元素地址

  1. 数组指针:
int arr[5] = {1, 2, 3, 4};
int (*p)[5] = &arr; // &arr拿到的是数组地址,数组有5个元素,每个元素类型是int

使用
for: (*p)[i]; 如果只是这样,不方便,因为有更简单的写法
数组指针常见于二维数组
对于二维数组,arr是{1, 2, 3}地址,即第一行的地址,每一行都是一个元素,数组名是首元素地址,第一行是它的首元素
数组指针+1,会一行一行跳

int arr[3][5] = {{1, 2, 3}, {2, 3}, {}}

用法1:因为数组指针拿一维数组看着不顺,但是拿二维数组看着还行
(*(p+i))[j],解引用 * (p+i)
用法2:用二维数组做函数参数时:
void printf( int (*p)[3][5] )
{
}
读代码:

** int (parr3[10])[5]; **
拆:
int (
) [5];外面看,它是一个大小为5的数组
parr3[10] 是个数组,有10个元素
总体:parr3是个数组,有10个元素,每个元素是指针,每个指针又指向包含5个有元素的数组,且每个元素是整型
请添加图片描述
parr3[10]:这是数组,存10个元素,剩下的是它存的内容,为指针,每个指针指向大小为5的int数组。
请添加图片描述
上图:分别为:数组、指针数组(parr与[]大小先结合,成了数组,剩余int* 意思所存为整型指针,一般优先级)、
数组指针(和parr2先结合,用括号因为优先级问题,存parr别管,就是想数组指针,这个数组存10个int型数据)
最后一个:内部先是parr3[10],这是个数组,大小为10的数组,所存为指针,所存的指针又指向大小为5的int类型数组。
函数参数写为:一维数组,大小可以不写,但写上也没用。

二维数组传参:
  1. 传什么大小二维数组,就用什么大小二维数组接收,简单且非常没问题,函数位置的列一定要给定
  2. 用数组指针来接收
  3. 用二级指针
    请添加图片描述
    以上是一维、二维数组传参,下面是指针传参
    指针传参
    一级指针传参
    int* p;
    int p;
    两者没区别
    连续定义两个指针: int
    p1, p2; p2没用星,所以错误
    应该:int *p1, *p2;

二级指针传参:
给一个指针的地址,函数参数就用二级指针来传
数组指针用一维写法,且大小为列大小,意义是原始的数组一行看作一个元素,而这个元素大小为5,而外层第几个元素,用*(p+i)访问即可。

int main()
{
	int arr[3][5] = { {1, 2, 3, 4, 5}, {6, 7, 8, 9, 10}, {11, 12, 13, 14, 15} };
	//int(*p[3])[5] = &arr[0];
	int(*p)[5] = arr;
	for (int i = 0; i <3 ; i++)
	{
		for (int j = 0; j <5 ; j++)
		{
			printf("%d ", (*(p+i))[j]);
		}
		printf("\n");
	}
	return 0;
}

函数指针:
指向函数的指针,存放函数地址
请添加图片描述
请添加图片描述
两种输出结果都是函数地址,对于函数来说,函数名和&函数名,都是它的地址

pf和*结合,说明是指针, 去名字,剩下是函数类型,后面是参数,前面是返回类型;
int (*pf)(int, int) = &ADD;
使用的时候(右边)int sum = (*pf)(2, 3);
其实不用解引用pf,也可以
因为Add和&Add都是函数地址,所以发现直接函数指针pf也可以调用函数,且加多个****,还是可以用这个函数

总之,今天的数组指针,会访问二维数组即可。

函数指针====

请添加图片描述

  1. 解析:
    void ()()中, (*)()是 函数指针,加void说明无返回,
    上面整体加()说明在强制类型转换,0,如何理解把0地址处转为一个函数指针类型:
    现在,0已经成了地址,加
    是在解引用,然后说明我在调用一个函数,那个函数无参,所以后面再加(),相当于在调用那个函数。
    总之,调用地址0的函数,是《C语言缺陷》
    写一个void test()函数,无参无返回,
    (*test)();可以调用直接,*也可以不用
    test();
  2. 解析:
    signal先和后面括号结合是函数,
    调用signal时,调用完返回函数地址,该函数地址中所存函数为void,参数为int
  • 分析:
    先把中间去掉,剩下是void(*)(int),这部分一看就是个函数指针,它指向的函数有void表明不会返回,int是参数类型。
    再看内部:signal是函数声明,有两个参数: int,函数指针:viud ( * )(int),该函数指针指向一个参数为int返回类型为void的函数。
  • 细写:
    signal函数是一个函数声明
    signal函数的返回类型也是一个函数指针,该函数指向一个参数int,返回类型void的函数

也可以把它简化:
请添加图片描述

  • 加一个typedef用法:
    平时情况下, p1是指针而p2不是指针,但通过typedef定义 int* 后, ** p4也是指针 **
    请添加图片描述
    而用#define 起不到这个效果
    因为#define相当于把 int*叫成了别的abc,下面我用abc,直接置换过来了,如下图
    p5、p6和p1、p2一样
    请添加图片描述
    函数指针数组:是一种数组,存放函数指针,放相同类型函数地址,即相同类型函数
    int (*pf)(int, int) = add; 这是个函数指针,下面要写一个函数指针数组,所以改它的名字
    int(*pf[4])(int, int) = add; XXX 且这里右边要改,因为数组初始化右值是 {Add, Sub }
    函数指针用途:转移表

** 指向函数指针数组的指针 **
用指针指向函数指针数组:

int(*pf)(int, int);	; // 函数指针
int(*pfArr[5])(int, int); //  函数指针数组
ppfarr = &pfArr;	// 

这个ppfarr类型是上面类型再加(*ppfarr)
请添加图片描述
函数指针最多用在回调函数:
函数内部在实现的时候,它获取到了别的函数的地址,即:在写一个函数的时候,把函数的指针作为参数传递给另一个函数,当这个指针被调用其所指向的函数时,就说这是回调函数。
回调函数不是该函数的实现直接调用,二十特定时间发生时,由另乙方调用,用于对该时间或条件进行响应。

在一定条件下,通过函数地址来调用

qsort()

int int_cmp(const void * p1, const void p2 )
{
return (
(int *)p1 - *(int *)p2);
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值