【C语言】指针剖析(完结)

news2024/7/4 22:28:36

©作者:末央&

©系列:C语言初阶(适合小白入门)
©说明:以凡人之笔墨,书写未来之大梦

在这里插入图片描述

目录

  • 回调函数
    • 概念
    • 回调函数的使用 - qsort函数
  • sizeof/strlen深度理解
    • 概念
    • 手脑并用
      • 1.sizeof-数组/指针专题
      • 2.strlen-数组/指针专题
    • 指针面试题专题

回调函数

概念

回调函数就是一个通过调用函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。

例如:

#include<stdio.h>
void test1()
{
	printf("hello\n");
}
void test2(void(*p)())
{
	p(); //指针p被用来调用其所指向的函数
}
int main()
{
	test2(test1);//将test1函数的地址传递给test2
	return 0;
}

在该代码中test1函数不是由该函数的实现方直接调用,而是将其地址传递给test2函数,在test2函数中通过函数指针间接调用了test1函数,那么函数test1就被称为回调函数。

回调函数的使用 - qsort函数

其实回调函数并不是很难见到,在用于快速排序的库函数qsort中便运用了回调函数。

void qsort(void*base,size_t num,size_t width,int(*compare)(const void*e1,const void*e2));

qsort函数的第一个参数是待排序的内容的起始位置;第二个参数是从起始位置开始,待排序的元素个数;第三个参数是待排序的每个元素的大小,单位是字节;第四个参数是一个函数指针。qsort函数的返回类型为void。
qsort函数的第四个参数是一个函数指针,该函数指针指向的函数的两个参数的参数类型均为const void*,返回类型为int。当参数e1小于参数e2时返回小于0的数;当参数e1大于参数e2时返回大于0的数;当参数e1等于参数e2时返回0。

列如,我们要排一个整型数组:

#include<stdio.h>
int compare(const void* e1, const void* e2)
{
	return *((int*)e1) - *((int*)e2);
}//自定义的比较函数
int main()
{
	int arr[] = { 2, 5, 1, 8, 6, 10, 9, 3, 5, 4 };
	int sz = sizeof(arr) / sizeof(arr[0]);//元素个数
	qsort(arr, sz, 4, compare);//用qsort函数将arr数组排序
	return 0;
}

注意:qsort函数默认将待排序的内容排为升序,如果我们要排为降序可将自定义的比较函数的两个形参的位置互换一下即可。(本来是a>b交换(升序),结果b>a交换(降序))

在qsort函数中我们传入了一个函数指针,最终qsort函数会在其内部通过该函数指针调用该函数,那么我们的这个自定义比较函数就被称为回调函数。

sizeof/strlen深度理解

概念

在这里插入图片描述

手脑并用

1.sizeof-数组/指针专题

//x86环境
int a[] = {1,2,3,4};
printf("%d\n",sizeof(a+0));	//1
printf("%d\n", sizeof(*&a));//2
char arr[] = "abcdef";
printf("%d\n", sizeof(*arr));//3
int a[3][4] = { 0 };
printf("%d\n", sizeof(a[0] + 1));//4
printf("%d\n", sizeof(a + 1));//5
printf("%d\n", sizeof(*(a + 1)));//6
printf("%d\n", sizeof(a[3]));//7

1.这里不同于我们常见的sizeof(a)我们在不是单独一个数组名a在sizeof里面,所以我们计算的不是整个数组的大小。这里a数组名是首元素地址,指针就是地址地址就是指针,地址+0不变,指针大小在x86环境下4,x64环境下为8。所以答案是4

2.这里有两种角度来解释
角度一:
*和&抵消了(上一章提到过),就是sizeof(a)计算整个数组的大小为16
角度二:
&a是数组的地址,数组的地址是不是要用一个数组指针来存放。int( * )[4]是数组指针变量类型, 而 * 的访问字节数取决于变量类型,那么就是访问16个字节的数组指针。

3.*arr,arr是首元素地址,*找到字符’a’,sizeof()计算’a’占用空间,结果为1字节

4.和第1题有一点相似,这里要注意a[0]是一维数组的数组名(二维数组的每个元素是一个一维数组),而数组名没有单独放在sizeof内部而是+1,地址+1还是一个地址a[1],则为4(x86环境)

5.这里很容易加1加为整个二维数组,其实不是,还是同上一题,a为二维数组名但是不是单独放在sizeof内部,则数组名是首元素地址而不是计算整个二维数组大小,数组首元素是第一个一维数组,+1就是指向第二个一维数组的地址,地址结果就为4

6.这里还记得我们上一节讲的指针和数组互化公式吗*(数组名+变量/常量)=数组名[变量/常量]这里*(a+1)=a[1],就是第二个一位数组名,为16

7.或许有很多人看了一眼就直接说是越界访问,理论上有道理,但是我们都忽略了sizeof他实际上不会真正去计算表达式的,他是不会访问a[3]这块空间的,他是靠表达式结果的类型来出结果的,a[3]的类型就是int 4就为16

总结:如果遇到很难分析的题目,就记住一点sizeof里面的表达式并不会真正计算,他的结果是取决于表达式结果的类型包括sizeo(a[0]+1)他的表达式结果是一个地址,地址的类型是指针,指针就是4/8个字节。其他以此类推

2.strlen-数组/指针专题

//x86环境
char arr[] = { 'a','b','c','d','e','f' };
printf("%d\n", strlen(*arr));	//1
char* p = "abcdef";
printf("%d\n", strlen(&p));	//2

1.这个代码有问题,arr是首元素地址,*arr找到首元素arr[0],就是’a’,然后strlen函数参数是需要一个地址,他会把’a’==97当作一个地址来访问,可是我们并没有97这个地址的访问权限,这里就会形成非法访问
2.这里我们直接上图
在这里插入图片描述
这里我们可以直接看到他们&p和p的地址明显不一样,p是指针变量,存储的是"abcdef"这个常量字符串的地址。而&p是一个地址是指针变量的地址.所以在这个地址不知道什么时候遇到\0,故而结果为随机值

指针面试题专题

//在X86环境下
//假设结构体的⼤⼩是20个字节
//程序输出的结构是啥?
struct Test
{
```int Num;
 char *pcName;
 short sDate;
 char cha[2];
 short sBa[4];
}*p = (struct Test*)0x100000;
int main()
{
 printf("%p\n", p + 0x1);
 printf("%p\n", (unsigned long)p + 0x1);
 printf("%p\n", (unsigned int*)p + 0x1);
 return 0;
}

第一个就是结构体指针+1,既然是指针他的±运算就取决于他的指针类型,int*一次跳过4个字节,题目中告知结构体大小是20字节。所以我们+1就跳过20个字节,则16进制表示100014

第二个很容易出错,把他强制转换为unsigned long形,这是无符号整数+1就是100001

第三个则是无符号整形指针+1,类型一次跳过4个字节,则为100004

//假设环境是x86环境,程序输出的结果是啥?
#include <stdio.h>
int main()
{
 int a[5][5];
 int(*p)[4];
 p = a;
 printf( "%p,%d\n", &p[4][2] - &a[4][2], &p[4][2] - &a[4][2]);
 return 0;
}

这里我们之间用一张图来阐述:
在这里插入图片描述
p是一个int(*)[4]的数组指针,但是a的类型是int( * )[5],所以我们第一个值和第二个值按理来说都是负数,但是第一个值是以地址的形式来输出,"地址在内存中不分原反补码,但是-4在内存中是以补码的形式存储,所以直接打印FF FF FF FC

第二个打印原码-4

#include <stdio.h>
int main()
{
 char *a[] = {"work","at","alibaba"};
 char**pa = a;
 pa++;
 printf("%s\n", *pa);
 return 0;
}

在这里插入图片描述

指针数组a每一个元素是一个char指针,pa存储第一个元素char地址,+1就是指向下一个元素(另外一个char*),打印他的字符串at
4.

#include <stdio.h>
int main()
{
 char *c[] = {"ENTER","NEW","POINT","FIRST"};
 char**cp[] = {c+3,c+2,c+1,c};
 char***cpp = cp;
 printf("%s\n", **++cpp);
 printf("%s\n", *--*++cpp+3);
 printf("%s\n", *cpp[-2]+3);
 printf("%s\n", cpp[-1][-1]+1);
 return 0;
}

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
结果就是:
在这里插入图片描述

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

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

相关文章

C++ | Leetcode C++题解之第205题同构字符串

题目&#xff1a; 题解&#xff1a; class Solution { public:bool isIsomorphic(string s, string t) {unordered_map<char, char> s2t;unordered_map<char, char> t2s;int len s.length();for (int i 0; i < len; i) {char x s[i], y t[i];if ((s2t.coun…

HDFS详细介绍以及HDFS集群环境部署【hadoop组件HDFS笔记】(图片均为学习时截取的)

HDFS详细介绍 HDFS是什么 HDFS是Hadoop三大组件(HDFS、MapReduce、YARN)之一 全称是&#xff1a;Hadoop Distributed File System&#xff08;Hadoop分布式文件系统&#xff09;&#xff1b;是Hadoop技术栈内提供的分布式数据存储解决方案 可以在多台服务器上构建存储集群&…

C : 线性规划例题求解

Submit Page TestData Time Limit: 1 Sec Memory Limit: 128 Mb Submitted: 93 Solved: 49 Description 求解下述线性规划模型的最优值min &#xfffd;1&#xfffd;1&#xfffd;2&#xfffd;2&#xfffd;3&#xfffd;3&#xfffd;.&#xfffd;. &…

Java 并发编程常见问题

1、线程状态它们之间是如何扭转的&#xff1f; 1、谈谈对于多线程的理解&#xff1f; 1、对于多核CPU&#xff0c;多线程可以提升CPU的利用率&#xff1b; 2、对于多IO操作的程序&#xff0c;多线程可以提升系统的整体性能及吞吐量&#xff1b; 3、使用多线程在一些场景下可…

StringUTF_16错误认识字节长度

众所周知&#xff0c;在 UTF-8 编码中&#xff0c;中文字符通常占用 3 个字节: import java.nio.charset.StandardCharsets;/*** author shenyang* version 1.0* info untitled* since 2024/6/30 上午9:42*/ public class Test {public static void main(String[] args) {Stri…

【机器学习】机器学习的重要方法——强化学习:理论,方法与实践

目录 一、强化学习的核心概念 二、强化学习算法的分类与示例代码 三.强化学习的优势 四.强化学习的应用与挑战 五、总结与展望 强化学习&#xff1a;理论&#xff0c;方法和实践 在人工智能的广阔领域中&#xff0c;强化学习&#xff08;Reinforcement Learning, RL&…

第3章-数据类型和运算符

#本章目标 掌握Python中的保留字与标识符 理解Python中变量的定义及使用 掌握Python中基本数据类型 掌握数据类型之间的相互转换 掌握eval()函数的使用 了解不同的进制数 掌握Python中常用的运算符及优先级1&#xff0c;保留字与标识符 保留字 指在Python中被赋予特定意义的一…

【操作系统】内存管理——页面分配策略(个人笔记)

学习日期&#xff1a;2024.6.28 内容摘要&#xff1a;页面分配策略和内存映射文件&#xff0c;内存映射文件 页面分配置换策略 基本概念 驻留集&#xff0c;指请求分页存储管理中给进程分配的物理块的集合&#xff0c;在采用了虚拟存储技术的系统中&#xff0c;驻留集大小一…

docker harbor仓库搭建,主从库复制

背景&#xff1a;需要主机安装docker-ce和docer-compose #1.安装相关依赖. yum install -y yum-utils device-mapper-persistent-data lvm2 #2.下载官方的docker yum源文件 yum-config-manager --add-repo https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo …

专题三:Spring容器ApplicationContext初始化

前面我们构建好了Spring源码&#xff0c;接下来肯定迫不及待来调试啦&#xff0c;来一起看看大名鼎鼎ApplicationContext 新建模块 1、基础步骤 2、重要文件 build.gradle plugins {id java }group org.springframework version 5.2.6.RELEASErepositories {mavenCentral(…

DDD学习笔记五

模型引力场&#xff1a;聚合 强作用力体现&#xff1a; 某个领域模型是另一些模型存在的前提&#xff0c;没有前者&#xff0c;后者就失去了生存的意义。 一组领域模型之间存在关联的领域逻辑&#xff0c;任何时候都不能违反。 一组领域模型必须以一个完整的、一致的状态呈现给…

专业指南:U盘数据恢复全攻略

一、引言&#xff1a;U盘数据恢复的重要性 在信息化日益发展的今天&#xff0c;U盘已成为我们日常生活中不可或缺的存储设备。然而&#xff0c;由于各种原因&#xff0c;U盘中的数据可能会面临丢失的风险。U盘数据恢复技术便应运而生&#xff0c;它旨在帮助用户找回因误删除、…

多平台自动养号【开心版】偷偷使用就行了!

大家好&#xff0c;今天我无意间发现了一款【多平台自动养号工具】&#xff0c;看了一下里面的功能还是挺全面的&#xff0c;包含了【抖音&#xff0c;快手&#xff0c;小红薯】还有一些截流功能 虽然这款工具功能强大&#xff0c;但美中不足的是需要付费的。但别担心&#xf…

线性结构之栈结构

栈是一种只能从一端存取数据并且遵循“后进先出”原则的线性存储结构。这句话中体现了栈结构的三个特征——只能从一端存取数据&#xff0c;遵循“后进先出”的原则和线性存储结构。因此如果我们要实现一个栈结构的数据结构&#xff0c;就必须要满足这三点要求。提到线性结构&a…

构建高效业财一体化管理体系

构建高效业财一体化管理体系 业财一体化战略意义 提升决策质量 强化数据支撑&#xff1a;通过整合业务与财务数据&#xff0c;为决策提供准确、实时的信息基础&#xff0c;确保分析的深度与广度。促进业务与财务协同&#xff1a;打破信息孤岛&#xff0c;实现业务流程与财务管…

最流行的文件同步软件

PanguFlow是一款免费的文件同步软件&#xff0c;他支持文件的全量同步、支持文件的增量同步、支持文件的实时备份&#xff0c;支持双向同步&#xff0c;支持三向同步甚至多向同步&#xff0c;支持无人值守运行。 PanguFlow数据同步软件下载地址https://pan.baidu.com/s/1GLjFR…

博客都在使用的打字机效果,居然这么简单?

效果展示 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>Document</title><style>body …

在Ubuntu下将pulseaudio换成pipewire

1、为什么要将pulseaudio换成pipewire&#xff1f; PulseAudio 是一个成熟且广泛使用的音频服务器&#xff0c;适合一般桌面音频需求&#xff0c;但在性能和延迟上有一定限制。PipeWire 是一个更现代的解决方案&#xff0c;旨在统一音频和视频处理&#xff0c;提供高性能和低延…

【TB作品】密码锁,ATMEGA128单片机,Proteus仿真

题目 5 &#xff1a;密码锁 使用单片机实现简易密码锁&#xff0c;通过输入密码&#xff0c;实现门锁的开启&#xff08;控制继电器&#xff09;。 具体要求如下&#xff1a; &#xff08;1&#xff09;当输入正确密码后&#xff0c;继电器开启。 &#xff08;2&#xff09;当三…

Java web应用性能分析之【prometheus监控K8s指标说明】

常规k8s的监控指标 单独 1、集群维度 集群状态集群节点数节点状态&#xff08;正常、不可达、未知&#xff09;节点的资源使用率&#xff08;CPU、内存、IO等&#xff09; 2、应用维度 应用响应时间 应用的错误率 应用的请求量 3、系统和集群组件维度 API服务器状态控…