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

[C][可变参数列表]详细讲解

目录

  • 1.宏含义及使用
  • 2.宏原理分析
    • 1.原理
    • 2.宏理解


1.宏含义及使用

  • 依赖库stdarg.h
  • va_list
    • 其实就是char*类型,方便后续按照字节进行指针移动
  • va_start(arg, num)
    • 使arg指向可变参数部分(num后面)
  • va_arg(arg, int)
    • 先让arg指向下个元素,然后使用相对位置 – 偏移量,访问当前元素
      • :访问了当前数据的同时,又让arg指向了后续元素
  • va_end
    • 将arg指针设置为NULL,防止野指针
  • 使用示例
    int FindMax(int num, ...)
    {va_list arg;va_start(arg, num);int max = va_arg(arg, int); // 根据类型,获取可变参数列表中的第一个数据//获取并比较其他的for (int i = 0; i < num - 1; i++){int cur = va_arg(arg, int);if (max < curr){max = curr;}}va_end(arg);return max;
    }int main()
    {int max = FindMax(5,11,22,33,44,55);printf("max = %d\n", max);return 0;
    }
    
  • 注意事项
    • 可变参数必须从头到尾逐个访问
      • 如果在访问了几个可变参数之后想半途终止,这是可以的
      • 但是,如果想一开始就访问参数列表中间的参数,那是不行的
    • 参数列表中至少有一个命名参数
      • 如果连一个命名参数都没有,就无法使用va_start
    • 这些宏是无法直接判断实际存在参数的数量
    • 这些宏无法判断每个参数的类型
    • 如果在va_arg中指定了错误的类型,那么其后果是不可预测的
      • 整型提升除外

2.宏原理分析

1.原理

  • 可变参数列表对应的函数,最终调用也是函数调用,也要形成栈帧
  • 栈帧形成前,临时变量是要先入栈的,根据之前所学,参数之间位置关系是固定的
  • 通过之前的汇编的学习,发现了短整型在可变参数部分,会默认进行整形提升(char short float整型提升成int/double),那么函数内部在提取该数据的时候,就要考虑提升之后的值,如果不加考虑,获取数据可能会报错或者结果不正确

2.宏理解

  • 都有什么?
    // va_list其实就是char*类型,方便后续按照字节进行指针移动
    typedef char * va_list;#define va_start _crt_va_start
    #define va_arg _crt_va_arg
    #define va_end _crt_va_end
    
  • #define va_start _crt_va_start依赖实现
    // 这个宏特别好理解,结合栈帧中临时参数的压入位置
    #define _crt_va_start(ap,v) ( ap = (va_list)_ADDRESSOF(v) + _INTSIZEOF(v) )
    
  • #define va_arg _crt_va_arg依赖实现
    // 这个设计特别巧妙,先让ap指向下个元素,然后使用相对位置-偏移量,访问当前元素
    // 访问了当前数据的同时,还让ap指向了后续元素,一举两得
    #define _crt_va_arg(ap,t) ( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) )
    
  • #define va_end _crt_va_end依赖实现
    // 这个宏特别好理解,将ap指针设置为NULL
    #define _crt_va_end(ap) ( ap = (va_list)0 )
    
  • _ADDRESSOF(v)理解
    // 取参数的地址,也很好理解
    #define _ADDRESSOF(v) ( &(v) )
    
  • _INTSIZEOF(n)理解,难点
    #define _INTSIZEOF(n) ( (sizeof(n) + sizeof(int) - 1) & ~(sizeof(int) - 1) )
    
    • 前提
      • 为了后面方便表述,假设sizeof(n)的值 --> n(char 1,short 2, int 4)
      • 在32位平台下测试,sizeof(int) == 4,其他情况暂时不考虑
    • _INTSIZEOF(n)的意思:计算一个最小数字x,满足x>=n && x%4==0,其实就是一种4字节对齐的方式
      • 是什么?
        • 比如n是:1,2,3,4 对n进行向sizeof(int)的最小整数倍取整的问题 就是 4
        • 比如n是:5,6,7,8 对n进行向sizeof(int)的最小整数倍取整的问题 就是 8
      • 为什么要有这个4字节对齐
        • 结合之前栈帧的学习和上面代码的测试结果
      • 怎么办到的
        • 第一步理解:4的倍数
          • 既然是4的最小整数倍取整,那么本质是:x=4*mm是具体几倍,对7来讲,m就是2,对齐的结果就是8,m具体是多少,取决于n是多少
            • 如果n能整除4,那么m就是n/4
            • 如果n不能整除4,那么m就是n/4+1
          • 上面是两种情况,如何合并成为一种写法呢?
            • 常见做法是 (n+sizeof(int)-1))/sizeof(int) -> (n+4-1)/4
            • 如果n能整除4
              • 那么m就是(n+4-1)/4 -> (n+3)/4,+3的值无意义,会因取整自动消除,等价于n/4
            • 如果n不能整除4
              • 那么n=最大能整除4部分+r,1 <= r < 4
              • 那么m就是(n+4-1)/4 --> (能整除4部分+r+3)/4,其中4 <= r+3 < 7 --> 能整除4部分/4 + (r+3)/4 -> n/4+1
        • 第二步理解:最小4字节对齐数
          • 搞清楚了满足条件最小是几倍问题,那么,计算一个最小数字x,满足 x>=n && x%4==0,就变成了((n+sizeof(int)-1)/sizeof(int))[最小几倍] * sizeof(int)[单位大小] -> ((n+4-1)/4)*4
          • 这样就能求出来4字节对齐的数据了,其实上面的写法,在功能上,已经和源代码中的宏等价了
        • 第三步理解:理解源代码中的宏
          • 拿出简洁写法:((n+4-1)/4)* 4,设w=n+4-1,那么表达式可以变化成为 (w/4)*4,而4就是 2 2 2^2 22w/4不就相当于右移两位吗?再次*4不就相当左移两位吗?先右移两位,在左移两位,最终结果就是,最后2个比特位被清空为0!
          • 需要这么费劲吗?
            • w & ~3不香吗?
          • 所以,简洁版(n+4-1) & ~(4-1)
          • 原码版( (sizeof(n) + sizeof(int) - 1) & ~(sizeof(int) - 1) ),无需先/,再*
  • 图解
    请添加图片描述

相关文章:

[C][可变参数列表]详细讲解

目录 1.宏含义及使用2.宏原理分析1.原理2.宏理解 1.宏含义及使用 依赖库stdarg.hva_list 其实就是char*类型&#xff0c;方便后续按照字节进行指针移动 va_start(arg, num) 使arg指向可变参数部分(num后面) va_arg(arg, int) 先让arg指向下个元素&#xff0c;然后使用相对位置…...

54. 螺旋矩阵【rust题解】

题目 给你一个 m 行 n 列的矩阵 matrix &#xff0c;请按照 顺时针螺旋顺序 &#xff0c;返回矩阵中的所有元素。 示例 示例 1 输入&#xff1a;matrix [[1,2,3],[4,5,6],[7,8,9]] 输出&#xff1a;[1,2,3,6,9,8,7,4,5] 示例 2 输入&#xff1a;matrix [[1,2,3,4],[5,6,…...

学习笔记——网络参考模型——TCP/IP模型(传输层)

四、TCP/IP模型-传输层 一、TCP 1、TCP定义 TCP(Transmission Control Protocol&#xff0c;传输控制协议)∶为应用程序提供可靠的面向连接的通信服务。目前&#xff0c;许多流行的应用程序都使用TCP。 连接&#xff1a;正式发送数据之前&#xff0c;提前建立好一种虚拟的&…...

Java中的Instant

在Java中&#xff0c;Instant 是 java.time 包中的一个类&#xff0c;用于表示时间轴上的一个瞬时点&#xff0c;通常以纳秒精度表示。它通常用于表示机器可读的时间戳&#xff0c;而不是人类可读的时间表示&#xff08;如日期和时间&#xff09;。 Instant 主要用于时间计算和…...

PostgreSQL的锁介绍

PostgreSQL的锁介绍 PostgreSQL 中的锁机制是一种用于控制数据并发访问的手段&#xff0c;确保数据库的完整性和一致性。在实际应用中&#xff0c;合理使用锁可以避免数据不一致和减少死锁的发生。 锁类型 PostgreSQL 提供了多种锁类型&#xff0c;以下是一些常见的锁&#…...

4分之1外螺纹怎么编程:挑战与策略解析

4分之1外螺纹怎么编程&#xff1a;挑战与策略解析 在机械制造领域&#xff0c;螺纹编程是一项至关重要的技术任务。当面对如4分之1外螺纹这样的具体需求时&#xff0c;编程人员需要综合运用专业知识与编程技巧&#xff0c;以确保螺纹的精确度和生产效率。本文将围绕四个方面、…...

运用selenium爬取京东商品数据储存到MySQL数据库中

使用Selenium爬取京东商品数据并存储到MySQL数据库中的过程可以分为几个步骤&#xff1a; 1. 准备工作 安装所需库 确保你已经安装了Python环境以及以下库&#xff1a; selenium&#xff1a;用于自动化浏览器操作。pymysql 或 mysql-connector-python&#xff1a;用于连接M…...

K8S SWCK SkyWalking全链路跟踪工具安装

官方参考&#xff1a;如何使用java探针注入器? 配置两个demo&#xff0c;建立调用关系&#xff0c; 首先创建一个基础镜像dockerfile from centos 先安装java 参考: linux rpm方式安装java JAVA_HOME/usr/java/jdk1.8.0-x64 CLASSPATH.:$JAVA_HOME/lib/tools.jar PATH…...

Apache Omid Idea Debug 环境搭建

IDEA 搭建 Apache Omid 源码 DEBUG 环境 Apache Omid 在 Apache HBase 之上提供了多行分布式事务的能力&#xff0c;支持全局 MVCC 功能。简单介绍编译过程。 1.下载 HBase2 并启动 https://dlcdn.apache.org/hbase/ 配置环境变量 export HBASE_HOME/xxx/hbase-2.4.18 exp…...

【面试宝藏】Go并发编程面试题

深入Go语言并发编程 Go语言以其简洁、高效的并发处理能力而闻名。在Go中&#xff0c;通过各种同步机制和原子操作&#xff0c;可以轻松地实现高性能并发编程。本文将深入探讨Go语言中的并发编程&#xff0c;包括Mutex、RWMutex、Cond、WaitGroup、原子操作等内容。 1. Mutex几…...

④单细胞学习-cellchat细胞间通讯

目录 1&#xff0c;原理基础 流程 受体配体概念 方法比较 计算原理 2&#xff0c;数据 3&#xff0c;代码运行 1&#xff0c;原理基础 原文学习Inference and analysis of cell-cell communication using CellChat - PMC (nih.gov) GitHub - sqjin/CellChat: R toolk…...

即时通讯平台及门户系统WorkPlus打造移动应用管理平台

在全球化和数字化时代&#xff0c;企业管理和沟通的方式正发生着巨大的变化。为了实现高效的协作和资源共享&#xff0c;企业越来越倾向于使用即时通讯及门户系统。这两种系统结合起来&#xff0c;可以提供一套完整的沟通和信息发布平台&#xff0c;促进内部协作和信息管理。 …...

React@16.x(12)ref 转发-forwardRef

目录 1&#xff0c;介绍2&#xff0c;类组件如何使用4&#xff0c;应用场景-高阶组件HOC 1&#xff0c;介绍 上篇文章中提到&#xff0c;ref 只能对类组件使用&#xff0c;不能对函数组件使用。 而 ref 转发可以对函数组件实现类似的功能。 使用举例&#xff1a; import Re…...

电脑世界的大冒险:用人体比喻让孩子轻松理解电脑20240603

电脑世界的大冒险&#xff1a;用人体比喻让孩子轻松理解电脑 作为一名在IT行业的老程序猿&#xff0c;我见证了电脑技术的飞速发展&#xff0c;也亲身体验了科技给生活带来的翻天覆地的变化。然而&#xff0c;在这个日新月异的数字时代&#xff0c;我意识到&#xff0c;与孩子…...

构建智慧银行保险系统的先进技术架构

随着科技的不断发展&#xff0c;智慧银行保险系统正日益受到关注。在这个数字化时代&#xff0c;构建一个先进的技术架构对于智慧银行保险系统至关重要。本文将探讨如何构建智慧银行保险系统的先进技术架构&#xff0c;以提升服务效率、降低风险并满足客户需求。 ### 1. 智慧银…...

来自大厂硬盘的降维打击!当希捷酷玩520 1TB SSD卷到369,请问阁下该怎么应对?

来自大厂硬盘的降维打击&#xff01;当希捷酷玩520 1TB SSD卷到369&#xff0c;请问阁下该怎么应对&#xff1f; 哈喽小伙伴们好&#xff0c;我是Stark-C~ 今年4月份的时候因为电脑上的游戏盘突然挂掉&#xff0c;为了性价比选购了希捷酷玩520 1TB SSD&#xff0c;同时我也是…...

什么是封装?为什么是要封装?

封装是面向对象编程中的一种核心概念&#xff0c;它是将数据和操作数据的方法结合起来&#xff0c;形成一个整体&#xff0c;对外只暴露必要的接口&#xff0c;隐藏内部的具体实现细节。 封装的目的是为了实现信息隐藏和代码的模块化&#xff0c;具体原因如下&#xff1a; 1.…...

Spring Cloud | 服务 “注册与发现“ 框架 : Eureka框架

目录&#xff1a; Eureka 的 "工作机制" :一、Eureka 的 "工作原理" ( 两大组件 ) :1.1 Eureka Server ( 服务注册中心 )1.2 Eureka Client ( 服务/服务实例&#xff0c;其存在 "两种角色" : ①服务提供者 ②服务消费者 ) :Eureka Client 的 含义…...

编译链接问题

问题描述 C语言在编译的时候&#xff0c;提示链接的时候没有找到相应的方法 问题分析 代码文件结构&#xff1a; test.c test/1.c test/1.h test.c代码&#xff1a; #include “test/1.h” void main() { hello(); } test/1.c代码&#xff1a; void hello() { printf(“hel…...

电涡流的形成范围

电涡流的形成范围涉及多个方面&#xff0c;主要受到导体材料、磁场变化速度、导体形状和尺寸以及磁场方向的影响。以下是对这些因素的详细分析&#xff1a; 导体材料&#xff1a;金属和合金是最容易产生电涡流的材料&#xff0c;而非金属材料&#xff08;如陶瓷、塑料等&#…...

龙虎榜——20250610

上证指数放量收阴线&#xff0c;个股多数下跌&#xff0c;盘中受消息影响大幅波动。 深证指数放量收阴线形成顶分型&#xff0c;指数短线有调整的需求&#xff0c;大概需要一两天。 2025年6月10日龙虎榜行业方向分析 1. 金融科技 代表标的&#xff1a;御银股份、雄帝科技 驱动…...

定时器任务——若依源码分析

分析util包下面的工具类schedule utils&#xff1a; ScheduleUtils 是若依中用于与 Quartz 框架交互的工具类&#xff0c;封装了定时任务的 创建、更新、暂停、删除等核心逻辑。 createScheduleJob createScheduleJob 用于将任务注册到 Quartz&#xff0c;先构建任务的 JobD…...

WordPress插件:AI多语言写作与智能配图、免费AI模型、SEO文章生成

厌倦手动写WordPress文章&#xff1f;AI自动生成&#xff0c;效率提升10倍&#xff01; 支持多语言、自动配图、定时发布&#xff0c;让内容创作更轻松&#xff01; AI内容生成 → 不想每天写文章&#xff1f;AI一键生成高质量内容&#xff01;多语言支持 → 跨境电商必备&am…...

vue3+vite项目中使用.env文件环境变量方法

vue3vite项目中使用.env文件环境变量方法 .env文件作用命名规则常用的配置项示例使用方法注意事项在vite.config.js文件中读取环境变量方法 .env文件作用 .env 文件用于定义环境变量&#xff0c;这些变量可以在项目中通过 import.meta.env 进行访问。Vite 会自动加载这些环境变…...

Android 之 kotlin 语言学习笔记三(Kotlin-Java 互操作)

参考官方文档&#xff1a;https://developer.android.google.cn/kotlin/interop?hlzh-cn 一、Java&#xff08;供 Kotlin 使用&#xff09; 1、不得使用硬关键字 不要使用 Kotlin 的任何硬关键字作为方法的名称 或字段。允许使用 Kotlin 的软关键字、修饰符关键字和特殊标识…...

三分算法与DeepSeek辅助证明是单峰函数

前置 单峰函数有唯一的最大值&#xff0c;最大值左侧的数值严格单调递增&#xff0c;最大值右侧的数值严格单调递减。 单谷函数有唯一的最小值&#xff0c;最小值左侧的数值严格单调递减&#xff0c;最小值右侧的数值严格单调递增。 三分的本质 三分和二分一样都是通过不断缩…...

【LeetCode】算法详解#6 ---除自身以外数组的乘积

1.题目介绍 给定一个整数数组 nums&#xff0c;返回 数组 answer &#xff0c;其中 answer[i] 等于 nums 中除 nums[i] 之外其余各元素的乘积 。 题目数据 保证 数组 nums之中任意元素的全部前缀元素和后缀的乘积都在 32 位 整数范围内。 请 不要使用除法&#xff0c;且在 O…...

论文阅读:LLM4Drive: A Survey of Large Language Models for Autonomous Driving

地址&#xff1a;LLM4Drive: A Survey of Large Language Models for Autonomous Driving 摘要翻译 自动驾驶技术作为推动交通和城市出行变革的催化剂&#xff0c;正从基于规则的系统向数据驱动策略转变。传统的模块化系统受限于级联模块间的累积误差和缺乏灵活性的预设规则。…...

DiscuzX3.5发帖json api

参考文章&#xff1a;PHP实现独立Discuz站外发帖(直连操作数据库)_discuz 发帖api-CSDN博客 简单改造了一下&#xff0c;适配我自己的需求 有一个站点存在多个采集站&#xff0c;我想通过主站拿标题&#xff0c;采集站拿内容 使用到的sql如下 CREATE TABLE pre_forum_post_…...

大数据治理的常见方式

大数据治理的常见方式 大数据治理是确保数据质量、安全性和可用性的系统性方法&#xff0c;以下是几种常见的治理方式&#xff1a; 1. 数据质量管理 核心方法&#xff1a; 数据校验&#xff1a;建立数据校验规则&#xff08;格式、范围、一致性等&#xff09;数据清洗&…...