当前位置: 当前位置:首页 > 休闲 > 【c语言】-指针进阶(上) 正文

【c语言】-指针进阶(上)

2024-05-09 04:23:05 来源:口口声声网 作者:探索 点击:359次

【c语言】-指针进阶(上)

文章目录

  • 前言
  • 1.字符指针
  • 2.指针数组
  • 3.数组指针
    • 3.1数组指针的针进定义
    • 3.2数组名vs&数组名
    • 3.3数组指针的使用
  • 4.数组参数、指针参数
    • 4.1一维数组传参
    • 4.2二维数组传参
    • 4.3一级指针传参
    • 4.4二级指针传参
  • 5.函数指针
  • 6.函数指针数组
  • 7.指向函数数组的阶上指针

前言

指针是c语言的精华,我们之前已经学习了c语言的针进初阶指针,对初阶指针有了一个大概的阶上了解:

1.指针就是一个变量,是针进用来存放地址的,一个地址唯一标识一块内存空间,阶上可以通过这个地址来访问这个对应内存空间的针进数据
2.指针的大小是固定的4/8个字节(32位平台下是4个字节,64位平台下是阶上8个字节。同时我们平时通常看到的针进是x86,其实就是阶上32位平台,只是针进以前的叫法,被沿用至今。阶上x64就是针进64位平台,可千万不能以为x86就是阶上86位平台)
3.指针的类型决定了指针+1/-1时指针往前跳几个字节,即指针的针进步长,以及指针解引用时的权限
4.指针的运算

接下来就是我对指针进阶的学习。这里对指针的研究的广度和深度将会更进一步。


1.字符指针

字符指针的一般使用方法是

#includeint main(){	char ch = 'a';	char* pc = &ch;	return 0;}

而还有一种使用方式是:

int main(){	const char* pch = "hello world";	printf("%s", ch);	return 0;}

这时可能第一次见到的同学会比较诧异,为什么可以把字符串赋给一个字符指针呢?
其实这样使用在语法上是支持的,它的意思其实是把一个常量字符串的首字符’h’的地址存放到指针变量pch里面。 它的打印结果是就是:

在这里插入图片描述

这里的是一个常量字符串,那么一般在声明这样的一个字符串指针时,我们要在指针变量前面加上一个const修饰,表示指向的是一个常量。

从这个知识点可以往后衍伸。
C会把常量字符存储到一个单独的内存空间,当几个指针。指向同一个字符串的时候,他们实际上会指向同一块内存空间。但是相同的字符串去初始化不同的数组时,就会开辟出不同的内存空间。这句话理解可能会比较抽象,我们通过一段代码来解释一下:

#include 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[10]; //整形指针的数组char *arr2[4]; //一级字符指针的数组char **arr3[5];//二级字符指针的数组

3.数组指针

3.1数组指针的定义

数组指针是数组还是指针?我们从名字就可以看出来。
草莓味饼干是草莓还是饼干?答案是饼干。
那么数组指针就是指针。一个什么指针?一个指向数组的指针

声明方式:
我们先看一段代码:

int *p1[10];int (*p2)[10];//p1, p2分别是什么?

p1是指针数组。p2是数组指针。 为什么呢?
解释:*的优先级比[]更高,我们看p1,p1先和[]结合,那么它是一个数组。再与’*‘结合,说明它是一个存放指针变量的数组。
p2先和*结合,说明p2是一个指针,再与[]结合,说明它是一个指向数组的指针变量。

3.2数组名vs&数组名

我们来看一段代码:

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

运行结果如下:
在这里插入图片描述
打印结果一样,那么我们是不是就可以认为数组名等于&数组名了呢?
其实不是的。
我们再看一段代码:

#include 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的地址是一样的,但是它们+1的结果截然不同,arr+1跳过了1个整型,而&arr+1跳过了10个整型,即一个数组的长度。
实际上。arr是首元素的地址,而&arr是整个数组的地址。那么这个地址的类型是什么?怎么接收呢?

本例中,&arr的类型是:int(*)[10],是一种数组指针类型。
数组的地址+1,跳过了整个数组的大小,所以&arr+1相对于&arr的差值是40.

3.3数组指针的使用

当我们想传入一个数组到函数里去的时候,这时候数组指针就起效果了。
看以下代码:
#include
void print_arr1(int arr[3][5], int row, int col) {
printf("%d ", arr[i][j]);
}

学了指针数组和数组指针我们来一起回顾并看看下面代码的意思:

void print_arr2(int (*arr)[5], int row, int col) {    int i = 0;    for(i=0; i

我们可以看到,当我们想传入一个二维数组的时候,我们知道数组名是首元素地址,而二维数组的首元素却是一个数组,这样,我们函数在接收数组指针时就需要数组指针类型来接收。这就是数组指针的作用。

4.数组参数、指针参数

我们经常遇到这样一个问题,当我们想把数组或者指针传给函数时,函数的参数我们应该如何设计呢?

4.1一维数组传参

当我们要传入一维数组时,我们的函数的参数应该怎么设置呢?这里我已经进行了一个汇总

#include 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 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[][])//no//{}void test(int arr[][5])//ok{}//总结:二维数组传参,函数形参的设计只能省略第一个[]的数字。//因为对一个二维数组,可以不知道有多少行,但是必须知道一行多少元素。//这样才方便运算。void test(int* arr)//no{}void test(int* arr[5])//no{}void test(int(*arr)[5])//ok{}void test(int** arr)//no{}int main(){	int arr[3][5] = { 0 };	test(arr);}

同样的,我也用代码对它进行了归纳总结。一定要搞清楚它当中的逻辑。为什么可以,为什么不可以,从它传入数据的类型的本质出发。

4.3一级指针传参

例子:

#include void print(int *p, int sz) { int i = 0; for(i=0; i

4.4二级指针传参

#include 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.函数指针

老惯例,我们先来看代码:

#include void test(){ printf("hehe\n");}int main(){ printf("%p\n", test); printf("%p\n", &test); return 0; }

这段代码的打印结果是一致的,函数不需要区分这个。因为区分了也没有什么意义。重点我们放在后面

函数指针的变量初始化:

void (*pfun1)();

pfun1先与*结合,表示pfun1是一个指针,一个什么指针呢?一个指向返回值为void,没有参数的函数。

6.函数指针数组

函数指针数组是什么?是数组,什么数组?存放函数指针的数组,而函数指针前面已经介绍了。
函数指针数组的定义:

int (*parr1[10])();
int parr210;
int (
)() parr3[10];

这个其实是不需要去记忆的,只需要按照以下思路:

首先找到你的变量名:以parr1为例,看它左右,左边有一个*,右边有一个括号,我们知道括号的优先级比*高,所以我们可以先知道它是一个数组。是一个什么数组,它左边有一个*号,那么我们知道它是一个指针数组。同时后面跟着括号,以及前面有一个int,我们知道它是一个存放没有参数的返回类型是int的函数的指针的数组。

它的作用:转移表
仍然通过一段代码来理解:

#include 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; int(*p[5])(int x, int y) = { 0, add, sub, mul, div }; //转移表 while (input) {      printf( "      
作者:焦点
------分隔线----------------------------