[C]基础11.深入理解指针(3)

  • 博客主页:向不悔
  • 本篇专栏:[C]
  • 您的支持,是我的创作动力。

文章目录

  • 0、总结
  • 1、字符指针变量
  • 2、数组指针变量
    • 2.1 数组指针变量是什么?
    • 2.2 数组指针变量怎么初始化?
  • 3、二维数组传参的本质
  • 4、函数指针变量
    • 4.1 函数指针变量的创建
    • 4.2 函数指针变量的使用
    • 4.3 两端有趣的代码
      • 4.3.1 typedef关键字
  • 5、函数指针数组
  • 6、转移表


0、总结

在这里插入图片描述

1、字符指针变量

思考字符数组和常量字符串的区别?经思考,发现,字符数组里的数组是可变的,常量字符串是不可变的。

#include <stdio.h>
int main()
{char arr[] = "abcdef";char* p1 = arr;*p1 = 'w';const char* p2 = "abcdef";// *p2 = 'w';printf("%s\n", p1);printf("%s\n", p2);return 0;
}
运行:
wbcdef
abcdef

如图加深理解:

我们知道,字符指针变量一般使用如下:

#include <stdio.h>
int main()
{char ch = 'w';char* pc = &ch;*pc = 'z';printf("%c\n", *pc);return 0;
}

那么,为什么这个也可以呢?

#include <stdio.h>
int main()
{const char* pstr = "hello world.";printf("%s\n", pstr);return 0;
}

经思考,代码const char* pstr = "hello world.";本质上是把字符串首字符的地址放到了pstr中。如图:

《剑指Offer》中收录了一道和字符串相关的题,如下:

#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;
}
运行:
str1 and str2 are not same
str3 and str4 are same

在C/C++中,常量字符串会被存储到一个共享的内存区域。当多个指针指向相同的常量字符串时,它们实际上指向同一块内存。而如果用相同的常量字符串初始化不同的数组,则会为每个数组开辟独立的内存块。因此,str1str2指向不同的内存,而str3str4指向同一块内存。

2、数组指针变量

2.1 数组指针变量是什么?

数组指针变量是指针变量,存放的是数组的地址,指向数组的指针变量。

思考以下,哪个是数组指针变量?

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

结论:

  • p1是指针数组,具体的说,一个包含10个指针的数组,每个指针指向一个int
  • p2是数组指针,具体地说,指向一个包含10个int的数组。
  • 主要区别在于括号的使用,括号的位置决定了声明的优先级。p1是数组优先,p2是指针优先。
  • 值得注意:[]的优先级要高于*号的,所以声明数组指针,就必须加上()来保证p2先和*结合。

2.2 数组指针变量怎么初始化?

数组指针变量是用来存放数组地址的,因此用&数组名。如下:

#include <stdio.h>
int main()
{char arr[10] = { 0 };char* p1 = arr;printf("%p\n", p1);printf("%p\n", p1+1);// 初始化数组指针变量char (*p2)[10] = &arr;printf("%p\n", p2);printf("%p\n", p2+1);return 0;
}
运行(32位平台):
00B5F7A8
00B5F7A9
00B5F7A8
00B5F7B2

经调试,可知,&arrp2的类型是完全一致的。

数组指针类型解析:

int      (*p)        [10]    = &arr;|         |           ||         |           ||         |           p指向数组的元素个数|         p是数组指针变量名 p指向的数组的元素类型

3、二维数组传参的本质

二维数组的数组名表示的就是第一行的地址,是一维数组的地址。所以第一行的地址的类型是数组指针类型。二维数组传参本质上也是传递了地址,传递的是第一行这个一维数组的地址。形参可以写成指针形式的,如下:

#include <stdio.h>
void print(int (*ptr)[5], int r, int c)
{for (int i = 0; i < r; i++){for (int j = 0; j < c; j++){printf("%d ", *(*(ptr + 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;
}
运行:
1 2 3 4 5
2 3 4 5 6
3 4 5 6 7

总结:

二维数组传参,形参部分可以写成数组,也可以写成指针形式。

4、函数指针变量

4.1 函数指针变量的创建

函数指针变量用来存放函数地址的,未来通过地址能够调用函数。如下:

#include <stdio.h>
void test()
{printf("hello\n");
}
int main()
{printf("test:   %p\n", test);printf("&test:  %p\n", &test);return 0;
}
运行:
test:   008513CA
&test:  008513CA

可知,函数是有地址的,有两个方式:

  • 函数名就是函数的地址
  • &函数名获得函数的地址。

如果要把函数的地址存放起来,就需要创建函数指针变量,函数指针变量的写法跟数组指针写法非常类似,如下:

#include <stdio.h>
void test()
{printf("hello\n");
}
void (*pf1)() = &test;
void (*pf2)() = test;int Add(int x, int y)
{return x + y;
}
int (*pf3)(int, int) = &Add;
int (*pf4)(int, int) = Add;  // x和y写上或者省略都是可以的。

函数指针类型解析:

int      (*pf3)        (int x, int y) |         |            ------------|         |                  ||         |                  pf3指向函数的参数类型和个数的交代|         函数指针变量名pf3指向函数的返回类型int (*) (int x, int y) // pf3函数指针变量的类型

4.2 函数指针变量的使用

#include <stdio.h>int Add(int x, int y)
{return x + y;
}int main()
{int (*pf)(int, int) = Add;printf("%d\n", (*pf)(2, 3));printf("%d\n", pf(3, 5));return 0;
}
运行:
5
8

4.3 两端有趣的代码

代码1:

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

代码2:

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

这两段代码均出自:《C陷阱和缺陷》这本书。

4.3.1 typedef关键字

typedef是用来类型重命名的,可以将复杂的类型进行简单化。

例如,可以对unsigned int进行简化,如下:

typedef unsigned int uint;

如果对指针类型呢?如下:

// 将int * 重命名为 ptr_t
typedef int* ptr_t;

但是对于数组指针和函数指针稍微有点区别:

比如说,数组指针类型int(*)[5],需要重命名为parr_t,那可以这样写:

typedef int(*parr_t)[5];

函数指针类型的重命名也是一样的,比如将void(*)(int)类型重命名为pf_t,就可以这样写:

typedef void(*pf_t)(int);

因此,如果对void (*signal(int, void(*)(int)))(int);进行简化,可以这样写:

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

5、函数指针数组

把函数的地址存放到一个数组中,那么这个数组就叫函数指针数组,以下哪个是函数指针数组?

int (*parr1[3])();
int *parr2[3]();
int (*)() parr3[3];

答案是:parr1

parr1先和[]结合,说明parr1是数组,那么数组内容是什么呢?

int (*)()类型的函数指针。

6、转移表

函数指针数组的用途一般为转移表

举个例子,计算机通过函数指针数组的实现:

#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
int add(int a, int b)
{return a + b;
}
int sub(int a, int b)
{return a - b;
}
int mul(int a, int b)
{return a * b;
}
int div(int a, int b)
{return a / b;
}int main()
{int x, y;int input = 1;int ret = 0;int (*p[5])(int x, int y) = { 0, add, sub, mul, div };do {printf("**************\n");printf("1:add    2:sub\n");printf("3:mul    4:div\n");printf("0:exit        \n");printf("**************\n");printf("请选择:");scanf("%d", &input);if ((input <= 4 && input >= 1)){printf("输入操作符:");scanf("%d %d", &x, &y);ret = (*p[input])(x, y);printf("ret = %d\n", ret);}else if (input == 0)printf("退出计算机\n");else printf("输入有误\n");} while (input);return 0;
}

完。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/77771.shtml

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

相关文章

【漏洞复现】CVE-2024-38856(ApacheOfbiz RCE)

【漏洞复现】CVE-2024-38856&#xff08;ApacheOfbiz RCE&#xff09; 1. 漏洞描述 Apache OFBiz 是一个开源的企业资源规划&#xff08;ERP&#xff09;系统。它提供了一套企业应用程序&#xff0c;用于集成和自动化企业的许多业务流程。 这个漏洞是由于对 CVE-2023-51467 的…

C++入门小馆: 深入string类(二)

嘿&#xff0c;各位技术潮人&#xff01;好久不见甚是想念。生活就像一场奇妙冒险&#xff0c;而编程就是那把超酷的万能钥匙。此刻&#xff0c;阳光洒在键盘上&#xff0c;灵感在指尖跳跃&#xff0c;让我们抛开一切束缚&#xff0c;给平淡日子加点料&#xff0c;注入满满的pa…

【nginx】服务的信号控制

目录 1. 说明2. 常用信号及作用3. 信号控制的具体操作3.1 获取 Nginx 主进程 PID3.2 发送信号 4. 应用场景4.1 重新加载配置文件4.2 日志切割 5. 平滑升级 Nginx6. 注意事项 1. 说明 1.Nginx 的信号控制是其管理服务的重要机制&#xff0c;通过向主进程发送特定信号&#xff0…

Ubuntu下展锐刷机工具spd_dump使用说明

spd_dump使用说明 源码地址&#xff1a;https://github.com/ilyakurdyukov/spreadtrum_flash 编译环境准备&#xff1a; sudo apt update sudo apt install git sudo apt install build-essential sudo apt install libusb-1.0-0-devIf you create /etc/udev/rules.d/80-spd…

鸿蒙NEXT开发LRUCache缓存工具类(单例模式)(ArkTs)

import { util } from kit.ArkTS;/*** LRUCache缓存工具类&#xff08;单例模式&#xff09;* author 鸿蒙布道师* since 2025/04/21*/ export class LRUCacheUtil {private static instance: LRUCacheUtil;private lruCache: util.LRUCache<string, any>;/*** 私有构造函…

笔记:react中 父组件怎么获取子组件中的属性或方法

在子组件中我们可以使用下面两个方法去暴露你所要放行的属性或方法&#x1f447; 1.useImperativeHandle 2.orwardRef 搭配使用例子 import React, { useState, forwardRef, useImperativeHandle } from "react"function Son(props, ref) {const [data] useStat…

《浔川代码编辑器v2.0内测(完整)报告》

一、测试概述 浔川代码编辑器v2.0经过为期五周的封闭内测&#xff0c;累计提交了186份测试报告。本次内测主要针对v2.0新增的多语言支持、AI辅助编码、性能优化等核心功能进行全面验证。 二、测试环境 - 硬件配置&#xff1a;i7-12700H/16GB RAM/512GB SSD - 操作系统&#xf…

ubuntu18.04安装QT问题汇总

1、Could not determine which ”make“ command to run. Check the ”make“ step in the build configuration.” sudo apt-get install clang sudo apt-get install build-essential sudo apt-get install libqt4-dev 2、fatal error: sqlite3.h: No such …

基于单片机的BMS热管理功能设计

标题:基于单片机的BMS热管理功能设计 内容:1.摘要 摘要&#xff1a;在电动汽车和储能系统中&#xff0c;电池管理系统&#xff08;BMS&#xff09;的热管理功能至关重要&#xff0c;它直接影响电池的性能、寿命和安全性。本文的目的是设计一种基于单片机的BMS热管理功能。采用…

CSS基础-即学即用 -- 笔记1

目录 前言CSS 基础1. 层叠样式表来源理解优先级源码顺序经验法则继承inherit 关键字initial 关键字 2. 相对单位em 和 rem响应式面板视口的相对单位使用vw定义字号使用calc()定义字号自定义属性&#xff08;即CSS变量&#xff09; 3. 盒模型调整盒模型 前言 只需一分钟就能学会…

Linux中服务器时间同步

简单介绍 在 redhat 8 之前&#xff0c;时间同步服务是使用 NTP&#xff08;网络时间协议&#xff09;来实现的&#xff0c;在 redhat 8 及之 后使用是 NTP 的实现工具 chrony 来实现时间同步。 在 redhat 8 及之后&#xff0c;默认情况下已经安装好 chrony 软件并已经开机启…

让SQL飞起来:搭建企业AI应用的SQL性能优化实战

我上一篇文章已经讲解过了如何使用公开的AI模型来优化SQL.但这个优化方法存在一定的局限性.因为公开的AI模型并不了解你的数据表结构是什么从而导致提供的优化建议不太准确.而sql表结构又是至关重要的安全问题,是不能泄露出去的.所以在此背景下我决定搭建一个自己的AI应用在内网…

小迪安全-112-yii反序列化链,某达oa,某商场,影响分析

yii是和tp一样的框架 入口文件 web目录下 相对tp比较简单一些&#xff0c;对比tp找一下他的url结构 对应的位置结构 这个contorllers文件的actionindex就是触发的方法 控制器&#xff0c;指向的index文件&#xff0c;就可以去视图模块看index文件 这就是前端展示的文件 自…

自定义多头注意力模型:从代码实现到训练优化

引言 在自然语言处理和序列生成任务中,自注意力机制(Self-Attention)是提升模型性能的关键技术。本文将通过一个自定义的PyTorch模型实现,展示如何构建一个结合多头注意力与前馈网络的序列生成模型(如文本或字符生成)。该模型通过创新的 MaxStateSuper 模块实现动态特征…

动态LOD策略细节层级控制:根据视角距离动态简化远距量子态渲染

动态LOD策略在量子计算可视化中的优化实现 1. 细节层级控制:动态简化远距量子态渲染 在量子计算的可视化中,量子态通常表现为高维数据(如布洛赫球面或多量子比特纠缠态)。动态LOD(Level of Detail)策略通过以下方式优化渲染性能: 距离驱动的几何简化: 远距离渲染:当…

Java 泛型使用教程

简介 Java 泛型是 JDK 5 引入的一项特性&#xff0c;它提供了编译时类型安全检测机制&#xff0c;允许在编译时检测出非法的类型。泛型的本质是参数化类型&#xff0c;也就是说所操作的数据类型被指定为一个参数。 泛型的好处&#xff1a; 编译期检查类型安全 避免强制类型转…

Leetcode - 周赛446

目录 一、3522. 执行指令后的得分二、3523. 非递减数组的最大长度三、3524. 求出数组的 X 值 I四、3525. 求出数组的 X 值 II 一、3522. 执行指令后的得分 题目链接 本题就是一道模拟题&#xff0c;代码如下&#xff1a; class Solution {public long calculateScore(String…

【更新完毕】2025泰迪杯数据挖掘竞赛A题数学建模思路代码文章教学:竞赛论文初步筛选系统

完整内容请看文末最后的推广群 基于自然语言处理的竞赛论文初步筛选系统 基于多模态分析的竞赛论文自动筛选与重复检测模型 摘要 随着大学生竞赛规模的不断扩大&#xff0c;参赛论文的数量激增&#xff0c;传统的人工筛选方法面临着工作量大、效率低且容易出错的问题。因此&…

计算机视觉与深度学习 | RNN原理,公式,代码,应用

RNN(循环神经网络)详解 一、原理 RNN(Recurrent Neural Network)是一种处理序列数据的神经网络,其核心思想是通过循环连接(隐藏状态)捕捉序列中的时序信息。每个时间步的隐藏状态 ( h_t ) 不仅依赖当前输入 ( x_t ),还依赖前一时间步的隐藏状态 ( h_{t-1} ),从而实现…

AI速读:解锁LLM下Game Agent的奇妙世界

在 AI 浪潮中&#xff0c;大语言模型&#xff08;LLMs&#xff09;正重塑游戏智能体格局。想知道基于 LLMs 的游戏智能体如何运作&#xff0c;在各类游戏中有何惊艳表现&#xff0c;未来又将走向何方&#xff1f; 大型语言模型&#xff08;LLMs&#xff09;的兴起为游戏智能体的…