我刚刚阅读了一些代码,发现该人正在使用arr[-2]
访问arr
之前的第二个元素,如下所示:
|a|b|c|d|e|f|g|
^------------ arr[0]
^---------- arr[1]
^---------------- arr[-2]
这是允许的吗?
我知道arr[x]
与*(arr + x)
相同。所以arr[-2]
是*(arr - 2)
,这似乎没问题。你觉得怎么样?
答案 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)
关于为什么有人想要使用负面索引,我在两种情况下使用过它们:
有一个组合数字表,告诉你comb [1] [ - 1] = 0;您可以在访问表之前检查索引,但这样代码看起来更干净,执行速度更快。
将一个中心放在桌子的开头。例如,你想要使用类似的东西
while (x < a[i]) i--;
但是你应该检查i
是否为正
解决方案:设置为a[-1]
为-DBLE_MAX
,以便x<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
库作者使用它。可能会有所帮助。