提示:以下内容都是针对指针练习展开的,相当于是拿指针练手,增加对指针的理解。有小伙伴不理解,勿看!
指针 操作 二维数组
提示:二维数组
1.c语言中并不存在,真正的二维数组。
2.二维数组本质 是一维数组的一维数组
3.二维数组 也 符合数组的特点 //连续性,有序性,单一性
问题:
1.怎么针对二维数组确定需要定义什么类型的指针?
例如:
int a [2][3];
&a[0]
//a[0] --- int[3]
//&a[0] --- int[3] *
//c语言中 不支持 int[3] *
//正确写法 int(*)[3]
int(*p)[3] = a; //p指向二维数组 a
//p的基类型 int[3]
*p <=> a[0] // 相当于是内部这个一维数组的数组名
(*p)[0]
*(*p+0) //**p
*(*(p+1)+1) <=> a[1][1]
*(*(p+i)+j) <=> a[i][j] //里面的*号是对a[i]取地址,而a[i]等价为*(a+i),在机器语言中其实[]就代表
*(),而*(p+i)+1;代表a[i][1],也就是说i的值确定了,+1代表在a[i]
[],i确定下,对二维数组最里面的a[j]访问,(这里指是a[i][j]的地址)。
注意:
二维数组的操作
从二维数组的本质 进行的
二维数组的本质 :一维数组的 一维数组
直接的访问操作 也是 一维一维的展开
练习:
1. 求出整形数组a[N][N]主对角线最大值和辅对角线最小值
int *Input(int (*a)[n],int n)
{
int(*p)[n]=a;
int i=0;
int j=0;
for(i=0;i<n;i++)
{
for(j=0;j<n;j++)
{
scanf("%d",&*(*(p+i)+j));
}
}
}
int Maxzhu(int (*a) [n],int n)//这里int (*a) [n],代表a的首地址,即a[0].
{
int(*p)[n]=a;
int i=1;
int j=1;
int max=**p;
while(i==j&&j<n&&i<n)
{
if(*(*(p+i)+j)>max)
{
max=*(*(p+i)+j);
}
i++;
j++;
}
return max;
}
int Minfu(int (*a) [n],int n)
{
int(*p)[n]=a;
int i=1;
int j=1;
int min=*(*(p)+n-1);
while(i<n)
{
if(*(*(p+i)+n-i-1)<min)
{
min=*(*(p+i)+n-1-i);
}
i++;
}
return min;
}
int main()
{
int i=0,n;
printf("In put n: ");
scanf("%d",&n);
int a[n][n];
Input(a,n);//调用输入函数,将值输进去。
printf("Max= %d\n",Maxzhu(a,n));//主对角线最大值
printf("Min= %d\n",Minfu(a,n));//副对角线最大值
return 0;
}
实现一个函数,找出二维数组中能被3整除的数
void Zhenchu(int (*a)[5],int n)
{
int (*p)[5]=a;
int i=0;
int j=0;
for(i=0;i<n;i++)
{
for(j=0;j<n;j++)
{
if(*(*(p+i)+j)%3==0)
{
printf("%d\n ",*(*(p+i)+j));
}
}
}
}
int main(void)
{
int n;
int a[5][5]={1,2,3,4,5,6,7,8,9,15,41};
Zhenchu(a,5);
return 0;
}
指针 操作 二维字符型数组
操作二维字符型数组其实和操作二维数组是差不多的。唯一有区别的是字符串有结束标志'\0';
char s[][10] = {"hello","world","china"};
char (*p)[10] = s; //p指向二维数组s
//*(*(p+i)+j)
练习:
输入三个字符串 ,排序输出
int Strcmp(const char *s1,const char *s2)//自己写的函数,比较字符串大小
{
int ret = 0;
while (*s1 == *s2 && *s1 != '\0' && *s2 != '\0')
{
++s1;
++s2;
}
ret = *s1 - *s2;
return ret;
}
char *Strcpy(char *dest,const char *src)//把src的值复制到dest。
{
char *ret = dest;
while (*src != '\0')
{
*dest = *src;
dest++;
src++;
}
*dest = '\0';
return ret;
}
void Erwei(char (*a)[20],int n)
{
char (*p)[20]=a;
int i=0;
int j=0;
for(i=0;i<n-1;i++)
{
for(j=i+1;j<3;j++)
{
if(Strcmp(*(p+i),*(p+j))>0)
{
char b[20];
Strcpy(b,(*p+i));
strcpy(*(p+i),*(p+j));
strcpy(*(p+j),b);
}
}
}
}
int main(void)
{
int n;
char a[][20]={"hello","word","abc"};
Erwei(a,3);
for(int i=0;i<3;i++)
puts(a[i]);
return 0;
}
指针的数组:
概念
指针数组是数组,用来存放指针的。
比如:
int arr[10]={0}; //整型数组,数组用来存放整型(int)
char ch[5]={0}; //字符数组, 数组用来存放字符(char)
int* parr[4]; //指针数组,数组用来存放整型指针(int*)
char* pch[5]; //指针数组,数组用来存放字符指针(char*)
用法:
char s[3][10]={"hello","nihao","word"};
char s[10] = "hello";//存放字符串数据
char *p = "hello";
char s[10]; //s的数据类型 char[10]
char[10] s1[3]; //二维数组 //此时的二维数组的元素 是一个一维字符型数组
//定义一个存放 字符串 的一维数组
char *p = "hello"; //p的类型 char *
//char *的指针变量p 相当于代表一个字符串
char *p1 = "hello";
char *p2 = "world";
char *p3 = "china";
char* pstr[3]= {"hello","world","china"}; //数组 --- 数组中存放是 各个字符串的地址
//地址 ---存放这地址数据的数组 --- 指针的数组 ---指针数组
char **q = pstr; //q 二级指针
练习:
找出字符串中最大值
char *Maxstr(char **p,int n)
{
char *max=*p;
for(int i=1;i<n;i++)
{
if(strcmp(p[i],max)>0)
{
strcpy(max,p[i]);
}
}
return max;
}
int main(void)
{
int n;
char s[][20]={"hello","word","abc"};
char *p[]={s[0],s[1],s[2]};
puts(Maxstr(p,3));
return 0;
}
练习:
在以上的代码中进行排序
void Maop(char **p,int n)
{
char *max;
for(int i=0;i<n-1;i++)
{
for(int j=0;j<n-i-1;j++)
{
if(strcmp(*(p+j),*(p+j+1))>0)
{
max=*(p+j);
*(p+j)=*(p+j+1);
*(p+j+1)=max;
}
}
}
}
总结
(1)int(*p)[10]
p先和*
结合,说明p是一个指针变量,然后指向的是一个大小为10个整型的数组。
所以p是一个指针,指向一个数组,叫数组指针。
(2)注意
[]
的优先级高于*
,所以必须加上()来保证p先和*
结合。
函数指针:
概念
函数指针 的本质是一个指针,该指针的地址指向了一个函数,所以它是指向函数的指针。
我们知道,函数的定义是存在于代码段,因此,每个函数在代码段中,也有着自己的入口地址,函数指针就是指向代码段中函数入口地址的指针。
通过指针 的方式 来调用函数
那我们先了解函数名代表什么?
函数名 --- 代表函数的入口地址
int add(int a,int b) // 函数
// 函数名 - 代表函数的入口地址
// 函数名对应的数据类型
// int (int a,int b) //函数类型
// 代表一类函数
// 返回值为int型
// 带有两个int型的形参变量
声明形式
基类型 (*函数名)(基类型,基类型)
int (*p) (int,int)
说明:
1.可以定义一个函数类型的指针变量
来保存函数的入口地址
2.有了这个指针变量
通过指针变量 进行函数调用
思考:为什么要使用函数指针?
那么,有不少人就觉得,本来很简单的函数调用,搞那么复杂干什么?其实在这样比较简单的代码实现中不容易看出来,当项目比较大,代码变得复杂了以后,函数指针就体现出了其优越性。
举个例子,如果我们要实现数组的排序,我们知道,常用的数组排序方法有很多种,比如快排,插入排序,冒泡排序,选择排序等,如果不管内部实现,你会发现,除了函数名不一样之外,返回值,包括函数入参都是相同的,这时候如果要调用不同的排序方法,就可以使用指针函数来实现,我们只需要修改函数指针初始化的地方,而不需要去修改每个调用的地方(特别是当调用特别频繁的时候)。
举例:
#include <stdio.h>
int add(int a,int b)
{
return a + b;
}
int sub(int a,int b)
{
return a - b;
}
int mul(int a,int b)
{
return a * b;
}
int div(int a,int b)
{
return a / b;
}
//以上是实现两个数的加减乘除
void processData(int a,int b,int(*p)(int,int))//在这里我们直接用函数指针直接调用函数就可以实
现输出不同函数。
{
printf("result: %d\n",p(a,b));
}
int main(int argc, const char *argv[])
{
processData(1,2,add);
processData(1,2,sub);
processData(1,2,mul);
processData(1,2,div);
return 0;
}
回调函数
函数指针的一个非常典型的应用就是回调函数。
什么是回调函数?
回调函数就是一个通过指针函数调用的函数。其将函数指针作为一个参数,传递给另一个函数。
回调函数并不是由实现方直接调用,而是在特定的事件或条件发生时由另外一方来调用的。
举例:
#include<stdio.h>
#include<stdlib.h>
//函数功能:实现累加求和
int func_sum(int n)
{
int sum = 0;
if (n < 0)
{
printf("n must be > 0\n");
exit(-1);
}
for (int i = 0; i < n; i++)
{
sum += i;
}
return sum;
}
//这个函数是回调函数,其中第二个参数为一个函数指针,通过该函数指针来调用求和函数,并把结果返回给主调函数
int callback(int n, int (*p)(int))
{
return p(n);
}
int main(void)
{
int n = 0;
printf("please input number:");
scanf("%d", &n);
printf("the sum from 0 to %d is %d\n", n, callback(n, func_sum)); //此处直接调用回调函数,而不是直接调用func_sum函数
return 0;
}
总结:
1.指针
指针概念
指针的定义 //基类型 * 变量名
指针的初始化 和 赋值
//野指针 NULL,没有赋值
核心用途
被调修改主调
使用方法:
1.要修改谁,把谁的地址传过去
2.被调函数中,一定要有对应的 *p运算
3.传过去的实参地址,一定要对应一块有效的内存空间
-------------------------------------------------------------
2.指针 + 数组
指针 操作 一维整型数组
指针 操作 一维字符型数组
核心思路:
1.定义一个什么类型的指针变量
//参考第2点
2.获得那个位置的地址 //数组的特点
//数组首元素的地址 &a[0] //就是数组名
指针操作数组元素的过程:
指针运算
p+1 //代表p+1的地址,在P上移动基类型大小的字节
*p //代表对P进行取值
--------------------------- ------------------------------------------
指针 + 二维数组
1.二维数组本质 ---一维数组
2.数组的特点 //连续空间
3.指针类型的定义 //int(*p)[ ];
4.指针访问数组元素的 过程
*(*(p+i) + j)