我一直在读到,在 C 中,使用指针算术通常比数组访问的下标更快。即使使用现代(据称是优化的)编译器也是如此吗?
如果是这样,当我开始从学习 C 转向 Objective-C 和 Cocoa 时,情况是否仍然如此?在 Mac 上?
在 C 和 Objective-C 中,哪种数组访问的首选编码风格是?哪个(由各自语言的专业人士)认为更清晰、更“正确”(因为缺乏更好的术语)?
最佳答案
您需要了解这种说法背后的原因。你有没有问过自己为什么它更快?让我们比较一些代码:
int i;
int a[20];
// Init all values to zero
memset(a, 0, sizeof(a));
for (i = 0; i < 20; i++) {
printf("Value of %d is %d\n", i, a[i]);
}
它们都是零,真是令人惊讶:-P 问题是,a[i]
意味着什么?实际上是低级机器代码?它的意思是a
在内存中。i
乘以单个项目的大小 a
到那个地址(int 通常是四个字节)。a
获取一个值时,a
的基地址与 i
的乘法结果相加由四。如果只是解引用一个指针,则不需要执行步骤1.和2.,只需执行步骤3。int i;
int a[20];
int * b;
memset(a, 0, sizeof(a));
b = a;
for (i = 0; i < 20; i++) {
printf("Value of %d is %d\n", i, *b);
b++;
}
此代码可能更快......但即使是这样,差异也很小。为什么会更快? “*b”与上述第 3 步相同。但是,"b++"与步骤 1. 和步骤 2 不同。 "b++"将指针增加 4。(important for newbies: running
++
on a pointer will not increase the pointer one byte in memory! It will increase the pointer by as many bytes in memory as the data it points to is in size. It points to anint
and theint
is four bytes on my machine, so b++ increases b by four!)
i
快按四并将其添加到指针。在任何一种情况下,您都有一个加法,但在第二个中,您没有乘法(您避免了一次乘法所需的 CPU 时间)。考虑到现代 CPU 的速度,即使阵列是 1 个 mio 元素,我想知道您是否真的可以对差异进行基准测试。-Os
,这意味着优化代码大小和速度,但不要做会显着增加代码大小的速度优化,不像-O2
,更不像-O3
):_main:
pushl %ebp
movl %esp, %ebp
pushl %edi
pushl %esi
pushl %ebx
subl $108, %esp
call ___i686.get_pc_thunk.bx
"L00000000001$pb":
leal -104(%ebp), %eax
movl $80, 8(%esp)
movl $0, 4(%esp)
movl %eax, (%esp)
call L_memset$stub
xorl %esi, %esi
leal LC0-"L00000000001$pb"(%ebx), %edi
L2:
movl -104(%ebp,%esi,4), %eax
movl %eax, 8(%esp)
movl %esi, 4(%esp)
movl %edi, (%esp)
call L_printf$stub
addl $1, %esi
cmpl $20, %esi
jne L2
addl $108, %esp
popl %ebx
popl %esi
popl %edi
popl %ebp
ret
与第二个代码相同:_main:
pushl %ebp
movl %esp, %ebp
pushl %edi
pushl %esi
pushl %ebx
subl $124, %esp
call ___i686.get_pc_thunk.bx
"L00000000001$pb":
leal -104(%ebp), %eax
movl %eax, -108(%ebp)
movl $80, 8(%esp)
movl $0, 4(%esp)
movl %eax, (%esp)
call L_memset$stub
xorl %esi, %esi
leal LC0-"L00000000001$pb"(%ebx), %edi
L2:
movl -108(%ebp), %edx
movl (%edx,%esi,4), %eax
movl %eax, 8(%esp)
movl %esi, 4(%esp)
movl %edi, (%esp)
call L_printf$stub
addl $1, %esi
cmpl $20, %esi
jne L2
addl $124, %esp
popl %ebx
popl %esi
popl %edi
popl %ebp
ret
嗯,这是不同的,这是肯定的。 104 和 108 的数字差异来自变量 b
(在第一个代码中,堆栈上少了一个变量,现在我们多了一个,改变了堆栈地址)。 for
中的真实代码差异循环是movl -104(%ebp,%esi,4), %eax
相比movl -108(%ebp), %edx
movl (%edx,%esi,4), %eax
实际上在我看来,第一种方法似乎更快(!),因为它发出一个 CPU 机器代码来执行所有工作(CPU 为我们完成所有工作),而不是有两个机器代码。另一方面,下面的两个汇编命令的运行时间可能比上面的要短。i
乘以 4,然后在获取值之前将两者相加),这些语句曾经是绝对真理古往今来越来越值得怀疑。还有谁知道 CPU 内部是如何工作的?上面我将一个组装指令与另外两个指令进行了比较。NSArray
或 NSMutableArray
),这是完全不同的野兽。但是,在这种情况下,无论如何您都必须使用方法访问这些数组,没有可供选择的指针/数组访问。
https://stackoverflow.com/questions/233148/