从SO 中的这篇文章中,很明显 C 支持负索引。
为什么要在程序中支持这种潜在的内存违规?
编译器不应该至少抛出一个负索引警告吗?(我正在使用 GCC)
或者这个计算是在运行时完成的?
编辑1: 有人可以暗示它的用途吗?
编辑2: 对于3。)使用数组/指针中的循环计数器
[]
表示索引的运行时计算。
计算在运行时完成。
负索引不一定会导致违规,并且有其用途。
例如,假设您有一个当前指向数组中第 10 个元素的指针。现在,如果您需要在不更改指针的情况下访问第 8 个元素,您可以使用负索引 -2 轻松完成。
char data[] = "01234567890123456789";
char* ptr = &data[9];
char c = ptr[-2]; // 7
这是一个使用示例。
无限脉冲响应过滤器部分地根据最近的先前输出值计算。通常,会有一些输入值数组和一个要放置输出值的数组。如果当前输出元素是 y i,则 y i可以计算为 y i = a 0 •x i + a 1 •x i–1 +a 2 •y i–1 +a 3 •y i–2。
为此编写代码的一种自然方式是:
void IIR(float *x, float *y, size_t n)
{
for (i = 0; i < n; ++i)
y[i] = a0*x[i] + a1*x[i-1] + a2*y[i-1] + a3*y[i-2];
}
观察什么时候i
是零,y[i-1]
并且y[i-2]
有负指数。在这种情况下,调用者负责创建一个数组,将最初的两个元素设置为输出的“起始值”(通常为零或从前一个缓冲区保留的值),并将指针传递给第一个新值的位置是要写的。因此,这个例程IRR
通常接收指向数组中间的指针并使用负索引来寻址某些元素。
为什么要在程序中支持这种潜在的内存违规?
因为它遵循指针算法,并且在某些情况下可能有用。
编译器不应该至少抛出一个负索引警告吗?(我正在使用 GCC)
array[10]
同样的原因,当您访问数组只有 10 个元素时,编译器不会警告您。因为它将这项工作留给了程序员。
或者这个计算是在运行时完成的?
是的,计算是在运行时完成的。
详细说明 Taymon 的回答:
float arr[10];
float *p = &arr[2];
p[-2]
现在完全可以了。我还没有看到负索引的良好用途,但是如果您通常无法确定您是否指向有效范围之外,那么为什么标准应该排除它。
数组下标只是解引用内存中任意位置的指针的语法糖。编译器无法就负索引向您发出警告,因为它在编译时不知道指针将指向的位置。任何给定的指针算术表达式可能会或可能不会导致内存访问的有效地址。
OP:为什么要支持……潜在的内存违规?
它有潜在的用途,因为正如 OP 所说,它是潜在的违规行为,而不是特定的内存违规行为。C 是关于允许用户做很多事情,包括他们需要吊死自己的所有绳子。
OP:......抛出一个负指数警告......
如果担心,请使用unsigned
index 或更好,请使用size_t
.
OP ...在运行时完成计算?
是的,经常像 中一样a[i]
,其中i
不是一个常数。
OP:暗示它的用途?
示例:一个人正在处理点数组 (Pt) 中的一个点,并且想要确定中点是否是删除的候选对象,因为它是重合的。假设调用函数已经确定Mid
既不是第一个点也不是最后一个点。
static int IsCoincident(Pt *Mid) {
Pt *Left = &Mid[-1]; // fixed negative index
Pt *Right = &Mid[+1];
return foo(Left, Mid, Right);
}
a[b]
做同样的事情*(a+b)
。由于后者允许否定b
,所以前者也是如此。
使用负数组索引的示例。
我使用负索引来检查消息协议。例如,一种协议格式如下所示:
<nnn/message/f>
或者,同样有效:
<nnn/message>
该参数f
是可选的,如果提供,则必须是单个字符。
如果我想获得 character 的值f
,我首先获得一个指向该>
角色的指针:
char * end_ptr = strchr(msg, '>');
char f_char = '1'; /* default value */
现在我检查是否f
提供并提取它(这里是使用负数组索引的地方):
if (end_ptr[-2] == '/')
{
f_char = end_ptr[-1];
}
请注意,我省略了错误检查和其他与此示例无关的代码。