理论上两种方法中哪一种更快,为什么? (指向字符串的指针必须是常量。)
目的地[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++;
}
答案 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
(请记住,只需要在下一个序列点之前应用副作用,这不一定在表达式评估之后立即应用)。
因此从理论上讲,两个版本之间不应该有太大差异。了解特定平台的唯一方法是对两个版本进行编码并比较它们的性能。