gpt4 book ai didi

objective-c - 通过指针运算与 C 中的下标访问数组值

转载 作者:IT老高 更新时间:2023-10-28 11:46:08 25 4
gpt4 key购买 nike

我一直在读到,在 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 thepointer one byte in memory! It willincrease the pointer by as many bytesin memory as the data it points to isin size. It points to an int and theint is four bytes on my machine, so b++increases b by four!)


    好的,但为什么它会更快?因为给一个指针加四比乘 i 快按四并将其添加到指针。在任何一种情况下,您都有一个加法,但在第二个中,您没有乘法(您避免了一次乘法所需的 CPU 时间)。考虑到现代 CPU 的速度,即使阵列是 1 个 mio 元素,我想知道您是否真的可以对差异进行基准测试。
    您可以通过查看它生成的程序集输出来检查现代编译器可以优化任何一个以同样快的速度。您可以通过将“-S”选项(大写 S)传递给 GCC 来实现。
    这是第一个C代码的代码(使用了优化级别 -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 为我们完成所有工作),而不是有两个机器代码。另一方面,下面的两个汇编命令的运行时间可能比上面的要短。
    作为结束语,我想说取决于您的编译器和 CPU 功能(CPU 提供什么命令以何种方式访问​​内存),结果可能是两种方式。任何一个都可能更快/更慢。除非您将自己完全限制为一个编译器(也意味着一个版本)和一个特定的 CPU,否则您无法确定。由于 CPU 可以在单个汇编命令中执行越来越多的操作(很久以前,编译器确实必须手动获取地址,将 i 乘以 4,然后在获取值之前将两者相加),这些语句曾经是绝对真理古往今来越来越值得怀疑。还有谁知道 CPU 内部是如何工作的?上面我将一个组装指令与另外两个指令进行了比较。
    我可以看到指令的数量不同,这样的指令需要的时间也可能不同。此外,这些指令在其机器演示中需要多少内存(毕竟它们需要从内存传输到 CPU 缓存)是不同的。然而,现代 CPU 不会按照您提供指令的方式执行指令。它们将大指令(通常称为 CISC)拆分为小的子指令(通常称为 RISC),这也使它们能够在内部更好地优化程序流程以提高速度。事实上,第一条指令和下面的两条其他指令可能会产生相同的子指令集,在这种情况下,没有任何可测量的速度差异。
    关于Objective-C,它只是带有扩展的C。因此,在 C 语言中适用的所有内容都将适用于 Objective-C,也适用于指针和数组。另一方面,如果您使用对象(例如, NSArrayNSMutableArray ),这是完全不同的野兽。但是,在这种情况下,无论如何您都必须使用方法访问这些数组,没有可供选择的指针/数组访问。

    关于objective-c - 通过指针运算与 C 中的下标访问数组值,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/233148/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com