深入理解指针(三)
目录
1.字符指针变量
//为了严谨,加上const,让其不能进行修改。
const char* p = "abcdef";//把他想象成一个字符数组,指针向首个元素的地址。
//这种称为常量字符数组,是不可以修改里面的内容的
printf("%c ", "abcdef"[2]);
//p[1] = 'a'; //这里是修改不了的
《剑指offer》中收录了⼀道和字符串相关的笔试题,我们⼀起来学习⼀下:
char str1[] = "hello bit.";
char str2[] = "hello bit.";
const char* str3 = "hello bit.";
const char* str4 = "hello bit.";
if (str1 == str2)
printf("str1 and str2 are same\n");
else
printf("str1 and str2 are not same\n");
if (str3 == str4)
printf("str3 and str4 are same\n");
else
printf("str1 and str2 are not same\n");
这题就考到字符指针变量的用法
1.首先看str1他是一个字符数组,存放内容是字符串,str2也是,两个是独立的数组,地址是没有任何关系的。
2.其次看str3和str4,他们都是字符指针数组都指向“hello bit”这个字符串的首元素的地址,所以他们地址是一样的。
2.数组指针
2.1数组指针是什么?
之前我们学习了指针数组,指针数组是⼀种数组,数组中存放的是地址(指针)。 数组指针变量是指针变量?还是数组? 答案是:指针变量。
我们已经熟悉:
• 整形指针变量: int * pint; 存放的是整形变量的地址,能够指向整形数据的指针。
• 浮点型指针变量: float * pf; 存放浮点型变量的地址,能够指向浮点型数据的指针。
那数组指针变量应该是:存放的应该是数组的地址,能够指向数组的指针变量。
//例子
int n = 100;
int* pn = &n;
char ch = 'w';
char* pc = &ch;
float f = 3.14f;
float* pf = &f;
int arr[10] = { 0 };
//指向数组的一个指针
int (*parr)[10] = &arr;//取出的事整个数组的地址,不应该是数组的首元素地址嘛
//两边要同时符合 int (*)[] =int (*)[]
//数组指针
int* p1[10];//p1是指针数组,数组10个元素,每个元素的类型是int * ,所以p1是指针数组
int(*p2)[10];//p2是数组指针,指针指向的是数组,数组有10个元素,每个元素类型是int
int arr[10] = { 0 };
int* pa = arr;//这个是指向数组首元素的地址
//p+1 //是数组的下一个元素
printf("pa = %p\n", pa);
printf("pa + 1=%p\n", pa + 1);
int(*p)[10] = &arr; //这个指向整个数组
//p +1 //是直接跳过这个数组
printf("p =%p \n", p);
printf("p + 1 =%p\n ", p + 1);
2.2数组指针如何初始化
数组指针变量是⽤来存放数组地址的,其实获取数组地址就是初始化了,那怎么获得数组的地址呢?就是我们之前学习的 &数组名 。
int arr[10] = { 0 };
//&arr得到的就是数组的地址
int(*p)[10] = &arr;
数组指针类型解析:
2.3数组指针简单应用
//数组指针的简单应用(实际场景不应在此应用过于麻烦)
int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
int (*p)[10] = &arr;
int i = 0;
for (i = 0; i < 10; i++)
{
//推理
//p = &arr -> *p = *&arr=arr
//-> *p = arr ->(*p)[i] = arr[i];
printf("%d ", (*p)[i]);
}
3.二维数组传参本质
首先要明白
1.二维数组的首元素其实是第一行,
2.而二维数组的每一行代表一维数组
3.所以说二维数组的首元素地址就是第一行的地址。
二维数组接收形参的方式
3.1用数组的形式接收
void swap(int arr[3][5],int r ,int c )//这里行可以省略但是列不行
{
int i = 0;
for (i = 0; i < r; i++)
{
int j = 0;
for (j = 0; j < c; j++)
{
printf("%d ", arr[i][j]);
}
printf("\n");
}
}
int arr[3][5] = { {1,2,3},{4,5,6},{7,8,9} };
swap(arr, 3, 5);
3.2运用指针数组来接收形参
因为二维数组每一行都代表代表一个一维数组,用数组指针指向每一行(一维数组),并且二维数组传参传的是首元素的地址,而首元素的是一维数组,所以要用到指向数组的指针,即数组指针。
参考一下模拟二维数组
//用数组指针来接收
void swap(int (*p)[5], int r, int c)//这里行可以省略但是列不行
{
int i = 0;
for (i = 0; i < r; i++)
{
int j = 0;
for (j = 0; j < c; j++)
{
//*(*(p+i)+j) = *(p[i]+j) //这里跟指针数组实现模拟二维数组那里内容是一样的写法
printf("%d ", *(*(p+i)+j));//指针的解引用
}
printf("\n");
}
}
int arr[3][5] = { {1,2,3},{4,5,6},{7,8,9} };
swap(arr, 3, 5);
4.函数指针变量
4.1什么是函数指针变量
函数指针变量应该是⽤来存放函数地址的,未来通过地址能够调⽤函数的
其实函数也是有地址的,不信下面可以测试一下
int ax()
{
return 0;
}
printf("%p \n", &ax);
printf("%p \n", ax);
可以看出来函数的数组名和取地址都是函数的地址,那么有地址就可以用到指针了。
4.2函数指针的使用
int ax(int x,int y)
{
return x + y;
}
int (*p)(int,int) = &ax;//p就是函数指针变量
//int (*)(int, int) //这个是函数指针类型
int ret = ax(3, 6);
printf("%d \n", ret);
//函数指针调用函数
int ret2 = (*p)(3, 6);
printf("%d \n", ret2);
//可以去掉*号,因为ax也是地址,而p存放ax的地址。
int ret3 = (*p)(3, 6);
printf("%d \n", ret3);
int (*pf)(int, int) = ax;
int ret4 = (*p)(3, 6);
printf("%d \n", ret4);
char* ax2(char x)
{
return 0;
}
//当函数是指针函数时
char* (*pf2)(char) = ax2;
4.3涉及函数指针的两段代码
//涉及函数指针的两段代码
(*(void (*)())0)();
//上述代码是一次函数调用
//1.(void (*)()) 是一个函数指针类型,函数参数为空返回类型是void
//2..(*(void (*)())0)把0整数值,强制转换为一个函数的地址,
//3.去调用0地址处的函数。
void (* signal(int, void(x)(int)))(int);
//这是一个函数声明
//1.singal是一个函数,参数为(int, void(x)(int))第一个是int,
// 第二个是函数指针类型,该指针指向函数参数是int,返回类型是void
//2.void (* signal(int, void(x)(int)))(int),去掉里面的函数,得到
//void (*)(int) 其实也是一个函数指针类型
// 该指针指向函数参数是int,返回类型是void
5.typedef关键字使用
关键字typedef用法
typedef 是⽤来类型重命名的,可以将复杂的类型,简单化。
⽐如, unsigned int 写起来不⽅便,如果能写成 uint 就⽅便多了,那么我们可以使⽤
typedef unsigned int ax;
写一个数组指针
typedef int(* ax2)[10];
写一个函数指针
typedef void (*ax3)(int, int);
6.函数指针数组
当要实现一个指针多个函数的地址时候就用到函数指针数组。
#include<stdio.h>
int arr(int x, int y)
{
return x + y;
}
int arr2(int x, int y)
{
return x + y;
}
int arr3(int x, int y)
{
return x + y;
}
int arr4(int x, int y)
{
return x + y;
}
int main()
{
//当要实现一个指针多个函数的地址时候就用到函数指针数组
int (* pa[4])(int, int) = { arr,arr2,arr3,arr4 }; //写法只是加上[]即可,其实还是函数指针类型
for (int i = 0; i < 4; i++)
{
printf("%d \n", pa[i](2,3));
}
return 0;
}
7.数组指针数组的简单应用
举例:计算器的⼀般实现:
#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;
}
int main()
{
int x, y;
int input = 1;
int ret = 0;
do
{
printf("*************************\n");
printf(" 1:add 2:sub \n");
printf(" 3:mul 4:div \n");
printf(" 0:exit \n");
printf("*************************\n");
printf("请选择:");
scanf("%d", &input);
switch (input)
{
case 1:
printf("输⼊操作数:");
scanf("%d %d", &x, &y);
ret = add(x, y);
printf("ret = %d\n", ret);
break;
case 2:
printf("输⼊操作数:");
scanf("%d %d", &x, &y);
ret = sub(x, y);
printf("ret = %d\n", ret);
break;
case 3:
printf("输⼊操作数:");
scanf("%d %d", &x, &y);
ret = mul(x, y);
printf("ret = %d\n", ret);
break;
case 4:
printf("输⼊操作数:");
scanf("%d %d", &x, &y);
ret = div(x, y);
printf("ret = %d\n", ret);
break;
case 0:
printf("退出程序\n");
break;
default:
printf("选择错误\n");
break;
}
} while (input);
return 0;
}
使用函数指针数组来实现
void men()
{
printf("*************************\n");
printf(" 1:add 2:sub \n");
printf(" 3:mul 4:div \n");
printf(" 0:exit \n");
printf("*************************\n");
}
int arr(int x, int y)
{
return x + y;
}
int arr2(int x, int y)
{
return x - y;
}
int arr3(int x, int y)
{
return x * y;
}
int arr4(int x, int y)
{
return x / y;
}
int main()
{
int x = 0;
int (*pa[])(int, int) = { NULL,arr,arr2,arr3,arr4 };//尽量让其函数对应菜单功能,null用来顶替0位置。
int count = 0, count2 = 0;
do
{
men();
printf("请选择\n");
scanf("%d", &x);
if (x == 0)
{
printf("退出计算器\n");
}
else if (x >= 1 && x <= 4)
{
printf("请输入操作数: \n");
scanf("%d %d", &count, &count2);
int ret = pa[x](count, count2);
printf("%d \n", ret);
}
else
{
printf("输入错误请重新选择 \n");
}
} while (x);
return 0;
}
两者对比可以看出,函数指针的实现更简写了。