string [x] vs * string ++

时间:2010-11-08 13:08:43

标签: c

理论上两种方法中哪一种更快,为什么? (指向字符串的指针必须是常量。)

目的地[count] 和* 目标++ 之间的确切区别是什么?每次通话时目的地[计数] 是否继续从 0 移动到计数? * destination ++ 只是在每次通话时加1吗?

char *const string = "Hello world!";
char *destination = malloc(strlen(string) + 1);
int count = 0;
while(string[count] != '\0')
{
    destination[count] = string[count];

    count++;
}

char *const string = "Hello world!";
char *destination = malloc(strlen(string) + 1);
char *ptr = string;
while(*ptr != '\0')
{
    *destination++ = *ptr++;
}

8 个答案:

答案 0 :(得分:6)

这取决于编译器。使用现代编译器,实际上无法在单指令级别上预测任何有关优化的内容,而无需实际查看生成的代码。两者都可能编译成等效的指令。

也就是说,destination[count]在每次通话时从0循环到count。它需要destination[0]的内存位置,将count的{​​{1}}类型的大小添加*destination,然后查看。 (当然,这很天真地说 - 编译器可能会做得更快。)

另一方面,*destination++占用destination[0]的内存位置(已成为数组的开头,因为您已更改{{1} }),查看那里,并添加destination类型的大小,以便*destination引用过去destination[0]

答案 1 :(得分:5)

在过去的好时光,第二种方法更快。但是使用现代编译器优化,它们只是相同的

这是因为:

destination[count] = string[count];

会做

*(destination+count) = *(string+count);

每个循环中需要再进行两次add操作。现代编译器会为你做这件事。

答案 2 :(得分:5)

我们为什么要推测?我们可以尝试并找出答案。我使用gcc -O3 -g(在x86上)编译代码并反汇编结果。比我预期的更多的变化,所以我将集中在中间的位置,我们期望两者之间的大部分差异。 第一种情况下循环的核心:

0x00000030 <foo+48>:    mov    %dl,(%edi,%esi,1)
0x00000033 <foo+51>:    movzbl 0x1(%ecx),%edx
0x00000037 <foo+55>:    inc    %eax
0x00000038 <foo+56>:    inc    %ecx
0x00000039 <foo+57>:    mov    %eax,%esi
0x0000003b <foo+59>:    test   %dl,%dl
0x0000003d <foo+61>:    jne    0x30 <foo+48>

第二种情况下循环的核心:

0x00000080 <foo2+48>:   mov    %dl,(%eax)
0x00000082 <foo2+50>:   movzbl 0x1(%ecx),%edx
0x00000086 <foo2+54>:   inc    %eax
0x00000087 <foo2+55>:   inc    %ecx
0x00000088 <foo2+56>:   test   %dl,%dl
0x0000008a <foo2+58>:   jne    0x80 <foo2+48>

在此基础上,第二个可能会快一点。但实际上,它在实践中并没有太大的区别。 L1缓存保持两个循环都很好,目标内存未缓存,因此差异没有实际意义。祝你好好测量两者之间的差异。

答案 3 :(得分:2)

取决于CPU。在x86上,第一个版本稍微快一些(或者至少会导致代码略短),因为它只需要一个指令从字符串[count]加载,一条指令写入目标[count]。所以伪代码看起来有点像这样(每行=一个汇编指令)

register = *(string+count) 
*(destination+count) = register
count += 1
compare register, 0
jump to line 1 if nonzero

,第二个版本将是

register = *ptr
*destination = register
ptr += 1
destination += 1
compare register, 0
jump to line 1 if nonzero

在实践中,任何优化编译器都将优化此代码,并且,根据其复杂程度,编译器甚至可以将第一个版本转换为第二个版本,反之亦然。所以没有人知道哪一个会更快。

答案 4 :(得分:0)

*destination++实际上在每次调用时添加sizeof(*destination)(它取决于指针大小),而destination[count]应该执行*(destination + count * sizeof(*destination)),但它可能仍然由编译器优化。< / p>

答案 5 :(得分:0)

destination[count]destination[]中的 count -th元素(基于0)。 count++确保在每次输入循环时,destination[count]超过上一次迭代中访问的最后一个元素(当然,第一个入口除外)。

*destination++ = *ptr++;与以下内容相同:

*destination = *ptr;
destination++;
ptr++;

请注意,destination++会将指针值增加sizeof(*destination),这将指向数组中的下一个元素,而不是destination(或*destination)指向的数据。 / p>

对于现代编译器,两者都将被优化为几乎相同的二进制文件,因此在速度方面选择哪一个并不重要。

答案 6 :(得分:0)

如果“string”作为函数中的参数出现,则可以将其用作迭代器而不是“ptr”。那么你将有一个较少的变量来保持堆栈。这可能意味着编译器可以将所有局部变量保存在寄存器中,这样可以提高性能。

当然这取决于,如果编译器设法取消“计数”,你将不会获得任何性能增量。

答案 7 :(得分:0)

从高层次来看,这两项操作都涉及取消引用和添加。表达式destination[count]被评估为*(destination + count)。表达式*destination++(很可能)被评估为*destination; destination += 1(请记住,只需要在下一个序列点之前应用副作用,这不一定在表达式评估之后立即应用)。

因此从理论上讲,两个版本之间不应该有太大差异。了解特定平台的唯一方法是对两个版本进行编码并比较它们的性能。