【问题标题】:stack question: a pointer points to a variable inside function(stack)堆栈问题:指针指向函数(堆栈)内的变量
【发布时间】:2020-01-03 04:35:26
【问题描述】:

在函数中定义的变量是在堆栈上创建的。然后,当函数调用完成时,变量因堆栈入/出而消失。

以下代码正在传递一个数据结构

typedef struct
{
    test_out_t  output;
    test_in_t   input;
} message_t;

typedef struct
{
    uint8_t    len;
    uint8_t*   data_out;
} test_out_t;

typedef struct
{
    uint8_t    len;
    uint8_t*   data_in;
} test_in_t;

函数调用是 无效测试(message_t *msg);

在函数中,我定义了一个数组,并将指针指向这个数组(内存位置)。但是,当函数调用完成时,我希望指针指向值变为未确定/零,因为堆栈变量已经消失。

但是,如果我在函数内部调用 printf(),它仍然具有堆栈变量的值。

使用以下代码,msg.output.data_out 包含在函数中创建的数组的值。

如果注释掉 test() 中的 printf。 msg.output.data_out 全部为零。

    #include <stdio.h>
    #include <stdint.h>
    #include <string.h>

#define DATA_SIZE   (8)


typedef struct
{
    uint8_t    len;
    uint8_t*   data_out;
} test_out_t;

typedef struct
{
    uint8_t    len;
    uint8_t*   data_in;
} test_in_t;

typedef struct
{
    test_out_t  output;
    test_in_t   input;
} message_t;



void test(message_t *msg);



void test(message_t *msg)
{
    uint8_t  stackdata[DATA_SIZE];
    memset(stackdata, 0, DATA_SIZE);
    for (int i=0; i<DATA_SIZE; i++)
      stackdata[i] = i+1;

    msg->output.len = DATA_SIZE;
    msg->output.data_out = stackdata;

    uint8_t data2[msg->input.len];
    memcpy(&data2, msg->input.data_in, msg->input.len);

    for (int i=0; i<msg->input.len; i++)
      printf("0x%X\t", data2[i]);
}

int main(void) {

  message_t msg;
  uint32_t data2 = 0x12345678;
  msg.input.len = 4;
  msg.input.data_in = (uint8_t*)&data2;


  test(&msg);
  printf("\n");
  for (int i=0; i<msg.output.len; i++)
    printf("0x%X\t", msg.output.data_out[i]);
  return 0;
}

我假设一些与 printf() 相关的东西

顺便说一句,我正在使用在线编译器来运行代码。

https://repl.it/languages/c

【问题讨论】:

  • “我期待指针指向的值变为未确定/零,因为堆栈变量已经消失。”您如何以及为什么期望来自明显未定义的行为?以及如何区分“未确定”值和“确定”值?
  • 如果您有指向数据的指针,没有什么可以阻止您访问数据。但是访问在其范围之外的堆栈上分配的数据是未定义的行为。

标签: c


【解决方案1】:

C 的规则说,当对象的生命周期结束时1,不向您保证它。因此,您无法正确使用它,因为您对此没有任何保证。规则并没有说任何东西都会擦除或随机化对象。

在典型的实现中,当函数返回时,堆栈指针会更改为指向新的堆栈顶部。没有任何额外的工作可以擦除堆栈上的任何数据。所以它仍然存在,直到发生其他事情。

这并不意味着您可以可靠地使用堆栈上的空间。有很多事情可以改变数据或改变您对它的使用:

  • 其他例程调用将使用堆栈。
  • 当前例程中的其他操作可能会使用堆栈。
  • 信号可能会导致堆栈被使用。
  • 如果编译器发现您对对象的使用不受支持,则其优化可能会以意想不到的方式改变您的程序。

脚注

1 “对象的生命周期是程序执行期间保证为其保留存储的部分”(C 2018 6.2.4 1)。对于函数中定义的常规对象,它们的生命周期在函数执行结束时结束(通常是因为函数返回,但也可能是因为执行了longjmp 或程序正在终止)。因此,当生命周期结束时,这意味着存储不再保证保留。存储仍然存在。没有什么可以保证改变它。所有的变化是,在函数执行时,您可以保证为该对象保留存储空间,并且在函数结束后,保证就消失了。

【讨论】:

  • 说得通,未定义的行为。
猜你喜欢
  • 1970-01-01
  • 2020-03-09
  • 1970-01-01
  • 2015-11-16
  • 2013-01-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-10-16
相关资源
最近更新 更多