c语言之进阶指针

1. 字符指针

在指针的类型中我们知道有一种指针类型为字符指针 char* ;用来存放字符变量的地址。
(1)一般使用:

int main()
{
char ch = 'w';
char *pc = &ch;
*pc = 'w';
return 0;
}

(2)还有一种使用方式如下:

int main()
{
const char* pstr = "hello word";
printf("%s\n", pstr);
return 0;
}

代码 const char* pstr = “hello word”,并不是把字符串都放进到指针变量里,而是把字符串的首元素地址放到了指针变量里。
(3)那就有可这样的面试题:

#include <stdio.h>
int main()
{
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("str3 and str4 are not same\n");
return 0;
}

运行程序我们可以发现:
在这里插入图片描述
到底为什么结果是这样呢?
在这里插入图片描述

2. 指针数组——是数组

字符数组——存放字符的数组
整型数组——存放整型的数组
指针数组——存放指针的数组
int* arr1[5]//存放整型指针
char* arr2[5]//存放字符指针

//模拟使用一个二维数组
int main()
{
	int arr1[] = { 1,2,3,4,5 };
	int arr2[] = { 2,3,4,5,6 };
	int arr3[] = { 3,4,5,6,7 };
	        //int*  int*  int*
	//指针数组
	int* arr[] = { arr1, arr2, arr3 };
	int i = 0;
	for (i = 0; i < 3; i++)
	{
		int j = 0;
		for (j = 0; j < 5; j++)
		{
			printf("%d ", arr[i][j]);
		}
		printf("\n");
	}

	return 0;
}

在这里插入图片描述

3. 数组指针——是指针

3.1 数组指针的定义

整形指针: int * pint; 能够指向整形数据的指针。
浮点型指针: float * pf; 能够指向浮点型数据的指针。
那数组指针应该是:能够指向数组的指针。
下面代码哪个是数组指针?

int *p1[10];
int (*p2)[10];
//p1, p2分别是什么?
int (*p)[10];
//解释:p先和*结合,说明p是一个指针变量,然后指着指向的是一个大小为10个整型的数组。
所以p是一个指针,指向一个数组,叫数组指针。
//这里要注意:[]的优先级要高于*号的,所以必须加上()来保证p先和*结合
//p1为指针数组,里面放了10个指针变量

3.2 &数组名VS数组名

先看这样一段代码

#include <stdio.h>
int main()
{
int arr[10] = {0};
printf("%p\n", arr);
printf("%p\n", &arr);
return 0;
}

在这里插入图片描述
这里我们分别打印两者的地址,你会发现这两个地址是一样的,难道真的就没有区别吗?稍安勿躁,我们继续再看下面的代码

#include <stdio.h>
int main()
{
  int arr[10] = { 0 };
  printf("arr = %p\n", arr);
  printf("&arr= %p\n", &arr);
  printf("arr+1 = %p\n", arr+1);
  printf("&arr+1= %p\n", &arr+1);
  return 0;
}

在这里插入图片描述
通过结果我们不难发现arr就是首元素的地址,而&arr是整个数组的地址,因此arr+1是跳过4个字节,而&arr+1是跳过40个字节。

数组名的理解

数组名是数组首元素的地址
但是存在2个例外:
1. sizeof(数组名),这里的数组名表示整个数组,sizeof(数组名)计算的是整个数组的大小,
单位是字节
2/ &数组名,这里的数组名表示整个数组,取出的是数组的地址

3.3 数组指针的使用

下面我们来看一段代码:

#include <stdio.h>
void print_arr1(int arr[3][5], int row, int col)
{
	int i = 0;
	for (i = 0; i < row; i++)
	{
		for (int j = 0; j < col; j++)
		{
			printf("%d ", arr[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} };
	print_arr1(arr, 3, 5);
	return 0;
}

在这里插入图片描述
这是我们平时输出二维数组的一个函数,这在我们之前的扫雷游戏中就有体现,传参print_arr1(arr, 3, 5);,形式参数·用(int arr[3][5], int row, int col)来接收。但实际上这里只是为了我们初学者方便理解,才进行用二维数组来接收的,实际上是数组指针的形式
我们再来看另一段代码:

void print_arr2(int(*arr)[5], int row, int col)
{
	 int i = 0;
	 for (i = 0; i < row; i++)
	 {
		 for (int j = 0; j < col; j++)
		 {
			 printf("%d ", arr[i][j]);
		 }
		 printf("\n");
	 }
}
//数组名arr,表示首元素的地址
//但是二维数组的首元素是二维数组的第一行
//所以这里传递的arr,其实相当于第一行的地址,是一维数组的地址,
可以数组指针来接收
int main()
{
	int arr[3][5] = { {1,2,3,4,5},{2,3,4,5,6 }, {3,4,5,6,7} };
	print_arr2(arr, 3, 5);
	return 0;
}

在这里插入图片描述
学了指针数组和数组指针我们来一起回顾并看看下面代码的意思:

int arr[5];
//这个就是数组,存储了5个整型元素的整型数组,类型为int
int *parr1[10];
//这个为数组指针,存储了10个整型指针类型的数组,类型为int*
int (*parr2)[10];
//这个为指针数组,,指向10个元素的数组 
int (*parr3[10])[5];
//这个为数组,里面放的是指向数组指针的地址,数组指针指向的数组有10个元素,而这个数组有5个元素
//类型有5个元素

4. 数组传参和指针传参

4.1 一维数组传参

#include <stdio.h>
void test(int arr[])//ok? 用数组来接收肯定可以
{}
void test(int arr[10])//ok?  类似第一个,只不过[]里放的元素可以随机
{}
void test(int* arr)//ok?   本质上就是地址,所以用指针变量来接收
{}
void test2(int* arr[20])//ok?    用本身来接收肯定可以
{}
void test2(int** arr)//ok?   因为传过来的是指针数组,类型是int*,所以为int* *
{}
int main()
{
	int arr[10] = { 0 };
	int* arr2[20] = { 0 };
	test(arr);
	test2(arr2);
}

4.2 二维数组传参

void test(int arr[3][5])//ok? 可以
{}
void test(int arr[][])//ok? 不可以
{}
void test(int arr[][5])//ok?  可以
{}
//总结:二维数组传参,函数形参的设计只能省略第一个[]的数字。
//因为对一个二维数组,可以不知道有多少行,但是必须知道一行多少元素。
//这样才方便运算。
void test(int *arr)//ok? 不可以,因为传过来的只是第一行首元素的地址
{}
void test(int* arr[5])//ok? 不可以
{}
void test(int (*arr)[5])//ok? 可以(上面对此问题已经进行了说明)
{}
void test(int **arr)//ok? 不可以
{}
int main()
{
int arr[3][5] = {0};
test(arr);
}

4.3 一级指针传参

#include <stdio.h>
void print(int *p, int sz)
{
int i = 0;
for(i=0; i<sz; i++)
{
printf("%d\n", *(p+i));
}
}
int main()
{
int arr[10] = {1,2,3,4,5,6,7,8,9};
int *p = arr;
int sz = sizeof(arr)/sizeof(arr[0]);
//一级指针p,传给函数
print(p, sz);
return 0;
}

那么一直指针作为形参,能接受什么类型的参数呢?
在这里插入图片描述

4.4 二级指针传参

#include <stdio.h>
void test(int** ptr)
{
printf("num = %d\n", **ptr);
}
int main()
{
int n = 10;
int*p = &n;
int **pp = &p;
test(pp);
test(&p);
return 0;
}

同理 ,当函数的参数为二级指针的时候,可以接收什么参数?
在这里插入图片描述

5. 函数指针

函数指针中&函数名和函数名没有什么区别,这个与数组名不一样,如下图
在这里插入图片描述
在这里插入图片描述
一方面从地址方面进行了论述,另一方面从使用上也说明了两者没有区别。
(上面例子均有一个Add函数,就是实现a+b的)
这个函数指针的定义类似数组指针。

int Add(int x, int y)
{
	return x + y;
}
int main()
{
 int(*pf2)(int,int)=Add;//定义,先是指针(*pf2),然后是参数(int,int),
                         //最后是函数的返回类型int
 int ret=(*pf2)(3,5)//使用,理解来看,pf2是函数的地址,(*pf2) 就是使用pf2的地址
 //但是上面例子显示,没有*也是可以的
 int ret=pf2(3,5);
 //这样也是可以的,这也就是函数使用的本质Add(2,3)
} 

再看下面这一段代码

//代码1
(*(void (*)())0)();

这个代码就是在调用0地址处的函数,函数没有参数。

首先从0这个地方入手,0的类型是整型,而(viod(*)())将0强制类型转化为函数指针类型,说明0地址处有函数,用这个指针变量解引用,去访问0这个函数。然后没有参数

//代码2
void (*signal(int , void(*)(int)))(int);

这个代码是一次函数声明,声明的是signal函数

还是从signal这儿入手,这个函数的参数有2个:
第一个是int类型
第二个是函数指针类型,该类型是void(*)(int), 该函数指针指向的函数,参数是int,返回类型是viod
signal函数的返回类型也是函数指针类型,该类型是void(*)(int),该函数指针指向的函数,参数是int,返回类型是void

我们再来思考一个问题代码二这么复杂,我们该如何去简化这个代码

typedef void (*pfun_t)(int);
pfun_t signal(int, pfun_t);

宏定义一个函数,注意的是函数指针类型,需要把名字写到()里面

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值