指针进阶篇(1)

简介: 指针进阶篇(1)

🤔 前言🤔

我们在初级阶段的《指针》章节已经接触过了,我们知道了指针的概念:

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

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

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

4. 指针的运算。

这个章节,我们继续探讨指针的高级主题>

一、😊字符指针😊

在指针类型中我们知道有一种指针类型为字符指针char*。

一般使用:

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


还有一种使用方式如下:

int main()
{
  const char* pstr = "hello world.";//这里是把一个字符串的首字符地址存放在了pstr指针变量中。
  printf("%s", pstr);
  return 0;
}


运行结果>


ee2252e8801a4f02858dfc6e0ae5857e.png


代码const char* pstr = "hello world.";

特别容易让我们以为是把字符串hello world存放到字符指针pstr里了,但本质上是把字符串hello world.首字符的地址放到了pstr中。

f84bf2f6e7024058aab0e4574613cede.png


上面代码的意思是把一个常量字符串的首字符 h 的地址存放到指针变量 pstr 中。

我们来看一道面试题>

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


直接看结果>

b83230d54e7a4c50ad1b2aa8e1a3a924.png

画图解释>



b268e71824f54e24998eb571f3f5c3b0.png

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


二、😜指针数组😜

《初阶指针》章节我们也学了指针数组,指针数组是一个存放指针的数组。

这里我们再复习一下,下面指针数组是什么意思?


int* arr1[10]; //整形指针的数组
char *arr2[4]; //一级字符指针的数组
char **arr3[5];//二级字符指针的数组

三、😝数组指针😝

3.1数组指针的定义

数组指针是指针?还是数组?

答案是:指针。

我们已经熟悉:

整型指针:int * pint;能够指向整型数据的指针。

浮点型指针:float * pf,能够指向浮点型数据的指针。

那数组指针应该是:能够指向数组的指针。

我们来看看下面这些代码那个是数组指针?

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


解释>

int (*p)[10];

p先和*结合,说明p是一个指针变量,然后指向的是一个大小为10个整型的数组,所以p是一个指针,指向一个数组,叫数组指针。

这里注意:[ ]的优先级是要高于*号的,所以必须加上()来保证p先和*结合。


3.2&数组名VS数组名

对于下面数组:

 

int arr[10];

arr和&arr分别是啥?

我们知道arr是数组名,数组名表示数组的首元素地址。

那么&arr数组名又是个啥?

我们来看一段代码:



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

运行结果>

4fca7757eba8426c830c09375baff3dc.png

两个结果相同可见数组名和&数组名打印的地址是一样的。

难道就没有什么区别了吗?

我们再来看一段代码:

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


运行结果>


0cae363836024c35993210e57cebc730.png

根据上面代码我们发现,其实&arr和arr,虽然值是一样的,但是意义应该不一样的。


实际上:&arr表示的是数组的地址,而不是数组书元素的地址。


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


3.3数组指针的使用

数组指针怎么使用呢?

既然数组指针指向的是数组,那数组指针中存放的应该是数组的地址。

看代码>

#include <stdio.h>
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[10];
int *parr1[10];
int(*parr2)[10];
int(*parr3[10])[5];


0aa5ce53c5274bb5af1f98d16367636e.png


四、🌝数组参数,指针参数🌝

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

4.1一维数组传参

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

4.3一级指针传参

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


那么当一个函数的参数部分为一级指针的时候,函数能接受什么参数?

1. void test1(int *p)
2. {}
3. //test1函数能接收什么参数?


函数传递参数可以是>

int main()
{
    int a = 10;
    int* p = &a;
    int arr[10];
    test1(arr);
    test1(&a);
    test1(p);
    return 0;
}


4.4二级指针传参

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


那当函数参数为二级指针的时候,可以接收什么参数?

void test(char** p)
{}
int main()
{
  char c = 'b';
  char* pc = &c;
  char** ppc = &pc;
  char* arr[10];
  test(&pc);
  test(ppc);
  test(arr);
  return 0;
}



🍀小结🍀

今天我们学习了字符指针、指针数组、数组指针、数组参数、指针参数,相信大家看完有一定的收获。


种一棵树的最好时间是十年前,其次是现在! 把握好当下,合理利用时间努力奋斗,相信大家一定会实现自己的目标!加油!创作不易,辛苦各位小伙伴们动动小手,三连一波💕💕~~~,本文中也有不足之处,欢迎各位随时私信点评指正!

相关文章
|
5月前
|
存储 C语言
C语言 — 指针进阶篇(下)
C语言 — 指针进阶篇(下)
39 0
|
5月前
|
存储 C语言 C++
C语言 — 指针进阶篇(上)
C语言 — 指针进阶篇(上)
53 0
|
5月前
|
C语言 C++
C语言之指针进阶篇_回调函数(3)
C语言之指针进阶篇_回调函数(3)
42 0
|
5月前
|
存储 C语言 C++
【C语言进阶篇】关于指针的八个经典笔试题(图文详解)
【C语言进阶篇】关于指针的八个经典笔试题(图文详解)
259 0
|
5月前
|
C语言
【C语言进阶篇】什么还没学会指针? 一篇文章让你彻底搞懂指针的奥秘
【C语言进阶篇】什么还没学会指针? 一篇文章让你彻底搞懂指针的奥秘
48 0
|
11月前
|
C语言 C++
C语言之指针进阶篇(3)
C语言之指针进阶篇(3)
45 0
|
11月前
|
C语言
C语言之指针进阶篇(2)
C语言之指针进阶篇(2)
47 0
|
11月前
|
C语言 C++
C语言之指针进阶篇(1)
C语言之指针进阶篇(1)
79 0
|
存储 C语言
C语言 — 指针进阶篇(下)
指针基础篇回顾可以详见: 指针基础篇(1)指针基础篇(2) 指针进阶篇分为上下两篇,上篇介绍1 — 4,下篇介绍5 — 6 字符指针 数组指针 指针数组 数组传参和指针传参 函数指针 函数指针数组 指向函数指针数组的指针 回调函数
56 0
|
存储 C语言 C++
C语言 — 指针进阶篇(上)
指针进阶篇分为上下两篇,上篇介绍1 — 4,下篇介绍5 — 6 字符指针 数组指针 指针数组 数组传参和指针传参 函数指针 函数指针数组 指向函数指针数组的指针 回调函数
74 0