栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > C/C++/C#

千变万化的指针----C语言篇

C/C++/C# 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

千变万化的指针----C语言篇

文章目录
  • 1、字符指针
    • 1、简单介绍
    • 2、例子讲解
  • 2、指针数组与数组指针
    • 1、什么是指针数组
    • 2、什么是数组指针
    • 3、数组名与&数组名
    • 4、数组指针的使用
  • 4、数组参数、指针参数
    • 1、二维数组传参
    • 2、一级指针传参
    • 3、二级指针传参
  • 5、函数指针
    • 1、函数指针介绍
    • 2、利用函数指针实现简单计算器
  • 6、指针数组题目解析

1、字符指针 1、简单介绍

在写指针之前,我们先来回忆一下初阶指针的知识。

1. 指针就是个变量,用来存放地址,地址唯一标识一
    块内存空间。
2. 指针的大小是固定的4/8个字节(32位平台/64位
   平台)。
3. 指针是有类型,指针的类型决定了指针的+-整数的
    步长,指针解引用操作的时候的权限。


这是字符指针最经典的用法,通过地址去改变一个变量的值。
再看一种用法

通过地址打印字符串,并不是将整个字符串地址放入,而是将字符串首元素的地址放到字符指针中,通过首元素找到找到整个字符串,从而打印整个字符串。

2、例子讲解

我们来看一道简单的题目
下面代码运行的结果是什么?

答案是:
首先来看str1与str2,他们都是数组,需要先开辟一块内存,才可以存放数据。而str3与str4存放的是同一个字符串“hello world”首元素h的地址,所以str3与str4相等

2、指针数组与数组指针 1、什么是指针数组

int arr[ ]:整形数组是存放整形的数组;
char arr[ ]:字符数组是存放字符的数组;
因此,指针数组是存放指针的数组;


2、什么是数组指针

数组指针是指向数组的指针,本质上是指针,其形如 int (*p2)[ ],

p2是数组指针,p2可以指向一个数组,数组的中有10个类型为 int 的元素。当然,如果想深入了解数组指针,我们就必须先来搞清楚数组名与&数组名之间的不同。

3、数组名与&数组名

一般情况下,数组名表示的是首元素地址,但是也有两种特殊情况。
1、sizeof(数组名): 此时数组名就代表的整个元素的地址。

2、&数组名:此时,数组名代表的是整个数组的地址,因此我们取出的是整个数组的地址。

从这张图片上我们并不能看出来三者的区别,只知道三者指向的是同一个地址。
接下来,看下一张图

从这张图中我们就可以清晰地看出三者之间的区别,


注意:单位是字节。

接下来回归重点,数组指针与指针数组

整形指针是用来存放整形的指针
字符指针是用来存放字符的指针
数组指针是用来存放数组的指针

注意:

4、数组指针的使用

非常规使用方法(不推荐)

打印二维数组,普通方法

我们思考一个问题,arr是数组首元素地址,那么二维数组arr的首元素地址是什么呢?二维数组arr首元素的地址是第一行的地址,因此,我们传参传过去的是数组第一行的地址

	void print2(int(*p)[5], int r, int c)
	{
		for (int i = 0; i < r; i++)
	    {
			for (int j = 0; j < c; j++)
			{
				printf("%d ", *(*(p + i) + j));
	//p指向的是数组第一行的地址,
	//*(p+i)指向的就是数组arr第i行的地址
	//*(*(p+i)+j)指向的就是数组arr的第i行第j列的元素
	
			}
			printf("n");
	    }
	}
	int main()
	{
		int arr[3][5] = { 1,2,3,4,5,3,4,5,6,7,4,5,6,7,8 };
		//print1(arr, 3, 5);
		print2(arr, 3, 5);
		//将数组第一行的地址传过去,
		//因此要用数组指针接收

    }


我们来回顾一下下面代码的意思

int arr[5];  //arr是整型数组
int *parr1[10];  //parr1是整型指针数组
int (*parr2)[10];  //parr2是数组指针
int (*parr3[10])[5];  //parr3是存放数组指针的数组

4、数组参数、指针参数 1、二维数组传参
void test(int* arr)// error
//参数是应该用一个数组指针接收

void test(int *arr[5])//error
//int *arr[5] 是一个指针数组,不是指针

void test(int(*arr)[5])//ok
//数组指针是正确的

void test(int** arr)//error
//二级指针是放一级指针地址的变量,并不能接收一行元素的地址


int main()
{
	int arr[3][5] = { 0 };
	test(arr);//将数组第一行的地址传上去
	return 0;
}
2、一级指针传参
void print(int* p)

int main()
{
	//当我的参数部分是指针时,我们可以这样传参
	int a = 10;
	int* ptr = &a;
	int arr[10];


	print(&a);
	print(ptr);
	print(arr);

}
3、二级指针传参
//如果函数的形式参数是二级指针。
//调用函数可以这样传参
void test(int** p)

int main()
{
	int* p1;
	int** p2;
	int* arr[10];

	test(&p1);
	test(p2);
	test(arr);
}
5、函数指针 1、函数指针介绍

数组指针:指向数组的指针;
函数指针:指向函数的指针;

从上图可以看出,&函数名取出的确实是函数的地址
对于函数来说,&函数名与函数名得到的都是函数的地址
函数指针形如:

int Add(int x, int y)
{
	return (x + y);
}
int main()
{
	int (*pf)(int, int) = &Add;//函数指针
	//当我们想调用函数指针时
	int ret = (*pf)(2,3);
	//也可以这样写 int ret = pf(2,3);
	//也可以这样写 int ret = Add(2,3);
	printf("%d",ret);// 5
}

2、利用函数指针实现简单计算器

我们先看看用普通方法怎么实现计算器

//先把每个函数写出来
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("*************************n");
		printf("请选择:");
		scanf("%d", &input);
		switch (input)
		{
			case 1:
				printf("输入操作数:");
				scanf("%d %d", &x, &y);
				ret = add(x, y);
				printf("ret = %dn", ret);
				break;
			case 2:
				printf("输入操作数:");
				scanf("%d %d", &x, &y);
				ret = sub(x, y);
				printf("ret = %dn", ret);
				break;
			case 3:
				printf("输入操作数:");
				scanf("%d %d", &x, &y);
				ret = mul(x, y);
				printf("ret = %dn", ret);
				break;
			case 4:
				printf("输入操作数:");
				scanf("%d %d", &x, &y);
				ret = div(x, y);
				printf("ret = %dn", ret);
				break;
			case 0:
				printf("退出程序n");
				break;
			default:
				printf("选择错误n");
				break;
		}
	} while (input);

	return 0;
}

接下来用函数指针实现简单计算器

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 Calc(int(*pf)(int x, int y))
//不管传上来的是什么函数,我们统一用函数指针接收
{
	int x, y;
	int ret = 0;
	printf("请输入两个操作数:>n");
	scanf("%d %d", &x, &y);
	ret = pf(x, y);
	printf("%dn", ret);
}
int main()
{
	int input = 0;

	do
	{
		printf("*************************n");
		printf(" 1:add           2:sub n");
		printf(" 3:mul           4:div n");
		printf("*************************n");
		printf("请选择:");
		scanf("%d", &input);
		switch (input)
		{
			case 1:
				Calc(add);
				break;
			case 2:
				Calc(sub);
				break;
			case 3:
				Calc(mul);
				break;
			case 4:
				Calc(div);
				break;
			case 0:
				printf("退出程序n");
				break;
			default:
				printf("选择错误n");
				break;
		}
	} while (input);

	return 0;
}
6、指针数组题目解析

直接看代码

int main()
{
	int a[] = { 1,2,3,4 };

	printf("%dn", sizeof(&a + 1));//4/8
	//&a取出的是数组的地址
	//&a-->  int(*)[4]
	//&a+1 是从数组a的地址向后跳过了一个(4个整型元素的)数组的大小
	//&a+1还是地址,是地址就是4/8字节
	//
	printf("%dn", sizeof(&a[0]));//4/8
	//&a[0]就是第一个元素的地址
	//计算的是地址的大小
	printf("%dn", sizeof(&a[0] + 1));//4/8
	//&a[0]+1是第二个元素的地址
	//大小是4/8个字节
	//&a[0]+1 --> &a[1]
	//

	printf("%dn", sizeof(a));//16
	//sizeof(数组名),数组名表示整个数组,计算的是整个数组的大小,单位是字节
	printf("%dn", sizeof(a + 0));//4
	//a不是单独放在sizeof内部,也没有取地址,所以a就是首元素的地址,a+0还是首元素的地址
	//是地址,大小就是4/8个字节
	printf("%dn", sizeof(*a));//4
	//*a中的a是数组首元素的地址,*a就是对首元素的地址解引用,找到的就是首元素
	//首元素的大小就是4个字节
	printf("%dn", sizeof(a + 1));
	//这里的a是数组首元素的地址
	//a+1是第二个元素的地址
	//sizeof(a+1)就是地址的大小
	printf("%dn", sizeof(a[1]));//4
	//计算的是第二个元素的大小
	printf("%dn", sizeof(&a));//4/8
	//&a取出的数组的地址,数组的地址,也就是个地址
	printf("%dn", sizeof(*&a));//16
	//&a----> int(*)[4]
	//&a拿到的是数组名的地址,类型是 int(*)[4],是一种数组指针
	//数组指针解引用找到的是数组
	//*&a ---> a
	// 
	//&和*抵消了
	//*&a ---> a
	return 0;
}
int main()
{
	char arr[] = { 'a','b','c','d','e','f' };
	printf("%dn", sizeof(arr));//6
	//sizeof(数组名)
	printf("%dn", sizeof(arr + 0));//4/8
	//arr + 0 是数组首元素的地址
	printf("%dn", sizeof(*arr));//1
	//*arr就是数组的首元素,大小是1字节
	//*arr --> arr[0]
	//*(arr+0) --> arr[0]
	printf("%dn", sizeof(arr[1]));//1
	printf("%dn", sizeof(&arr));//4/8
	//&arr是数组的地址,是地址就是4/8个字节
	printf("%dn", sizeof(&arr + 1));//4/8
	//&arr + 1是数组后的地址
	//
	printf("%dn", sizeof(&arr[0] + 1));//4/8
	//&arr[0] + 1是第二个元素的地址
	//
	return 0;
}
int main()
{
	char arr[] = { 'a','b','c','d','e','f' };

	printf("%dn", strlen(arr));//随机值
	printf("%dn", strlen(arr + 0));//随机值

//	printf("%dn", strlen(*arr));//--> strlen('a');-->strlen(97);//野指针
//	printf("%dn", strlen(arr[1]));//-->strlen('b')-->strlen(98);

	printf("%dn", strlen(&arr));//随机值
	printf("%dn", strlen(&arr + 1));//随机值-6
	printf("%dn", strlen(&arr[0] + 1));//随机值-1

	return 0;
}
int main()
{
	//char arr[] = { 'a', 'b', 'c', 'd', 'e', 'f' };

	char arr[] = "abcdef";
	//strlen是求字符串长度的,关注的是字符串中的,计算的是之前出现的字符的个数
	//strlen是库函数,只针对字符串
	//sizeof只关注占用内存空间的大小,不在乎内存中放的是什么
	//sizeof是操作符

	//[a b c d e f ]
	printf("%dn", strlen(arr));//6
	printf("%dn", strlen(arr + 0));//6
	//printf("%dn", strlen(*arr));//err
	//printf("%dn", strlen(arr[1]));//err
	printf("%dn", strlen(&arr));//6
	printf("%dn", strlen(&arr + 1));//随机值
	printf("%dn", strlen(&arr[0] + 1));//5


	//[a b c d e f ]
	//printf("%dn", sizeof(arr));//7
	//printf("%dn", sizeof(arr + 0));//4/8
	//printf("%dn", sizeof(*arr));//1
	//printf("%dn", sizeof(arr[1]));//1
	//printf("%dn", sizeof(&arr));//4/8
	//printf("%dn", sizeof(&arr + 1));//4/8
	//printf("%dn", sizeof(&arr[0] + 1));//4/8

	return 0;
}
int main()
{
	int a[3][4] = { 0 };
	printf("%dn", sizeof(a));
	printf("%dn", sizeof(a[0][0]));
	printf("%dn", sizeof(a[0]));
	a[0]是第一行这个一维数组的数组名,单独放在sizeof内部,a[0]表示第一个整个这个一维数组
	sizeof(a[0])计算的就是第一行的大小
	printf("%dn", sizeof(a[0] + 1));
	a[0]并没有单独放在sizeof内部,也没取地址,a[0]就表示首元素的地址
	就是第一行这个一维数组的第一个元素的地址,a[0] + 1就是第一行第二个元素的地址
	printf("%dn", sizeof(*(a[0] + 1)));
	a[0] + 1就是第一行第二个元素的地址
	*(a[0] + 1))就是第一行第二个元素
	printf("%dn", sizeof(a + 1));//4/8
	a虽然是二维数组的地址,但是并没有单独放在sizeof内部,也没取地址
	a表示首元素的地址,二维数组的首元素是它的第一行,a就是第一行的地址
	a+1就是跳过第一行,表示第二行的地址
	printf("%dn", sizeof(*(a + 1)));//16
	*(a + 1)是对第二行地址的解引用,拿到的是第二行
	*(a+1)-->a[1]
	sizeof(*(a+1))-->sizeof(a[1])
	
	printf("%dn", sizeof(&a[0] + 1));//4/8
	&a[0] - 对第一行的数组名取地址,拿出的是第一行的地址
	&a[0]+1 - 得到的是第二行的地址
	
	printf("%dn", sizeof(*(&a[0] + 1)));//16
	printf("%dn", sizeof(*a));//16
	a表示首元素的地址,就是第一行的地址
	*a就是对第一行地址的解引用,拿到的就是第一行
	
	printf("%dn", sizeof(a[3]));//16
	printf("%dn", sizeof(a[0]));//16

	int a = 10;
	sizeof(int);
	sizeof(a);


	return 0;
}

如果有错误,欢迎私信指正!

暑期编程PK赛 得CSDN机械键盘等精美礼品!
转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/1015873.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号