在 C99 中指向数组开始之前的元素

pointing to element before array start in C99

我有一个整数数组:

int* counters = (int *) calloc(N, sizeof(int));

必须使用基于一个的索引进行索引,例如第一个元素的索引为 1,第二个元素的索引为 2,等等。由于性能非常重要,我决定使用一个技巧:

int* oneIndexedCounters = counters - 1;

允许我使用基于 1 的索引而不从索引中减去 1:

// A[i] - contains one based indexes
for (int i = 0; i < K; i++) {
    oneIndexedCounters[A[i]] += B[i]; // many times in code
    // some other operations on oneIndexedCounters
}

代替:

for (int i = 0; i < K; i++) {
    counters[A[i]-1] += B[i];
    // ...
}

counters 数组由我的函数返回,所以我无法在数组开头分配虚拟元素。

当您不取消引用该指针时,指向数组之前的一个元素是否有效(例如,当数组位于内存页面边界上时)? 或者是否有其他不那么棘手且性能良好的解决方案?

Is pointing one element before array valid (for example when array is on memory page boundary) when you are not dereferencing that pointer?

不,无效。

int* oneIndexedCounters = counters - 1;

counters - 1 未指向有效对象,该操作调用未定义的行为。

(C99, 6.5.6p8) "If both the pointer operand and the result point to elements of the same array object, or one past the last element of the array object, the evaluation shall not produce an overflow; otherwise, the behavior is undefined."

正如 正确指出的那样,从指向数组对象初始元素的指针中减去 1 具有未定义的行为——即使您没有取消引用它。 (我记得,"Numeric Recipes in C" 的某些版本使用这种技术来模拟 Fortran 数组语义。如果你“幸运”,它可以 "work"。)

有一个替代方案:分配一个比您需要的长 1 个元素的数组,并通过指向第二个元素(元素 1)的指针访问它的元素。

例如:

int *counters = calloc(N+1, sizeof(*counters));

然后您可以通过 counters[N] 安全地访问 counters[1],就像您有一个从 1 开始的数组一样。唯一的缺点是您有一个从未使用过的附加元素 0。如果它是一些相对较小类型的数组,那应该无关紧要。

关于calloc的题外话:

严格来说,calloc() 不能保证将浮点对象初始化为 0.0,或指向 NULL 的指针。实际上可以保证将整数初始化为 0; post-C99 技术勘误和 C11 中明确了此保证。如果您碰巧知道您的系统将浮点 0.0 和空指针表示为全零,那么使用 calloc 可能没问题——但我会添加一条注释来明确假设. 0.0NULL使用全零位的系统很少见。 100% 可移植性的重要性由您决定。