在使用 malloc 分配的 2D char 数组上使用 strcpy

Using strcpy on a 2D char array allocated with malloc

提问人:MrByte 提问时间:1/20/2022 更新时间:1/21/2022 访问量:199

问:

我有一个问题,我无法找出解决方案。我必须将一些代码编程为μC,但我不熟悉它。

我必须创建一个分析并将其结果显示在机器的屏幕上。分析已准备就绪且功能正常。但是将分析结果放到屏幕上是我的问题。

我必须将所有结果存储在全局数组中。由于堆栈在机器上确实受到限制,因此我必须将其带到更大的堆中。链接器就是这样制作的,每个动态分配最终都会在堆上。但这是用 C 语言完成的,所以我不能使用“new”。但是使用 malloc 分配的所有内容最终都会自动出现在堆上,这就是我需要使用 malloc 的原因,但我以前没有使用过它,所以我遇到了真正的麻烦。屏幕的问题是,它只接受 char 数组。

总而言之:我必须创建一个全局 2D 字符数组,其中包含多达 100 个位置的结果,并且我必须使用 malloc 为其分配内存。

为了使它更加复杂,我必须在 buffer.h 文件中声明带有“extern”的变量,并且必须在 buffer.c 文件中实现它。

所以我的 buffer.h 行看起来像这样:

extern char * g_results[100][10];

在 buffer.c 中,我使用:

g_results[0][0] = malloc ( 100 * 10 )

每个字符都是 1 个字节,因此数组的大小应为 1000 字节,以保存 100 个长度为 9 的结果,1 终止 /0。右?

现在我尝试在strcpy的帮助下将结果存储到这个数组中。 我在分析结束时的 for 循环中执行此操作。

for (int i = 0; i < 100, i++)
{
  // Got to convert it to text 1st, since the display does not accept anything but text.
  snprintf(buffer, 9, "%.2f", results[i]);
  strcpy(g_results[i][0], buffer);
}

然后,我遍历屏幕上的g_results_buffer并显示内容。问题是:它仅适用于第一个结果。一切都如我所愿。

但所有其他行都是空的。我检查了结果数组,所有值都存储在其中,所以这不是问题的原因。此外,这些值不会被覆盖,它实际上是第一个值。

我看不出这里的问题是什么。

我的猜测是:

a) 使用 malloc 的分配未正确完成。只为第一个元素分配空间?当我删除 [0][0] 时,我收到编译器错误:“赋值到具有数组类型的表达式”。但我不知道这应该意味着什么。

b) 指针的(完全)错误使用。有没有办法将该数组声明为非指针,但仍在堆上?

我真的需要你的帮助。

如何将第一个元素之后的 results-array 的结果存储到 g_results 数组中?

C 多维阵列 malloc 微控制器 strcpy

评论

0赞 kaylum 1/20/2022
请提供完整的代码作为最小的可重现示例,以及确切的输入、预期结果和实际结果。不要求也不想要完全转储您的代码。创建一个小而完整的示例来说明问题。
1赞 wildplasser 1/20/2022
so the array should have the size of 1000 byte to hold 100 results with the length of 9 and 1 terminating /0. Right?错。该数组包含 100*10 个指针
0赞 MrByte 1/20/2022
@wildplasser好吧,这不是我想要或需要的。我该如何解决这个问题?我需要一个 2D 数组,它可以为所有 100 个位置存储一个值。

答:

0赞 KamilCuk 1/20/2022 #1

这一切都很奇怪,你似乎只是想要:

// array of 100 arrays of 10 chars
char g_results[100][10];

for (int i = 0; i < 100, i++) {
   // why snprintf+strcpy? Just write where you want to write.
   snprintf(g_results[i], 10, "%.2f", results[i]);
   //                                  ^^^^^^^^ has to be float or double
   //                     ^^ why 9? The buffer has 10 chars.
}

只为第一个元素分配空间?

是的,你是,你只将第一个元素分配给 。g_results[0][0]malloc ( 100 * 10 )

指针的错误用法。有没有办法将该数组声明为非指针,但仍在堆上?

不。要在堆上分配某些内容,您必须调用 .malloc

但是没有理由使用堆,尤其是您在微控制器上,尤其是您知道要分配多少个元素。堆是未知的,如果你知道你正好想要 100 x 10 x 字符,就拿它们。

总的来说,考虑阅读一些 C 书籍

我不知道这应该意味着什么。

不能将数组作为一个整体进行赋值。您可以逐个分配给数组元素。

评论

0赞 MrByte 1/21/2022
您好,感谢您的回答。静态内存分配的问题在于,这是在一个小的内部内存中完成的,该内存几乎已满。我没有空间把它存放在那里。但设备具有更大的外部 RAM,链接器使用外部 RAM 进行动态内存分配。这就是为什么我需要动态地进行它,以将其放入外部内存中。
0赞 Eric Postpischil 1/20/2022 #2

我必须将所有结果存储在全局数组中。由于堆栈在机器上确实受到限制,因此我必须将其带到更大的堆中。

“全局数组”和“更大的堆”是不同的东西。C 没有真正的全局命名空间。它确实具有具有静态存储持续时间的对象,为程序的整个执行保留了内存。人们使用“堆”来指代动态分配的内存,该内存从程序请求它(如 )到程序释放它(如 )保留。mallocfree

在函数外部声明的变量具有其名称的文件范围、外部或内部链接以及静态存储持续时间。这些与动态内存不同。所以不清楚你想要什么内存:静态存储持续时间还是动态内存?

“堆”用词不当。正确地说,这个词指的是一种数据结构。您可以简单地将其称为“已分配内存”。“堆”可用于组织可用于分配的内存块,但它可用于其他目的,并且内存管理例程可以使用其他数据结构。

链接器就是这样制作的,每个动态分配最终都会在堆上。

链接器将对象模块链接在一起。它与堆无关。

但是使用 malloc 分配的所有内容最终都会自动出现在堆上,这就是我需要使用 malloc 的原因,...

分配内存时,它不会最终位于堆中。堆(如果它用于内存管理)是已释放的内存的保留位置,直到再次分配它。分配内存时,内存将从堆中取出。

屏幕的问题是,它只接受 char 数组。

目前尚不清楚。也许您的意思是必须通过提供字符串来与某些显示设备进行通信。

总而言之:我必须创建一个全局 2D 字符数组,其中包含多达 100 个位置的结果,并且我必须使用 malloc 为其分配内存。

这在你帖子的开头会很有用。

所以我的 buffer.h 行看起来像这样:

extern char * g_results[100][10];

这声明了一个包含 100 个数组的数组,该数组包含 10 个指向 的指针。因此,您将有 1,000 个指向字符串的指针(从技术上讲,指向字符串第一个字符的指针为 1,000 个指针,但我们通常将指向字符串第一个字符的指针称为指向字符串的指针)。这不太可能是你想要的。如果需要 100 个字符串,每个字符串最多 10 个字符(包括这 10 个字符中的终止 null 字节),那么指向 100 个数组(每个数组,每个数组)的指针就足够了。这可以通过以下方式声明:char *

extern char (*g_results)[100][10];

但是,在处理数组时,我们通常只使用指向数组第一个元素的指针,而不是指向整个数组的指针:

extern char (*g_results)[10];

在 buffer.c 中,我使用:

g_results[0][0] = malloc ( 100 * 10 )

每个字符都是 1 个字节,因此数组的大小应为 1000 字节,以保存 100 个长度为 9 的结果,1 终止 /0。右?

该空间足以容纳 100 个 10 字节字符串的实例。它不适用于您的原始声明,这需要 1,000 个指针的空间。extern char * g_results[100][10];

但是,在更改为 之后,我们现在必须将返回的地址分配给 ,而不是 。我们可以通过以下方式分配所需的空间:g_resultsextern char (*g_results)[10];mallocg_resultsg_results[0][0]

g_results = malloc(100 * sizeof *g_results);

或者,不分配内存,只需使用静态存储:

char g_results[100][10];

现在我尝试在strcpy的帮助下将结果存储到这个数组中。我在分析结束时的 for 循环中执行此操作。

for (int i = 0; i < 100, i++) { // Got to convert it to text 1st, since the display does not accept anything but text. snprintf(buffer, 9, "%.2f", results[i]); strcpy(g_results[i][0], buffer); }

没有必要使用;您可以将结果直接发送到最终内存。buffersnprintf

由于是 100 个数组的数组,而数组为 10 个数组。当数组用作表达式时,它会自动转换为指向其第一个元素的指针,除非它是 的操作数 ,一元的操作数 ,或者是用于初始化数组的字符串文字(在定义中)。因此,您可以使用来获取应该写入字符串的地址:g_resultscharg_results[i]charsizeof&g_results[i]i

snprintf(g_results[i], sizeof g_results[i], "%.2f", results[i]);

关于这方面的一些说明:

  • 我们看到数组在自动转换和不自动转换的情况下都使用。参数将转换为 .在 中,给出数组的大小,而不是指针。g_results[i]&g_results[i][0]sizeof g_results[i]sizeof
  • 传递到的缓冲区长度不需要减少 1 即可允许终止 null 字符。 自行处理。因此,我们传递了全尺寸,.snprintfsnprintfsizeof g_results[i]

但所有其他行都是空的。

那是因为你的声明是错误的。它声明了 1,000 个指针,而您只在 中存储了一个地址,因此所有其他指针都未初始化。g_resultsg_results[0][0]

评论

0赞 MrByte 1/21/2022
这非常有帮助,解决了我的问题。谢谢。我只想澄清几件事:我不是μC程序员,我只能相信我被告知的事情。他们告诉我,该设备有一个小型内部RAM(几KB)和一个外部RAM(几MB),内部RAM用于静态内存分配,外部RAM用于动态内存分配,他们称内部RAM为“堆栈”,外部称为“堆”。我必须尽可能地对它进行静态编程 - 以便机器每次都做同样的事情。你对显示器猜对了。它使用仅接受 char 数组的 API。