当前位置: 首页 > news >正文

【C语言】深入讲解指针(上)

文章目录

  • 前言
  • 字符指针
  • 指针数组
  • 数组指针
    • 数组指针的定义
    • &数组名和数组名
    • 数组指针的使用
  • 指针和数组传参
    • 一维数组传参
    • 二维数组传参
    • 一级指针传参
    • 二级指针传参
  • 结束

前言

之前我们初步了解了指针的概念,没有看过的大家可以移步到【C语言】初阶指针详解, 接下来我们将继续深入了解指针,本内容由于过长会分上、中、下3
篇进行讲解,接下来我们就进入深入了解指针的第一篇。

字符指针

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

一般使用

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

还有一种使用方法为

 int main()
{char arr[20] = {"hello bit"};char* pc = &arr;return 0;}

上面的代码只是将字符数组的首元素的地址给了字符指针,不要理解为把整个字符数组存放在了字符指针里。

下面有一道面试题

#include <stdio.h>
int main()
{char str1[] = "hello bit.";char str2[] = "hello bit.";char *str3 = "hello bit.";char *str4 = "hello bit.";if(str1 ==str2)printf("str1 and str2 are same\n");elseprintf("str1 and str2 are not same\n");      if(str3 ==str4)printf("str3 and str4 are same\n");elseprintf("str3 and str4 are not same\n");return 0;
}

这里的输出结果为
输出结果
这里str3和str4指向的是一个字符串常量,c \c++会把常量字符串单独存储在一个内存区域,当几个指针指向同一个字符串的时候,其实他们指向的是同一个内存单元,但用相同字符串初始化的不同数组,数组会开辟自己独立的空间,所以str1和str2不同,str3和str4相同。

指针数组

指针数组我们在指针初阶已经了解过了,是一个存放指针的数组。
下面我们来复习一下,下面指针数组的意思。

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

数组指针

数组指针的定义

我们回顾完指针数组,再来了解一个新的概念数组指针,其本质是一个指向数组的指针。
下面那一个是数组指针

int *p1[10];
int (*p2)[10];

p1, p2分别是什么?
我们先回忆一下操作符的优先级
在这里插入图片描述
[ ] 引用操作符的优先级高于 * 解引用操作符。

  • p1会先跟[ ]结合说明p1是一个数组,数组元素类型为int * 所以p1为指针数组。
  • p2先跟 * 结合说明p2是一个指针,指向的数据类型为int [10],所以p2为数组指针。

这里需要注意的是[ ]优先级高于* 所以要用()保证p2先于 * 结合

&数组名和数组名

对于数组

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

结果
我们可以看到&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+1和arr相差4个字节,arr和arr+1相差40个字节,从这里我们可以看出arr和&arr
值一样但意义不一样。
实际上arr表示数组首元素的地址,&arr表示的是整个数组地址,所以arr+1跳过整个数组的大小。

数组指针的使用

我们讲了数组指针的定义,接下来讲如何使用。
过去我们有⼀个⼆维数组的需要传参给⼀个函数的时候,我们是这样写的:

#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");}
}int main()
{int arr[3][5] = {1,2,3,4,5,6,7,8,9,10};print_arr1(arr, 3, 5);print_arr2(arr, 3, 5);return 0;
}

这⾥实参是⼆维数组,形参也写成⼆维数组的形式,那还有什么其他的写法吗?

这里我们先分析一下二维数组,二维数组可以看成每个元素都为一维数组的数组,也就是⼆维数组的每个元素是⼀个⼀维数组。那么⼆维数组的⾸元素就是第⼀⾏,是个⼀维数组。如下图:
在这里插入图片描述

所以,根据数组名是数组⾸元素的地址这个规则,⼆维数组的数组名表⽰的就是第⼀⾏的地址,是⼀ 维数组的地址。根据上⾯的例⼦,第⼀⾏的⼀维数组的类型就是 int [5] ,所以第⼀⾏的地址的类型就是数组指针类型 int(*)[5] 。那就意味着⼆维数组传参本质上也是传递了地址,传递的是第⼀⾏这个⼀维数组的地址,那么形参也是可以写成指针形式的。如下:

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_arr2(arr, 3, 5);return 0;
}

总结:二维数组传参,可以写成二维数组的形式,也可以写成数组指针的形式。

指针和数组传参

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

一维数组传参

#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?
{}
int main()
{int arr[10] = {0};int *arr2[20] = {0};test(arr);test2(arr2);
}

对于arr:

  1. arr是一个一维数组,形参可以写成一维数组的形式。
  2. arr表示数组首元素的地址,是一个指向整形的指针常量,形参可以写成一级指针的形式。

对于arr2

  1. arr2是一个指针数组,其本质也是一个一维数组,形参也可以写成一维数组的形式。
  2. arr2 是数组首元素的地址,数组元素类型为int*的指针类型,arr2表示为二级指针,形参可以写成二级指针的形式。

二维数组传参

void test(int arr[3][5])//ok?
{}
void test(int arr[][])//ok?
{}
void test(int arr[][5])//ok?
{}
void test(int *arr)//ok?
{}
void test(int* arr[5])//ok?
{}
void test(int (*arr)[5])//ok?
{}
void test(int **arr)//ok?
{}
int main()
{int arr[3][5] = {0};test(arr);
}

void test(int* arr[5])
void test(int **arr)
上面黄色的代码可以接收二维数组吗?
我们分析一下形参的类型,

  • arr先跟* 结合,说明是一个指针,指向的数据类型为int * ,所以形参的数据类型为二级指针,但我们传的是一个二维数组,数据类型不匹配,所以二级指针不可以接收二维数组的传参。
  • arr先跟[5]结合,说明是一个数组,各元素的数据类型为int * ,所以形参的数据类型为指针数组,但我们传的是一个二维数组,数据类型不匹配,所以指针数组不可以接收二维数组的传参。

总结:

  • 二维数组传参,函数形参的设计只能省略第一个[]的数字。
    因为对一个二维数组,可以不知道有多少行,但是必须知道一行多少元素,这样才方便运算。
  • 二维数组传参的指针形式写成 type(*) [ ]

一级指针传参

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

思考:

当一个函数的参数为一级指针时,可以传什么参数?

答案:

  • 一维数组的数组名
  • 一级指针

二级指针传参

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

思考:

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

答案:

  • 二级指针
  • 指针数值的数组名

结束

到这里深度讲解指针(上)就结束了,后续还有中、下两篇,感兴趣的可以多多关注我的博客。
各位大佬有什么疑问可以留在评论区里,你们的支持是我最大的动力
。🌟🌟🌟


http://www.mrgr.cn/news/932.html

相关文章:

  • VueX 使用
  • SQL进阶技巧:数据清洗如何利用组内最近不为空的数据填充缺失值。【埋点日志事件缺失值填充】
  • hive学习(四)
  • 成为Python砖家(3): 何时产生字节码 .pyc 文件
  • 群晖NAS本地搭建可远程交互的大型语言模型LLM聊天机器人
  • HarmonyOs透明弹窗(选择照片弹窗样式)
  • 微前端架构下的负载均衡实现:策略与技术
  • 【项目】Java文档搜索引擎测试报告
  • 域名注册查询方法
  • 一文读懂 服务器
  • JVM 类加载机制
  • JavaScript初级——对象和函数
  • 详细了解JavaScript中的原型链和继承机制
  • 我遇到的flutter问题以及答案(一)
  • windows调试ios记录
  • 深度学习--tensorflow/keras出现各种维度不匹配问题解决
  • STM32之MPU6050实战
  • MiniCPM-V: A GPT-4V Level MLLM on Your Phone论文阅读
  • 所生成项目的处理器架构“MSIL”与引用“***”的处理器架构“x86”不匹配。
  • Android 使用`layer-list`打造精美的背景