我正在处理一个用例,其中我希望有dynamic array个指针,这些指针指向某种类型的 struct .

我注意到一些奇怪的行为,我希望有人能帮忙澄清.

首先,我将使用static array个指针来分享passing test.


TEST test_f_chunk_array(void) {
  f_chunk* first;
  f_chunk* second;

  test_f_chunk_array_setup(&first, &second);

  // just validating that the setup is working as expected.
  ASSERT_EQ_FMT(1ul, (*first->first)->bytes->offset, "%zu");
  ASSERT_EQ_FMT(5ul, (*second->first)->next->bytes->offset, "%zu");
  ASSERT_EQ_FMT(6ul, (*second->first)->bytes->offset, "%zu");

  f_chunk* arr[2] = {NULL};
  arr[0] = first;
  arr[1] = second;

  // original pointers were not mutated, not surprising.
  ASSERT_EQ_FMT(1ul, (*first->first)->bytes->offset, "%zu");
  ASSERT_EQ_FMT(5ul, (*second->first)->next->bytes->offset, "%zu");
  ASSERT_EQ_FMT(6ul, (*second->first)->bytes->offset, "%zu");

  f_chunk* first_chunk = arr[0];
  f_chunk* second_chunk = arr[1];

  // awesome, retrieval works as expected.
  ASSERT_EQ_FMT(1ul, (*first_chunk->first)->bytes->offset, "%zu");
  ASSERT_EQ_FMT(5ul, (*second_chunk->first)->next->bytes->offset, "%zu");
  ASSERT_EQ_FMT(6ul, (*second_chunk->first)->bytes->offset, "%zu");

  PASS();
}

现在,对于使用dynamic arrayfailing test人,我注意到了一些我不太理解的行为.

TEST test_f_chunk_dynamic_array(void) {
  f_chunk* first;
  f_chunk* second;

  test_f_chunk_array_setup(&first, &second);

  // just validating that the setup is working as expected.
  ASSERT_EQ_FMT(1ul, (*first->first)->bytes->offset, "%zu");
  ASSERT_EQ_FMT(5ul, (*second->first)->next->bytes->offset, "%zu");
  ASSERT_EQ_FMT(6ul, (*second->first)->bytes->offset, "%zu");

  // using dynamic array.
  size_t size = 2;
  f_chunk** arr = malloc(size * sizeof(f_chunk*));

  arr[0] = first;
  arr[1] = second;

  /*
    sanity check, I wouldn't expect for the underlying struct to be mutated
    as the array would only assign a copy of the pointer value?  
  */
  ASSERT_EQ_FMT(1ul, (*first->first)->bytes->offset, "%zu");
  // ^^^^ segfaults.

  ASSERT_EQ_FMT(5ul, (*second->first)->next->bytes->offset, "%zu");
  ASSERT_EQ_FMT(6ul, (*second->first)->bytes->offset, "%zu");

  f_chunk* first_chunk = arr[0];
  f_chunk* second_chunk = arr[1];

  // this doesn't work either.
  ASSERT_EQ_FMT(1ul, (*first_chunk->first)->bytes->offset, "%zu");
  ASSERT_EQ_FMT(5ul, (*second_chunk->first)->next->bytes->offset, "%zu");
  ASSERT_EQ_FMT(6ul, (*second_chunk->first)->bytes->offset, "%zu");

  PASS();
}

很明显,我错过了什么,但我不确定是什么.我认为分配空间并在其上存储指针地址不会改变指针所指向的 struct .

Edit

分享设置,如果它有助于澄清.

void test_f_chunk_array_setup(f_chunk** first, f_chunk** second) {
  f_bytes* first_bytes;
  f_bytes* second_bytes;
  f_bytes* third_bytes;
  f_bytes_new(&first_bytes, true, 1ul);
  f_bytes_new(&second_bytes, true, 5ul);
  f_bytes_new(&third_bytes, false, 6ul);


  f_bytes_node* first_node;
  f_bytes_node* second_node;
  f_bytes_node* third_node;
  f_bytes_node_new(&first_node, first_bytes);
  f_bytes_node_new(&second_node, second_bytes);
  f_bytes_node_new(&third_node, third_bytes);

  first_node->next = NULL;
  second_node->next = NULL;
  third_node->next = second_node;

  f_chunk* first_chunk;
  f_chunk* second_chunk;

  f_chunk_new(&first_chunk, 1, &first_node, &first_node);
  f_chunk_new(&second_chunk, 2, &third_node, &second_node);

  *first = first_chunk;
  *second = second_chunk;
}

Second Edit

最小可重复示例:https://godbolt.org/z/6c8z7coYq

推荐答案

截至问题修订版4,

函数结尾处的这部分代码

f_chunk_new(&first_chunk, 1, &first_node, &first_node);
f_chunk_new(&second_chunk, 2, &third_node, &second_node);

*first = first_chunk;
*second = second_chunk;

暗示f_chunk_new的原型可能存在问题.在Godbolt链接中,f_chunk_new的原型是 f_chunk_new(f_chunk** out, int current, f_some_struct_node** firstref, f_some_struct_node** lastref),其中第三个参数是双指针,您要将&first_node传递给它.这通常表示您希望修改函数中的first_node,并在以后使用修改后的值,但您并不需要这样做.

由于*firstref在函数中根本没有修改,所以没有理由将指向非常数的指针(在本例中是双指针,但这是无关的)作为参数,然后将局部变量的地址传递给它.这永远是一个错误.

您遇到这种行为的最终原因是因为您在f_chunk struct 中存储了一个局部变量的地址,实际上是在test_f_chunk_array_setup中执行first_chunk->first=&first_node.在test_f_chunk_array_setup返回之后,变量first_node的生命周期结束,现在您的 struct 中有一个悬空指针.稍后,当您在测试函数中执行*first_chunk->first时,您正在访问一个生命周期已经结束的变量,这是未定义的行为.test_f_chunk_array似乎行得通,而test_f_chunk_dynamic_array行不通,这纯粹是巧合,你在这两种情况下都有不确定的行为.

C标准本质上没有堆栈和堆的概念,但如果它帮助您更好地理解它,那么是的,您的 comments 是正确的, node 是堆分配的,但指针仍然是堆栈变量,存储堆栈变量的地址以供以后使用是错误的.

C++相关问答推荐

librsvg rsvg_handle_get_dimensions获取像素大小与浏览器中的渲染大小没有不同

Mise()在虚拟内存中做什么?

为什么GCC可以调用未定义的函数?

增加getaddrinfo返回的IP地址数量

如何将字符串argv[]赋给C中的整型数组?

为什么内核使用扩展到前后相同的宏定义?

无效指针值在函数调用之间莫名其妙地改变

为什么GCC C23中的关键字FALSE不是整数常量表达式?

如何创建一个C程序来存储5种动物的名字,并在用户 Select 其中任何一种动物时打印内存地址?

向上强制转换C中的数值类型总是可逆的吗?

C++中矢量类型定义和数据保护的高效解决方案

使用错误的命令执行程序

函数的限制限定指针参数允许优化调用方函数吗?

防止C++中递归函数使用堆栈内存

为什么我从CSV文件中进行排序和搜索的代码没有显示数据的所有结果?

按长度对argv中的单词进行排序

';\n&39;和';\r&39;中的';\n&39;之间有什么关系?

用C++构建和使用DLL的困惑

在C中定义函数指针?

我们可以在不违反标准的情况下向标准函数声明添加属性吗?