C 何时(不)将嵌套数据结构的一部分存储到临时变量中——相当/难看,更快/更慢?

C 何时(不)将嵌套数据结构的一部分存储到临时变量中——相当/难看,更快/更慢?,c,language-agnostic,coding-style,readability,maintainability,C,Language Agnostic,Coding Style,Readability,Maintainability,在一个数组/struct/union中读取多个数字/字符串的最佳方法是什么,它本身嵌套在一个或多个父数组/struct/union中 没有临时变量的第一个示例: printf("%d %d\n", a[9][3], a[9][4]); int *b = a[9]; printf("%d %d\n", b[3], b[4]); foo[i]->bar.red[j][0]++; foo[i]->bar.red[j][1]++; foo[i]->bar.red[j][2]++;

在一个数组/struct/union中读取多个数字/字符串的最佳方法是什么,它本身嵌套在一个或多个父数组/struct/union中

没有临时变量的第一个示例:

printf("%d %d\n", a[9][3], a[9][4]);
int *b = a[9];
printf("%d %d\n", b[3], b[4]);
foo[i]->bar.red[j][0]++;
foo[i]->bar.red[j][1]++;
foo[i]->bar.red[j][2]++;
foo[i]->bar.red[j][3]++;
int *p = foo[i]->bar.red[j];
p[0]++;
p[1]++;
p[2]++;
p[3]++;
带有临时变量的第一个示例:

printf("%d %d\n", a[9][3], a[9][4]);
int *b = a[9];
printf("%d %d\n", b[3], b[4]);
foo[i]->bar.red[j][0]++;
foo[i]->bar.red[j][1]++;
foo[i]->bar.red[j][2]++;
foo[i]->bar.red[j][3]++;
int *p = foo[i]->bar.red[j];
p[0]++;
p[1]++;
p[2]++;
p[3]++;
我敢肯定,上面第一个例子中的临时变量非常愚蠢,但在下面的第二个例子中,它是有意义的,并且使用一个看起来更好,对吗

不带临时变量的第二个示例:

printf("%d %d\n", a[9][3], a[9][4]);
int *b = a[9];
printf("%d %d\n", b[3], b[4]);
foo[i]->bar.red[j][0]++;
foo[i]->bar.red[j][1]++;
foo[i]->bar.red[j][2]++;
foo[i]->bar.red[j][3]++;
int *p = foo[i]->bar.red[j];
p[0]++;
p[1]++;
p[2]++;
p[3]++;
带有临时变量的第二个示例:

printf("%d %d\n", a[9][3], a[9][4]);
int *b = a[9];
printf("%d %d\n", b[3], b[4]);
foo[i]->bar.red[j][0]++;
foo[i]->bar.red[j][1]++;
foo[i]->bar.red[j][2]++;
foo[i]->bar.red[j][3]++;
int *p = foo[i]->bar.red[j];
p[0]++;
p[1]++;
p[2]++;
p[3]++;

那么你的底线在哪里呢?我意识到编译器足够聪明,可以插入任何间接寻址,以通过这种方式产生最佳效率的程序集,但是(假设假设性能非常关键的代码)可能会有例外?从代码清晰性/可维护性的角度来看,如果有的话,你的经验法则是什么?

这并不能准确地回答你的问题,但我不得不强调这一点,因为这是大多数程序员经常犯的错误

经验法则是:避免过早优化。
现代的编译器已经足够快速,可以自己执行琐碎的优化。
编写易于理解和维护的代码,并遵循您所在团队的编码标准/实践。

按照要求正确工作,为瓶颈剖析代码,然后尝试优化发现瓶颈的代码

这并不能准确地回答您的问题,但我不得不强调这一点,因为这是大多数程序员经常犯的错误

经验法则是:避免过早优化。
现代的编译器已经足够快速,可以自己执行琐碎的优化。
编写易于理解和维护的代码,并遵循您所在团队的编码标准/实践。

按照要求正确工作,为瓶颈剖析代码,然后尝试优化发现瓶颈的代码

首先,我认为这纯粹是代码可读性/可维护性的问题,这是个人的偏好。编译器现在比我们聪明(笑话)。:-)

就我个人而言,我通常考虑在两种情况下提取临时变量(或函数,这同样适用于它们):

  • 当用户可以对其进行命名,使其名称不言自明,并且说明的内容多于初始表达式时,或者
  • 如果我必须重复某段代码至少三次
因此,我留下的第一个例子如下:

printf("%d %d\n", a[9][3], a[9][4]);
第二个问题是:

int *p = foo[i]->bar.red[j];
p[0]++;
// ...
或者,(通常但不总是)更好:


首先,我认为这纯粹是代码可读性/可维护性的问题,这是个人的偏好。编译器现在比我们聪明(笑话)。:-)

就我个人而言,我通常考虑在两种情况下提取临时变量(或函数,这同样适用于它们):

  • 当用户可以对其进行命名,使其名称不言自明,并且说明的内容多于初始表达式时,或者
  • 如果我必须重复某段代码至少三次
因此,我留下的第一个例子如下:

printf("%d %d\n", a[9][3], a[9][4]);
第二个问题是:

int *p = foo[i]->bar.red[j];
p[0]++;
// ...
或者,(通常但不总是)更好:


我的经验法则是:添加一个临时变量,如果你能给它一个清晰的、有意义的名称,使代码更容易理解的话。 第二个建议是:将使用此变量的(小)部分用大括号括起来。通过这种方式,意图是明确的,如果需要重构,代码几乎准备就绪(大括号中的代码将很容易提取到函数中)

在第二个示例中,这将导致:

/*previous code
...
*/
int *quadruplet = foo[i]->bar.red[j];
{ /*intentionaly meaningless braces that prepare the introduction 
  of a function (incrementQuadruplet) in a later refactoring if needed*/
  quadruplet[0]++;
  quadruplet[1]++;
  quadruplet[2]++;
  quadruplet[3]++;
}
/*following code
...
*/

我的经验法则是:添加一个临时变量,如果你能给它一个清晰的、有意义的名称,使代码更容易理解的话。 第二个建议是:将使用此变量的(小)部分用大括号括起来。通过这种方式,意图是明确的,如果需要重构,代码几乎准备就绪(大括号中的代码将很容易提取到函数中)

在第二个示例中,这将导致:

/*previous code
...
*/
int *quadruplet = foo[i]->bar.red[j];
{ /*intentionaly meaningless braces that prepare the introduction 
  of a function (incrementQuadruplet) in a later refactoring if needed*/
  quadruplet[0]++;
  quadruplet[1]++;
  quadruplet[2]++;
  quadruplet[3]++;
}
/*following code
...
*/

我发现更容易理解的是:
auto&red=foo[I]->bar.red;红色[j][0]++;红色[j][1]++我发现更容易理解的:
auto&red=foo[I]->bar.red;红色[j][0]++;红色[j][1]++
您的第一个示例很有趣,因为当我阅读第一个版本时,我假设
a
int
s的二维数组,而第二个版本很明显,
a
必须是指向
int
s数组的指针的一维数组。(这在第二个例子中可能也是正确的,但是当我到达那里的时候,我已经阅读了整个第一个例子,所以我不知道如果不是这样我会怎么想。)但我不认为这本身就是使用临时变量的理由。希望
a
foo[i]>bar.red
的性质在上下文中是清楚的!哦,人力资源部,奇怪。好的,是的,这很有道理。不知怎的,我发现这很不自然——我的本能是写
int*b=&a[9][0]--但您的版本完全有效。没关系。:-)是的,这很有道理,而且我非常习惯(说)
intd[20];int*e=d甚至
inta[20][20];int(*f)[20]=a。只是不知何故,我从来没有想过让像
a[10]
这样的东西衰变为指针。但我的直觉可能只是奇怪和非典型的;我所有的团队开发经验都是其他语言,所以我唯一习惯的C++习语是我自己使用的。请不要理我。:-)您的第一个示例很有趣,因为当我阅读第一个版本时,我假设
a
是一个二维数组