【问题标题】:How to improve the code for dynamically allocated strings in C?如何改进 C 中动态分配字符串的代码?
【发布时间】:2020-05-28 13:41:25
【问题描述】:

所以我现在正在练习和学习 C,遇到了来自 CodeWars 的一个相当简单的挑战,要求打印一串“Aa~”、“Pa!”和“Aa!”取决于 n 是否

我想确保通过这些问题了解基础知识

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

  2. 当我尝试返回答案时,尤其是当“val”为 1 或 0 时,为什么会收到“malloc: *** error for object 0x100000fab: pointer being free was not assigned” 类型的错误?我在某处读到将指针(字符串?)答案更改为 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 %s\n", 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;
}

【问题讨论】:

  • answer = NULL; // why does this work -- 没有。它可能看起来有效,但它后面的行不会做任何事情,因为您的指针不再指向原始内存。很可能你那里有内存泄漏。
  • 鉴于char *ptr = (char*) malloc(n*4);,当此函数的调用者在其返回的指针上调用free() 时,ptr = "" 既是内存泄漏,也是内存损坏的可能来源。
  • strcat(ptr, first); 导致未定义的行为。 strcat() 要求参数为以 null 结尾的字符串,但您从未在分配后初始化 ptr 指向的内存。
  • ptr = strcat(ptr, second); 在我看来像是缓冲区溢出。
  • 如果你不使用argcargv,不要声明它们。

标签: c char malloc


【解决方案1】:

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

您可以在malloc() 调用后立即使用strcpy() 修复该问题并检查:

strcpy(ptr, "");

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

ptr[0] = 0;

您似乎也分配的缓冲区太短了。如果您编写n-1Aa~ 副本(4 个字节)加上Pa! Aa! 的一个副本(包括终止零时为8 个字节!)您实际上需要4 * (n+1) 作为空间。所以要么总是分配它,要么在n &lt; 6需要额外字节的情况下这样做。

这也是个问题:

ptr = "";

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

此外,在您设置ptr = "" 后,您不再拥有对您分配的缓冲区的任何引用,这意味着您很可能只是造成了内存泄漏!

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

最后,free(NULL); 可以工作(如,不会抛出错误),因为这是其规范的一部分,您可以向它传递一个 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,因为它总是返回它的第一个参数,所以将它分配在那里并没有真正改变任何东西。

【讨论】:

    【解决方案2】:
    1. 我认为“char *anything”意味着它是表示字符串的约定。因此,如果我没有像我想的那样设置像“char **ptr”这样的指针,我不确定为什么下面的代码会起作用。

    你正在为一个字符串分配内存,所以它只需要是char *char ** 将用于包含多个字符串的数组,或用于指向包含指向字符串的指针的变量的指针。

    1. 为什么会出现“malloc: *** 对象 0x100000fab 错误:未分配指针被释放”

    当您执行ptr = ""; 时,您会明白这一点。执行此操作后,ptr 不再指向使用malloc 分配的内存,而是指向该字符串文字。如果要将分配的内存设置为空字符串,可以这样做

    ptr[0] = '\0';
    

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

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

    不需要else 块。除非 CPU 出现故障(在这种情况下,所有赌注都关闭),否则除了您测试的 3 之外没有其他可能性。但是,您应该在调用malloc() 之前检查n &lt; 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] = '\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;
    }
    

    【讨论】:

      猜你喜欢
      • 2020-10-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-03-09
      • 1970-01-01
      • 2013-08-15
      • 2013-03-09
      • 2018-01-29
      相关资源
      最近更新 更多