旅游网站项目计划书,广告公司名字怎么取好,商城类的网站一般怎么做,建设银行储蓄卡申请网站目录 字符指针变量数组指针变量数组指针变量是什么数组指针变量怎么初始化 二维数组传参的本质函数指针变量函数指针变量的创建函数指针变量的使用代码typedef关键字 函数指针数组转移表 字符指针变量
字符指针在之前我们有提到过#xff0c;#xff08;字符#xff09;字符指针前面的字符代表着存储的元素为字符类型而指针则是表示这存储的方式。 写法为char* 一般使用的方式如下
nt main()
{
char ch w;
char *pc ch;
*pc w;
return 0;
}还有一种使用方式如下
int main()
{
const char* pstr hello jack.;
printf(%s\n, pstr);
return 0;
}值得注意的是代码 const char pstr “hello jack.”; 特别容易以为是把字符串 hello jack 放到字符指针 pstr 里了但是本质是把字符串 hello jack. 首字符的地址放到了pstr中* 《剑指offer》中有一道和字符串有关的题如下
#include stdio.h
int main()
{
char str1[] hello jack.;
char str2[] hello jack.;
const char *str3 hello jack.;
const char *str4 hello jack.;
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(str3 and str4 are not same\n);
return 0;
}这里的char str1[]和charstr2[]在创造时是单独开辟了一个储存空间因此二者的储存地址是不同的 所以打印出来的是str1 and str2 are not same
char str3和charstr4中因为带有了指针因此指针指向的字符串hello jack是已经单独创造了一个储存空间而str3和str4则是存储字符串的储存地址因此最后是str3 and str4 are same
数组指针变量
数组指针变量是什么
之前的是指针数组现在的数组指针其实理解方式是相同的数组指针储存的元素是数组而储存的方式就是通过指针储存数组的地址 我们举一些其他熟悉的例子 • 整形指针变量 int * pint; 存放的是整形变量的地址能够指向整形数据的指针。 • 浮点型指针变量 float * pf; 存放浮点型变量的地址能够指向浮点型数据的指针
下面代码哪个是数组指针变量
1int *p1[10];
2int (*p2)[10];p1[10]是指针数组 (p2)[10]是数组指针 我们发现这二者的差别就在于括号指针数组是没有括号的而数组指针是有括号的我们可以这样理解 * || p [10]我们把指针数组分开一个是指针,一个是数组p[10]表示数组p[10]里面存放的是指针 p|| [10]我们可以看到p和结合起来了因此可以理解为将数组p的地址通过指针的形式存放起来
数组指针变量怎么初始化
数组指针变量是用来存放数组地址的那怎么获得数组的地址呢就是我们之前学习的 数组名
int arr[10]{0}
arr//取的是整个数组的地址如果要存放个数组的地址就得存放在数组指针变量中如下:
int(*p)[10]arr;我们通过调试可以看到p和arr的内存地址是一样的 我们对int *p[10]arr进行解析 int*p[10] arr | ___ | ___ | ___ | | ___ | ___ | ___ | | ___ | ___ | ___ 整个数组的地址 | ___ | ___ p指向数组的元素个数 | ___ p是数组指针的变量名 p指向的数组的元素类型
二维数组传参的本质
过去我们有一个二维数组的需要传参给一个函数的时候我们是这样写的
#include stdio.h
void test(int a[3][5], int r, int c)
{
int i 0;
int j 0;
for(i0; ir; i)
{
for(j0; jc; j)
{
printf(%d , a[i][j]);
}
printf(\n);
}
}
int main()
{
int arr[3][5] {{1,2,3,4,5}, {2,3,4,5,6},{3,4,5,6,7}};
test(arr, 3, 5);//arr传的是数组首元素地址
return 0;
}这里实参是二维数组形参也写成二维数组的形式那还有什么其他的写法吗
首先我们再次理解一下二维数组二维数组起始可以看做是每个元素是一维数组的数组也就是二维数组的每个元素是一个一维数组。那么二维数组的首元素就是第一行是一个维数组
所以根据数组名是数组首元素的地址这个规则二维数组的数组名表示的就是第一行的地址是一维数组的地址。 根据上面的例子第一行的一维数组的类型就是 int [5] 所以第一行的地址的类型就是数组指针类型 int(*)[5] 。 那就意味着二维数组传参本质上也是传递了地址传递的是第一行这个一维数组的地址那么形参也是可以写成指针形式的。如下
#include stdio.h
void test(int (*p)[5], int r, int c)
{
int i 0;
int j 0;
for(i0; ir; i)
{
for(j0; jc; j)
{
printf(%d , *(*(pi)j));
}
printf(\n);
}
}
int main()
{
int arr[3][5] {{1,2,3,4,5}, {2,3,4,5,6},{3,4,5,6,7}};
test(arr, 3, 5);
return 0;
}可能会有人感到疑惑,为什么不是指针数组呢?我来谈谈我个人的理解: 我们再来回顾一下指针数组的含义: 指针数组是数组里面存放的指针也就是地址,好像和二维数组存放一维数组的地址是一样的. 但是当我们传递参数时我们发现传递的是指针的地址,而不是一维数组的地址,也就是说因为指针是存放的一维数组的地址,而如果我们用了指针数组的话,我们就是取了指针的地址
总结:二维数组传参形参的部分可以写成数组也可以写成指针形式
函数指针变量
函数指针变量的创建
什么是函数指针变量呢 根据前面学习整型指针数组指针的时候我们的类比关系我们不难得出结论 函数指针变量应该是用来存放函数地址的未来通过地址能够调用函数的。 那么函数是否有地址呢 我们做个测试
#include stdio.h
void test()
{
printf(hehe\n);
}
int main()
{
printf(test: %p\n, test);
printf(test: %p\n, test);
return 0;
}运行结果如下: 确实打印出来了地址所以函数是有地址的函数名就是函数的地址当然也可以通过 函数名 的方式获得函数的地址。 如果我们要将函数的地址存放起来就得创建函数指针变量函数指针变量的写法其实和数组指针非常类似。如下:
void test()
{
printf(hehe\n);
}
void (*pf1)() test;
void (*pf2)() test;
int Add(int x, int y)
{
return xy;
}
int(*pf3)(int, int) Add;
int(*pf3)(int x, int y) Add;//x和y写上或者省略都是可以的,我们只需要函数参数的类型即可我们对int *pf3[int x,int y]进行解析 intpf3[int x,int y] | ___ | ___ ___ | | ___ | ___ ___ | | ___ | ___ ___ | | ___ | ___ ___ pf3指向函数的参数类型和个数的交代 | ___ 函数指针变量名 pf3指向函数的返回类型 int () (int x, int y) //pf3函数指针变量的类型
函数指针变量的使用
通过函数指针调用指针指向的函数
#include stdio.h
int Add(int x, int y)
{
return xy;
}
int main()
{
int(*pf3)(int, int) Add;
printf(%d\n, (*pf3)(2, 3));
printf(%d\n, pf3(3, 5));
return 0;
}运行结果如下:
代码
代码1:
(*(void (*)())0)();
代码2:
void (*signal(int , void(*)(int)))(int);代码1:我们分布解析:( * (void ( * )( ) ) 0 )()可以拆成(a)(),a( void ( * ) ( ) )0 我们先分析a,首先我们看到void()()就应该想到这是一个返回类型为void的函数类型,并且括号将*号括了起来,说明这是函数指针,而那个0可能会把我们头都搞大,但是我们举一个例子
int a0;
char bA;
int ca(int)b;这是强制类型转换,也就是说0被强制转换为一个返回值为void参数为空的函数指针 而(*a)()又是一个函数指针,因此这段代码有两个函数指针
代码2: void (signal(int , void()(int)))(int)我们依然将代码才分开: void(a)(int), asignal(int,b), bvoid()(int) 显然这几个都是函数,第一个和第三个都为函数指针,第二个b为void返回类型的函数,然后a的参数包含了int 和void,返回类型为signal
typedef关键字
typedef是用来类型重命名的可以将复杂的类型简单化,也可以理解为设置一个代号,方便理解并且减少代码的长度 比如,你觉得 unsigned int 写起来不方便如果能写成 uint 就方便多了那么我们可以使用
typedef unsigned int uint;
//这里感觉和define有点类似
define a 8如果是指针类型能否重命名呢其实也是可以的比如将 int* 重命名为 ptr_t ,这样写
typedef int* ptr_t但是对于数组指针和函数指针稍微有点区别 比如我们有数组指针类型 int(*)[5] ,需要重命名为 parr_t 那可以这样写
typedef int(*parr_t)[5]; //新的类型名必须在*的右边函数指针类型的重命名也是一样的比如将 void(*)(int) 类型重命名为 pf_t ,就可以这样写
typedef void(*pfun_t)(int);//新的类型名必须在*的右边
要简化代码可以这样写
typedef void(*pfun_t)(int);
pfun_t signal(int, pfun_t);这里面其实有一些规律,我们设置新的类型名时,都会将类型名写在函数的变量中
函数指针数组
数组是一个存放相同类型数据的存储空间我们已经学习了指针数组 比如:
int *arr[10];
//数组的每个元素是int*那要把函数的地址存到一个数组中那这个数组就叫函数指针数组那函数指针的数组如何定义呢?
int (*parr1[3])();
int *parr2[3]();
int (*)() parr3[3];parr1 先和 [ ] 结合说明parr1是数组数组的内容是什么呢 是 int (*)() 类型的函数指针。
转移表
函数指针数组的用途转移表
#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;
}