如何给网站做优化代码/网站关键词优化怎么做的
1、一个简单的函数
int Add(int x, int y)
{return x + y;
}int main()
{printf("%d", Add(2, 3, 4, 5, 6));return 0;
}
这一段足够简单的代码,闭眼都能知道运行结果会在屏幕上打印 5 。那编译器是怎么处理后面的 4、5、6 ?
我们再看看这个函数。
void MyTest(int a, int b, int c, int d)
{printf("%d", a);
}
似乎参数 b、c、d 的设定是多余的。不论这三个参数传入什么值,都不影响结果。那上述的 Add 函数是不是也能看作后续 4、5、6 对应的参数没有用到,所以没有表现出任何现象?
带着这个问题,再看一个函数:
void MyTest_2(int num)
{int* ptr = #for (int i = 0; i <= num; i++){printf("%d ", *ptr);ptr++;}
}
是不是有点懵?对形参取地址是个什么操作?还要对形参的指针进行移动再打印出来又是什么鬼?预感上,大概率会报非法访问。
那不妨,我们在 main 函数里调用一下?
MyTest_2(0);
MyTest_2(1);
MyTest_2(10);
然而结果是:
虽然返回了一堆不明所以的值,但返回代码 0 说明程序压根就没有报错。而参数 0 、 1 、 10 都被完整打印出来了,简直是毁三观。
知道你很急,但是你先别急,再在 main 函数中试试这个足以让人懵逼的例子:
MyTest_2(5, 100, 20, 35, 40, 114514);
结果更毁三观了:
什么玩意?明明 MyTest 创建时只设定了一个参数,为什么传入六个参数能全部打印出来?是不是说明,一开始的 Add 调用,后面的 4、5、6 也一并进行了传参,只是没有在函数内部进行使用?
要弄懂这个问题,首先得了解编译器对函数调用时的参数是怎么处理,传参过程又是怎么样的。
2、函数传参过程
2.1、栈帧建立之前
调用函数时,系统会在内存中创建对应函数的栈帧。关于栈帧建立及销毁这部分内容可以看这一篇开头部分:函数栈帧简述。
而在进行栈帧建立之前,程序还会执行一系列的操作。以这段代码为例,直接在汇编中看看 ret 赋值时的 Add 调用,汇编指令到底做了什么:
int Add(int x, int y)
{return x + y;
}int main()
{int ret = Add(0xAB, 0xCD, 0xEF, 0xAA, 0xDD);return 0;
}
当前栈帧是 main 函数,根据以上汇编指令,在调用 Add 函数之前,程序将 0xAB、0xCD、0xEF、0xAA、 0xDD 这五个参数逆序放入 main 函数栈顶( ESP 是栈顶寄存器)。
这一步实际上就是函数传参。通过这一步得出结论,不论函数在创建时定义了多少个形参,甚至不定义形参,只要在调用时,函数名后的括号内写入参数,就一定会进行传参。
2.2、参数调用
上述两句汇编代码首先是将 ebp+8 位置的值存入 eax 寄存器,再让 eax 寄存器中的值 += ebp+12 位置的值。而 ebp+8 的地址与 ebp+12 的地址分别储存了 0xAB 和 0xCD 。
至此就是一次完整的传参及参数调用。
也就是说,只需要知道第一个参数的地址,那么剩下的参数即使不在创建函数时定义,也可以通过第一个参数的地址进行访问。就此,最开始的 MyTest_2 函数产生的现象也就解释完毕。
3、可变参数列表
3.1、定义阐述
严格来说 C 语言的函数参数数量并不是固定的,那么在应用上根据传入的各个参数类型及第一个参数的地址,对函数传入任意参数个数,只需要通过某种方式在函数内部进行调用,那么函数的灵活性和扩展性就大大提高了。
很好, printf 也是这么想的。在使用 printf 时,第一个参数中有几个占位符,后续就带几个参数,各位对这规则应该已经形成肌肉记忆了。而对于之前的 MyTest_2 函数,唯一定义的参数便是后续传入有效参数的个数。而为了语义上更加直观,像这类可对后续参数进行操作的函数在创建时,一般会加上三个点。当然,也是为了语义,将变量名改为 argc (argument count):
void MyTest_2(int argc, ...)
{int* ptr = &argc;for (int i = 0; i <= argc; i++){printf("%d ", *ptr);ptr++;}
}
如以上函数中用其中某个参数确定后续参数的个数,那么这一系列参数就叫可变参数列表。
3.2、初步实现
虽然在 MyTest_2 中已经初步实现了带可变参数列表的函数创建,但这个函数好像没什么用。所以这里再举一个例子,求若干浮点数的和:
double Sum(int argc, ...)
{double sum = 0.;//创建可变参数列表的头部指针,将指针指向列表第一个元素double* ptr = (double*)(&argc + 1);//遍历可变参数列表,求和for (int i = 1; i <= argc; i++){sum += *ptr;//指针指向下一个参数ptr++;}return sum;
}
这代码貌似没问题,但传入的参数列表仅限于 double 类型,如果传入的参数是一个整型变量呢?由于内部只能通过指针访问,根本无法知晓外部传入的变量类型,而且编译器也不会对可变参数列表中的参数类型作检查。
所以,如果列表的参数类型不一致,第一个参数除了附带参数的数量信息外,还应附带每个参数的类型。解决办法可以参照 printf 的第一个参数。在此之前,先了解一个点,函数在传参时,汇编指令会对参数进行类型提升和 4 字节对齐。也就是说,char、short 的类型会被提升为 int ,而 float 类型直接提升为 double 。
修改后如下:
//format字符串只允许d或f,不区分大小写
double Sum(const char* format, ...)
{double sum = 0.;int count = strlen(format);//创建可变参数列表的头部指针,将指针指向列表第一个元素char* ptr = (char*)(&format) + sizeof(char*);for (int i = 0; i < count; i++){//遇到字符d或者D以整型处理if (format[i] == 'd' || format[i] == 'D'){sum += (double)*((int*)ptr);//指针指向下一个参数ptr += sizeof(int);}//遇到字符f或者F以双精度浮点型处理else if (format[i] == 'f' || format[i] == 'F'){sum += *((double*)ptr);//指针指向下一个参数ptr += sizeof(double);}}return sum;
}
至此已经很接近 printf 的参数调用方式了。
3.3、可变参数列表宏
调用 stdio.h 头文件便可以使用专用于处理可变参数列表的四个宏:
va_list:用于创建读取可变参数列表的指针;
typedef char* va_list;
__crt_va_start:将可变参数列表的指针指向列表第一个参数;
#define __crt_va_start_a(ap, v) ((void)(ap = (va_list)_ADDRESSOF(v) + _INTSIZEOF(v)))
#define __crt_va_start(ap, x) __crt_va_start_a(ap, x)
__crt_va_arg:获取可变参数列表的指针当前指向的参数,并将指针指向下一个参数;
#define __crt_va_arg(ap, t) (*(t*)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)))
__crt_va_end:用于销毁可变参数列表的指针。
#define __crt_va_end(ap) ((void)(ap = (va_list)0))
此外对上述 _INTSIZEOF 和 _ADDRESSOF 也需要作了解:
#define _ADDRESSOF(v) (&(v))
#define _INTSIZEOF(n) ((sizeof(n) + sizeof(int) - 1) & ~(sizeof(int) - 1))
上述的 _INTSIZEOF 比较难以理解。它的运算结果是 4 字节对齐,这个公式有点巧妙,有兴趣可以自行理解。
接下来先将上面的代码用这几个宏改造一下:
double Sum(const char* format, ...)
{double sum = 0.0;va_list ptr;__crt_va_start(ptr, format);for (int i = 0; i < strlen(format); i++){if (format[i] == 'd' || format[i] == 'D'){sum += __crt_va_arg(ptr, int);}else if (format[i] == 'f' || format[i] == 'F'){sum += __crt_va_arg(ptr, double);}}__crt_va_end(ptr);return sum;
}
不过这几个宏不推荐使用,因为随着编译器的不同,很可能某些编译器并不支持这些宏,可移植性大大降低。这里主要是提供宏的思路,至于宏的实现也已经展示,各位完全可以根据这些宏通过纯 C 代码实现。
相关文章:

深入理解C语言的函数参数
1、一个简单的函数 int Add(int x, int y) {return x y; }int main() {printf("%d", Add(2, 3, 4, 5, 6));return 0; } 这一段足够简单的代码,闭眼都能知道运行结果会在屏幕上打印 5 。那编译器是怎么处理后面的 4、5、6 ? 我们再看看这个函…...

【C++】策略模式
目录 一、简介1. 含义2. 特点 二、实现1. 策略接口(Strategy Interface)2. 具体策略类(Concrete Strategies)3. 上下文类(Context)4. 使用策略模式 三、总结如果这篇文章对你有所帮助,渴望获得你…...

什么时候使用匿名类,匿名类解决了什么问题?为什么需要匿名类 ?
匿名类通常在以下场景下使用: 一次性使用: 当你需要创建一个类的实例,但该类只在一个地方使用,而不打算在其他地方重复使用时,可以考虑使用匿名类。 简化代码: 当创建一个小型的、一次性的类会让代码更简洁…...

怎么让gpt帮忙改文章 (1) 快码论文
大家好,今天来聊聊怎么让gpt帮忙改文章 (1),希望能给大家提供一点参考。 以下是针对论文重复率高的情况,提供一些修改建议和技巧: 怎么让GPT帮忙改文章 一、背景介绍 随着人工智能的发展,自然语言处理技术已经成为了许…...

Android源码下载流程
1.使用repo方式: https://github.com/jeanboydev/Android-ReadTheFuckingSourceCode/blob/master/article/android/framework/Android-Windows%E7%8E%AF%E5%A2%83%E4%B8%8B%E8%BD%BD%E6%BA%90%E7%A0%81.md 2.使用git方式: Windows 环境下载 Android 源…...

ArrayList与顺序表(带完整实例)
【本节目标】 1. 线性表 2. 顺序表 3. ArrayList的简介 4. ArrayList使用 5. ArrayList的扩容机制 6. 扑克牌 1.线性表 线性表(linear list)是n个具有相同特性的数据元素的有限序列。 线性表是一种在实际中广泛使用的数据结构,常见的线性表…...

智能冶钢厂环境监控与设备控制系统(边缘物联网网关)
目录 1、项目背景 2、项目功能介绍 3、模块框架 3.1 架构框图 3.2 架构介绍 4、系统组成与工作原理 4.1 数据采集 4.2 指令控制 4.3 其他模块 4.3.1 网页、qt视频流 4.3.2 qt搜索进程 5、成果呈现 6、问题解决 7、项目总结 1、项目背景 这个项目的背景是钢铁行业的…...

【Python】conda镜像配置,.condarc文件详解,channel镜像
1. conda 环境 安装miniconda即可,Miniconda 安装包可以到 http://mirrors.aliyun.com/anaconda/miniconda/ 下载。 .condarc是conda 应用程序的配置文件,在用户家目录(windows:C:\users\username\),用于…...

实战章节:在Linux上部署各类软件
详细资料见文章的资源绑定 一、前言 1.1 为什么学习各类软件在Linux上的部署 在前面,我们学习了许多的Linux命令和高级技巧,这些知识点比较零散,同学们跟随着课程的内容进行练习虽然可以基础掌握这些命令和技巧的使用,但是并没…...

铭飞CMS list 接口 SQL注入漏洞复现
0x01 产品简介 铭飞CMS是一款基于java开发的一套轻量级开源内容管理系统,铭飞CMS简洁、安全、开源、免费,可运行在Linux、Windows、MacOSX、Solaris等各种平台上,专注为公司企业、个人站长快速建站提供解决方案 0x02 漏洞概述 铭飞CMS在5.2.10版本以前list 接口处存在sql注入…...

Linux指令初始
1.ls指令 语法 : ls [ 选项 ][ 目录或文件 ] 功能 :对于目录,该命令列出该目录下的所有子目录与文件。对于文件,将列出文件名以及其他信息。 ls 常用:-a 列出目录下的所有文件,包括以 . 开头的隐含文件。 …...

Nginx命令---启动nginx
介绍 使用命令启动nginx。 命令 nginx目录/bin/nginx...

【UE5】监控摄像头效果(下)
目录 效果 步骤 一、多摄像机视角切换 二、摄像头自动旋转巡视 三、摄像头跟踪拍摄 效果 步骤 一、多摄像机视角切换 1. 打开玩家控制器“MyPlayerController”,添加一个变量,命名为“BP_SecurityCameraArray”,类型为“BP_SecurityCa…...

binkw32.dll丢失怎么办?这5个方法都可以解决binkw32.dll丢失问题
binkw32.dll文件是什么? binkw32.dll是一个动态链接库文件,它是Windows操作系统中的一个重要组件。它包含了许多用于处理多媒体文件的函数和资源,如视频、音频等。当我们在电脑上打开或播放某些多媒体文件时,系统会调用binkw32.d…...

C语言-每日刷题练习
[蓝桥杯 2013 省 B] 翻硬币 题目背景 小明正在玩一个“翻硬币”的游戏。 题目描述 桌上放着排成一排的若干硬币。我们用 * 表示正面,用 o 表示反面(是小写字母,不是零),比如可能情形是 **oo***oooo,如果…...

Qt设置类似于qq登录页面(ikun)
头文件 #ifndef WIDGET_H #define WIDGET_H#include <QWidget> #include <QWindow> #include <QIcon> #include <QLabel> #include <QMovie> #include <QLineEdit> #include <QPushButton>QT_BEGIN_NAMESPACE namespace Ui { class…...

Qt 如何使用VTK显示点云
开发环境 ubuntu 20.04 VTK 8.2 编译VTK 下载源码 git clone --recursive https://gitlab.kitware.com/vtk/vtk.git 使用版本管理工具,切换版本到8.2 更改编译选项,这里使用cmake-gui进行配置 1、编译类型修改为Release 2、安装路径可以设置…...

Ganache结合内网穿透实现远程或不同局域网进行连接访问
文章目录 前言1. 安装Ganache2. 安装cpolar3. 创建公网地址4. 公网访问连接5. 固定公网地址 前言 Ganache 是DApp的测试网络,提供图形化界面,log日志等;智能合约部署时需要连接测试网络。 Ganache 是一个运行在本地测试的网络,通过结合cpol…...

Qt槽函数不响应不执行的一种原因:ui提升导致重名
背景: 一个包含了组件提升的ui,有个按钮的槽函数就是不响应,于是找原因。 分析: 槽函数的对应一是通过connect函数绑定信号,二是on_XXX_signal的命名方式。界面上部件的槽函数通常是第二种。 我反复确认细节&#…...

vuepress路径问题,导致图片不显示
图片不显示,报 Uncaught SyntaxError: Unexpected token <错误 很可能就是:路径配置原因 1.当设置为 / 时,VuePress 会假设你的站点将部署到服务器的根路径, 例如 https://yourdomain.com/。 2.生成的页面链接和资源引用将以…...

QT 重定向qdebug输出到自绘界面
因为在嵌入式中调试qt需要查看输出信息,特意写了一个类用户便捷查看qdebug信息 界面如下: 提供了开始,停止,保存,清空,退出功能,具体代码下文给出 文件如下 #ifndef QDEBUGREDIRECT_H #define QDEBUGREDIRECT_H /**qdebug 重定向类 定向到界面控件*李吉磊 2023.12.7* */#in…...

前端(一):HTML+CSS
参考课程:23最新版web前端开发_哔哩哔哩_bilibili 文档:GitHub - codeNiuMa/HTML-md-file: 学习HTML课程时的资料 目录 1 HTML 1.1 骨架 DOCTYPE html标签 head标签 body标签 title标签 meta标签 1.2 标签标题h1 1.3 段落p 1.4 水平线 1.5 图片img 1.6 …...

如何使用Matlab完成窗口与子窗口
目录 一、前言 二、主窗口与主窗口按钮 三、子窗口 四、调用函数并显示在子窗口中的文本框中 五、关闭子窗口 一、前言 有时候需要借用Matlab完成一个图窗功能,但是我们的程序不仅拥有功能,还拥有一些子功能,那么我们该如何借助Matlab完…...

Threejs之相机基础
参考资料 正投影相机…相机控件MapControls 知识点 注:基于Three.jsv0.155.0 正投影相机正投影相机-Canvas尺寸变化包围盒Box3地图案例(包围盒、正投影)相机动画(.position和.lookAt())不同方向的投影视图旋转渲染结果(.up相机上方向)管道漫游案例OrbitControls…...

2024SIA上海国际轴承工业展览会 ▎参行业盛会 展轴研风采
2024SIA上海国际轴承工业展览会 内容:1、轴承制品展区:2、轴承设备展区:3、轴承零件展区: 国际轴承展丨轴承工业展丨轴承装备展丨上海轴承展丨上海轴承工业展丨上海轴承装备展 2024上海国际轴承工业展览会将会于2024年7月24-26日…...

SQLMap介绍
预计更新SQL注入概述 1.1 SQL注入攻击概述 1.2 SQL注入漏洞分类 1.3 SQL注入攻击的危害 SQLMap介绍 2.1 SQLMap简介 2.2 SQLMap安装与配置 2.3 SQLMap基本用法 SQLMap进阶使用 3.1 SQLMap高级用法 3.2 SQLMap配置文件详解 3.3 SQLMap插件的使用 SQL注入漏洞检测 4.1 SQL注入…...

平头哥玄铁系列 RISC-V 芯片及开发板
1、玄铁 9 系列概述 玄铁 8 系列 基于C-SKY架构,玄铁 9 系列基于 RISC-V 架构。E 系列为 RISC-V 32 位,C 系列为 RISC-V 64 位。 E902:超低功耗 RSIC-V 架构处理器 E902 采用 2 级极简流水线兼容 RISC-V 架构且对执行效率等方面进行了增强&a…...

Android 删除浏览器导航页面修改默认主页
Android 删除浏览器导航页面修改默认主页 近来收到客户需求反馈,需要删除浏览器导航页面并将百度设置为默认主页,具体修改参照如下: 删除浏览器导航页面: /vendor/mediatek/proprietary/packages/apps/Browser/src/com/android…...

【Stm32-F407】Keil uVision5 下新建工程
①双击鼠标左键打开Keil uVision5,选择 Project 下的 New uVision Project ; ②在期望的文件夹下创建一个工程,并按如下要求操作; ③添加文件类型,按如下要求操作 ④如有需要可添加相关启动文件在工程文件夹下并添加到…...

linux中文件服务器NFS和FTP服务
文件服务器 NFSNFS介绍配置nfs文件共享服务端客户端 FTPftp介绍FTP基础ftp主动模式ftp被动模式 Vsftp 服务器简介vsftpd配置安装vsftpd[ftp的服务端]编辑配置文件匿名用户设置创建本地用户使用ftp服务 客户端操作匿名用户登录本地用户登录lftp服务 NFS NFS介绍 文件系统级别共…...