【问题标题】:storage duration of compound literals复合文字的存储时间
【发布时间】:2015-04-23 09:44:14
【问题描述】:

不知怎么理解blocks中定义的复合字面量的存储时间是自动的,推理如下:

让我们假设复合字面量定义在重复调用的函数或块中;当第一次调用这个函数时,如果它不是在静态内存中,计算机如何创建文字? (我的意思是他怎么知道它的价值??例如它是 (int [2]) {2,4} 还是 (int [5]) {5,4,2,1,4}),如果它不知何故存在于任何地方,计算机如何再次知道它的内容? (当他试图为第二次和每一次后续调用再次构建它时)在它第一次传递之后。

其他文字如字符串文字和普通文字的情况是它们在静态内存中,这是非常合理的,因为如果他没有将这个值存储在某个地方,计算机如何知道它的值。

谁能给我解释清楚??

【问题讨论】:

  • 你的问题有点含糊。我认为您没有很好地解释您的要求。也许编辑它?
  • 也许你是对的,但老实说我已经尽力解释了,现在我会稍等片刻,也许有人能理解它。
  • 我看不出复合文字与自动变量有何不同,复合文字主要是临时变量的语法糖(在大多数情况下可以放在相同的位置)。跨度>
  • 如果函数中有int tmp[2] = { 2, 4 };,编译器将{2,4}存储在哪里?甚至:如果有int tmp = 42;,那42存储在哪里?
  • @mafso; ^^年度问题!

标签: c compound-literals


【解决方案1】:

您的问题没有得到正确解释。如果您想了解复合文字的范围,那么标准会这样说:

C11:6.5.2.5 复合文字(P5):

[...] 如果复合文字出现在函数体之外,则该对象具有静态存储持续时间;否则,它具有与封闭块关联的自动存储持续时间。

复合字面量的初始化如下:

[...] 如果递归输入块,每次都会创建一个新的对象实例。对象的初始值是不确定的。如果为对象指定了初始化,在执行块时每次到达声明或复合字面量时执行;否则,每次达到声明时,值都会变得不确定。

复合文字是动态创建的,是未命名的数组/结构文字。它们像其他变量一样在内存的数据段中为stored,但您无法通过名称访问它。

【讨论】:

  • 好的,你的第二部分真的很接近我的要求,但还不完全是,所以我会尝试解释更多。问题是如果计算机不知道初始化的值,他如何对某个变量进行初始化。简而言之:复合文字存储在哪里,直到程序为它初始化一些变量,甚至调用一个带有它作为参数的函数?如果它没有永久存储在某个地方(即具有静态存储持续时间),那么计算机如何知道它具有哪个特定值???
  • 我添加了一个段落。这是你要求的吗?
【解决方案2】:

也许将对象称为“复合文字”有点误导。它实际上并不是那么字面意思。

考虑一个实际的例子是有帮助的,即使它有点笨:

/* This is NOT the way to solve this problem */
double cubic(double x, double a, double b, double c, double d) {
  double* powers = (double []){1, x, x*x, x*x*x};
  double* coefficients = (double []){a, b, c, d};
  double sum = 0;
  for (int i = 0; i < 4; ++i) sum += powers[i]*coefficients[i];
  return sum;
}

该函数中有两个复合文字,很明显它们都不能预先构造,因为它们依赖于函数参数。

幸运的是,C 编译器是一个编译器。它不限于从现有常量的副本创建新的数据结构。它可以生成代码,在堆栈上分配两个数组(“自动存储”),然后适当地填充它们。

编译器为这些复合文字分配堆栈空间并不难,因为它确切地知道它们有多大。实际上,生成的代码就像我写的一样:

double cubic(double x, double a, double b, double c, double d) {
  double _powers_values_[4];
  _powers_values_[0] = 1;
  _powers_values_[1] = x;
  _powers_values_[2] = x*x;
  _powers_values_[3] = x*x*x;
  double* powers = _powers_values_;
  // ...

如果您查看原始函数的生成代码,您会看到这样的结果。

还要注意powerscoefficients 都是可变的,所以我可以在函数中修改它们:

/* This is NOT the way to solve this problem */
double cubic(double x, double a, double b, double c, double d) {
  double* powers = (double []){1, x, x*x, x*x*x};
  double* coefficients = (double []){a, b, c, d};
  for (int i = 0; i < 4; ++i) coefficients[i] *= powers[i];
  for (int i = 1; i < 4; ++i) coefficients[i] += coefficients[i+1];
  return coefficients[3];
}

当然,复合文字可能只有常量值:

double* coefficients = (double []){17, 6, -3, 2.5};

但正如所写,该数组仍然是可变的,因此编译器需要安排函数具有值的新副本。如果我愿意,我可以明确表示数组是不可变的:

const double* coefficients = (const double []){17, 6, -3, 2.5};

现在允许编译器使用静态文字,而不是制作不必要的副本。但是,理论上,复合文字仍然具有自动作用域,从函数返回指向它的指针将是未定义的行为。

【讨论】:

  • 谢谢!你的回答教会了我一些新东西。我不知道复合文字可以包含变量。但现在我的问题是:形成您的代码,很明显编译器可以生成为文字分配位置的代码,然后具有由函数参数提供的值(x、a、b、c、d 就是这种情况) 但是第一个文字中的 ( 1 ) 呢?编译器用这个做什么?
  • @Lockon2000:硬件允许您指定像1 这样的小常量作为机器指令的一部分是很常见的,尽管浮点常量不太常见。因此,1 可能在分配之前不存在于内存中,或者编译器可能从代码中其他地方存在的常量 1 的任何实例复制到新创建的数组中。这是一个实现细节,根本不重要。 (在某些机器上,0计算x-x,其中x 是寄存器中发生的任何内容。)
  • 关于您的最后修改,在最后几行中您说“现在允许编译器使用(静态文字),而不是制作不必要的副本。但是,(理论上),复合文字仍然有(自动作用域),并且从函数返回指向它的指针将是未定义的行为。“所以复合文字中常量的持续时间实际上是静态的,尽管没有相应地处理。
  • @Lockon2000:可能是静态的。你不知道,编译器也没有义务告诉你。您需要假设它是自动的。但这是指复合文字作为一个整体对象。用于计算文字值的常量与代码中的任何其他常量没有什么不同。如果您写x += 1.0;,您将无法知道1.0 在内存中的位置——如果确实如此的话——但是你不在乎。你只关心x 添加了 1.0。如果机器有floating increment 指令,编译器可以使用它。
  • 好的,这正是我要寻找的。谢谢!真的,我为我相当模糊的问题向大家道歉。
【解决方案3】:

编译器知道自动变量的值。将包含已经存在的任何值。有时(例如在循环中)它们可能碰巧包含上次的值,但那是运气,而不是意图。

编译器将自动变量放在堆栈上,与函数参数和返回地址混合在一起。除非代码显式初始化它们,否则自动变量的值将与之前占用这些内存位置的位模式相同。

【讨论】:

  • 对不起,这不是我要问的。下面答案的第二部分更接近我正在寻找的内容,尽管仍然不是。我当然知道原因是我模糊的解释。
  • 未初始化的自动变量包含不确定的值。您所描述的是未定义行为的可能表现(还有其他可能性)
猜你喜欢
  • 2020-10-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-01-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多