精华内容
下载资源
问答
  • 最后一次解释:ub不是“不去搞清楚工作原理”的借口,软件工程的任何一层都是在制造抽象隐藏细节,也必定会存在无法精确给出定义的情况。编译器处理ub也不是瞎处理,其背后也有自己的逻辑,我的目的是这样一个...

    更新:评论里文章都没看完,我怎么解释都不听,张口就骂的,直接删评论,不再一一回复。

    最后一次解释:ub不是“不去搞清楚工作原理”的借口,软件工程中的任何一层都是在制造抽象隐藏细节,也必定会存在无法精确给出定义的情况。编译器处理ub也不是瞎处理,其背后也有自己的逻辑,我的目的是从这样一个简单的程序出发,能够了解一些更加本质的东西,如果你有信心一辈子不碰任何自己或者别人的ub,那请你左上角退出,别在这里嘴臭。

    我最后最后重申一遍,这篇文章不是任何一道“题目”的“答案”!如果谁把这个题目出成给大一新生的考试题目,我第一个骂出题人,但是如果你在工程中遇到与变长参数相关的bug呢?难道你就丢下一句ub不了了之?还是你觉得你不用学习这方面的知识,就能完美定位并解决bug?

    谢谢。


    同学们好,让我们打开本节课的课本:《The art and science of C》

    857f038dfd1b85089a5f8e0be63d614a.png

    做一道非常简单的题目:

    ad4ae96d6c1c0475d899b134dbe654ae.png

    请问:题目的输出是?


    把这道题目交给5个初学者去做,大概会各自到自己的电脑上跑一遍,然后把自己编译器给出的结果当作正确答案。当然,或许当一些mac用户兴奋地打开clang的时候,编译器会直接告诉他float不能用%ld来输出,编译都过不了。

    除此以外,考虑到大部分初学者都在使用windows+dev cpp(别问我为什么不是vs),然后在如今的9102年基本正常人用的电脑都是x64架构,他们得到的答案多半是:

    0 0 123123123 456456456

    桥豆麻袋!如果说后两个输出还算正常的话,前面两个3.0,就算强行转成了long,怎么着也不应该输出0啊?

    一位同学作为本校lug的一员,冷笑一声,道:windows就是垃圾,看我的!于是他掏出自己的linux+gcc,也跑了一遍这个小程序,结果得到的竟然是:

    123123123 456456456 94356794144432 140193333050752

    甚至后面两个值还会变来变去,着实令人费解。

    好巧不巧,靠窗倒数第二排的同学,手上拿着的赫然是一台古老的32位windows机器,同样拿devcpp编译运行一下,岂料结果竟然更加不同,赫然是:

    0 1074266112 0 1074266112

    正是风云变幻,世事难料,一个看起来区区几行的程序,竟然得出了截然不同的结果!这到底是人性的毁灭还是道德的沦丧?(逃)

    看到这里,可能很多人要说,这实际上是ub,即undefined behavior,也就是说理论上生成什么汇编指令,执行结果如何,全看编译器的心情,本就没有什么讨论的意义。不过有趣的地方在于,这些输出结果并不是那么的不可预测,而是精确地符合对应系统和架构的调用惯例的。

    某种意义上,你可以看出这些编译器的实现往往都是遵循着KISS原则——keep it simple and stupid——在符合标准规定的前提下,以最直观的方式写出来的程序。

    好了,话不多说,让我们先从(对各位猿们来说)最常见的环境:linux+x64来讲起好了。


    上回提到,linux+x64下程序的运行结果是:

    123123123 456456456 94356794144432 140193333050752

    其中后面两个值还会变来变去,说明它实际上越界了,读取到了未定义的内存区域,也就没有一个固定的值。因此让我们疑惑的主要问题在于,为什么printf直接跳过了前面两个浮点数,优先打印出了123123123和456456456这两个整数呢?

    这里我们就需要提到调用惯例这个概念了。这里假定读者有关于汇编等原理的基础知识,在此基础上,我们可以大致这么描述:各种操作系统和架构之间有着各种各样的差异,而从c语言的层面上来说,函数调用永远是一样的,因此为了实现源码级跨平台,我们需要一种方法来“实现”c中的函数调用,而为了让不同编译器编译出来的二进制文件能够互相调用,我们希望这种方法是标准、通用的。这种方法就是调用惯例了。

    那么,linux+x64的调用惯例长什么样呢?我们可以通过查看标准文档来一探究竟:

    https://www.uclibc.org/docs/psABI-x86_64.pdfwww.uclibc.org

    当然,这里无需读者阅读整篇文档,让我们直奔重点——3.2.3节Parameter Passing(传参)。

    首先注意到的是,linux+x64下所有函数调用都优先使用寄存器——这一点继承于x86的fastcall调用惯例,而与cdecl(通过栈传参)则有较大不同,这样做最大的好处是可以提高函数调用的速度。在我们这份标准中可以看到,其调用惯例允许将函数的前六个参数使用寄存器传递,依次分别为rdirsirdxrcxr8r9,而在此之后的参数仍然需要通过栈来进行传递。

    另外,由于x64架构定义了MMX寄存器, 函数参数中的浮点数将被直接存入MMX寄存器中,不计入上述的6个参数范围,从xmm0xmm7, 共有8个可用,更多的参数同样需要通过栈传递。

    e0b09e35c8146716a66162cf870ebcbf.png

    在此之后,文档也给出了函数返回值的传递方法,在此不多赘述,感兴趣的读者可以直接阅读原文。另外,文档中也给出了一个例子,便于读者理解参数传递的具体方法:

    dbfc11787e88f3e4763d966ded61d05c.png

    在这里,我们需要格外注意到的一点是,由于在参数传递的过程中,整数(以及可以视作整数的指针等)与浮点数被完全分成了两组,因而只有整数与整数之间、浮点数与浮点数之间的传递顺序得到了保存,整数与浮点数交错的顺序则无从得知。

    也就是说,当调用一个参数为int, float和一个参数为float, int的函数时,在寄存器中存储的值是完全相同的。(当然前提时整数和浮点数的值都不变)

    这对于绝大多数普通的函数都不存在问题——因为传参的方法以标准的方法得到了指定,被调用的函数只需要用同样的规则解析一遍自己的参数列表,就可以知道哪个参数被存在了什么地方,从中读取便是——

    ——但偏偏还有一种东西叫做变长参数,这便导致了问题的产生。


    如果读者用c语言写过处理边长参数的程序,应该知道其处理的基本方法:

    #include <stdarg.h>
    
    void foo(int n, ...) {
      va_list args;
      va_start(args, n);
      ...
      int i = va_arg(args, int);
      ...
      va_end(args);
    }

    在x86的cdecl调用惯例中,其实现原理很容易就能猜个大概:va_startargs初始化为n在栈中地址之后的地方(注意这里的n实际上并不是取的值,而是通过某种宏操作来实现需要的效果);va_argargs的位置开始,读取sizeof(int)这么多字节的内容作为返回值,并且更新args的地址; va_end则负责一切可能的收尾处理 。

    然而在x64的调用惯例中,就出现了一个严重的问题:va_arg需要从参数列表中取出“下一个”参数,然而由于MMX寄存器的存在,我们无法辨别下一个应该是整数还是浮点数,为了兼容性又不好随便修改接口,怎么办呢?

    我们不如先想象一下printf这一类的函数的大致实现方法,因为它们是我们需要首先考虑兼容性的目标。由于printf的格式字符串中按顺序给出了所有需要的参数类型,其实现方法很大程度上就是解析格式字符串,在找到某个需要读取的参数时,根据其类型调用va_arg。也就是说,我们只要解析格式字符串,就可以得到整个函数应有的函数签名,再使用调用惯例所规定的传参方法,就可以得出每个参数所在的位置了。

    当然实现中不可能要求stdarg中的什么东西去主动解析格式字符串,而是采用一种简单但有效的解决方案——这里便是之前所说的KISS原则——即:因为函数签名转换为传参步骤是按照参数顺序一个一个来的,理论上我们可以通过“之前的所有参数类型”和“下一个参数类型”决定该参数所应该处于的位置。那么,在va_arg中,我们也可以保存“之前所有的参数类型”,在得到“下一个参数类型”之后,解析出其所应在的位置,从此位置读出参数的值即可。

    上面这几段话可能有些拗口,需要多读几遍才能理解,不过其体现的正是,用最有效也最简单地方法解决问题,往往正好能够得到最好的结果。

    于是我们便实现了我们的所有目标:保持stdarg接口不变,保证源码级兼容,并且实现变长参数的功能。一切都很美妙,世界一片光明,仿佛天堂就在眼前。

    ——是吗?

    我们之所以能够根据格式字符串中的参数类型,获取到调用时传入的值,建立在这样一个前提上,即,传入时的参数类型和格式字符串完全匹配,至少在整数和浮点数的数量和顺序上应当匹配。不然的话,stdargs甚至做不到按照顺序将浮点数输出为整数,而是直接会访问越界,产生的后果难以预测。

    回过头来看最前面的例子,可以发现的是其反映的正是这个现象。为什么输出的是:

    123123123 456456456 94356794144432 140193333050752

    现在的我们已经能够解释这个现象的产生原理了:在读到第一个%ld的时候,printf内部执行了va_arg(args, long),就从“第二个整数参数”(因为格式字符串是char *,也算一个)的rsi寄存器中进行读取,也就得到了123123123这个值;第二个%ld如法炮制,读到了“第三个整数参数”的rdx,也就是456456456

    当读到第三个%ld的时候,同样的道理,va_arg试图从“第四个整数参数”也就是rcx中取值,然而我们在调用printf的时候只给出了三个整数参数,没有给rcx赋值,从而va_arg读到的实际上是这个寄存器之前的值,其值没有明确的定义,大致上也可以理解为随机的了。第四个%ld则不再赘述,原理相同。


    至此,我们就基本讲完了这个问题在linux+x64下的解法,迫于篇(tou)幅(fa)所限,另外两种情况只能放到下次再讲了。

    课后习题(雾):

    1. 请读者根据linux+x64的调用惯例,写一个自己的stdargs.h,只需支持intchar *doublefloat更新:评论中指出实现完整的stdargs.h在x64下需要编译器支持来分析语法,不太可能自己实现。因此我们这里将题目约定为,变长参数之前正好有一个整数类型的参数,这样的话应该就能够实现了。
    2. 写一个自己的简单版printf来验证上一题中的stdargs.h可用。
    展开全文
  • /*PB POST可以正常上传数据,但是无法放入数组中(PB中不知道怎么指定MIME),PHP的$_POST[]数组和$_REQUEST[]无法取得值  请改用$GLOBALS['HTTP_RAW_POST_DATA']来获得提交的POST数据 account=a&name=b */ echo ...
    

    <?php
    /*PB POST可以正常上传数据,但是无法放入数组中(PB中不知道怎么指定MIME),PHP的$_POST[]数组和$_REQUEST[]无法取得值
     请改用$GLOBALS['HTTP_RAW_POST_DATA']来获得提交的POST数据

    account=a&name=b
    */
    echo $GLOBALS['HTTP_RAW_POST_DATA'];
    ?>


    注意:xml,json是必须用这个来接收。

    展开全文
  • JSON概念以及语法和怎么从JSON中取值

    千次阅读 2019-08-03 23:09:24
    什么是JSON JavaScript 对象表示法...4.括号保存数组 JSON的值的取值范围 1.数字(整型或者浮点) 2.字符串(用单引号或者双引号括起来) 3.逻辑值(true或者false) 4.数组括号) 5.对象(花括号)...

    什么是JSON

    JavaScript 对象表示法(JavaScript Object Notation),现在是存储和交换文本信息的语法

    JSON的语法格式

    1.数据在键值对中
    2.由逗号分割
    3.花括号保存对象
    4.中括号保存数组

    JSON的值的取值范围

    1.数字(整型或者浮点)
    2.字符串(用单引号或者双引号括起来)
    3.逻辑值(true或者false)
    4.数组(中括号)
    5.对象(花括号)
    6.null(很少用)

    具体例子

    基本格式
    在这里插入图片描述
    访问该页面
    说明键的值可以加引号也可不加。
    在这里插入图片描述
    嵌套格式1
    在这里插入图片描述
    访问该页面(因为只是html,可以直接访问,不用部署到服务器)
    在这里插入图片描述
    嵌套格式2
    在这里插入图片描述
    结果
    在这里插入图片描述

    从JSON中取值

    1.json对象.键名
    在这里插入图片描述
    在这里插入图片描述
    2.json对象[“键名”]
    在这里插入图片描述
    在这里插入图片描述

    3.数组对象[索引]
    在这里插入图片描述
    在这里插入图片描述
    4.遍历(遍历键的值)
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    5.遍历(遍历键和值)
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    6.遍历数组
    在这里插入图片描述
    结果
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    太多图了我就不放上来了(偷个懒)

    认真看看文档发现JSON还挺简单的,以前觉得它很神奇哈哈

    展开全文
  • 已知数组arr,现在需要标题栏按照月循环,假如17年1月至18年12月。假如数组arr的confirm_m项和标题的月相同时,则显示数组arr的confirm_m项对应confirm_value的值。 比如confirm_m为1804,则显示成这种形式: ...
  • 信任不是指没有误会而是总会给对方把误会解释清楚的机会python索引取值可以后往前取吗如果是取列表或者字符串的话,用切片应该是最简单的 例如:a = [1,2,3,4,5]print(a[-2:]) #结果是[4,5]小编总是勇敢的离开一...

    如何在python列表中查找某个元素的索引

    方法二:利用enumerate函数。信任不是指没有误会而是总会给对方把误会解释清楚的机会

    python中索引取值可以从后往前取吗

    35e9896eafb0ca28fca3d4b5b624d51b.png

    如果是取列表或者字符串的话,用切片应该是最简单的 例如:a = [1,2,3,4,5]print(a[-2:]) #结果是[4,5]小编总是勇敢的离开一个人却不懂如何巧妙的靠近一个人。

    b['state'].index("ohio") 返回TypeError: 'Int64Index' object is not 世上最酸的感觉不是吃醋,而是无权吃醋。吃醋也要讲名份,和他相爱的是另一个人,他的醋也就轮不到你吃,自有另一个人光明正大地吃醋。原来,吃不到的醋才是最酸的。

    直接上实例: df = pd.DataFrame(np.random.randn(5,3),index = list('abcde'),columns = ['one','two','three']) #创建一个数据框 df 内容 获取所有的列名,并形成列表:list(df.keys()) 获取所有的行名,并形成列表:list(df.index) 如果要获有那么一瞬间突然觉得,小编所有的等待在你眼里都没有意义,因为换不来你的任何珍惜,不是小编不懂得坚持,是太久没听到你的回音,所以这一次小编决定走了。

    python 怎么取dataframe的索引值真正的爱情需要等待谁都可以说爱你但不是人人都能等你。

    python如何将列表作为列表的索引

    比如 a=[1,2,3,4,5,6,7,8,9,10] b=[3,7,9] b 作为一个列表怎么作为索引自从会玩QQ后,小编发现小编拼音越来越好了,读书都没这效果

    python怎么获取列表元素的索引只有不爱你的人,才会把离开你的理由说得动听又感人,爱你的人就算被现实狠狠折磨,再见两个字也对你说不出。

    #用enumerate函数,最好不要用index ,因为列表有相同的元素的时候,index就傻冒了for i,j in enumerate(('a','b','c')): print i,j 0 a1 b2 c小编向来擅长自小编保护的方式是:一旦察觉对方冰冷的态度小编就会退避三舍,不会主动去捂热这段关系。

    Python如何从列表中找出所有目标元素的索引

    以下是小编写的程序,但是找不出bug在哪,分享解。 解释:myfind程序是要从ydef myfind(x,y): return [ a for a in range(len(y)) if y[a] == x]善良的另一个名字叫蠢货!很多事不是忍了就可以解决的。

    如何从python中的多索引数据中获取索引号有些风景只能喜欢却不能收藏,就像有的人只适合遇见却不适合久伴一样。

    Python中怎么获取二维数组中指定列的数据。

    python 列表获取下标时有个值不在列表中也被索引到...小编有一个列表nums = [4, 5, 6, 7, 0, 1, 2]当小编print(nums[3])时显示的...你的num是一个列表,列表的索引是指列表元素在列表中的位置。列表的第一个位置的索引是0,而不是1,所以num[3]=7,你的num最后的索引应该是6,所以num[8]肯定是不存在的。

    展开全文
  • js数组

    2010-12-07 21:25:09
    Array literals ...方法() 来取值和调用,而数组是通过下标来取值,Array Literals跟数组有很多相似,都是某数据类型的集合,但是Array Literals根本来说,是个对象,声明和调用,跟数组是有区别 ...
  • 最近看一本书叫《改善java程序的151个建议》,发现这么一个方法。 ``` public static int ...可是读者发现这个方法有问题,排序的是克隆对象,取值却还是原来的数组中取. 请教大家,这个方法应该到底怎么写。
  • 举个例子先建立一个5x5的二维数组多维的切片是按照各个维度分别取这里就分别输出第一维的2-3和第二维的3-5(索引0开始)。这里是行取全部,列取第3-5。这里应该是大家最疑惑的地方了,为什么列的参数改成None,输出...
  • 举个例子先建立一个5x5的二维数组多维的切片是按照各个维度分别取这里就分别输出第一维的2-3和第二维的3-5(索引0开始)。这里是行取全部,列取第3-5。这里应该是大家最疑惑的地方了,为什么列的参数改成Non...
  • ES6中的解构特性能让我们从对象(Object)或者是数组(Array)中取值的时候更方便,同时写出来的代码在...那在有这个特性之前,我们一般怎么从对象或数组中取值呢?来看看下面的代码: let list = [3, 5] let first
  • 每个结果的列储存在一个数组的单元,偏移量 0 开始。 注意,这里是0开始偏移,也就是说不能用字段名字来取值,只能用索引来取值,所以如下代码是取不到值的:while($row = mysql_fetch_row($res)){echo $row['...
  • 一.大数加法定义两个足够大的数字,其数值远超过long的取值范围,设该大数的位数有两百位,求其相加所得大数加法的核心思想详见此链接,内有详细的动画演示,这里不再...//逆序数组的最后一项开始查找,进行反逆...
  • 在上题的基础上可以得到一个整型数组数组元素的取值从0开始,如 a={0,1,2,0,1,1……} 要求求解上述数组的划分,算法如下: 1.选取数组元素的最大值max和最小值min 2.建立max-min+1个数组或字符串,将第i元素...
  • 然后循环队列中取值 也就是队首出队,访问 然后把左、右子树(注意:有顺序之分)加入到队列中 在每一层计算平均值,加入到结果数组res中 Python实现: class Solution: def averageOfLevels(self, root: ...
  • python 取列表偶数和奇数位置的值

    千次阅读 2020-08-08 09:21:19
    python的列表等于其他语言的数组 首先了解下一般取列表怎么按索引取值,也就是列表的切片: list[i:j] 就是从列表的索引 i 到索引j 个的值...list[1::2] 这里缺省了j 但是i定义了1 也就是从数组第二个数开始取 ,所
  • 前言:在前面我们介绍了二分查找,但是我们考虑一下,为什么一定要折半呢?而不是折四分之一或者更多...同样,比如要在取值范围在 0 ~ 10000 之间的100个元素从小到大均匀分布的数组中查找5,我们自然而然地先考虑数组
  • 同样,比如要在取值范围在 0 ~ 10000 之间的100个元素从小到大均匀分布的数组中查找5,我们自然而然地先考虑数组下标较小的开始查找。 以上的分析其实就是插值查找的思想,它是二分查找的改进。 基本思想: 根据要...
  • 插值查找

    2019-10-03 20:45:50
    在介绍插值查找之前,首先考虑一个问题,为什么二分查找算法中一定是折半,而不是折四分之一,或者折更多呢?...同样地,比如要在取值范围1~10000之间100个元素从小到大均匀分布的数组中查找5,我...
  • 那么我们大到小排序,那么第k个就是最小值最大的 然后如果有2个数组,我们怎么处理(结构体) 首先第一个我们还是那么处理,不过到k的时候我们就会取值,然后我们删除前面一个值,那么当前...
  • 10.10.6 sed输出设置shell变量 102 10.11 快速一行命令 102 10.12 小结 103 第11章 合并与分割 104 11.1 sort用法 104 11.1.1 概述 104 11.1.2 sort选项 104 11.1.3 保存输出 105 11.1.4 sort启动方式 ...
  • 差值查找—Java实现

    2018-08-20 16:19:10
    在介绍插值查找之前,首先考虑一个新问题,为什么上述算法一定要是折半,而不是折四分之一或者折更多呢? ... 同样的,比如要在取值范围1 ~ 10000 之间 100 个元素从小到大均匀分布的数组中查...
  • 3.4.8 找出数组中出现次数超过一半的数,现在有一个数组,已知一个数出现的次数超过了一半,请用O(n)的复杂度的算法找出这个数。 3.4.9 找出被修改过的数字 3.5.0 设计DNS服务器中cache的数据结构。要求设计一个...
  • 如果数组中的值才会触发 useEffect 第一个参数中的函数。返回值(如果有)则在组件销毁或者调用函数前调用。 <ol><li>比如第一个 useEffect 中,理解起来就是一旦 count 值发生改变,则修改 documen.title...
  • 经过跟踪变量值发现循环变量i的阀值pSysHead->dbf_count的数值为0xFFFFFFFF,该值是被破坏的内存数据库获取的,正常情况下该值小于127。而pDBFat是数据库的起始地址,如果pSysHead->dbf_count值异常过大,将...
  • 10.10.6 sed输出设置shell变量 102 10.11 快速一行命令 102 10.12 小结 103 第11章 合并与分割 104 11.1 sort用法 104 11.1.1 概述 104 11.1.2 sort选项 104 11.1.3 保存输出 105 11.1.4 sort启动方式 105 ...
  • 10.10.6 sed输出设置shell变量 102 10.11 快速一行命令 102 10.12 小结 103 第11章 合并与分割 104 11.1 sort用法 104 11.1.1 概述 104 11.1.2 sort选项 104 11.1.3 保存输出 105 11.1.4 sort启动方式 105 ...

空空如也

空空如也

1 2 3 4
收藏数 76
精华内容 30
关键字:

怎么从数组中取值