C中是否允许使用负数组索引?

时间:2010-08-13 03:23:14

标签: c arrays

我刚刚阅读了一些代码,发现该人正在使用arr[-2]访问arr之前的第二个元素,如下所示:

|a|b|c|d|e|f|g|
       ^------------ arr[0]
         ^---------- arr[1]
   ^---------------- arr[-2]

这是允许的吗?

我知道arr[x]*(arr + x)相同。所以arr[-2]*(arr - 2),这似乎没问题。你觉得怎么样?

9 个答案:

答案 0 :(得分:156)

这是正确的。从C99§6.5.2.1/ 2:

  

下标的定义   operator []是E1 [E2]   与(*((E1)+(E2)))相同。

没有魔力。它是1-1等价的。与取消引用指针(*)时一样,您需要确保它指向有效的地址。

答案 1 :(得分:62)

仅当arr是指向数组中的第二个元素或更高元素的指针时,此方法才有效。否则,它无效,因为您将访问数组边界之外的内存。所以,例如,这是错误的:

int arr[10];

int x = arr[-2]; // invalid; out of range

但这没关系:

int arr[10];
int* p = &arr[2];

int x = p[-2]; // valid:  accesses arr[0]

然而,使用负下标是不常见的。

答案 2 :(得分:10)

听起来不错。但是,你很难合理地需要它。

答案 3 :(得分:7)

arr可能指向数组的中间位置,因此arr[-2]指向原始数组中的某些内容而不会越界。

答案 4 :(得分:7)

我不确定这是多么可靠,但我刚刚在64位系统(LP64推测)上阅读了关于负数组索引的以下警告:http://www.devx.com/tips/Tip/41349

作者似乎在说,具有64位寻址的32位int数组索引可能导致错误的地址计算,除非数组索引被明确提升为64位(例如,通过ptrdiff_t强制转换)。我实际上已经看到了他的性质的错误与PowerPC版本的gcc 4.1.0,但我不知道它是否是编译器错误(即应该按照C99标准工作)或正确的行为(即索引需要转换为64用于正确行为的位?)

答案 5 :(得分:1)

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

我记得编译器设计原理, 假设a是一个int数组,int的大小是2, &安培; a的基址是1000。

a[5]如何运作 - >

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

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

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

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

它将在990位置返回我的对象​​。 通过这种逻辑,我们可以在C中访问Array中的负索引。

答案 6 :(得分:1)

关于为什么有人想要使用负面索引,我在两种情况下使用过它们:

  1. 有一个组合数字表,告诉你comb [1] [ - 1] = 0;您可以在访问表之前检查索引,但这样代码看起来更干净,执行速度更快。

  2. 将一个中心放在桌子的开头。例如,你想要使用类似的东西

     while (x < a[i]) i--;
    
  3. 但是你应该检查i是否为正 解决方案:设置为a[-1]-DBLE_MAX,以便x&lt;a[-1]始终为false。

答案 7 :(得分:0)

#include<stdio.h>
int main()// negative index
{ int i=1, a[5]= {10,20,30,40,50};
int* mid = &a[5]; //legal;address,not element there
for(;i<6;++i)
printf(" mid[ %d ] = %d;", -i, mid[-i]);
}

答案 8 :(得分:-1)

我想分享一个例子:

GNU C++ 库 basic_string.h

[注意:正如有人指出这是一个“C++”示例,它可能不适合“C”这个主题。我写了一个“C”代码,它与示例具有相同的概念。至少,GNU gcc 编译器没有抱怨任何东西。]

它使用 [-1] 将指针从用户字符串移回管理信息块。因为它有足够的空间分配一次内存。

说 ” * 这种方法的巨大优势是字符串对象 * 只需要一个分配。 所有的丑陋都被限制住了 * 在单个 %pair 内联函数中,每个内联函数都编译为 * 单个@a 添加指令:_Rep::_M_data(),和 * string::_M_rep();以及得到一个的分配函数 * 原始字节块和足够的空间并构造一个 _Rep * 对象在前面。 "

源代码: https://gcc.gnu.org/onlinedocs/gcc-10.3.0/libstdc++/api/a00332_source.html

   struct _Rep_base
   {
     size_type               _M_length;
     size_type               _M_capacity;
     _Atomic_word            _M_refcount;
   };

   struct _Rep : _Rep_base
   {
      ...
   }

  _Rep*
   _M_rep() const _GLIBCXX_NOEXCEPT
   { return &((reinterpret_cast<_Rep*> (_M_data()))[-1]); }

说明:

*  A string looks like this:
*
*  @code
*                                        [_Rep]
*                                        _M_length
*   [basic_string<char_type>]            _M_capacity
*   _M_dataplus                          _M_refcount
*   _M_p ---------------->               unnamed array of char_type
*  @endcode
*
*  Where the _M_p points to the first character in the string, and
*  you cast it to a pointer-to-_Rep and subtract 1 to get a
*  pointer to the header.
*
*  This approach has the enormous advantage that a string object
*  requires only one allocation.  All the ugliness is confined
*  within a single %pair of inline functions, which each compile to
*  a single @a add instruction: _Rep::_M_data(), and
*  string::_M_rep(); and the allocation function which gets a
*  block of raw bytes and with room enough and constructs a _Rep
*  object at the front.
*
*  The reason you want _M_data pointing to the character %array and
*  not the _Rep is so that the debugger can see the string
*  contents. (Probably we should add a non-inline member to get
*  the _Rep for the debugger to use, so users can check the actual
*  string length.)
*
*  Note that the _Rep object is a POD so that you can have a
*  static <em>empty string</em> _Rep object already @a constructed before
*  static constructors have run.  The reference-count encoding is
*  chosen so that a 0 indicates one reference, so you never try to
*  destroy the empty-string _Rep object.
*
*  All but the last paragraph is considered pretty conventional
*  for a C++ string implementation.

//使用之前的概念,写一个示例C代码

#include "stdio.h"
#include "stdlib.h"
#include "string.h"

typedef struct HEAD {
    int f1;
    int f2;
}S_HEAD;

int main(int argc, char* argv[]) {
    int sz = sizeof(S_HEAD) + 20;

    S_HEAD* ha = (S_HEAD*)malloc(sz);
    if (ha == NULL)
      return -1;

    printf("&ha=0x%x\n", ha);

    memset(ha, 0, sz);

    ha[0].f1 = 100;
    ha[0].f2 = 200;

    // move to user data, can be converted to any type
    ha++;
    printf("&ha=0x%x\n", ha);

    *(int*)ha = 399;

    printf("head.f1=%i head.f2=%i user data=%i\n", ha[-1].f1, ha[-1].f2, *(int*)ha);

    --ha;
    printf("&ha=0x%x\n", ha);

    free(ha);

    return 0;
}



$ gcc c1.c -o c1.o -w
(no warning)
$ ./c1.o 
&ha=0x13ec010
&ha=0x13ec018
head.f1=100 head.f2=200 user data=399
&ha=0x13ec010

库作者使用它。可能会有所帮助。