进阶指针大全(上篇)

简介: 进阶指针大全(上篇)

前言


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

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;


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

18193c5b54aa45058bfa0e779fe2ffd9.png


二、指针数组


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];  //数组指针


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


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


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

注:sizeof(数组名)表示整个数组

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

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

e1e71176192e4fafb2a41d23bb3d26db.png


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

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


4cc24db148ff45bc9b7219a2163e921c.png

我们可以发现,虽然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;
}


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

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


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


四、数组、指针参数


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


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

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


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

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


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


传参小结


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

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


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

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



结束语


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


                                                   -----------小白tq
目录
相关文章
|
5月前
|
C语言
指针进阶(C语言终)
指针进阶(C语言终)
|
5月前
|
机器学习/深度学习 搜索推荐 算法
【再识C进阶2(下)】详细介绍指针的进阶——利用冒泡排序算法模拟实现qsort函数,以及一下习题和指针笔试题
【再识C进阶2(下)】详细介绍指针的进阶——利用冒泡排序算法模拟实现qsort函数,以及一下习题和指针笔试题
|
5月前
|
C语言
指针进阶(回调函数)(C语言)
指针进阶(回调函数)(C语言)
|
5月前
|
存储 C语言 C++
指针进阶(函数指针)(C语言)
指针进阶(函数指针)(C语言)
|
5月前
|
编译器 C语言
指针进阶(数组指针 )(C语言)
指针进阶(数组指针 )(C语言)
|
5月前
|
搜索推荐
指针进阶(2)
指针进阶(2)
47 4
|
5月前
指针进阶(3)
指针进阶(3)
41 1
|
5月前
|
C++
指针进阶(1)
指针进阶(1)
43 1
|
5月前
|
存储 安全 编译器
C++进阶之路:何为引用、内联函数、auto与指针空值nullptr关键字
C++进阶之路:何为引用、内联函数、auto与指针空值nullptr关键字
42 2
|
5月前
|
Java 程序员 Linux
探索C语言宝库:从基础到进阶的干货知识(类型变量+条件循环+函数模块+指针+内存+文件)
探索C语言宝库:从基础到进阶的干货知识(类型变量+条件循环+函数模块+指针+内存+文件)
49 0