【重生之我要苦学C语言】深入理解指针4

深入理解指针4

字符指针变量

指针指向字符变量

char ch = 'w';
char* p = &ch;

指针指向字符数组

char arr[10] = "abcdef";
char* p = arr;
printf("%s\n", arr);
printf("%s\n", p);

结果是一样的

也可以写成:

char* p = "abcdef";//常量字符串
//将字符串首字符a的地址赋值给p

字符数组可以存放字符串,字符数组的内容可以修改
常量字符串和数组是非常相似的,也是在一个连续的空间中存放了多个字符,但是常量字符串的内容不能修改

*p='w';//错误的,常量字符串不能被修改

因此可以写为:

const char* p = "abcdef";

看一道题:

int main(){char strl[] = "hello bit.";char str2[] = "hello bit.";const char* str3 = "hello bit.";const char* str4 = "hello bit.";if (str1 == str2)printf("strl and str2 are same\n");//1elseprintf("strl and str2 are not same\n"); //2if (str3 == str4)printf("str3 and str4 are same\n"); //3elseprintf("str3 and str4 are not same\n");//4return 0;
}

问:打印结果
答案为:2 3

str3和str4完全相同,为了节省内存,str4并不会被创建

在这里插入图片描述

str1,str2为数组名,是数组首元素的地址,两个数组创建的是不同的空间,首元素地址不同,故str1!=str2
str3,str4比较的是两个指针变量中存放的地址,所以str3==str4

数组指针变量

存放的是数组的地址——指向数组的指针

&arr——数组的地址

int arr[5] = { 0 };
int(*p)[5] = &arr;
//p为数组指针变量
//int: p指向的数组的元素类型
char arr[8];
char(*pc)[8] = &arr;
char* arr[8];
char*(*pc)[8] = &arr;//
//pc数组指针变量

int arr[6] = { 1,2,3,4,5,6 };
int*(*p)[6] = &arr;

为例:
因为:

  *p = *&arr = arr;

所以求数组长度:

printf("%zd\n", sizeof(arr));

或者:

printf("%zd\n", sizeof(*p));

输出数组:

for (i = 0;i < 6;i++) {printf("%d ", arr[i]);
}

或者:

for (i = 0;i < 6;i++) {printf("%d ", (*p)[i]);
}

但最简单的方法还是取出数组首元素的地址:

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main() {int arr[6] = { 1,2,3,4,5,6 };int* p = arr;int i = 0;for (i = 0;i < 6;i++) {printf("%d ", p[i]);}return 0;
}

二维数组传参本质

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
void Print(int arr[3][5], int r, int c) {//形参写成数组形式int i = 0, j = 0;for (i = 0;i < r;i++) {for (j = 0;j < c;j++) {printf("%d ", arr[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} };//  1  2  3  4  5//  2  3  4  5  6//  3  4  5  6  7//写一个函数打印arr数组Print(arr, 3, 5);//实参传数组名return 0;
}

二维数组每一行都是一个一维数组,所以二维数组可以理解为一维数组的数组,也就是说二维数组的每个元素其实是个一维数组

Print(arr, 3, 5);

arr是二维数组的数组名,数组名表示数组首元素的地址(就是二维数组第一行的地址)

根据前面学过的一维数组的传参本质,可知,二维数组的形参可以写为:

void Print(int (*arr)[5], int r, int c) {
 p[i][j]=*(*(p+i)+j)*(p+i)——>第i+1行数组名

函数指针变量

printf("%p\n", &Add);//函数的地址
printf("%p\n", Add);//函数名也是函数的地址
int (*pf)(int x, int y ) = &Add;
int (*pf)(int x, int y ) = Add;
//pf就是函数指针变量
//形参的名字不会被使用,也可以省略
int (*pf)(int, int) = &Add;
int (*pf)(int, int) = Add;
//pf=Add
int r=(*pf)(a, b);
int r = Add(a, b);
int r=pf(a, b);
//一样

指针可以指向任何内存中的对象,变量、数组、函数

辨析两个语句:

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

可以分开来看:

void(*)()函数指针类型
(void(*)())0强制类型转换,0变成了地址,0的位置是一个函数
*(void(*)())0解引用
(*(void(*)())0)()函数调用
  • 将0这个整数强制转换成函数指针类型
  • 调用0地址处的函数
2
void(*signal(int, void(*)(int)))(int);
signal函数名
(int, void(*)(int))函数的两个参数
void(*)(int)指针类型
void(*……)(int);函数指针类型——>函数返回类型是一个函数指针类型,signal(int, void(*)(int))为函数名(参数,参数)

上面的代码是一个函数的声明
函数的名字是signal
signal函数的参数有两个,第一个是int类型,第二个是函数指针类型void(*)(int),这个函数指针指向的函数有一个int参数,返回类型是void
signal函数的返回值类型也是一个函数指针类型,这个函数指针指向的函数有一个int参数,返回类型是void

typedef关键字

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

typedef unsigned int uint;//将unsigned int重命名为uint

指针类型重命名

typedef int* ptr_t;
//指针类型int*重命名为 ptr_t:
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
typedef int* ptr_t;
int main() {int* p1, p2;ptr_t p3, p4;return 0;
}

p2不是int*类型

在这里插入图片描述
应变为:

int* p1,* p2;

数组指针类型重命名

typedef int(*parr_t)[5]; //新的类型名必须在*的右边
//数组指针类型int(*)[5],需要重命名为parr_t

函数指针类型的重命名

 typedef void(*pf_t)(int);//新的类型名必须在*的右边//将 void(*)(int)类型重命名为 pf_t

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

将这段代码简化:

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

函数指针数组

 类比:指针数组char* arr1[5];//字符指针数组int* arr2[6];//整形指针数组

函数指针数组是数组,里面存放的都是函数的地址

int Add(int x, int y) {return x + y;
}
int Sub(int x, int y) { return x - y; 
}
int main() {int (*pf1)(int, int) = Add;int (*pf2)(int, int) = Sub;//参数和返回类型一样int (*parr[4])(int ,int) = {Add,Sub};

在这里插入图片描述

调用:

int r = parr[0](100, 200);//300
int r = parr[1](100, 200);//-100

转移表

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
void menu() {printf("**********************\n");printf("*** 1.add    2.sub ***\n");printf("*** 3.mul    4.div ***\n");printf("***     0.exit     ***\n");printf("**********************\n");
}
int Add(int x, int y) {return x + y;
}
int Sub(int x, int y) { return x - y; 
}
int Mul(int x, int y) { return x * y; 
}
int Div(int x, int y) { return x / y; 
}
//写一个简易的计算器
//1.实现加减乘除
//2.不需要退出程序可以继续计算
int main() {int input = 0;do{menu();printf("请选择");scanf("%d",&input);int a = 0, b = 0, r = 0;switch (input) {case 1:printf("请输入两个操作数");scanf("%d%d", &a, &b);r = Add(a, b);printf("%d\n", r);break;case 2:printf("请输入两个操作数");scanf("%d%d", &a, &b);r = Sub(a, b);printf("%d\n", r);break;case 3:printf("请输入两个操作数");scanf("%d%d", &a, &b);r = Mul(a, b);printf("%d\n", r);break;case 4:printf("请输入两个操作数");scanf("%d%d", &a, &b);r = Div(a, b);printf("%d\n", r);break;case 0:printf("退出计算器");break;default:printf("选择错误,重新选择");break;}} while (input);return 0;
}

如果想增加计算器的功能,那么代码就会越来愈长,越来越冗余
这时就可以使用函数指针数组

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
void menu() {printf("**********************\n");printf("*** 1.add    2.sub ***\n");printf("*** 3.mul    4.div ***\n");printf("***     0.exit     ***\n");printf("**********************\n");
}
int Add(int x, int y) {return x + y;
}
int Sub(int x, int y) { return x - y; 
}
int Mul(int x, int y) { return x * y; 
}
int Div(int x, int y) { return x / y; 
}
int main() {int input = 0;//转移表int (*pfArr[])(int, int) = { NULL,Add,Sub,Mul,Div };//整型双目运算//指针类型和指针指向参数的返回类型要一样do{menu();printf("请选择");scanf("%d",&input);int a = 0, b = 0, r = 0;if (input == 0) {printf("退出计算器");}else if (input >= 1 && input <= 4) {printf("请输入两个操作数");scanf("%d%d", &a, &b);int r=pfArr[input](a, b);printf("%d\n", r);}else printf("选择错误,重新选择");} while (input);return 0;
}

END……

午后读一本书。晚上在杏花树下
喝酒,聊天,直到月色和露水清凉。
在梦中,行至岩凤尾蕨茂盛的空空山谷,
鸟声清脆,一起在树下疲累而眠。
醒来时,我尚年少,你未老。
——庆山

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

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

相关文章

Java | Leetcode Java题解之第525题连续数组

题目&#xff1a; 题解&#xff1a; class Solution {public int findMaxLength(int[] nums) {int maxLength 0;Map<Integer, Integer> map new HashMap<Integer, Integer>();int counter 0;map.put(counter, -1);int n nums.length;for (int i 0; i < n;…

Docker部署学习

目录 前言 一、实验环境准备 二、Docker常见命令 三、Docker数据卷 四、Docker自定义镜像 五、Docker网络相关 六、Docker项目部署实践 七、Docker知识追问强化 前言 1. Docker是用以项目上线部署的工具 2. Docker并不会很难&#xff0c;只要你跟着将所有的命令敲一遍…

二叉树和堆

目录 1.二叉树的概念及结构 1.1概念 1.2特殊的二叉树 1.3二叉树的性质 1.4二叉树的存储结构 2.二叉树的顺序结构及实现&#xff08;堆&#xff09; 2.1二叉树的顺序结构 2.2堆的概念及结构 2.3堆的实现 2.3.1堆的插入 2.3.2堆的删除 2.3.3 Heap.h 2.3.4 Heap.c 2.…

linux驱动-输入子系统框架分析

接下来&#xff0c;来分析三个结构体 打开这个文件drivers\input\evdev.c 第一步&#xff1a;要找到它的入口函数evdev_init 看到了&#xff0c;入口的位置注册了一个 input_handler&#xff0c;并且对里面的值完成赋值&#xff0c;和之前学习&#xff0c;我自己注册platform驱…

【sqlmap使用】

sqlmap简介 sqlmap 目录结构 sqlmap常用参数 sqlmap实现注入 测试注入点&#xff0c;检测到注入点后&#xff0c;直接爆数据库名 python sqlmap.py –u http://172.16.12.2/7/9/strsql.php --data "usernameadmin" --dbs注意sqlmap在使用过程中可能会出现几个需要…

Redis为什么用跳表实现有序集合

Redis为什么用跳表实现有序集合 手写一个跳表 为了更好的回答上述问题以及更好的理解和掌握跳表&#xff0c;这里可以通过手写一个简单的跳表的形式来帮助读者理解跳表这个数据结构。 我们都知道有序链表在添加、查询、删除的平均时间复杂都都是 O(n) 即线性增长&#xff0c…

微服务核心——网关路由

目录 前言 一、登录存在的问题归纳 二、*微服务网关整体方案 三、认识微服务网关 四、网关鉴权实现 五、OpenFeign微服务间用户标识信息传递实现 六、微服务网关知识追问巩固 前言 本篇文章具体讲解微服务中网关的实现逻辑、用于解决什么样的问题。其中标题中标注* 涉…

消息中间件类型介绍

ActiveMQ&#xff1a; ActiveMQ可是个老将了&#xff0c;它功能全面、稳定可靠&#xff0c;还支持多种协议和编程语言。如果你需要一个兼容性好、易于集成的消息中间件&#xff0c;ActiveMQ可是个不错的选择。 RabbitMQ&#xff1a; RabbitMQ以其简单易用和高性能著称。它支持丰…

5G在汽车零部件行业的应用

5G技术在汽车零部件行业的应用正在不断深入&#xff0c;为行业的智能化、自动化和高效化转型提供了强大的技术支持。 1、5G技术特点与优势 5G技术具有高速度、低延迟、大连接和切片技术等特点与优势。这些特性为汽车零部件行业提供了稳定、可靠、高效的通信连接&#xff0c;使…

MySQL【二】

查询列 SELECT [ALL | DISTINCT ] * | 列名1[,……列名n] FROM 表名; 查询所有选课学生的学号&#xff0c;结果去除重复值 select distinct sno from sc; 选择行 查询满足条件的数据集 SELECT 字段列表 FROM 表名 WHERE 查询条件 查询不属于数学系或外国语系的学生全部信息 …

【LLM论文日更】LongReward:利用人工智能反馈改进长上下文大语言模型

论文&#xff1a;https://arxiv.org/pdf/2410.21252代码&#xff1a;https://github.com/THUDM/LongReward机构&#xff1a;清华大学 & 中科院 & 智谱领域&#xff1a;长上下文LLM发表&#xff1a;arxiv 研究背景 研究问题&#xff1a;这篇文章要解决的问题是如何在长…

Windows Terminal终端美化

Windows Terminal 1. 下载&#xff1a; 终端&#xff1a; 直接在微软的store中搜索 windows terminal &#xff0c;直接获取即可 美化用到的字体&#xff1a;https://www.nerdfonts.com/font-downloads 这里的随便一个都可以&#xff0c;下载解压后&#xff0c;选中所有ttf文…

Go语言基础语法

一、创建工程 说明&#xff1a; &#xff08;1&#xff09;go.mod文件是go项目依赖管理文件&#xff0c;相当于前端的package.json&#xff0c;也就是Java项目中的Maven的pom.xml。 二、打印数据到控制台 &#xff08;1&#xff09;引入fmt &#xff08;2&#xff09;使用fmt…

【数据结构】二叉树——层序遍历

层序遍历 一、层序遍历二、层序遍历&#xff08;递归&#xff09;三、层序遍历&#xff08;非递归&#xff09;四、总结 一、层序遍历 层序遍历是一种广度优先遍历 以图上二叉树为例&#xff0c;层序遍历就是按照二叉树的深度一层一层进行遍历 遍历顺序&#xff1a; A B C D …

使用DJL和PaddlePaddle的口罩检测详细指南

使用DJL和PaddlePaddle的口罩检测详细指南 完整代码 该项目利用DJL和PaddlePaddle的预训练模型&#xff0c;构建了一个口罩检测应用程序。该应用能够在图片中检测人脸&#xff0c;并将每张人脸分类为“戴口罩”或“未戴口罩”。我们将深入分析代码的每个部分&#xff0c;以便…

Pandas 数据清洗

1.数据清洗定义 数据清洗是对一些没有用的数据进行处理的过程。很多数据集存在数据缺失、数据格式错误、错误数据或重复数据的情况&#xff0c;如果要使数据分析更加准确&#xff0c;就需要对这些没有用的数据进行处理。 2.清洗空值 DataFrame.dropna(axis0, howany, threshN…

【GL08】STM32--ADC/DAC

一、ADC简介 ADC 即模拟信号到数字信号的转换&#xff0c;即用数字信号展现模拟的世界&#xff0c;所有的计算机或者数字处理器只能接受以 0 和 1 两种状态的数字信号&#xff0c;而对于模拟信号&#xff0c;则无法识别&#xff0c;而需要经过模拟数字转换器来感受模拟的世界。…

Blender进阶:着色器节点

11 着色器节点 11.1着色器 着色器Shader&#xff0c;负责给物体表面着色。 综合以下参数&#xff1a; -基础色-金属度、粗超度、透明度-法向-入射光颜色、强度、角度。。 着色器本质上是一段程序、算法&#xff0c;即着色器编程。 在节点编辑器中&#xff0c;支持算法的可…

SQLark百灵连接——整合项目监控过程

关键词&#xff1a;SQL编写、数据查询、数据导入、达梦数据库、项目管理、信息透明 项目监控背景 作为新手项目经理的我&#xff0c;经常觉得哪儿哪儿都是问题&#xff0c;今天催这个&#xff0c;明天推那个&#xff0c;可就是什么事都推不动&#xff0c;谁都不配合。后来&…

ELK配置转存redis缓存,采集nginx访问日志

在136服务器上部署mysql 启动mysql服务 可通过以下命令查找安装的软件包 怎么查找安装软件的日志文件位置rpm -qc mysql-server&#xff0c;即可显示mysql.log位置 也可通过查找配置文件中的log关键字来查找log文件日志位置 用awk命令&#xff0c;以切割&#xff0c;输出第二个…