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

news2024/11/15 4:32:30

文章目录

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

前言

之前我们初步了解了指针的概念,没有看过的大家可以移步到【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");
 	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;
}

这里的输出结果为
输出结果
这里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.coloradmin.cn/o/2048157.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

手撕C++入门基础

1.C介绍 C课程包括&#xff1a;C语法、STL、高阶数据结构 C参考文档&#xff1a;Reference - C Reference C 参考手册 - cppreference.com cppreference.com C兼容之前学习的C语言 2.C的第一个程序 打印hello world #define _CRT_SECURE_NO_WARNINGS 1 // test.cpp // …

day02--HTML CSS

一、HTML表单 表单的作用是用于采集用户再页面上填入的数据&#xff0c;并发送给后端服务器&#xff0c;经常用于用户注册、登录、xx信息添加、xx信息修改 1.1表单 1、input表示文本框 type属性&#xff1a;负责配置不同的输入框类型 text&#xff1a;普通文本框 password&…

服务器数据恢复—raid5阵列离线硬盘强制上线失败如何恢复数据?

服务器数据恢复环境&#xff1a; 某品牌2850服务器上有一组由6块SCSI硬盘组建的raid5磁盘阵列&#xff0c;上层操作系统为Redhat linuxext3文件系统。 服务器故障&初检&#xff1a; 服务器在运行过程中突然瘫痪&#xff0c;管理员对服务器中的raid进行检查后发现有两块硬盘…

P3572 [POI2014] PTA-Little Bird

[POI2014] PTA-Little Bird - 洛谷 核心思路 注意力惊人。 注意到&#xff0c;只有两种决策 选高过自己的树中代价最小的 或者 选低于自己的树种代价的最小的1。 取最小值 显然 不等式&#xff1a; 恒成立。 由此&#xff0c;维护一个优先队列即可。 AC 代码 #includ…

宠物空气净化器推荐买吗?清除浮毛的效果好吗

家里养了两只哈基米&#xff0c;它们每天的日常就是早上跑酷、中午跑酷、晚上还在跑酷&#xff0c;有时候看着很好玩&#xff0c;每天都活蹦乱跳的&#xff0c;这在说明它们很健康&#xff0c;我把它们养得很好&#xff0c;所以原谅它们经常跑跑跳跳得行为&#xff0c;虽然会把…

库室联管联控系统DW-S306|是一套智能化系统

装备库室联管联控系统&#xff08;DW-S306&#xff09;是依托互3D技术、RFID技术、数据库技术、AI、视频分析技术对库室装备进行统一管理、分析的信息化、智能化、规范化的系统。 本解决方案利用现有内部网络&#xff0c;部署部队装备库室联管联控系统&#xff0c;形成一套上下…

ai大模型之争-落地场景分析20240817

大模型之争&#xff1a; 目标&#xff1a;吸引客户的注意力&#xff0c;养成客户习惯&#xff0c;占领市场 结语 对于研发人员而言&#xff1a;浏览器插件&#xff0c;开发工具的大模型&#xff1a;通义灵码真是效率倍增的神器 对非研发普通人而言&#xff1a;增加很多便捷工…

DolphinScheduler集群部署问题(趟坑)总结

目录 官方文档 官方项目地址 问题解决 官方文档 DolphinScheduler | 文档中心 (apache.org) 官方项目地址 部署及使用过程中的问题可以参见项目Issue:Issues apache/dolphinscheduler GitHub GitHub - apache/dolphinscheduler at 3.2.2-release 问题解决 1、JVM在运…

ThreejsWebGPU运动残影demo

功能点 实例化SkinnedMesh 修改NodeMaterial着色器 节点材质系统 shader 语言 使用uniform和attribute 中合其他几篇博客中的内容 代码仓库 克隆后需要放到three源码同级别目录下 运行 three源码部分不在git仓库中(太大了) 使用vscode的live-server启动后访问 http://127.0.0.…

HarmonyOS NEXT - Navigation组件封装BaseNavigation

demo 地址: https://github.com/iotjin/JhHarmonyDemo 代码不定时更新&#xff0c;请前往github查看最新代码 在demo中这些组件和工具类都通过module实现了&#xff0c;具体可以参考HarmonyOS NEXT - 通过 module 模块化引用公共组件和utils 官方介绍 组件导航 (Navigation)(推…

​【迅为电子】RK3568驱动指南|第十七篇 串口-第197章 串口通信协议

瑞芯微RK3568芯片是一款定位中高端的通用型SOC&#xff0c;采用22nm制程工艺&#xff0c;搭载一颗四核Cortex-A55处理器和Mali G52 2EE 图形处理器。RK3568 支持4K 解码和 1080P 编码&#xff0c;支持SATA/PCIE/USB3.0 外围接口。RK3568内置独立NPU&#xff0c;可用于轻量级人工…

vue3 ts 集成 supermap

文章目录 1. 依赖安装2. 模块声明3. css 全局引入4. 地图加载 1. 依赖安装 npm install supermapgis/iclient-leafletnpm install leaflet2. 模块声明 env.d.ts declare module leaflet {const L: any;export default L; }declare module supermapgis/iclient-leaflet3. css …

高数3.5 极值与最值

1. 极值 1.1. 定义 1.2. 推论 1.3. 求极值的步骤 1.3.1 方法1 1.3.1.1 例题

BCLinux8.*构建部署nmap 7.95

定义SPEC文件 首先从nmap的github上下载SPEC定义文件&#xff0c;然后进行修改&#xff0c;如下&#xff1a; # To build a static RPM, add # --define "static 1" # to the rpmbuild command line. To build without Ncat, add # --define "buildnc…

FastHTML:使用 Python 彻底改变 Web 开发

什么是 FastHTML&#xff1f;&#x1f310; FastHTML 是一个现代 Python Web 应用程序框架&#xff0c;其真正目的是让 Python 开发人员轻松进行 Web 开发。它大大减少了对 JavaScript 和 CSS 构建交互式和可扩展 Web 应用程序的依赖。FastHTML 通过使用 Python 对象来表示 HTM…

centos虚拟机IP地址频繁变化的原因及解决策略

文章目录 centos虚拟机IP地址频繁变化的原因及解决策略虚拟机IP地址变化的原因解决虚拟机IP地址变化的策略1. 使用静态IP地址Windows系统&#xff1a;Linux系统&#xff1a; 2. 配置DHCP保留3. 使用虚拟化平台的网络功能4. 检查和更新网络驱动程序5. 优化网络配置脚本6. 监控和…

BIO,NIO,AIO编程实战

写在前面 关于IO分类以及IO模型等理论知识&#xff0c;可以参考io之io分类和io模型这篇文章。本文主要来实现Java中相关IO模型实现程序。 1&#xff1a;BIO blocking io&#xff0c;是Java io中对阻塞IO模型的具体实现。 因为不管是server端还是client端&#xff0c;都需要…

JavaSE的【反射】和【动态代理】

作为JavaSE的两个个基础知识&#xff0c;【反射】和【动态代理】被广泛运用到spring、spring boot、mybatis......等等各种地方&#xff0c;等到后面的学习中反复提到这个知识点就会开始懵逼&#xff0c;而且这两个知识点其实也是紧密相连的&#xff0c;很多文章和课程里也并没…

零基础STM32单片机编程入门(三十七) MPU6050陀螺仪传感器详解及实战源码

文章目录 一.概要二.MPU6050芯片介绍1.MEMS传感器原理2.MPU6050芯片简介3.芯片引脚定义4.XYZ轴方向5.芯片内部框图6.芯片常用寄存器 三.MPU6050模块原理图及与模块接口定义使用四.STM32单片机驱动MPU6050读取加速度角速度值实验五.CubeMX工程源代码下载六.小结 一.概要 MPU605…

嵌入式面经篇六——寄存器与存储器

文章目录 前言一、寄存器与存储器1、ARM 的 31 个通用寄存器 R0~R15 中&#xff0c;程序计数器 PC 为 R15、程序链接寄存器 LR 为 R14、堆栈指针寄存器 SP 为 R13。2、寄存器掉电会丢失数据吗&#xff1f;3、NOR Flash 与 NAND Flash 的区别&#xff1f;4、SRAM、DRAM、SDRAM的…