【发布时间】:2020-08-16 23:55:37
【问题描述】:
我最近注意到,在 C 中,array 和 &array 之间有一个重要区别,如下声明:
char array[] = {4, 8, 15, 16, 23, 42};
前者是指向一个字符的指针,而后者是一个指向6个字符数组的指针。同样值得注意的是,写作a[b] 是*(a + b) 的语法糖。事实上,你可以写2[array],它完全符合标准。
所以我们可以利用这些信息来写这个:
char last_element = (&array)[1][-1];
&array 的大小为 6 个字符,因此 (&array)[1]) 是指向位于数组后面的字符的指针。因此,通过查看[-1],我正在访问最后一个元素。
有了这个我可以例如交换整个数组:
void swap(char *a, char *b) { *a ^= *b; *b ^= *a; *a ^= *b; }
int main() {
char u[] = {1,2,3,4,5,6,7,8,9,10};
for (int i = 0; i < sizeof(u) / 2; i++)
swap(&u[i], &(&u)[1][-i - 1]);
}
这种最后访问数组的方法有缺陷吗?
【问题讨论】:
-
FWIW,像
void swap(char *a, char *b) { *a ^= *b; *b ^= *a; *a ^= *b; }这样的过于“聪明”的代码几乎总是比只使用临时值执行明显的代码效率低。 “从a加载寄存器1,从b加载寄存器2,玩按位操作(希望不是直接到内存!),然后存储值”比“从a加载寄存器1”效率低很多,从b加载寄存器2,将r2存储在a并将r1存储在b"。如果编译器没有优化“聪明”的代码,它总共可以执行 9 次加载/存储。而不是使用临时的 6 个最坏情况。 -
即使中间值是OOB,它总是计算一个有效的地址,所以应该没问题。
-
虽然显示的代码没有,但请注意当 a == b 时 swap() 会做什么。
-
sizeof char u[] 工作正常,但如果您要使用 alloc 系列分配数组,那么找到大小并因此使用这个技巧并非易事。在这种特殊情况下,您将其分配到堆栈上,您可以使用此构造。
-
恕我直言,缺陷在于它不可读,因此对您的程序员同事来说可维护性较差。破译正在发生的事情的认知努力依赖于对 C 类型系统的深入了解和假设。任何时候我都更喜欢
sizeof表达式。
标签: c arrays pointers language-lawyer