C中的这种表达式是否有效(在所有编译器上)? 如果是的话,这是不错的C?
char cloneString (char clone[], char string[], int Length)
{
if(!Length)
Length=64
;
int i = 0
;
while((clone[i++] = string[i]) != '\0', --Length)
;
clone[i] = '\0';
printf("cloneString = %s\n", clone);
};
这会更好,更糟,无动于衷吗?
char *cloneString (char clone[], char string[], int Length)
{
if(!Length)
Length=STRING_LENGTH
;
char *r = clone
;
while
( //(clone[i++] = string[i]) != '\0'
*clone++ = *string++
, --Length
);
*clone = '\0';
return clone = r
;
printf("cloneString = %s\n", clone);
};
Stackoverflow希望我为这个问题添加更多文字!
好!我关心
a。)表达式如c ==(a = b) b。)索引与指针之间的性能
有任何意见吗?
非常感谢。
答案 0 :(得分:2)
是的,它在语法上对所有编译器都有效(虽然语义上没有任何编译器),不,它不被认为是好的C.大多数开发人员会同意逗号运算符是一件坏事,并且大多数开发人员通常会同意单行代码应该只做一件特定的事情。 while
循环执行整个四并且具有未定义的行为:
i
; string[i]
分配给clone[i++]
; (未定义的行为:您应该在一个递增/递减它的语句中只使用i
一次)string[i]
不是0(但丢弃比较结果); Length
,并在递减后终止循环{。}}。更不用说假设Length == 0
为64,如果没有提供是一个可怕的想法,并留下足够的空间来容易被利用来破坏或破解该程序的更多未定义的行为。
我看到你自己写的并且你关心表现,这显然是你把所有东西都放在一起的原因。 不要。通过一起挤压语句而使代码缩短并不比代码更长,因为语句没有被挤压在一起。它仍然做同样数量的事情。在你的情况下,你通过一起挤压来引入错误。
答案 1 :(得分:2)
代码具有未定义的行为:
表达式
(clone[i++] = string[i])
以不依赖的方式从两个不同的子表达式修改和访问对象i
,这是不允许的。编译器可能会在i
中使用string[i]
的旧值,或者可能使用i
的新值,或者可能会执行完全不同且意外的操作。
答案 2 :(得分:1)
简单回答否。
Length
- 添加文档来说明这一点。...
答案 3 :(得分:1)
好的,所以我决定将我的评论发展成一个实际的答案。虽然这不能解决您问题中的特定代码,但它会解决潜在的问题,我认为您会发现它很有启发性,因为您可以使用它 - 让我们称之为指南 - 用于您的一般编程。
我所提倡的,特别是如果你只是学习编程,那就是专注于可读性,而不是你认为或被告知提高速度/性能的小噱头。
我们举一个简单的例子。迭代C
中的向量(不在C++
中)的惯用方法是使用索引:
int i;
for (i = 0; i < size; ++i) {
v[i] = /* code */;
}
当我开始编程时,我被告知v[i]
实际上被计算为*(v + i)
,因此在生成的汇编程序中,这已被分解(请注意,此讨论已简化):
i
与sizeof(int)
v
所以基本上你有3个操作。
让我们将其与通过指针访问进行比较:
int *p;
for (p = v; p != v + size; ++p) {
*p = /*..*/;
}
这样做的好处是*p
实际上只扩展为一条指令:
p
上的元素。 2个额外的指令不会很多,但是如果你的程序花费大部分时间在这个循环中(非常大size
或多次调用(包含这个的函数)循环)你意识到第二个版本使您的程序快3倍。那是很多。所以,如果你在我开始时就像我一样,你会选择第二种变体。不要!
因此,第一个版本具有可读性(您明确地描述了您访问向量i
的{{1}} - 元素),第二个版本使用噱头而不利于可读性(您说您可以访问一个记忆位置)。现在这可能不是不可读代码的最佳示例,但原则是有效的。
那么为什么我要告诉你使用第一个版本:在你牢牢掌握缓存,分支,归纳变量(以及更多)等概念以及它们如何应用于现实编译器和程序性能之前,你应该远离这些噱头,依靠编译器进行优化。它们非常智能,并且将为两种变体生成相同的代码(当然,启用了优化)。因此,第二个变体实际上只是可读性而且与第一个变体在性能上相同。
另一个例子:
v
自然的方式是编写第一个变体。您可能认为第二个提高了性能,因为您在循环的每次迭代中调用函数const char * str = "Some string"
int i;
// variant 1:
for (i = 0; i < strlen(str); ++i) {
// code
}
// variant 2:
int l = strlen(str);
for (i = 0; i < l; ++i) {
// code
}
。而且你知道获取字符串的长度意味着遍历所有字符串直到你到达结尾。所以基本上调用strlen
意味着添加内循环。哎哟必须减慢程序。不一定:编译器可以优化调用,因为它总是产生相同的结果。实际上,当您引入一个新变量时,您可能会受到伤害,该变量必须从一个非常有限的注册池中分配一个不同的寄存器(一个极端的例子,但不过这里要点)。
直到很久以后,不要把精力放在这样的事情上。
让我向您展示一些其他内容,它将进一步说明您对性能所做的任何假设都很可能是错误的和误导性的(我并不是想告诉您,您是一个糟糕的程序员 - 远非它 - 只是在您学习时,您应该将精力投入到绩效以外的其他方面):
让我们乘以两个矩阵:
strlen
与
for (k = 0; k < n; ++k) {
for (i = 0; i < n; ++i) {
for (j = 0; j < n; ++j) {
r[i][j] += a[i][k] * b[k][j];
}
}
}
两者之间的唯一区别是操作执行的顺序。它们是完全相同的操作(数量,种类和操作数),只是以不同的顺序。结果是等价的(加法是可交换的)所以在纸上它们应该花费大量的时间来执行。在实践中,即使启用了优化(一些非常智能的编译器可以重新排序循环),第二个示例可能比第一个示例慢2-3倍。甚至第一个变体距离最佳(速度方面)还有很长的路要走。
所以基本点:担心UB ,其他答案会告诉您,在此阶段不要担心性能。
答案 4 :(得分:0)
第二块代码更好。
该行
printf("cloneString = %s\n", clone);
因为之前有一个return语句,所以永远不会被执行。
为了使您的代码更具可读性,请更改
while
(
*clone++ = *string++
, --Length
);
到
while ( Length > 0 )
{
*clone++ = *string++;
--Length;
}
答案 5 :(得分:0)
这可能是解决问题的更好方法:
#include <stdio.h>
#include <string.h>
void cloneString(char *clone, char *string)
{
for (int i = 0; i != strlen(string); i++)
clone[i] = string[i];
printf("Clone string: %s\n", clone);
}
话虽如此,已经有了标准功能:
strncpy(const char *dest, const char *source, int n)
dest是目标字符串,source是必须复制的字符串。此功能最多可复制n个字符。
所以,你的代码将是:
#include <stdio.h>
#include <string.h>
void cloneString(char *clone, char *string)
{
strncpy(clone, string, strlen(string));
printf("Clone string: %s\n", clone);
}