关键词搜索

源码搜索 ×
×

进阶指针大全(上篇)

发布2023-01-07浏览396次

详情内容


前言

在步入正文前,我们先回顾一下指针的概念:

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


回顾结束,进入更深层次的指针

一、字符指针

先来介绍一个简单,字符指针-----char*,
常规使用:

int main()
{
	char str='w';
	char* p=&str;  //在p存放的是str的地址
	*p='w';   //利用地址直接修改str的值;
	return 0}

    另一种使用方法:

    int main()
    {
    	const char* p="abcdef";
    	printf("%s",p); //打印出abcdef;
    	return 0;
    
    • 1
    • 2
    • 3
    • 4
    • 5

    虽然可以打印出abcdef,但是不代表字符指针存放的是字符串abcdef;指针只能存放地址,而这里的本质是将字符串“abcdef”的首字符的地址存放在了p中。(注:const和指针的联系
    在这里插入图片描述

    二、指针数组

    int arr[10]; 存放整形的数组----整形数组
    char arr[10]; 存放字符的数组-----字符数组
    int* arr[10]; 存放指针的数组-----指针数组

    指针数组存放的是指针;如:int* p[3]={&a,&b,&c};存在三个指针分别指向a,b,c的地址。

    三、数组指针

    1.数组指针的定义

    数组指针是一个指向数组的指针,容易与指针数组弄混,区分小技巧(看最后2个字,如果是数组,则是数组,如果是指针则是指针)

    int* p[10];  //指针数组
    int(*p1)[10];  //数组指针
    
    • 1
    • 2

    注:为什么要括号呢?因为[]的优先级高于* ,所以会先和[]结合,变成数组,存放指针的数组;而如果有了括号,则是指针指向了一个大小为10个整形的数组

    2.数组名和&数组名的区别

    众所周知:数组名表示数组的首元素地址,那么&数组是什么呢?和数组名有什么关系?我们现在举行一个例子。
    注:sizeof(数组名)表示整个数组

    int main()
    {
    	int arr[10]={0};
    	printf("%p",arr);  //打印地址
    	printf("%p",&arr);  //打印地址
    	return 0;
    }
    

      废话不多说,先看运行结果:

      在这里插入图片描述
      事实证明:数组名和&数组名的地址是一样的,而且&是取地址符号,那么两个真的没区别嘛?不不不,让我们再看一段代码:

      int main()
      {
      	int arr[10]={0};
      	printf("arr=%p\n\n",arr);  //打印地址
      	printf("&arr=%p\n\n",&arr);  //打印地址
      	printf("arr+1=%p\n\n",arr+1);  //打印地址
      	printf("&arr+1=%p\n",&arr+1);
      	return 0;
      }
      
        8
      • 9

      在这里插入图片描述
      我们可以发现,虽然arr和&arr的地址相同,但是他们加1之后地址却不相同,所以他们的意义应该不一样。
      分析:int代表4个字节,arr+1和arr相差4,但是&arr和&arr+1却相差40(数组有10个整形元素,每一个都是4个字节)。
      解释:&arr代表的是数组的地址,而不是首元素的地址,所以当&arr+1则是跳过整个数组的大小,地址相差了40。
      注:其实&arr的类型是:int(*)[10],是一种数组指针类型数组的地址。例如:int(*p)[3]={&arr,&arr1,&arr2}; 其中的arr1、arr、arr2都是数组。

      3.数组指针的使用

      数组指针是指针指向了数组,那么存放的是数组的地址。
      代码使用:

      int main()
      {
      	int arr[5]={1,2,3,4,5};
      	int(*p)[10]=&arr;
      	return 0;
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6

      但是我们一般很少使用这种模式,一般数组指针都用于函数传参,如下:

      
      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;
      }
      
        8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
      • 22
      • 23
      • 24

      数组指针一般用于二维数组的传参,而数组指针的数组大小取决于二维数组的列。


      四、数组、指针参数

      说到底,我们会发现指针和数组环环相扣,尤其涉及到函数传参时,但是面对不同的传参,函数应该如何接收呢?现在我们来详细讲解讲解。

      1.一维数组传参

      #include <stdio.h>
      void test(int arr[])
      {}               //可以
      void test(int arr[10])
      {}	            //一和二是一样的道理,数组传参,数组接收,只不过没有规定数组大小
      void test(int* arr)
      {}	           //可以,传得地址,指针接收
      
      
      void test2(int* arr[20])
      {}			  //可以,数组传参,数组接收
      void test2(int** arr)/
      {}  		 //传过来的是一个数组,元素类型是int*
                  //二级指针就是用来存放一级指针的地址,当然也是可以的
      int main()
      {
      	int arr[10] = { 0 };
      	int* arr2[20] = { 0 };
      	test(arr);
      	test2(arr2);
      	return 0;
      }
      
      
        8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
      • 22
      • 23

      数组传参中最简单的就是数组传参,数组接收。一维数组传参时,接收也可以是一级指针,但不能是二级指针,因为二级指针存放的是一级指针的地址。

      2.二维数组传参

      void test(int arr[3][5])
      {}				// 可以,数组传参,数组接受
      void test(int arr[][])
      {}				//不可以,二维数组传参行可以省略,但列不能省略
      void test(int arr[][5])
      {}				//可以
      void test(int* arr)
      {}				//二维数组传参传递的是第一行的元素,当然不能指针接收
      void test(int* arr[5])
      {}				//同理类型也是不匹配的
      void test(int(*arr)[5])
      {}				//可以的,数组指针就是用来存放一个数组的地址,就是第一行的地址
      void test(int** arr)
      {}				//不可以,传的是整个数组的地址,但是接收的是一级指针的地址,类型不匹配
      int main()
      {
      	int arr[3][5] = { 0 };
      	test(arr);
      	return 0;
      
      }
      
      
        8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
      • 22

      二维数组传参时,数组接收时,行可以省略,列不能。

      3.一级指针传参

      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;
      }
      
      
        8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17

      4.二级指针

      void test(int** ptr) {
      	printf("num = %d\n", **ptr);
      }
      int main()
      {
      	int n = 10;
      	int* p = &n;
      	int** pp = &p;
      	test(pp);
      	test(&p);   //pp和&p是相同的地址
      	return 0;
      }
      
      
        8
      • 9
      • 10
      • 11
      • 12
      • 13

      传参小结

      当函数参数是一级指针,可接收:同类型的地址、同类型的指针、同类型的数组名

      void test1(int* p)
      {}
      //test1函数能接收什么参数?
      //int的类型的地址
      //int类型的指针
      //int 类型的数组名
      void test2(char* p)
      {}
      //test2函数能接收什么参数?
      
      
        8
      • 9
      • 10

      当函数参数是二级指针,可接收:一级指针的地址、同类型的数组名、同类型的二级指针

      void test(char** p) {
      
      }
      //一级指针的地址
      //char*类型的数组名
      //二级指针
      int main()
      {
      	char c = 'b';
      	char* pc = &c;
      	char** ppc = &pc;
      	char* arr[10];
      	test(&pc);
      	test(ppc);
      	return 0;
      }
      
      
        8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17

      结束语

      由于指针太深奥了,因此分为2部分写,下部是函数和指针的联系,不需要翻来覆去的,方便理解,若有不懂可在评论区留言,也可以私信我。

                                                         -----------小白tq
      
      • 1

      相关技术文章

      点击QQ咨询
      开通会员
      返回顶部
      ×
      微信扫码支付
      微信扫码支付
      确定支付下载
      请使用微信描二维码支付
      ×

      提示信息

      ×

      选择支付方式

      • 微信支付
      • 支付宝付款
      确定支付下载