就内存和处理时间而言,C 中的文字和变量有什么区别?

What is the difference between literals and variables in C, in terms of memory e processing time?

提问人:Zaratruta 提问时间:5/31/2023 更新时间:5/31/2023 访问量:83

问:

让我们考虑以下两段代码:

#include <stdio.h>
int main(){
    float pi = 3.14;
    float r,circumference ;
    scanf("%f",&r);
    circumference  = 2*pi*r;
    printf("Circumference:%f\n",circumference );
    return 0;
}

在这个中,我将值 3.14 存储在变量 pi 中。

#include <stdio.h>
int main(){
    float r,circumference ;
    scanf("%f",&r);
    circumference  = 2*3.14*r;
    printf("Circumference:%f\n",circumference );
    return 0;
}

在这个中,我直接使用文字值 3.14。

哪一个是最好的选择,为什么? 这两个选项在内存消耗和处理时间方面有什么区别?

C 变量 内存

评论

3赞 Ian Abbott 5/31/2023
为了更好地进行比较,请更改为以避免在乘以文本的版本中从到到和从此转换。3.143.14ffloatdoublefloat
4赞 Some programmer dude 5/31/2023
我建议你使用编译器资源管理器来比较为两个程序生成的汇编代码。请记住在启用优化的情况下进行构建。
2赞 Aconcagua 5/31/2023
我个人会只在一个位置(-> 标头!)并给 pi 一个更好的精度——并且有一个常量(和变量一样),所有计算都在同一个 FPU 上完成(具有相同的精度),无论如何,没有理由放弃精度......#definedoubledouble
3赞 tadman 5/31/2023
对于像这样的琐碎程序,实际上没有区别。在一些运行数十亿次的“热”代码中,如果您担心性能,则需要检查程序集输出,并进行基准测试以识别可衡量的性能问题。这段代码将运行得非常快,99.9999% 的时间将花费在等待人类输入数据上。哎呀,你可以从头开始计算 Pi,输出延迟几乎没有区别。
1赞 Aconcagua 5/31/2023
@tadman 在我看来,逗号;)后面的数字/九仍然太少

答:

2赞 chux - Reinstate Monica 5/31/2023 #1

哪一个是最好的选择,为什么?这两个选项在内存消耗和处理时间方面有什么区别?

内存消耗和处理时间差别不大。

这是一个计算,是一个,所以第二个可能会花费或多或少的内存和时间。不是因为常数与变量的差异,而是因为类型差异和转换。2*3.14*r;double2*pi*rfloat

即使更正了示例代码并始终使用相同的浮点类型,差异也很小。节省编码时间以进行更大规模的优化,并让功能良好的编译器进行低级优化。过早的优化真的是万恶之源吗?

  • 为清楚起见,编写代码。

  • 统一使用类型。

  • 使用浮点数。仅当存在令人信服的理由时才使用 。doublefloatlong double

    #define MY_PI     3.1415926535897932384626433832795
    #define MY_PI_F   3.1415926535897932384626433832795f
    #define MY_PI_LD  3.1415926535897932384626433832795L
  
    float r = ...
    float circumference = 2*MY_PI_F*r;
    // or with double
    double r = ...
    double circumference = 2*MY_PI*r;

请注意,由于类型更改,OP 的 2 个代码在 39.4% 的时间内功能不相同,计算提供了更好的答案。double

#include <stdio.h>
#include <stdlib.h>
#include <float.h>
#include <math.h>

int main() {
  unsigned long fi = 0, di = 0;
  float pi = 3.14;
   for (float r = FLT_MAX; r > 0.0; r = nextafterf(r, 0.0)) {
     fi++;
     float circumference1  = 2*3.14*r;
     float circumference2  = 2*pi*r;
     if (circumference1  != circumference2) {
       if (di == 0) printf("%.9g %.9g %.9g\n", r, circumference1, circumference2);
       di++;
     }
   }
   printf("Differences: %.1f%%\n", 100.0*di/fi);
}

使用时,我发现没有功能差异,但这在 C 中并不确定。 float circumference1 = 2*3.14f*r;

评论

0赞 chux - Reinstate Monica 5/31/2023
另请参阅:当分配给浮点数时,附加“f”何时会更改浮点常量的值?
0赞 Zaratruta 5/31/2023
为什么使用 double 是一种很好的做法?一般来说,如果我们不需要这样的精度,在我看来,使用浮点数似乎是一种更好的做法,因为它使用更少的内存。
0赞 chux - Reinstate Monica 5/31/2023
@Zaratruta 你为什么违背自己的建议和代码而不是?2*3.14*r2*3.14f*r
0赞 Zaratruta 5/31/2023
这只是一个错误。
0赞 Andrey Kachow 5/31/2023 #2

简短的回答是,文字通常比变量更适合记忆。或者由于编译器优化而没有区别。

即时值(又名数字文字)直接存储在指令*中。它存储在代码段中。如果使用变量,则在运行时在堆栈上分配内存。除非编译器优化了该值。

字符串文字以字符数组的形式存储在段中。无论如何,它们都会消耗内存。字符串变量是指针。数组起始地址的值存储在变量中,并在堆栈上分配。.text

如果将字符串文本作为参数传递,则地址的即时值将被推送到堆栈并消耗很少的内存。

如果您想同时获得可读性和内存的优势,您可能希望将宏与 一起使用。它将允许为幻数命名。#define

#define PI 3.14

宏是在预处理器编译之前将由其实际值替换的单词。你可以用它们实现许多有趣的事情。

*取决于架构。例如,在 ARM 中,某些值不能由指令表示,必须存储在只读存储器中。

评论

1赞 Eric Postpischil 5/31/2023
浮点常量通常不作为指令中的直接操作数存储。即使是 a 名义上也是 32 位,因此它不适合 32 位指令。一些架构提供了将浮点常量的一小部分编码为即时,并且某些架构可能提供从指令流加载完整 32 位值的方法,但它们通常存储在常量数据部分中,就像它们已声明一样。floatstatic const float x = 3.14f;
2赞 Eric Postpischil 5/31/2023
一般来说,这个答案写得很宽泛,可能太宽泛,无法向学生传达具体的知识。例如,“宏是将被其实际值替换的单词”并不真正正确。给定 ,不会被学生认为是 12 的“值”所取代,而是被预处理器标记 、 和 所取代,随后将在 C 语法中解析。而“字符串变量是指针”同样宽泛或模糊。C 标准中没有任何东西称为“字符串变量”。既有指向 的指针,也有 的数组。#define foo 3 * 4foo3*4charchar