指针的进阶理解

目录

1.字符指针

(注意:字符串常量本来就是常量,只是用const修饰过更加准确)

2.指针数组

3. 数组指针

3.1 数组指针的定义

3.2 &数组名VS数组名

3.3 数组指针的使用

4. 数组参数、指针参数

4.1 一维数组传参

4.2 二维数组传参

4.3 一级指针传参

4.4 二级指针传参

5. 函数指针

6. 函数指针数组

7. 指向函数指针数组的指针


指针的概念:

 

1.指针就是变量,用来存放地址,地址唯一标识一块内存空间

2.指针的大小固定4/8个字节(32位平台/64位平台)

3.指针是有类型,指针的类型决定了指针的+-整数的步长,解引用操作的时候的权限。

1.字符指针

在指针的类型中我们知道有一种指针类型为 char*

一般使用:

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

这里的 *pc就是指针变量,具体来说叫做字符指针变量。

还有一种使用方法如下:

int main()
{
    const char* pstr = "hello word.";//这里是把一个字符串放到pstr指针变量里了吗?
    printf("%s\n", pstr);
    return 0;
}

特别注意这里不是吧字符串hello word放到字符指针 pstr 里,而是把字符串首字符的地址放到了pstr中。

 一道经典的面试题:

#include <stdio.h>
int main()
{
    char str1[] = "hello word.";
    char str2[] = "hello word.";
    const char* str3 = "hello word.";
    const char* str4 = "hello word.";
    if (str1 == str2)
        printf("str1 and str2 相同\n");
    else
        printf("str1 and str2 are 不同\n");

    if (str3 == str4)
        printf("str3 and str4 相同\n");
    else
        printf("str3 and str4 are 不同\n");

    return 0;
}

 最终输出结果:

 

 这里str3和str4被const修饰过了,拥有了常属性,是一个常量字符串,C/C++会把常量字符串存储到单独的一个内存区域,当几个指针,指向同一个字符串的时候,他们实际会指向同一块内存,所以它两相同。

str1和str2创建的时候,用的是相同的常量字符串去初始化不同的数组的时候,就会开辟出不同的内存块,也就是不同的空间,这里比的不是字符串里面的内容,而是两块空间首字母的地址,所以它两不同。

(注意:字符串常量本来就是常量,只是用const修饰过更加准确)


2.指针数组

在之前的博客中我们已经学过了指针数组

顾名思义,指针数组就是一个存放指针的数组

int* arr1[10]; 
//数组名arr1,里面有10个元素,每个元素是int*,是一个存放整形指针的数组
char *arr2[4]; 
//数组名arr2,里面有4个元素,每个元素是cahr*,是一个存放字符型形指针的数组
char **arr3[5];
//数组名arr3,里面有5个元素,每个元素是cahr**,是一个存放二级指针的数组

3. 数组指针

3.1 数组指针的定义

数组指针是指针?还是数组?

答案是:指针。

整型指针——存放整型地址的指针——指向整型的指针 int*

字符指针——存放字符地址的指针——指向字符的指针 char*

浮点型指针——存放浮点型地址的指针——指向浮点型的指针 int*

数组指针——存放数组地址的指针——指向数组的指针

int (*p)[10];

解释:p先和*结合,说明p是一个指针变量,然后指针指向的是一个大小为十个整形的数组,所以p是一个指针,指向的是一个数组,叫数组指针。

注意:这里[ ]的优先级高于*号,所以必须加上()来保证p先和*结合。

3.2 &数组名VS数组名

首先我们先来看看&数组名和数组名以地址的形式在屏幕上打印出来的结果。

 我们发现他们的结果是一样的,证明他们取出的值都是一样的,那么他们到底有什么不一样的呢?我们现在把它加一看看。

总结: 数组名是首元素的地址

            &数组名取出的是整个数组的地址

            数组首元素的地址和数组的地址从值的角度来看是一样的,但是意义不一样


3.3 数组指针的使用

使用数组指针打印一个二维数组的内容。

#include<stdio.h>
void print(int(*p)[4], int r, int c)
{
	int i = 0;
	for (i = 0; i < r; i++)
	{
		int j = 0;
		for (j = 0; j < c; j++)
		{
			printf("%d",(*(p+i))[j]);
		}
		printf("\n");
	}

}
int main()
{
	int arr[3][4] = { {1,2,3,4},{2,3,4,5},{3,4,5,6} };
	print(arr, 3, 4);
//数组名是首元素的地址
//但是二维数组的首元素是二维数组的第一行
//所以这里传递的arr是第一行的的地址,是一维数组的地址
//所以用数组指针来接收
	return 0;
}


4. 数组参数、指针参数

在写代码的时候难免会把【数组】或者【指针】传递给函数,那函数的参数该如何设计呢?

4.1 一维数组传参

我们来分析一下下面这段代码。

 上面五种传参方式都是正确的,下面我们再来分析一下二维数组传参。

4.2 二维数组传参

 总结:二维数组传参,函数形参的设计只能省略第一个[ ]的数字,也就是行可以省略,列不不能省略。

4.3 一级指针传参

 总结:假设一个函数的参数是一个一级指针,只要方法正确,我们不仅仅能传一级指针上去,还能传一维数组的数组名和整形变量。

4.4 二级指针传参

总结:假设一个函数的参数是一个二级指针,只要方法正确,我们可以传二级指针变量和一级指针变量名和指针数组的数组名。

5. 函数指针

整型指针——存放整型地址的指针——指向整型的指针 int*

字符指针——存放字符地址的指针——指向字符的指针 char*

浮点型指针——存放浮点型地址的指针——指向浮点型的指针 int*

数组指针——存放数组地址的指针——指向数组的指针

函数指针———存放函数的指针——指向函数的指针

数组指针的写法:

int arr[10];

1.&arr        取出arr的地址;

2.*p=&arr        赋值给一个p,p是一个指针

3.*p[10]=&arr        指针p指向的是一个数组,数组里面有十个元素。

4.int (*p)[10]=&arr;        数组里面有十个元素,每个元素市整形

函数指针的写法

ADD(int x,int y)

{

}

1.&ADD                取出函数的地址

2.*p=&ADD        赋值给一个p,p是一个指针

3.(*p)(int,int )=&ADD        指针p指向的是一个函数

4.int (*p)(int,int)=&ADD          返回类型是int

想要使用函数指针,直接调用即可。

6. 函数指针数组

数组是一个存放相同类型数据的存储空间,那我们已经学习了指针数组, 比如:

int *arr[10];

//数组的每个元素是int

那要把函数的地址存到一个数组中,那这个数组就叫函数指针数组,那函数指针的数组如何定义呢?

int (*parr1[10])();

int *parr2[10]();

int (*)() parr3[10];

答案是:parr1

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 = 0;
	int y = 0;
	int input = 0;
	int ret = 0;
	do
	{
		printf("***********************\n");
		printf(" 1:add           2:sub \n");
		printf(" 3:mul           4:div \n");
		printf("*5:退出程序************\n");
		printf("***********************\n");
		printf("请选择:");
		scanf_s("%d", &input);
		switch (input)
		{
		case 1:
			printf("请输入操作数;");
			scanf_s("%d %d", &x, &y);
			ret=add(x, y);
			printf("%d\n", ret);
			break;

		case 2:
			printf("请输入操作数;");
			scanf_s("%d %d", &x, &y);
			ret = sub(x, y);
			printf("%d\n", ret);
			break;

		case 3:
			printf("请输入操作数;");
			scanf_s("%d %d", &x, &y);
			ret = mul(x, y);
			printf("%d\n", ret);
			break;

		case 4:
			printf("请输入操作数;");
			scanf_s("%d %d", &x, &y);
			ret = div(x, y);
			printf("%d\n", ret);
			break;

		case 0:
			printf("退出程序");
			break;
		default:
			printf("选择错误");
			break;
		}
	} while (input);
	return 0;
}

上述代码中我们发现有很多重复的代码,如果这个时候用上我们刚学的知识,就能简化代码。

#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(*pf[5])(int,int) = { 0,add,sub,mul,div };
//函数指针数组
//转移表


int main()
{
	int x = 0;
	int y = 0;
	int input = 0;
	int ret = 0;
	do
	{
		printf("***********************\n");
		printf(" 1:add           2:sub \n");
		printf(" 3:mul           4:div \n");
		printf("*5:退出程序************\n");
		printf("***********************\n");
		printf("请选择:");
		scanf_s("%d", &input);

		printf("请输入操作数;");
		scanf_s("%d %d", &x, &y);
		ret = pf[input](x, y);
		printf("%d", ret);
	} while (input);
	return 0;
}

7. 指向函数指针数组的指针

指向函数指针数组的指针是一个 指针

指针指向一个 数组 ,数组的元素都是 函数指针

如何定义?

void test(const char* str)
{
 printf("%s\n", str);
}
int main()
{
 //函数指针pfun
 void (*pfun)(const char*) = test;
 //函数指针的数组pfunArr
 void (*pfunArr[5])(const char* str);
 pfunArr[0] = test;
 //指向函数指针数组pfunArr的指针ppfunArr
 void (*(*ppfunArr)[5])(const char*) = &pfunArr;
 return 0;
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值