我有一个整数数组:
int* counters = (int *) calloc(N, sizeof(int));
必须使用一个基于索引的索引,例如第一个元素有索引1,第二个元素有索引2,等等。由于性能非常重要,我决定使用一个技巧:
int* oneIndexedCounters = counters - 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
数组,因此我无法在数组开头分配虚拟元素。
当你没有取消引用那个指针时,是否在数组有效之前指向一个元素(例如当数组在内存页边界上时)? 或者是否有其他解决方案不那么棘手并且提供了良好的性能?
答案 0 :(得分:4)
当你没有取消引用那个指针时,是否在数组有效之前指向一个元素(例如当数组在内存页边界上时)?
不,它无效。
int* oneIndexedCounters = counters - 1;
counters - 1
未指向有效对象,该操作会调用未定义的行为。
(C99,6.5.6p8)“如果指针操作数和结果都指向元素 相同的数组对象,或者超过数组最后一个元素的数组 对象,评估不得产生溢出;否则, 行为未定义。“
答案 1 :(得分:0)
正如ouah's answer正确指出的那样,从指向数组对象的初始元素的指针中减去1具有未定义的行为 - 即使您没有取消引用它。 (我记得有些版本的&#34; C&#34中的数字食谱;使用这种技术来模拟Fortran数组语义。它可以&#34;工作&#34;如果你很幸运。 )
还有一种方法:分配比你需要的更长的数组1元素,并通过指向第二个元素(元素1)的指针访问它的元素。
例如:
int *counters = calloc(N+1, sizeof(*counters));
然后,您可以通过counters[1]
安全地访问counters[N]
,就像您有一个基于1的数组一样。唯一的缺点是你有一个你从不使用的额外元素0。如果它是一个相对较小类型的数组,那不重要。
calloc
上的题外话:
严格地说,calloc()
并不保证将浮点对象初始化为0.0
,或指向NULL
。实际上保证将整数初始化为0
;该保证在C99后技术勘误和C11中明确规定。如果您碰巧知道您的系统表示浮点0.0
并将空指针表示为全位零,那么可能可以使用calloc
- 但我要添加评论使假设明确。 不使用0.0
和NULL
的所有位为零的系统很少见。由您来决定100%可移植性的重要性。