• c语言进阶篇:指针(一)


    ✨作者介绍:大家好,我是摸鱼王胖嘟嘟,可以叫我小嘟💕
    ✨作者主页:摸鱼王胖嘟嘟的个人博客主页.🎉
    🎈作者的gitee: 小比特_嘟嘟的个人gitee
    🎈系列专栏: 【从0到1,漫游c语言的世界】
    ✨小嘟和大家一起学习,一起进步!尽己所能,写好每一篇博客,沉醉在自己进步的喜悦当中🤭。如果文章有错误,欢迎大家在评论区✏️指正。让我们开始今天的学习吧!😊请添加图片描述

    💻前言

    🍁大家好哇~终于要开始攻克c语言重要的一个知识——指针,话不多说,让我们开始今天的学习吧!

    🎈字符指针

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

    int main()
    {
    	char ch = 'w';
    	char* pc = &ch;
    	*pc = 'w';
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    还有一种使用方式如下:

    int main()
    {
    	const char* pstr = "hello world!";
    	/*
    	这里是把一个字符串放到pstr指针变量里了吗?
    	pstr中放的不是"hello world!"这个字符串,
    	这个字符串有hello world!\0 13个字符,
    	char* 在32位电脑上只有4个字节,根本存不下13个字节
    	pstr中放的是字符串首元素h的地址(字符串"hello world!"的值是首元素a的地址)
    	%s进行打印时,只要提供一个地址就可以了,直到遇到字符串的结束标志\0才停下
    	通过pstr(首元素地址)就可以找到后面的所有字符,以%s的形式打印出字符串hello world!
    	*/
    	printf("%s\n", pstr);
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    在这里插入图片描述
    在这里插入图片描述

    #include
    
    int main()
    {
    	const char* p1 = "abcdef";//这种写法是将字符串首元素a的地址放到p1中
    	char p2[] = "abcdef";//这种写法才是将字符串"abcdef"放到p2数组中
    
    	printf("%s\n", p1);
    	printf("%s\n", p2);
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    我们可以看一下下面这道面试题:

    #include
    
    int main()
    {
    	char str1[] = "hello world!";
    	char str2[] = "hello world!";
    	const char* str3 = "hello world!";
    	const char* str4 = "hello world!";
    
    	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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    在这里插入图片描述

    这里str3和str4指向的是一个同一个常量字符串。C/C++会把常量字符串存储到单独的一个内存区域,当几个指针。指向同一个字符串的时候,他们实际会指向同一块内存。但是用相同的常量字符串去初始化不同的数组的时候就会开辟出不同的内存块。所以str1和str2不同,str3和str4不同。
    在这里插入图片描述

    🎈指针数组

    🍁指针数组,是数组,是用来存放指针的数组。
    详见:c语言基础篇:指针(初阶)

    🎈数组指针

    🎉数组指针的定义

    数组指针,是指针,是指向数组的指针。

    我们已经熟悉:
    整形指针:int * pint;能够指向整形数据的指针。
    浮点型指针:float * pf;能够指向浮点型数据的指针。
    那数组指针应该是:能够指向数组的指针。

    #include
    
    int main()
    {
    	int* p1[5];//p1是指针数组,是个数组,数组中每个元素的类型是int*
    	int(*p2)[5];//p2是数组指针,是个指针, * - 代表是个指针,
    				//p2指向的是个数组,int代表数组中每个元素的类型是int
    
    	return 0;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    🍁解释:p2先和 * 结合,说明p是一个指针变量,然后指着指向的是一个大小为5个整型的数组。所以p2是一个指针,指向一个数组,叫数组指针。
    🍁这里要注意:[]的优先级要高于*号的,所以必须加上()来保证p2先和 * 结合。

    🎉&数组名VS数组名

    我们知道arr是数组名,数组名表示数组首元素的地址。
    那&arr数组名到底是啥?
    我们看一段代码:

    #include
    
    int main()
    {
    	int arr[10] = { 0 };
    	printf("%p\n", arr);
    	printf("%p\n", &arr);
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    🍁运行结果如下:
    在这里插入图片描述
    可见数组名和&数组名打印的地址是一样的。
    难道两个是一样的吗?我们再看一段代码:

    #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;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    🍁运行结果如下:
    在这里插入图片描述
    根据上面的代码我们发现,其实&arr和arr,虽然值是一样的,但是意义应该不一样的。

    数组名通常表示的都是数组首元素的地址
    但是有2个例外:
    1. sizeof(数组名),这里的数组名表示整个数组,计算的是整个数组的大小
    2. &数组名,这里的数组名表示的依然是整个数组,所以&数组名取出的是整个数组的地址

    数组名是首元素的地址;
    &数组名是整个数组的地址。

    一级指针变量是用来存放地址的变量。 一级指针变量类型是:()*,如:int *
    二级指针变量是用来存放一级指针变量的地址的变量,也就是说二级指针变量是用来存放地址的地址的变量。
    二级指针变量类型是:()**,如:int **

    整型指针是用来存放整型的地址的。
    字符指针是用来存放字符的地址的。
    数组指针是用来存放数组的地址的

    🎉数组指针的使用

    主要用于二维数组
    打印二维数组中的所有元素:

    #include
    
    //二维数组中,数组名是首行的地址,就是一个一维数组的地址(一维数组不严谨来写:int arr[0][5])
    //这个一维数组有5个元素,每个元素的类型int,这个一维数组的数组名是arr[0]
    void print_zhizheng(int(*p)[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 ", *(*(p + i) + j));
    			 /*
    			 *(p + i)得到的是每行的数组名,arr[0],arr[1],arr[2],数组名是首元素的地址
    			 所以,*(*(p+i)+j)得到的就是每一个元素。
    			 */
    		}
    		printf("\n");
    	}
    }
    
    //数组传参数组接收
    void print_shuzu(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");
    	}
    }
    
    int main()
    {
    	int arr[3][5] = { 1,2,3,4,5,6,7,8,9,1,2,3,4,5,6 };
    	print_shuzu(arr, 3, 5);
    	printf("-----------------------------------\n");
    	print_zhizheng(arr, 3, 5);
    
    	return 0;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47

    🍁运行结果如下:
    在这里插入图片描述

    🍁一维数组的地址,放到数组指针中 。像这样: int(* p)[5]
    首行的地址,进行解引用得到的是首行的全部元素,也就是说得到的是首行的数组名,数组名是首元素的地址 p放的是首行的地址,类型是int( * )[5]
    ( * )p放的是数组名arr[0],数组名arr[0]是首元素的地址,类型是int[5]
    (p和 * p的值是一样的,但意义不一样,前者放的是首行的地址,后者放的是首元素的地址。)
    所以, * (p + i)得到的是每行的数组名,arr[0],arr[1],arr[2],数组名是首元素的地址
    所以,* (*(p+i)+j)得到的就是每一个元素。

    🎈数组参数、指针参数

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

    🎉一维数组传参

    #include
    
    void test1(int arr[])//OK?
    {}
    void test1(int arr[10])//OK?
    {}
    void test1(int* arr)//OK?
    {}
    void test2(int* arr[20])//OK?
    {}
    void test2(int* *arr)//OK?
    {}
    //数组传参的是首元素的地址,首元素类型是int*
    //说明接收的是一级指针变量的地址,用二级指针变量来接收
    
    int main()
    {
    	int arr1[10] = { 0 };
    	int* arr2[10] = { 0 };
    	test1(arr1);
    	test2(arr2);
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    🎉二维数组传参

    #include
    
    void test(int arr[3][5])
    {
    }
    void test(int arr[][5])
    {
    }
    
    void test(int(*arr)[5])
    {
    }
    //二维数组的数组名是首行的地址,首行是一个一维数组
    //此一维数组的类型是int[5],有5个元素,每个元素的类型是int
    int main()
    {
    	int arr[3][5];
    	test(arr);
    
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    🎉一级指针传参

    如果函数的形式参数是一级指针,调用函数的时候可以传什么实参呢?

    #include
    
    void print(int* p)
    {
    
    }
    
    int main()
    {
    	int a = 0;
    	int* p = &a;
    	print(p);//可以传指针变量
    	print(&a);//可以传变量的地址
    	int arr[10];//如果是数组呢?
    	print(arr);//可以传数组名
    
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    🎉二级指针传参

    #include
    
    void print(char** p)
    {
    
    }
    
    int main()
    {
    	char c = 'b';
    	char* pc = &c;
    	char** ppc = &pc;
    	print(ppc);//可以传二级指针变量
    	print(&pc);//可以传一级指针变量的地址
    	char* arr[10];//如果是指针数组呢?
    	print(arr);//可以传指针数组的数组名
    
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
  • 相关阅读:
    mysql单行,多行子查询
    导出excel单元格时实现换行
    ElasticSearch ( 二 ) 基本概念
    Shiro认证和鉴权的流程
    【计算机网络】运输层:单方向传输报文管道利用的情况
    英语语法汇总(13.虚拟语气)
    Linux基础概念--进程、子进程、进程组和会话
    Word处理控件Aspose.Words功能演示:使用Java合并MS Word文档
    什么是MyBaties的输入映射与输出映射
    CSS 清除浮动
  • 原文地址:https://blog.csdn.net/weixin_61341342/article/details/126006853