深度刨析指针Advanced 1

简介: 深度刨析指针Advanced 1

目录

😄前言

🤣回顾知识

🫠字符指针

😜指针数组

🤫 数组指针

🤔数组指针的定义

😶‍🌫️ &数组名和数组名

🤓数组指针的使用

😴 数组参数,指针参数

🤤一维数组传参

🫥二维数组传参

🤕一级指针传参

🤯二级指针传参

🥹总结


😄前言

在前面的《指针不可怕,请爱它呵护它》http://t.csdn.cn/m7yf2 这篇文章中,已经对指针的基础知识:指针是什么,指针的类型,野指针,二级指针指针的运算与使用这些知识进行了基础且具体的讲解。学完这些知识大家对平常中的指针问题已经基本可以解决了。但是指针的知识远远不止这些,接下来的内容可以在帮助大家对C语言中的指针再提高一个理解。

🤣回顾知识

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

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

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

4. 指针的运算

🫠字符指针

在指针的类型中我们知道有一种指针类型为字符指针char*,它一般这么使用

还有一种方式大家看看:

这个代码大家就是容易把const char* pstr认为里面存的是hello bit,但其实本质上里面存的是字符串首元素的地址。所以它解引用出来肯定也是首元素。用const是因为hello bit 是一个常量,不能被修改,就要用const限定。

理解了上面的内容的话,我们看下面一道代码:

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

我们来分析一下为什么是这个结果:

C语言中,常量字符串一般都是储存到一块单独的内存空间。当几个指针同时指向这个字符串的时候,就是指向同一块内存,同一个地址。所以str3==str4

但是同相同的常量字符串去初始化不同的数组,因为数组随时都有可以发生不同的改变,为了安全起见,就会开辟不同的内存块。所以str1!=str2

😜指针数组

在《指针不可怕,请爱它呵护它》中我们也讲过了指针数组,它就是一个存放指针的数组。

🤫 数组指针

🤔数组指针的定义

数组指针是数组还是指针呢?答案当然是指针啦。我们在之前的文章中已经知道:

整型指针:int *p 能够指向整数据的指针

浮点数指针:float *pf 能够指针浮点数数据的指针

所以数组指针:能够指向数组的指针

判断一下,哪个是数组指针:

解释:

注意:[]的优先级是高于*的,所以要加上()保证p和*先结合

😶‍🌫️ &数组名和数组名

这里有一个数组 int arr[10] ,我们知道arr是数组名,数组名是首元素地址。那这样的话,&arr又是什么捏?

通过代码观察:

我们发现arr和&arr的地址都是一样的,难道它们是一样的含义吗?

接下来我们再看一段代码:

#include <stdio.h>
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加一是跳过整个数组,而arr加一是跳过一个元素 。

int(*arr)[10]中&arr的类型是int(*)[10],是一种数组指针。

🤓数组指针的使用

数组指针指向的是数组,那里面存放的就是数组的地址。

代码:

一个数组指针的正常使用场景:

理解arr是数组首元素的地址,在二维数组中,首元素的地址就是第一行的地址,就可以用数组指针来接收。

//用数组指针打印二维数组
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));
      //我们可以这么理解
      //*(arr+i)就是就是数组的地址&arr解引用得到了数组arr
      //arr在解引用就得到了元素
    }
    printf("\n");
  }
}
int main()
{
  int arr[3][5] = { {1,2,3,4,5},{6,7,8,9,10},{11,12,13,14,15} };
  print_arr1(arr, 3, 5);
  //数组名arr,表示首元素的地址
  //但是二维数组的首元素是二维数组的第一行
  //所以这里传递的arr,其实相当于第一行的地址,是一维数组的地址
  //可以数组指针来接收
  print_arr2(arr, 3, 5);
  return 0;
}

现在我们就可以理解下面代码的意思了:

😴 数组参数,指针参数

在写代码的时候难免要把数组或者指针传给函数,那函数的参数怎么写呢?

🤤一维数组传参

#include <stdio.h>
void test(int arr[])//ok?
//ok
//数组传参,数组接受,空格不影响,本质上函数是接受的首元素的地址,不会创建
{}
void test(int arr[10])//ok?
//ok
//理由同上
{}
void test(int* arr)//ok?
//ok
//arr就是一个整型地址,可以拿一个整型指针接收
{}
void test2(int* arr[20])//ok?
//ok
//指针数组传参,指针数组接收
{}
void test2(int** arr)//ok?
//ok
//arr2是指针数组首元素的地址就是==指针的地址==地址的地址
//拿二级指针接收
{}
int main()
{
  int arr[10] = { 0 };
  int* arr2[20] = { 0 };
  test(arr);
  test2(arr2);
}

🫥二维数组传参

void test(int arr[3][5])//ok?
//ok
//二维数组传参 二维数组接收
{}
void test(int arr[][])//ok?
//err
//不可以省略列
{}
void test(int arr[][5])//ok?
//ok
{}
//总结:二维数组传参,函数形参的设计只能省略第一个[]的数字。
//因为对一个二维数组,可以不知道有多少行,但是必须知道一行多少元素。
//这样才方便运算。
void test(int* arr)//ok?
//err
//arr是二维数组第一行的地址,是一个一维数组,不能用int类型的指针接收
{}
void test(int* arr[5])//ok?
//err
//参数只可以用二维数组或者数组指针接收
//他是指针数组
{}
void test(int(*arr)[5])//ok?
//ok
//他就是数组指针
{}
void test(int** arr)//ok?
//err
//它是二级指针
//arr是二维数组第一行的地址,只能用数组指针接收或者二维数组
{}
int main()
{
  int arr[3][5] = { 0 };
  test(arr);
}

🤕一级指针传参

#include <stdio.h>
void print(int* p, int sz)
{
  int i = 0;
  for (i = 0; i < sz; i++)
  {
    //p+i就是相当与指针指向的地址不断向后加一
    printf("%d\n", *(p + i));
  }
}
int main()
{
  int arr[10] = { 1,2,3,4,5,6,7,8,9 };
  //一级指针p存放的是arr首元素的地址
  int* p = arr;
  int sz = sizeof(arr) / sizeof(arr[0]);
  //一级指针p,传给函数
  print(p, sz);
  //p==arr
  return 0;
}

当函数参数部分为一级指针的时候,函数可以接收什么参数呢?

看代码:

void test1(int* p)
{}
//test1函数能接收什么参数?
//可以接收 arr数组的数组名 整型变量的地址 一级指针
void test2(char* p)
{}
//test2函数能接收什么参数?
//字符变量的地址 一级指针
int main()
{
  int a = 0;
  int arr[10] = { 0 };
  int* pa = &a;
  test1(&a);//整型变量的地址
  test1(pa);//pa就是整型a的地址
  test1(arr);//arr是首元素的地址
  char ch = 'w';
  char* p = &ch;
  test2(&ch); //字符变量的地址
  test2(p); // p这个指针变量中就存放了字符的地址
  return 0;
}

🤯二级指针传参

#include <stdio.h>
void test(int** ptr)//用二级指针接收
{
  printf("num = %d\n", **ptr);
}
int main()
{
  int n = 10;
  int* p = &n;//一级指针 存的是n的地址
  int** pp = &p;//二级指针 存的是一级指针p的地址
  test(pp);//传二级指针 就是传的一级指针的地址
  test(&p);// &p 取出的是一级指针p的地址
  return 0;
}

当函数的参数部分为二级指针时,函数可以接收什么参数呢?

代码:

void test(char** p)//二级指针接收
{
}
int main()
{
  char c = 'b';
  char* pc = &c;
  char** ppc = &pc;
  char* arr[10];//指针数组
  test(&pc); // &pc时取出一级指针pc的地址就是二级指针
  test(ppc);//ppc 是二级指针
  test(arr);//Ok?
  //ok
  //arr是指针数组首元素的地址,首元素是一级指针 所以它也相当于一个二级指针
  return 0;
}

🥹总结

对于指针的进阶,里面的内容还是很多的,我打算分两篇文章来介绍。这篇文章我们重点介绍了数组指针和数组的传参,指针的传参。以代码的形式具体的分析了其中知识点,想必大家对于数组指针应该可以清楚的理解它,数组和指针的传参也知道什么可以传参,什么可以接收参数。接下来我们还会继续对进阶指针剩下的内容完善的,敬请期待

目录
相关文章
|
10月前
|
C语言
深度刨析指针Advanced 2
深度刨析指针Advanced 2
41 0
|
16天前
|
C语言
C语言:数组和指针笔试题解析(包括一些容易混淆的指针题目)
C语言:数组和指针笔试题解析(包括一些容易混淆的指针题目)
|
1月前
|
存储 程序员 编译器
爱上C语言:指针很难?来来来,看看这篇(基础篇)
爱上C语言:指针很难?来来来,看看这篇(基础篇)
|
5天前
|
C语言
c语言指针总结
c语言指针总结
11 1
|
16天前
|
存储 C语言
指针深入解析(C语言基础)带你走进指针,了解指针
指针深入解析(C语言基础)带你走进指针,了解指针
|
16天前
|
C语言 C++
C语言:指针运算笔试题解析(包括令人费解的指针题目)
C语言:指针运算笔试题解析(包括令人费解的指针题目)
|
18天前
|
安全 C语言
指针与字符串:C语言中的深入探索
指针与字符串:C语言中的深入探索
15 0
|
19天前
|
存储 监控 C语言
c语言的指针
c语言的指针
22 0
|
25天前
|
编译器 C语言 C++
初阶指针(C语言)
初阶指针(C语言)
|
1月前
|
存储 C语言
C语言 — 指针进阶篇(下)
C语言 — 指针进阶篇(下)
20 0