柳州游戏网站建设,网页升级未成年人自行离开,建设网站公司那里好相关的热搜问题,广州信息流推广公司排名文章目录 1.什么是可变参数列表2.可变参数列表的分析与使用2.1使用2.2分析原理2.3分析原码 1.什么是可变参数列表
对于一般的函数而言#xff0c;参数列表都是固定的#xff0c;而且各个参数之间用逗号进行分开。这种函数在调用的时候#xff0c;必须严格按照参数列表中参数… 文章目录 1.什么是可变参数列表2.可变参数列表的分析与使用2.1使用2.2分析原理2.3分析原码 1.什么是可变参数列表
对于一般的函数而言参数列表都是固定的而且各个参数之间用逗号进行分开。这种函数在调用的时候必须严格按照参数列表中参数的个数进行传参否则编译器就会报错。 如果现在我要求2个数中的最大值那么就可以这样写 现在我的需求变了我要求5个数的最大值那怎么写 如果现在我要求6个数的最大值呢 你还要将代码继续写下去吗那也太麻烦了吧我的需求变一点点你的代码就得变。 当然也可以先将数放在一个大的数组里面。但是现在不让你使用数组那你该怎么办呢----使用可变参数列表 此处的...就是可变参数列表num表示传入参数的个数。 可变参数至少有一个明确的参数。…表示其它元素其它元素可以有也可以没有
那么如何使用可变参数列表呢
2.可变参数列表的分析与使用
可变参数列表的使用需要借用四个宏。
va_listva_startva_argva_end
关于这四个宏的功能我们能后面会详细讲到。
2.1使用 注意事项 可变参数必须从头到尾逐个访问如果你一开始就想访问中间的元素这是办不到的。参数列表中至少有一个参数如果一个都没有则无法使用va_start这几个宏是无法直接判断实际存在参数的个数的必须给他传递参数个数 那不对呀我们使用的printf就是使用的可变参数那我们没有给它传递明确的参数呀 其实我们给它传递参了参数的个数我们的%d%c等格式控制符就说明了我传递了几个参数。这些宏无法判断每个参数的类型。 2.2分析原理
接下来我们就开始分析一下它的底层原理是如何实现的 在了解过函数栈帧的形成后我们知道函数调用时是会进行参数传递的而且参数在栈帧的形参过程中是从右向左入栈的。函数栈帧的创建与销毁可看这里 在汇编语言中通过查看内存我们看见看到确实是这样的 此时我们我们的最后压入栈中的元素5也就是num就在内存中的该位置 此时我们先猜测一下我的num就是我最后压入栈中的元素在栈中的地址较小那先前压入的元素就在num上面。既然我能找到num元素那我取出num的地址再加上一不就指向先前压入的元素了那我就能访问他们再继续让指针移动就可以将他们全部访问到。那到底是不是这样实现的呢而且地址1是加4/8个字节那其它类型char、short是怎么办的呢
下面我们就先来测试一下对于char类型它是怎么做的 在调试过程中给你我们可以发现char类型的数据在入栈是也是压入4个字节为什么会这样呢 movsx是什么汇编指令我们以前都是用的mov 看到这我就明白了char类型的数据会整型提升为整型然后在压入栈中。 这样就可以实现无论外部数据如何变化该函数都可以让指针4/8个字节找到数据了。 因此通过汇编我们可以看到在可变参数场景下 实际传入的参数使char、short、float在编译器编译的时候会自动进行提升。函数内部使用的时候根据类型提取数据更多的是通过int、double来进行 2.3分析原码
va_list
该类型其实就是对char*的重命名在此我们也就不赘述了。 va_end 该宏的作用就是将我们的arg指针置为NULL了避免了野指针。
va_start 这里我们的编译器对它们进行了封装而且该宏又调用了两个宏 将3个宏替换一下就是下面的结果。
#define __crt_va_start_a(arg, num) ((void)(arg (char*)((num)) _INTSIZEOF(num)))该宏是什么意思呢 就是对num进行取地址然后强转为char*指针再4赋值给arg最后将该指针强转为void类型。 这里为什么是4呢 这里的4其实是为了让数据在内存中4字节对齐向上取整 这个_INTSIZEOF宏我们稍后再看。 为什么强转为void类型 待… 执行va_start(arg, num)此时arg指针就指向了第一个元素 va_arg 再执行int max va_arg(arg, int);、 我们来看一下这个宏又是再干什么 #define __crt_va_arg(arg, int) (*(int*)((arg _INTSIZEOF(int)) - _INTSIZEOF(int)))该宏先执行画红线的部分即先将arg向下动完成指向下一个元素的任务然后再用arg减去刚才移动的距离又回到刚才的位置注意arg没回来最后通过强制转换提取出符合类型大小的数据 该宏有两个功能
arg指向下一个元素使arg回指然后取出地址中的内容
一行代码就执行了两个作用很巧妙。 然后代码通过循环num-1次就遍历了所有元素。
下面我们就来研究一下_INTSIZEOF宏是如何计算指针走 _INTSIZEOF(n)的意思就是计算一个最小数x满足xn x % 4 0 其实就是一种4字节的对齐方式。 例如
n是1234对n进行sizeof(int)最小整数被取整的问题 就是4。n是5678对n进行sizeof(int)最小整数被取整的问题 就是8。 那为什么要这样做呢 因为我们的数据在入栈的时候都是按照4/8字节对齐的方式存储的既然存的时候是按照4字节对齐的方式存的那你取的也要按照4字节对齐的方式取。 那该宏是怎么办到的呢
既然是对齐到4的最小整数倍处那么本质是n对应的4的最小整数倍 4*m。对n7来说m就是24的最小整数倍对齐数就是8。
如果n能整除4那么m就是n/4如果n不能整除4那么m就是n/41
上面两种情况如何合并为一种情况呢
n sizeof(int) - 1/sizeof(int) ----(n 4 - 1) / 4如果n能整除4那么m就是 (n4-1)/ 4 ----(n3)/4此时3就不起作用就是n/4如果n不能整除4那么n最大整除4的部分R(R为n%4) 1R4。那么m就是 (n4-1)/ 4 ----(最大整除4的部分R3)/4其中 4R3 7那最后m就等于了n/4 (R3 ) / 4------n/41
知道了一个数x是4的最小几倍那求x对应的4的对齐数就是
n sizeof(int) - 1/sizeof(int) * sizeof(int) ----((n4-1)*4)/4--- 最小几倍 ---现在和源码还不太一样那我们写一个简洁版 设n4-1 w那表达是就变为了( w / 4) * 4而4就是2 * 2那w/4不就相当于右移两位w*4就相当于左移两位先右移两位在左移两位最终的结果就是将最后两个比特位置为0了嘛 需要这么麻烦嘛 直接w ~3就可以了呀 所以最终式子就变成了这样n4-1 ~ (4-1)这不就跟源码一样了嘛 源码