如何改进 C 中动态分配字符串的代码



所以我现在正在练习和学习C,遇到了CodeWars的一个相当简单的挑战,要求打印出一串"Aa~","Pa!"和"Aa!",这取决于n是否<= 6。我知道我们如何用数组做到这一点,但为了提高效率,我想尝试使用动态分配的字符数组,例如使用 malloc。

我想确保我通过这些问题掌握基础知识

  1. 所以我知道在其他带有 int 的 malloc 示例中,我们设置了一个指针(int 类型)来指向分配的内存块。当我声明"char *ptr"指向分配的内存块时,它仍然是一个指针,因为请原谅我,我认为"char *anything"意味着它是表示字符串的约定。所以不确定如果我没有像我想象的那样设置像"char **ptr"这样的指针,为什么下面的工作有些。

  2. 为什么当我尝试返回答案时,尤其是当"val"为 1 或 0 时,我会收到"对象 0x100000fab 的 malloc:*** 错误:未分配被释放的指针"类型的错误?我在某处读到将指针(字符串?),答案更改为NULL将解决问题,但不完全确定为什么会起作用。

3 为了继续上述问题,为了释放空间,如果我们在一个函数中动态分配一个内存块,但需要从该函数返回一个值,最好的方法是什么?例如,我们是释放之后还是之前的空间?

谢谢大家的投入。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define val 1
char *sc(int); // function declaration/prototype 
int main(int argc, const char * argv[]) {
char *answer = sc(val);
printf("The answer is %sn", answer);
answer = NULL; // why does this work
free(answer);
return 0;
}
char *sc(int n) {
// if n < 6 then will have an extra "Aa!" after "Pa!" at the nth position
char *ptr = (char*) malloc(n*4);
if (ptr == NULL){
printf("malloc failed");
}
char *first = "Aa~ ";
char *second = "Pa! Aa!";
char *third = "Pa!";
if (n <= 6 && n >1) {
for (int i = 0; i <n-1; i++){
ptr = strcat(ptr, first);
}
ptr = strcat(ptr, second);
}
else if (n> 6){
for (int i = 1; i < n; i++) {
ptr = strcat(ptr, first);
}
ptr = strcat(ptr, third);
}
else if (n <= 1){
ptr = "";
}
else {
printf("Error!");
exit(0);
}
return ptr;
}

代码的主要问题是你没有从malloc()那里得到的缓冲区清零,所以第一个strcat()不一定写在字符串的开头,而是写在最后。

您可以在malloc()调用后立即使用strcpy()来解决此问题并检查:

strcpy(ptr, "");

或者,等效地,您可以将缓冲区的第一个字节设置为零。由于 C 字符串是以零结尾的字符串,因此将字符设置为零将指示它位于末尾:

ptr[0] = 0;

您似乎也分配了太短的缓冲区。如果您写入Aa~(4 个字节)的n-1副本加上一个Pa! Aa!的副本(包含终止零时为 8 个字节!),您实际上需要4 * (n+1)作为空间。因此,要么始终分配它,要么这样做n < 6以防您需要额外的字节。

这也是一个问题:

ptr = "";

因为现在您的ptr不再指向malloc()返回的缓冲区,而是指向二进制文件中的静态(空)字符串。这很可能是你从free()那里得到麻烦的地方,因为在你的二进制文件中的静态字符串上调用它肯定是错误的。

此外,设置ptr = ""后,您将不再引用您分配的缓冲区,这意味着您很可能刚刚创建了内存泄漏!

在这种情况下,您应该简单地使用strcpy()或将第一个字节设置为零。但是,如果您在程序开始时执行此操作,则无需在此处执行此操作。

最后,free(NULL);工作(如 in,不会引发错误),因为这是其规范的一部分,您可以向它传递一个 NULL 指针,它什么也不做。但请注意,它不会释放您分配的缓冲区,因此此处也有内存泄漏。

我会进一步重构代码的第二部分,这样你就不会有太多重复的附加字符串:

char *sc(int n) {
/* if n <= 6 then will have an extra "Aa!"
* after "Pa!" at the nth position.
*/
char *ptr;
if (n < 0) {
printf("Error!");
return NULL;
}
ptr = (char*) malloc(4 * (n+1));
if (ptr == NULL){
printf("malloc failed");
return NULL;
}
strcpy(ptr, "");
if (n > 1) {
for (int i = 1; i < n; i++){
strcat(ptr, "Aa~ ");
}
strcat(ptr, "Pa!");
if (n <= 6) {
strcat(ptr, " Ah!");
}
}
return ptr;
}

另请注意,您不需要每次都strcat()的结果赋值回ptr,因为它总是返回其第一个参数,因此在那里分配它实际上不会改变任何东西。

  1. 我认为"char *anything"意味着它是表示字符串的约定。所以不确定如果我没有像我想象的那样设置像"char **ptr"这样的指针,为什么下面的工作有些。

您正在为字符串分配内存,因此只需要char *.char **将用于包含多个字符串的数组,或指向包含指向字符串的指针的变量的指针。

  1. 为什么我收到"对象 0x100000fab 的 malloc:*** 错误:未分配正在释放的指针">

当你做ptr = "";时,你会得到它。执行此操作后,ptr不再指向分配的内存malloc,它指向该字符串文本。如果要将分配的内存设置为空字符串,可以

ptr[0] = '';

这会在字符串的第一个元素中放置一个空终止符。

您还需要在使用strcat()追加到字符串的代码之前执行此操作。否则,将追加到未初始化的数据。最简单的方法是在分配内存后立即执行此操作(然后您在n <= 1块中不需要它。

不需要else块。除了您测试的 3 种可能性之外,没有其他可能性,除非 CPU 出现故障(在这种情况下,所有赌注都关闭)。但是,您应该在调用malloc()之前检查n < 1,因为您无法分配负内存,并且malloc(0)可能会返回NULL

当你为ptr分配空间时,你需要为字符串的终止空值添加 1 个字节。

char *sc(int n) {
// if n < 6 then will have an extra "Aa!" after "Pa!" at the nth position
if (n >= 1) {
char *ptr = malloc(n*4 + 1);
} else {
char *ptr = malloc(1);
}
if (ptr == NULL){
printf("malloc failed");
exit(1);
}
ptr[0] = ''; // initialize empty string
char *first = "Aa~ ";
char *second = "Pa! Aa!";
char *third = "Pa!";
if (n <= 6 && n >1) {
for (int i = 0; i <n-1; i++){
ptr = strcat(ptr, first);
}
ptr = strcat(ptr, second);
}
else if (n> 6){
for (int i = 1; i < n; i++) {
ptr = strcat(ptr, first);
}
ptr = strcat(ptr, third);
}
else if (n <= 1){
// nothing to do
}
return ptr;
}

相关内容

  • 没有找到相关文章

最新更新