首页 文章

对于数组,为什么a [5] == 5 [a]?

提问于
浏览
1474

正如乔尔在Stack Overflow podcast #34中指出的那样,在C Programming Language(又名:K&R)中,在C中提到了数组的这个属性: a[5] == 5[a]

乔尔说它明白了.419660_ t . Why does a[5] == 5[a]

17 回答

  • 17

    因为数组访问是根据指针定义的 . a[i] 被定义为 *(a + i) ,这是可交换的 .

  • 274

    不错的问题/答案 .

    只是想指出C指针和数组不一样,虽然在这种情况下差异并不重要 .

    请考虑以下声明:

    int a[10];
    int* p = a;
    

    在a.out中,符号a位于数组开头的地址处,符号p位于存储指针的地址处,而该存储器位置处的指针值是数组的开头 .

  • 21

    从字面上回答这个问题 . x == x 并不总是如此

    double zero = 0.0;
    double a[] = { 0,0,0,0,0, zero/zero}; // NaN
    cout << (a[5] == 5[a] ? "true" : "false") << endl;
    

    版画

    false
    
  • 0

    指针类型

    1)指向数据的指针

    int *ptr;
    

    2)const指向数据的指针

    int const *ptr;
    

    3)const指向const数据的指针

    int const *const ptr;
    

    并且数组是我们列表中的类型(2)
    当你在那个指针中 define an arrayaddress is initialize
    我们知道我们可以在编译时抛出 ERROR

    我发现的主要区别是......

    我们可以通过地址重新初始化指针,但不能用数组重新初始化指针 .

    ======
    并回到你的问题......
    a [5]只不过是*(a 5)
    you can understand easily by
    a - 包含地址(人们称之为基地址)就像我们列表中的(2)类型的指针一样
    [] - 该操作符可以用指针*替换 .

    所以最后......

    a[5] == *(a +5) == *(5 + a) == 5[a]
    
  • 14

    在c编译器中

    a[i]
    i[a]
    *(a+i)
    

    是不同的方式来引用数组中的元素! (不是所有的WEIRD)

  • 188

    我认为其他答案正在忽略某些事情 .

    是的, p[i] 在定义上等同于 *(p+i) ,它(因为加法是可交换的)相当于 *(i+p) ,它(再次由 [] 运算符的定义)等效于 i[p] .

    (在 array[i] 中,数组名称被隐式转换为指向数组第一个元素的指针 . )

    但在这种情况下,加法的交换性并不是那么明显 .

    当两个操作数具有相同的类型,或者甚至是被提升为共同类型的不同数字类型时,交换性就非常有意义: x + y == y + x .

    但在这种情况下,我们特别谈论指针算法,其中一个操作数是指针而另一个是整数 . (整数整数是一个不同的操作,指针指针是无意义的 . )

    C标准对 + 运算符的描述(N1570 6.5.6)说:

    另外,两个操作数都应具有算术类型,或者一个操作数应是指向完整对象类型的指针,另一个操作数应具有整数类型 .

    它可以很容易地说:

    另外,两个操作数都应具有算术类型,或者左操作数应为指向完整对象类型的指针,右操作数应为整数类型 .

    在这种情况下, i + pi[p] 都是非法的 .

    在C语言中,我们确实有两组重载的 + 运算符,可以松散地描述为:

    pointer operator+(pointer p, integer i);
    

    pointer operator+(integer i, pointer p);
    

    其中只有第一个是真正必要的 .

    那么为什么会这样呢?

    C从C继承了这个定义,它从B得到它(数组索引的交换性在1972年Users' Reference to B中明确提到),它是从BCPL(1967年的手册)得到的,它甚至可以从早期的语言中得到它(CPL) ?Algol?) .

    因此,数组索引是根据加法来定义的,并且即使是指针和整数,这种加法也是可交换的,可以追溯到C的祖先语言几十年 .

    这些语言的类型远不如现代C语言 . 特别是,指针和整数之间的区别经常被忽略 . (在将 unsigned 关键字添加到语言之前,早期的C程序员有时会使用指针作为无符号整数 . )因此,由于操作数不同类型而使加法不可交换的想法可能不会达到防止它的语言 .

    多年来,对该规则的任何更改都会破坏现有代码(尽管1989 ANSI C标准可能是一个很好的机会) .

    改变C和/或C要求将指针放在左边,而整数放在右边可能会破坏一些现有代码,但不会损失真正的表达能力 .

    所以现在我们有 arr[3]3[arr] 意思完全相同,尽管后一种形式永远不会出现在IOCCC之外 .

  • 51

    而且当然

    ("ABCD"[2] == 2["ABCD"]) && (2["ABCD"] == 'C') && ("ABCD"[2] == 'C')
    

    这个的主要原因是在70 's when C was designed, computers didn' t中有很多内存(64KB很多),所以C编译器没有做太多的语法检查 . 因此“ X[Y] " was rather blindly translated into " *(X+Y)

    这也解释了“ += " and " ++ " syntaxes. Everything in the form " A = B + C ”具有相同的编译形式 . 但是,如果B与A是同一个对象,然后可以进行装配级别优化 . 但编译器不够明亮,无法识别它,因此开发人员必须( A += C ) . 类似地,如果 C1 ,则可以使用不同的程序集级别优化,并且开发人员必须再次使其显式化,因为编译器无法识别它 . (最近的编译器会这样做,所以这些天的语法很大程度上是不必要的)

  • 0

    我知道这个问题已得到解答,但我无法抗拒分享这个解释 .

    我记得编译器设计的原理,让我们假设 a 是一个 int 数组, int 的大小是2个字节, a 的基地址是1000 .

    a[5] 将如何运作 - >

    Base Address of your Array a + (5*size of(data type for array a))
    i.e. 1000 + (5*2) = 1010
    

    所以,

    类似地,当c代码被分解为3地址代码时, 5[a] 将变为 - >

    Base Address of your Array a + (size of(data type for array a)*5)
    i.e. 1000 + (2*5) = 1010
    

    所以基本上两个语句都指向内存中的相同位置,因此, a[5] = 5[a] .

    这个解释也是数组中负数索引在C中工作的原因 .

    即如果我访问 a[-5] 它会给我

    Base Address of your Array a + (-5 * size of(data type for array a))
    i.e. 1000 + (-5*2) = 990
    

    它将在990位置返回我的对象 .

  • 1757

    在C.

    int a[]={10,20,30,40,50};
     int *p=a;
     printf("%d\n",*p++);//output will be 10
     printf("%d\n",*a++);//will give an error
    

    指针是一个“变量”

    数组名称是“助记符”或“同义词”

    p++; 有效但 a++ 无效

    a[2] 等于2 [a],因为两者的内部操作都是

    “指针算术”内部计算为

    *(a+3) 等于 *(3+a)

  • 4

    C arrays中, arr[3]3[arr] 是相同的,它们的等效指针符号是 *(arr + 3)*(3 + arr) . 但相反 [arr]3[3]arr 不正确会导致语法错误,因为 (arr + 3)*(3 + arr)* 不是有效的表达式 . 原因是解除引用操作符应该放在表达式产生的地址之前,而不是在地址之后 .

  • 4

    不是答案,而只是一些思考的食物 . 如果类具有重载索引/下标运算符,则表达式 0[x] 将不起作用:

    class Sub
    {
    public:
        int operator [](size_t nIndex)
        {
            return 0;
        }   
    };
    
    int main()
    {
        Sub s;
        s[0];
        0[s]; // ERROR 
    }
    

    由于我们无法访问 int 类,因此无法执行此操作:

    class int
    {
       int operator[](const Sub&);
    };
    
  • 23

    它在Ted Jensen的C指南和阵列中有很好的解释 .

    Ted Jensen将其解释为:

    实际上,这是事实,即无论何时写一个[i]它都可以用*(a i)代替而没有任何问题 . 实际上,编译器将在任何一种情况下创建相同的代码 . 因此,我们看到指针算法与数组索引相同 . 两种语法都会产生相同的结果 . 这并不是说指针和数组是相同的,它们不是 . 我们只是说要识别数组的给定元素,我们可以选择两种语法,一种使用数组索引,另一种使用指针算法,产生相同的结果 . 现在,看一下这个最后一个表达式,它的一部分......(a i),是一个使用运算符和C状态规则的简单加法,这样的表达式是可交换的 . 即(a i)与(i a)相同 . 因此,我们可以像*(a i)一样轻松地编写*(i a) . 但*(我)可能来自我[a]!从所有这一切出现了一个奇怪的事实:if:char a [20];
    写一个[3] ='x';
    与写3 [a] ='x'相同;

  • 48

    C标准定义 [] 运算符如下:

    a[b] == *(a + b)

    因此 a[5] 将评估为:

    *(a + 5)
    

    5[a] 将评估为:

    *(5 + a)
    

    a 是指向数组第一个元素的指针 . a[5] 是从 a 进一步的值 elements ,与 *(a + 5) 相同,从小学数学我们知道它们是相等的(加法是commutative) .

  • 6

    我只是发现这个丑陋的语法可能是“有用的”,或者至少非常有趣,当你想要处理一个引用同一数组的位置的索引数组时 . 它可以替换嵌套的方括号,使代码更具可读性!

    int a[] = { 2 , 3 , 3 , 2 , 4 };
    int s = sizeof a / sizeof *a;  //  s == 5
    
    for(int i = 0 ; i < s ; ++i) {  
    
               cout << a[a[a[i]]] << endl;
               // ... is equivalent to ... 
               cout << i[a][a][a] << endl;  // but I prefer this one, it's easier to increase the level of indirection (without loop)
    
    }
    

    当然,我很确定实际代码中没有用例,但无论如何我发现它很有趣:)

  • 9

    对于C中的指针,我们有

    a[5] == *(a + 5)
    

    并且

    5[a] == *(5 + a)
    

    因此确实 a[5] == 5[a].

  • 196

    嗯,这是一个功能,只有语言支持才有可能 .

    编译器将 a[i] 解释为 *(a+i) ,表达式 5[a] 求值为 *(5+a) . 由于加法是可交换的,事实证明两者是相等的 . 因此表达式的计算结果为 true .

  • -3

    有一件事似乎没有人提到有关黛娜的问题 sizeof

    您只能向指针添加整数,不能将两个指针一起添加 . 这样,当向整数添加指针或向指针添加整数时,编译器总是知道哪个位具有需要考虑的大小 .

相关问题