猿创征文丨赶紧进来!!!轻松掌握C语言指针(进阶篇)

耐心看完~对指针不再恐惧…本文详细介绍了指针进阶篇内容:
指针和数组间的关系:字符指针, 一维数组和二维数组传参以及一维指针和二维指针传参

猿创征文丨赶紧进来!!!轻松掌握C语言指针(进阶篇)_第1张图片

指针的进阶

  • 一.字符指针
  • 二.指针和数组的关联
    • 1.指针数组
    • 2.数组指针
      • ①.数组指针的定义
      • ②.数组名和&数组名
      • ③.数组指针的使用
  • 三.数组参数和指针参数
    • 1.一维数组传参练习
    • 2.二维数组传参练习
    • 3.一级指针传参练习
    • 4.二级指针传参练习
  • 四.指针进阶总结

指针的主题,在这篇博客中->《指针初阶》里已经接触过了,我们知道了指针的概念:

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

今天要介绍的是指针进阶篇内容↓

一.字符指针

在指针的类型中我们知道有一种指针类型为字符指针 char* ;
一般使用:
还有一种使用方式如下:

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

这串代码字符指针变量pc存放着字符指针&ch,也就是ch字符变量在内存中申请的空间的地址,可以通过指针解引用操作访问这个字符变量…
再看看下面这一串代码

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

字符数组能够放"hello bit."这个字符串 而pstr这个字符指针放了这个字符串吗?,或者说这个代码是错误的
实际上,这串代码没有错,字符指针变量只存放一个字符指针,存放的是hello bit.这个字符串首元素地址,也就是h存放在内存中空间所在的地址
pstr 也就跟 一个字符数组数组名一样 表示字符串首元素地址也就是h的地址
而因为"hello bit."是常量字符串,常量是不可以被更改的,''所以需要const在*左边修饰,表示这个pstr指针指向的h的空间可以访问h但不能更改h

这也是和字符数组不一样的地方:
字符数组是开辟至少能够容纳这整个字符串的空间,并将这个字符串的一份拷贝放在数组里,并且在字符数组里可以更改存储的字符串不是对这个常量字符串进行修改.
而指针pstr存放的 就是这个常量字符串首元素在内存中的地址,直接访问的这常量字符串 因此不能对其修改但是能够正常访问输出字符串

注意:常量在内存中有一块特定的常量区空间存放这些常量,得到的也就是存放常量的空间所在内存的编号,并且相同的常量数据用的都是在同一个常量空间里的数据,一个程序出现两个一模一样的常量字符串并不会在常量区申请两个空间存放它们

二.指针和数组的关联

指针和数组关系比较密切,甚至两者有时可以相互等价理解
比如:数组名表示的就是这个数组首元素的地址,而首元素地址就是一个指针
数组是一块由多个物理地址连续的内存单元构成的有序空间
而我们找到数组里每个元素,就可以引用指针,精确找到数组里的每个元素
数组可以有指针数组,指针也可以是数组指针
数组作为实参传参本质上传的不是整个数组,是数组首元素地址
这也就说明数组和指针间关系密切…

1.指针数组

学过整形数组,字符数组吧,它们分别表示数组每个元素是整形或者字符,而指针数组表达的就是数组每个元素都是一个指针,而光指针表达也不完全,因为指针也有很多类型,准确来说可以是整形指针数组
表示数组每个元素都是一个整形指针,可以通过这些元素解引用访问其指向的整形空间…

这三个分别表示什么呢?
int* arr1[10];
char *arr2[4];
char **arr3[5];

首先看有没有括号,没有的话先看[],
第一个先看arr1[10] 表示是一个有10个元素的数组,而数组元素类型是什么 看前面* 表示 是指针数组 而什么类型的指针 int 即表示是整形类型的指针数组
第二个也就是字符指针数组.
注意:套娃来了…而第三个 我们知道char *arr表示是 字符指针变量 而char * *arr 则表示是字符指针类型的指针变量即二级字符指针变量 ,存放的是二级指针
表示的是存放一个字符指针的变量的变量,
而char ** arr[5]表达的就是二级字符指针数组 数组每个元素都是二级字符指针

2.数组指针

和上面的指针数组名字差不多但是完全不一样,数组指针表达的是一个指针,是一个指向数组的指针,而指向的数组是什么类型还要看具体情况

①.数组指针的定义

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

第一个p1[10]表达的是一个数组 而左边是int * 即是整形指针数组
第二个出现了括号,即先看括号里的类型 p2表示是一个指针即,将p2当做一个整体, int [10]即表示一个整形数组 ,联系在一起即为整形数组指针

②.数组名和&数组名

数组名知道是数组首元素地址,而&又是取地址,&arr又表示什么呢?

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

这串代码输出结果是什么?

猿创征文丨赶紧进来!!!轻松掌握C语言指针(进阶篇)_第2张图片

arr和&arr以地址形式输出的结果是一样的…
可见数组名和&数组名得到的地址是一样的

#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;
}

这串代码输出结果又是什么?

猿创征文丨赶紧进来!!!轻松掌握C语言指针(进阶篇)_第3张图片

前面两行和上面一样,但是后两行却不一样,
从上面代码可以看出&数组名和数组名的地址是相同,但是表达的意义是不同的
实际上:arr表示首元素地址,&arr表示的整个arr的地址
这两个地址都是以起始第一个内存单元的地址做代表,所以它们在形式上是完全一样的.

但是我们学过指针±整数,移动的是步长,而步长大小取决于指针的类型
arr+1 表示移动1个步长而arr是首元素地址,是一个整形指针所以移动4个字节,而&arr表示整个数组的地址+1表示移动整个数组大小个字节,整个数组大小为数组所有元素大小之和 数组内有10个元素,每个元素4个字节总大小为4*10=40.所以移动40个字节…而地址是十六进制形式表示最后成了经过进位得到了后面两个结果

注意:数组名表示的是首元素地址,只有两种情况数组名表示的是整个数组:
1.sizeof(数组名)
2. &数组名
这两种情况数组名表示的是整个数组,
1,为整个数组大小
2.为整个数组的地址
其他情况数组名一律为首元素地址!!!

③.数组指针的使用

由此得到了数组指针是用&数组名得到的,即表示指向数组的指针
而数组指针具体有什么用法呢?
看下面一串代码




#include 
int main()
{
int arr[10] = {1,2,3,4,5,6,7,8,9,0};
int (*p)[10] = &arr;//把数组arr的地址赋值给数组指针变量p
//但是我们一般很少这样写代码
return 0;
}

上面这串代码 将arr这个数组的地址存在了p这个数组指针变量里
但是一般很少这样使用
下面是一个具体使用示例↓

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

首先创建了一个二维数组,而二维数组名作为实参传参传的也是首元素的地址,
但是二维数组的首元素实际上是第一行,表示的也就是第一行的地址,而第一行地址就可以看做是一个一维数组的地址,此时形参可以用一维数组指针变量接受,也就体现出来数组指针的作用
通过数组指针最后遍历打印出二维数组的所有元素
当然 用一个相同大小的二维数组作为形参也可以接收
不管是什么形式接受,最后接收的都是首元素地址(数组指针)

学了这么多,来看看下面四个语句表示的是什么~
int arr[5];
int *parr1[10];
int (*parr2)[10];
int (*parr3[10])[5];

① 整形数组
②整形指针数组
③整形数组指针变量
整形数组指针数组 层层套娃不理解用文字可以表示为是每个元素都是一个整形数组指针的数组~

三.数组参数和指针参数

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

1.一维数组传参练习

判断一下下面代码一维数组名作为函数实参传参对应的五条函数形参写法接收 是正确还是错误…

#include 
void test(int arr[])// 1 ok?
{}
void test(int arr[10])//2 ok?
{}
void test(int *arr)//3 ok?
{}
void test2(int *arr[20])//4 ok?
{}
void test2(int **arr)//5 ok?
{}
int main()
{
int arr[10] = {0};
int *arr2[20] = {0};
test(arr);
test2(arr2);
}

1 is ok 一维数组名传参,函数形参用相应的一维数组接收是正确的,但接收的是首元素地址…
2 is ok 一维数组[]里可以不写元素个数因为对应的是接收实参这个数组首元素地址 ,也可以写上对应的10个元素…
3 is ok 一维数组名实际上也是一个首元素地址 是一个整形指针 ,形参可以对应用整形指针变量接收…
4 is not ok arr是整形数组的数组名 形参不能用整形指针数组接收…
5 is not ok arr也表示整形指针 不能用整形二级指针接收 …

总结:一维数组名传参 ,形参可以用对应的一维数组接受,[]里的数可以写也可以不写, 一维数组名也可以看做首元素地址传参 形参用对应的指针变量接受

2.二维数组传参练习

void test(int arr[3][5])//1  ok?
{}
void test(int arr[][])//2  ok?
{}
void test(int arr[][5])//3  ok?
{}

void test(int *arr)//4  ok?
{}
void test(int* arr[5])//5  ok?
{}
void test(int (*arr)[5])//6  ok?
{}
void test(int **arr)//7  ok?
{}
int main()
{
int arr[3][5] = {0};
test(arr);
}

1 is ok arr为二维数组名 作为参数传参 可以用对应的一个二维数组接收
2 is not ok 二维数组名传参 可以用二维数组接收, 行可以忽略不写 ,但是列一定得对应写上,因为如果不写计算机不知道你这个数组接收后的列是什么,只有知道了列 就可以反推有多少行 但是知道行无法确定一行有多少列
3 is ok 二维数组传参 可以用二维数组接收 此时行可以省略 列不能省略
4 is not ok 二维数组名相当于首元素地址 而二维数组首元素为第一行,即第一行地址传参 相当于一个一维数组指针传参 ,不能用整形指针变量接收
5 is not ok 形参是一个整形指针数组 对应的类型不同
6 is ok 实参作为一维数组指针传参 用一维数组指针变量可以接收
7 is not ok 二级指针变量 不能接收实参是二维数组名的参数

//总结:二维数组传参,函数形参的设计只能省略第一个[]的数字。 //因为对一个二维数组,可以不知道有多少行,但是必须知道一行多少元素,二维数组名也可以当做一维数组指针传参
//这样才方便运算。

3.一级指针传参练习

#include 
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;
}

arr表示数组名 ,可以理解为首元素地址即整形指针 此时用一个整形指针变量p 接收
最后p作为函数实参传参,实参用对应的整形指针变量接受
一 一对应
而sz得到的是arr这个数组所有元素个数
最后在函数里实现了将数组所有元素打印…
*(p+i)等价于p[i]

4.二级指针传参练习

#include 
void test(int** ptr)
{
printf("num = %d\n", **ptr);
}
int main()
{
int n = 10;
int*p = &n;
int **pp = &p;
int* arr[10]={&n};
test(pp);
test(&p);
test(arr);
return 0;
}

n是一个整形变量 &n得到的是一个整形指针 而p为整形指针变量
&p则为一个指向整形指针变量的指针是一个二级整形指针变量
pp是一个二级整形指针变量,一一对应接收值
pp作为实参传参是一个二级指针 函数的形参对应用一个二级整形指针变量ptr接收
&p是一个二级指针,同样符合形参二级指针ptr变量接受
ptr是一个二级整形指针变量 *ptr 解引用访问得到的是一个整形指针 **ptr得到的就是二级指针指向的指针变量里的指针指向的整形空间…

而arr为一个整形指针数组名,表示首元素地址也就是整形指针变量的地址,是一个指向整形指针的指针是一个二级整形指针作为函数实参传参,形参用一个二级整形指针变量接收
最后也就打印出了数字10…

猿创征文丨赶紧进来!!!轻松掌握C语言指针(进阶篇)_第4张图片

四.指针进阶总结

本文介绍了指针进阶内容字符指针.指针和数组间的关联,数组指针的定义,数组名的含义和使用,一维数组,二维数组,一级指针,二级指针传参,
上面都是些用的类型,但指针和数组不仅仅是这些
指针和数组的使用有很多种,套娃是其精髓,如:三维数组…三级指针…
学的深入…细细理解才能更进一步
下篇博客是最后指针完结篇介绍函数和指针间的关系…

猿创征文丨赶紧进来!!!轻松掌握C语言指针(进阶篇)_第5张图片

写文不易,给个一键三连支持支持吧~~~

你可能感兴趣的:(c语言(从0到1学习),#,C语言进阶(从入门到精通),c语言,c++,数据结构,算法)