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

C语言指针之 进阶

前言

今天来较为深入的介绍一下指针,希望大家能有所收获~

那么,先进行一些简单的基础知识复习吧。
在这里插入图片描述

字符指针

格式:char *

补充:

表达式“abcdef”的值是首字符a的地址
所以当像下面这么使用时,它的含义是:

char *pc = “abcde”

pc存储的是a的地址

printf(%s”, p)

打印出的abcde

提示:

此处可以理解为将abcde存储在一个数组里面,数组名就是首元素地址

那么由此可以引出一个比较少见的写法:


输出的是c

注意

因为字符串“abcde”是常量,不可改变,但是,p作为指针变量是可以改变的,这时候改变p指向的对象程序就会报错。

所以最后在*左边加上const,使p指向的对象不可改变

一道题:

看下面这段代码:
问:输出结果是什么

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

运行结果:

在这里插入图片描述

知识点:
当两个常量数组相同时,只会储存一份,
所以,虽然创建了两个变量,但存储的是同一个地址(同一个值)

图解:
在这里插入图片描述

指针数组

定义:存放指针的数组

用途:
可以用指针数组模拟一个二维数组

如下:

int main()
{int arr1[] = { 1,2,3,4,5 };int arr2[] = { 2,3,4,5,6 };int arr3[] = { 3,4,5,6,7 };int* arr[] = { arr1, arr2, arr3 };int i = 0;return 0;
}

数组指针

定义:指向数组的指针
格式(唯一):
存储元素的类型 (*p)[数组大小] = &arr

注意:要明确指定出数组的大小,如果不写就默认为0,程序会报错。

但数组指针的使用不常见,
因为当对数组指针进行解引用后,就是数组名,多此一举。
就算使用,也大多用于二维数组,下面举个例子

void print(int (*p)[5], int r, int c)
{int i = 0;for (i = 0; i < 3; i++){int j = 0;for (j = 0; j < 5; j++){printf("%d ", p[i][j]);}printf("\n");}
}int main()
{int arr[3][5] = { {1,2,3,4,5},{2,3,4,5,6},{3,4,5,6,7} };print(arr, 3, 5);return 0;
}

此处数组名指向的是二维数组的第一行
这里传递的 arr,也就是第一行的地址,是一维数组的地址
此处用数组指针来接收。

旧识新知

数组名指的是首元素的地址,
但有两个例外
1.sizeof(数组名)此处指的是整个数组

2.&数组名
取出的是数组的地址

那么创建一个变量存储数组的地址,他就是数组的指针,简称数组指针

但是 此处需要注意区分数组名和取地址数组名,虽然在打印时二者是一样的,但在运算时,因为 &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;
}

运行结果:
在这里插入图片描述
这样就能直观的看出二者的区别

一组辨析

大家可以思考一下下面几行的代码的含义是什么

int arr[5];
int *parr1[10];
int (*parr2)[10];
int (*parr3[10])[5];

解释:
在这里插入图片描述
在这里插入图片描述

数组传参和指针传参

接下来,我会为大家介绍三种传参情况
一维数组传参、二维数组传参、一级指针传参、二级指针传参

一维数组传参

看看下面这段代码,思考一下那种传参方式是可行的。

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

答案是:这些写法都可以。

数组传参传的是数组首元素的地址。

二维数组传参

我们先看这段代码:

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

知识点:

总结:二维数组传参,函数形参的设计只能省略第一个[]的数字。
因为对一个二维数组,可以不知道有多少行,但是必须知道一行多少元素,这样才可以运算。

一级指针传参

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

同样,二级指针传参要用同样是二级指针的形参来接收

思考;

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

有如下三种方式:
在这里插入图片描述

传进去一级指针的地址
传进去二级指针
传进去指针数组的首元素地址(也就是指针的地址)

函数指针

定义:指向函数的指针,在内存空间中存放的是函数的地址。

函数地址:&函数名
或者 直接写函数名
函数名就是函数的地址

代码示例:

#include <stdio.h>
void test()
{printf("hehe\n");
}
int main()
{printf("%p\n", test);printf("%p\n", &test);return 0;
}

程序输出的结果相同。

函数指针的存储

观察下面这段代码,看看那个才是函数指针

void test()
{printf("hehe\n");
}
//下面pfun1和pfun2哪个有能力存放test函数的地址?
void (*pfun1)();
void *pfun2();

答案:第一个
因为pfun1先与8结合,是一个指针,然后才指向这个函数。
这个函数不传参,并且返回类型为void

格式:
指针的类型 (*pf)(函数参数的类型) = 函数名

运用:

想通过函数指针来实现两个数的求和,可以怎么写呢?

代码如下;

int Add(int x, int y)
{return x + y;
}
int main()
{int (* pf2)(int, int) = &Add;//这里需要把类型强转换成整型int ret = (* pf2)(2, 3);printf("%d\n", ret);return 0;
}

所以函数调用可以写成:
pf(参数)

*个数随便写,但要用括号括起来,

(*pf)(参数)

提示:

在使用函数指针时,是不用写*的,因为pf本就是地址,想要调用函数,直接解引用就行,不用多写星号,并且此处写多少个星号都行,
写星号只是为了提醒其他人这个变量是函数指针,便于理解。

题目:

下面介绍两段有趣的代码,大家可以思考一下他们的含义是什么

1.

(*(void (*)())0)();

拆开来看:
void(*)()是一个函数指针类型,

对(void (*)())0,就是强制类型转换0,把0转换成可以接收函数地址的指针变量,

那么0地址处就有void(*)()这么一个函数,
所以这段代码的意思就是:调用0地址处的函数,这个函数没有参数,返回类型是void。

2.

void (*signal(int , void(*)(int)))(int);

这行代码我们从里往外看

图解如下:
在这里插入图片描述

可以通过使用typedef,简化成两行代码:

typedef void(*pfun_t)(int);
pfun_t signal(int, pfun_t);

结语

指针的知识暂时介绍到这里,下篇文章会继续介绍关于指针的知识,比如函数指针数组、指向函数指针数组的指针、回调函数等,我们下次见。

相关文章:

C语言指针之 进阶

前言 今天来较为深入的介绍一下指针&#xff0c;希望大家能有所收获&#xff5e; 那么&#xff0c;先进行一些简单的基础知识复习吧。 字符指针 格式&#xff1a;char * 补充&#xff1a; 表达式“abcdef”的值是首字符a的地址 所以当像下面这么使用时&#xff0c;它的含…...

C++单例模式

文章目录 1、什么是单例2、一个好的单例应该具备的条件3、懒汉模式与饿汉模式4、单例实现&#xff1a;线程安全、内存安全的懒汉式单例&#xff08;基于C11的智能指针和互斥锁&#xff09; 1、什么是单例 单例 Singleton 是设计模式的一种&#xff0c;其特点是只提供唯一一个类…...

C++ 析构函数

析构函数 析构函数于构造函数相对应&#xff0c;构造函数是对象创建的时候自动调用的&#xff0c;而析构函数就是对象在销毁的时候自动调用的 特点&#xff1a; 1&#xff09;构造函数可以有多个来构成重载&#xff0c;但析构函数只能有一个&#xff0c;不能构成重载 2&…...

CSS——字体选择

在网页设计和开发中&#xff0c;字体选择是一个非常重要的因素。字体不仅仅是文字的表现形式&#xff0c;它们还能够传达出一种特定的情感和风格。在CSS中&#xff0c;我们可以通过使用字体代码来定义网页中使用的字体。 CSS提供了一种简单而灵活的方式来设置字体。通过使用fo…...

SpringBoot自动装配及run方法原理探究

自动装配 1、pom.xml spring-boot-dependencies&#xff1a;核心依赖在父工程中&#xff01;我们在写或者引入一些SpringBoot依赖的时候&#xff0c;不需要指定版本&#xff0c;就因为有这些版本仓库 1.1 其中它主要是依赖一个父工程&#xff0c;作用是管理项目的资源过滤及…...

Mybatis实现JsonObject对象与JSON之间交互

项目中使用PostGresql数据库进行数据存储&#xff0c;表中某字段为Json类型&#xff0c;用于存储Json格式数据。PG数据库能够直接存储Json算是一大特色&#xff0c;很多特定情境下使用直接存储Json字段数据能够大量节省开发时间&#xff0c;提高后台数据查询和转换效率。 1、基…...

spring boot 集成 jetcache【基础篇:@Cached、@CreateCache、@CacheRefresh】

手打不易&#xff0c;如果转摘&#xff0c;请注明出处&#xff01; 注明原文&#xff1a;https://zhangxiaofan.blog.csdn.net/article/details/129832925 目录 前言 版本 配置通用说明 项目结构 代码 启动类 实体类 基础使用——增删改查&#xff08;Cached、CacheInv…...

个人对前后端分离的一些看法

内容简介&#xff1a;前端开发过程中能完全不依赖后端的才是真正的前后端分离指的是工作过程中&#xff0c;前端的的代码中往往会掺杂一些后端的逻辑。后端返回了一个json对象 前端开发过程中能完全不依赖后端的才是真正的前后端分离 指的是工作过程中&#xff0c;前端的的代码…...

TailWindCss 在Hbuilderx中使用

基于这个插件 weapp-tailwindcss 地址 本次说明基于HbuilderX 创建的项目非CLI 安装步骤按照文档走&#xff0c;先安装上几个依赖。然后是几个配置文件 tailwind-input.css tailwind的css文件用来引入到app.vue /* #ifdef H5 */ tailwind base; /* 如果是小程序的话&#x…...

Unity导入图片时,通过设置属性快速实现资源的压缩

是在学习tilemap绘制世界地图的时候发现的这个功能。 之前一直只是粗略的知道这部分是对应图片资源的压缩的。比如Compression是指的压缩质量&#xff0c;想要完全不压缩就设置None&#xff0c;会导致图片资源会大一些。 在我的例子工程中&#xff0c;其他图片资源的尺寸都是6…...

AlmaLinux 9 安装 Go 1.20

AlmaLinux 9 安装 Golang 1.20 1. 下载 go 安装包2. 安装 go3. 配置环境变量4. 确认 go 版本 1. 下载 go 安装包 访问 https://go.dev/dl/&#xff0c;下载你想安装的版本&#xff0c;比如 go1.20.7.linux-amd64.tar.gz&#xff0c; 2. 安装 go (可选)删除旧版本&#xff0c;…...

【Docker】数据库动态授权组件在Kubernetes集群下的测试过程记录

目录 背景 组件原理 测试设计 环境 测试脚本 脚本build为linux可执行文件 镜像构建 Dockerfile Docker build 镜像有效性验证 总结 资料获取方法 背景 我们都知道出于安全性考虑&#xff0c;生产环境的权限一般都是要做最小化控制&#xff0c;尤其是数据库的操作授…...

数据结构【第3章】——线性表

线性表的定义 线性表&#xff1a;零个或多个数据元素的有限序列。 1&#xff09;线性表是一个序列。即元素之间是有顺序的&#xff0c;若元素存在多个&#xff0c;则第一个元素无前驱&#xff0c;最后一个元素无后继&#xff0c;其他每个元素都有且只有一个前驱和后继。 2&a…...

MySql之分库分表

数据库瓶颈 不管是IO瓶颈还是CPU瓶颈&#xff0c;最终都会导致数据库的活跃连接数增加&#xff0c;进而逼近甚至达到数据库可承载的活跃连接数的阈值。在业务service来看&#xff0c; 就是可用数据库连接少甚至无连接可用&#xff0c;接下来就可以想象了&#xff08;并发量、吞…...

数据结构—图的遍历

6.3图的遍历 遍历定义&#xff1a; ​ 从已给的连通图中某一顶点出发&#xff0c;沿着一些边访问遍历图中所有的顶点&#xff0c;且使每个顶点仅被访问一次&#xff0c;就叫作图的遍历&#xff0c;它是图的基本运算。 遍历实质&#xff1a;找每个顶点的邻接点的过程。 图的…...

MySQL主从复制基于二进制日志的高可用架构指南

前言 在现代数据库架构中&#xff0c;MySQL主从复制技术扮演着重要角色。它不仅可以提升数据库性能和可扩展性&#xff0c;还赋予系统卓越的高可用性和灾难恢复能力。本文将深入剖析MySQL主从复制的内部机制&#xff0c;同时通过一个实际案例&#xff0c;展示其在实际场景中的…...

RestTemplate HTTPS请求忽略SSL证书

问题描述 使用RestTemplate发送HTTPS请求的时候&#xff0c;出现了这样的一个问题&#xff1a; sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification …...

Jenkins触发器时间、次数设定

触发器触发条件介绍 触发器触发条件公式&#xff1a;由5颗星组成 * * * * * 分别代表&#xff1a;分钟(0-59) 小时(0-23) 日期(1-31) 月份(1-12) 星期(0-6) 企业项目中常用场景介绍 场景1&#xff1a;接口脚本部分测试通过&#xff0c;部分还在进行&#xff0c;回归测试脚本执行…...

kafka partition的数据文件(offffset,MessageSize,data)

partition中的每条Message包含了以下三个属性&#xff1a; offset&#xff0c;MessageSize&#xff0c;data&#xff0c;其中offset表示Message在这个partition中的偏移量&#xff0c;offset不是该Message在partition数据文件中的实际存储位置&#xff0c;而是逻辑上一个值&…...

htnl根据轮播图图片切换背景色

htnl根据轮播图图片切换背景色 <!DOCTYPE html> <html><head><meta charset"UTF-8"><title>轮播图示例</title><link rel"stylesheet" href"https://cdn.jsdelivr.net/npm/swiper10/swiper-bundle.min.css&q…...

云计算——弹性云计算器(ECS)

弹性云服务器&#xff1a;ECS 概述 云计算重构了ICT系统&#xff0c;云计算平台厂商推出使得厂家能够主要关注应用管理而非平台管理的云平台&#xff0c;包含如下主要概念。 ECS&#xff08;Elastic Cloud Server&#xff09;&#xff1a;即弹性云服务器&#xff0c;是云计算…...

Oracle查询表空间大小

1 查询数据库中所有的表空间以及表空间所占空间的大小 SELECTtablespace_name,sum( bytes ) / 1024 / 1024 FROMdba_data_files GROUP BYtablespace_name; 2 Oracle查询表空间大小及每个表所占空间的大小 SELECTtablespace_name,file_id,file_name,round( bytes / ( 1024 …...

从深圳崛起的“机器之眼”:赴港乐动机器人的万亿赛道赶考路

进入2025年以来&#xff0c;尽管围绕人形机器人、具身智能等机器人赛道的质疑声不断&#xff0c;但全球市场热度依然高涨&#xff0c;入局者持续增加。 以国内市场为例&#xff0c;天眼查专业版数据显示&#xff0c;截至5月底&#xff0c;我国现存在业、存续状态的机器人相关企…...

ESP32读取DHT11温湿度数据

芯片&#xff1a;ESP32 环境&#xff1a;Arduino 一、安装DHT11传感器库 红框的库&#xff0c;别安装错了 二、代码 注意&#xff0c;DATA口要连接在D15上 #include "DHT.h" // 包含DHT库#define DHTPIN 15 // 定义DHT11数据引脚连接到ESP32的GPIO15 #define D…...

今日学习:Spring线程池|并发修改异常|链路丢失|登录续期|VIP过期策略|数值类缓存

文章目录 优雅版线程池ThreadPoolTaskExecutor和ThreadPoolTaskExecutor的装饰器并发修改异常并发修改异常简介实现机制设计原因及意义 使用线程池造成的链路丢失问题线程池导致的链路丢失问题发生原因 常见解决方法更好的解决方法设计精妙之处 登录续期登录续期常见实现方式特…...

2023赣州旅游投资集团

单选题 1.“不登高山&#xff0c;不知天之高也&#xff1b;不临深溪&#xff0c;不知地之厚也。”这句话说明_____。 A、人的意识具有创造性 B、人的认识是独立于实践之外的 C、实践在认识过程中具有决定作用 D、人的一切知识都是从直接经验中获得的 参考答案: C 本题解…...

LangChain知识库管理后端接口:数据库操作详解—— 构建本地知识库系统的基础《二》

这段 Python 代码是一个完整的 知识库数据库操作模块&#xff0c;用于对本地知识库系统中的知识库进行增删改查&#xff08;CRUD&#xff09;操作。它基于 SQLAlchemy ORM 框架 和一个自定义的装饰器 with_session 实现数据库会话管理。 &#x1f4d8; 一、整体功能概述 该模块…...

恶补电源:1.电桥

一、元器件的选择 搜索并选择电桥&#xff0c;再multisim中选择FWB&#xff0c;就有各种型号的电桥: 电桥是用来干嘛的呢&#xff1f; 它是一个由四个二极管搭成的“桥梁”形状的电路&#xff0c;用来把交流电&#xff08;AC&#xff09;变成直流电&#xff08;DC&#xff09;。…...

【实施指南】Android客户端HTTPS双向认证实施指南

&#x1f510; 一、所需准备材料 证书文件&#xff08;6类核心文件&#xff09; 类型 格式 作用 Android端要求 CA根证书 .crt/.pem 验证服务器/客户端证书合法性 需预置到Android信任库 服务器证书 .crt 服务器身份证明 客户端需持有以验证服务器 客户端证书 .crt 客户端身份…...

麒麟系统使用-进行.NET开发

文章目录 前言一、搭建dotnet环境1.获取相关资源2.配置dotnet 二、使用dotnet三、其他说明总结 前言 麒麟系统的内核是基于linux的&#xff0c;如果需要进行.NET开发&#xff0c;则需要安装特定的应用。由于NET Framework 是仅适用于 Windows 版本的 .NET&#xff0c;所以要进…...