【问题标题】:When returning pointers, when does data need to be static?返回指针时,数据什么时候需要是静态的?
【发布时间】:2019-08-20 00:29:54
【问题描述】:

我注意到一些关于指针和变量的问题,但我想了解详细信息。简单地说,绕过使用全局变量,我已经开始让函数返回指向函数内部数据的指针。有时,这些数据需要是静态的,有时则不需要。

例如,假设我有一个函数,它创建一个 2d 字符数组,用 'a' 填充它并返回一个指向该数组的指针。当调用者尝试使用该指针访问和修改二维数组所在的内存时,随机数据在那里而不是二维数组的内容。我发现将二维数组声明为静态可以解决此问题,因为数组的内容保存在其范围之外。

另一方面,假设我有一个声明结构的函数,并用值对其进行初始化。然后该函数返回一个指向该结构变量的指针。当试图通过指针访问结构的值时,调用者应该有无意义的数据,但有趣的是,它没有。我认为就像二维数组的情况一样,在函数调用之后,由于结构不是静态的,因此应该释放该内存中的数据。然而,它不是那样的,相反,我可以通过指针访问所有结构的元素,尽管它不是静态的。

总的来说,函数完成后,调用者可以访问和修改在该函数中创建的变量的数据,只要该数据是静态的(在二维数组的情况下),但有时变量的数据会保留在范围之外尽管没有被标记为静态(结构案例)。为什么?

【问题讨论】:

  • 正式地,只要函数返回,局部变量就不再存在。在实践中,有时数据会持续更长的时间。另一方面,static 变量会在程序的生命周期内持续存在。
  • 当包含它们的函数返回时,所有局部变量都会超出范围,因此指向所有类型的局部数据的指针都变得无效。如果您返回的指针似乎适用于某些类型但不适用于其他类型,这是因为运气,而不是任何定义。
  • 编程不是一门实验科学。 “试试看”是一种流行的做法,并且(并非总是建议)一种流行的教学技术,但很容易从中得出非常不准确的结果。
  • 我不一定要尝试。我只是在忘记将结构设为静态时遇到了它,这让我想知道为什么尽管它不是静态的,我仍然能够访问它。我认为解释是关于结构具有某种关于它们如何存储的属性。
  • @user3386109:如果你在函数内部定义了一个带有static的对象,它的作用域是本地的(块作用域),但是当函数返回时它不会停止存在。因此,“正式地,只要函数返回,局部变量就不再存在”的说法是不正确的。 C 标准不使用“变量”或“局部变量”来定义这些概念,因此您应该避免使用它们,因为它们错误地传达了规则。

标签: c arrays pointers struct static


【解决方案1】:

您对二维数组charstruct 中的数据在函数返回后是否持续存在的观察是偶然的结果,而不是C 标准定义的行为,您可能不依赖在他们。它们只是您的 C 编译器在特定情况下的行为方式的产物。它们在其他情况下可能会发生变化,并且可能不可靠。

只要你提供一个在函数返回后使用的对象(如通过返回一个指向该对象的指针),它就不能有自动存储持续时间。它可能有静态存储时长、分配存储时长或线程存储时长。

首先,让我们澄清一些术语。这对于理解概念很重要。 C 标准不使用术语“全局变量”。它通常不使用“变量”来描述对象,并​​且根本不使用“全局”来描述它们。你认为的变量是,在 C 标准中,一个 identifier 和一个 object。标识符是对象的名称,对象是可以表示值的数据存储区域。

C 标准是否保证对象可以在某个时间使用取决于对象的生命周期。生命周期是在程序执行期间对象存在在 C 计算模型中。生命周期部分取决于其存储持续时间。存储持续时间取决于对象的定义或创建方式和位置。

生命周期也受对象标识符的范围影响。范围是其中在源代码中标识符是可见(可以使用)。范围和生命周期之间存在关系,但它们是截然不同的东西。

有四种存储时长(还有一个特殊的临时存储时长,这里我不讨论):

  • 如果使用_Thread_local 声明标识符,则其对象具有线程存储持续时间。它的生命周期从线程创建时开始,到线程执行结束时结束。所以只要它的线程还在执行,它就可以在函数返回后使用。
  • 否则,如果使用staticextern 或在任何块或函数参数列表之外(实际上在任何函数之外)声明标识符,则其对象具有静态存储持续时间。它存在于程序的整个执行过程中,因此可以在执行期间的任何时间使用。
  • 否则,对于对象的任何标识符(而不是类型定义、函数等),其对象具有自动存储期限。它与声明它的语句块相关联。(块是花括号内的一系列语句,{ ... }。这可以是定义函数的主块或嵌套在其中的块。)它的生命周期在执行时结束相关联的块结束。 C 标准不保证当您在块执行结束后尝试使用自动对象时会发生什么。

请注意,当函数调用子例程时,函数的执行(包括其中的块)会暂时暂停,但不会结束。 (当函数返回时,执行结束,或者像abortexitlongjmp 这样的特殊例程被调用。)这意味着在子例程执行时对象仍然存在。即使子例程中的源代码具有与调用函数不同的范围也是如此。

由于上述原因,说您不能在其范围之外使用对象的陈述是错误的。 范围不是对象是否可以访问的决定因素。终生。

第四个存储期限:

  • 具有分配存储持续时间的对象由malloccallocreallocaligned_alloc 创建,并且没有名称(标识符)。分配的对象从分配时延伸到解除分配时。因此,如果一个函数分配一个对象并返回一个指向它的指针,则该指针可用于访问该对象,直到该对象被释放。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-12-26
    • 2012-09-16
    • 1970-01-01
    相关资源
    最近更新 更多