V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
z0z
V2EX  ›  分享发现

原来 c++竟然支持数组下标为负

  •  
  •   z0z · 2018-04-04 18:45:34 +08:00 via iPhone · 5100 次点击
    这是一个创建于 2185 天前的主题,其中的信息可能已经有所发展或是发生改变。
    毫无疑问的,这暴露了我是一个 c++盲的事实。

    不知道为什么我实在是有些兴奋。
    15 条回复    2018-04-20 19:42:59 +08:00
    MeteorCat
        1
    MeteorCat  
       2018-04-04 18:51:57 +08:00 via Android
    其实数组是可以为负的,Redis 有数组-1 位移运算的用法,当然这种高端黑科技用法不推荐
    MeteorCat
        2
    MeteorCat  
       2018-04-04 18:58:33 +08:00 via Android
    redis 源码的 sds.h 函数 sds_len 有这个方面实践,这算是比较偏门的黑魔法吧
    z0z
        3
    z0z  
    OP
       2018-04-04 19:07:17 +08:00
    @MeteorCat 嗯。


    1 #include<stdio.h>
    2 #include<stdlib.h>
    3 #include<stdint.h>
    4 #include<string.h>
    5
    6
    7 int main(void)
    8 {
    9 uint8_t arrary[8]={0,1,2,3,4,5,6,7};
    10 uint8_t *p=&arrary[4];
    11
    12 printf("p[-1]=%d\n",p[-1]);
    13
    14 return 0;
    15 }

    xxxxxxx@server5:~/temp/testarray$ ./test
    p[-1]=3

    刚试了一下,c 也支持。
    原来编译器还有这种功能。
    。。。。
    MeteorCat
        4
    MeteorCat  
       2018-04-04 19:09:56 +08:00 via Android
    @z0z 很好玩的特性,但是很容易就玩脱了,我感觉在 C++11 慢慢上来的时候,能够用 vector 这种容器就用得了
    Librazy
        5
    Librazy  
       2018-04-04 19:13:22 +08:00 via Android   ❤️ 1
    C++默认的下标表达式 E1[E2] 等同于表达式 *(E1 + E2) ,所以只要解引用 E1 + E2 是合法的就可以了
    Librazy
        6
    Librazy  
       2018-04-04 19:15:39 +08:00 via Android
    如何理解完全等同:试试 -1[p] 就知道了
    sfqtsh
        7
    sfqtsh  
       2018-04-04 19:21:26 +08:00 via Android
    这都可以的:
    int i = 2;
    char c = "abcdefg"[i];
    bumz
        8
    bumz  
       2018-04-04 19:33:48 +08:00
    int * a = new int[4] + 2;

    然后愉快地

    a[-2], a[-1], a[0], a[1]

    2333
    aheadlead
        9
    aheadlead  
       2018-04-04 19:58:10 +08:00
    火星了…

    C 你写成这样也可以:

    char *str="233333";
    putchar(1[str]);
    h4lbhg1G
        10
    h4lbhg1G  
       2018-04-04 20:00:49 +08:00
    似乎可以这么写

    int a[5]={0,1,2,3,4};
    2[a]=1[a]+0[a];
    Akiyu
        11
    Akiyu  
       2018-04-04 20:02:13 +08:00   ❤️ 1
    我也是才知道,之前没有做过这种骚操作
    之所以说是"骚操作",是因为你不知道那个地址里面是什么东西,类似于按照数组的类型去解释未知内存一样

    理解了原理就知道了
    它是拿着那个整数做偏移,然后按照数组的类型来拿取和解释那块内存(占多大,是如何存储的)

    引申:
    如果对汇编感兴趣的话就知道各种数组取值的不同写法,就像楼上的 1[p]一样
    idata+[寄存器], 寄存器+idata, 寄存器+寄存器+idata...(具体可能有误, 我不记得细节了
    不过都是那种概念,就是表示偏移地址寻址, 本质都是一样的,具体可以参照<<汇编语言>>)

    PS:
    如果楼主对这方面感兴趣的话,可以看看<<汇编语言>> (汇编有 x86 和 ARM,
    看完之后你会一边内心叫着 MMP, 一边想着底层真有趣)
    h4lbhg1G
        12
    h4lbhg1G  
       2018-04-04 20:06:44 +08:00
    似乎可以这么写

    嗯 看到上面的字符串 我想起一个黑魔法了

    char* str1="12345678";
    char* str2="abcdefghi";

    char* p=str2;

    for(int i=0;i<strlen(str1);i++)putchar((-i)[p]);
    gnaggnoyil
        13
    gnaggnoyil  
       2018-04-05 06:37:22 +08:00
    @h4lbhg1G 你这么写就不对了……指针运算其结果必须要么处于合法的数组或者 malloc 出来的空间的内部,要么是这个空间紧跟着的下一个地址,否则是未定义行为.
    h4lbhg1G
        14
    h4lbhg1G  
       2018-04-20 19:29:55 +08:00
    @gnaggnoyil #13 刚刚才看到 这就是假设两个字符串常量被紧凑放在内存里,如果编译器不这么按顺序连续分配内存(不太清楚 C 标准具体有没有规定)当然就不行了。

    有点函数内部用内联汇编的感觉吧。虽然我是感觉这种代码顶多只会在逆向破解的 poc 程序中出现
    gnaggnoyil
        15
    gnaggnoyil  
       2018-04-20 19:42:59 +08:00
    @h4lbhg1G
    >如果编译器不这么按顺序连续分配内存(不太清楚 C 标准具体有没有规定)

    当然没有.这玩意在标准中如果不是 implementation defined 那就是 unspecified.不过这不是关键点.关键点在于,只要你这段代码出现了越界的未定义行为,不管这两段 storage 的排列是如何,编译器的最终行为也是没有定义,不可预料的.
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   1040 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 30ms · UTC 19:19 · PVG 03:19 · LAX 12:19 · JFK 15:19
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.