【问题标题】:Why doesn't malloc allocate enough memory?为什么 malloc 没有分配足够的内存?
【发布时间】:2014-04-24 00:51:43
【问题描述】:

我真的被一段非常简单的代码困住了。 该程序采用./a.out -t=1,32,45,2 之类的参数,并在标准输出中打印逗号数量。但是有时执行会正常工作,并且经常会引发分段错误。

我在substr_cnt这行函数中发现了这个问题(我还在下面的代码中放置了相应的注释):

target_counting = (char *)malloc(sizeof(char)*(strlen(target)));

实际上 malloc 返回 NULL。如果我将sizeof(char) 更改为sizeof(char *),所有的工作都会像魅力一样开始,但我不明白为什么会这样。此外,在 main 函数中,我也使用 malloc,甚至使用同一行

arg_parameter = (char *) malloc(sizeof(char)*(strlen(argv[1] - 3)));

一切正常。

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

#define strindex(target, source) ((size_t) strstr(target, source) - (size_t) target)

int substr_cnt( char *target, char *source ) {
    int i=0;
    int cnt=0;
    char *target_counting;
    //this is NOT working
    target_counting = (char *)malloc(sizeof(char)*(strlen(target)));
    //this is working
    //target_counting = (char *)malloc(sizeof(char *)*(strlen(target)));

    if (target_counting == NULL) {
        printf("malloc failed\n");
        return -1;
    }
    strcpy(target_counting, target);
    while ((i=strindex(target_counting, source)) > 0) {
        strncpy(target_counting, target_counting + i + 1, strlen(target_counting));
        cnt++;
    }
    free(target_counting);
    return cnt;
}

int main( int argc, char *argv[] )
{
    int i;
    int default_behavior = 0;
    int arg_parametr_cnt;
    char *arg_parameter; 
    if (argc == 1) {
        default_behavior = 1;
    } else if (argv[1][0] == '-' && argv[1][1] == 't' && argv[1][2] == '=') {
        //this is working
        arg_parameter = (char *) malloc(sizeof(char)*(strlen(argv[1] - 3)));
        strncpy(arg_parameter, argv[1]+3, strlen(argv[1]));
        printf("%s\n", arg_parameter);
        arg_parametr_cnt = substr_cnt(arg_parameter, ",");
        printf("commas: %d\n", arg_parametr_cnt);
    }
    else {
        printf("wrong command line");
        return 1;
    } 
    return 0;
}

【问题讨论】:

  • 您应该知道 strncpy 不保证包含空终止符。这就是您的程序崩溃的部分原因。
  • 我不认为你想要这个:strlen(argv[1] - 3) in malloc in main()。应该是strlen(argv[1]) - 3

标签: c segmentation-fault malloc


【解决方案1】:

这里有几个问题,主要的一点,你根本不需要分配内存。您可以在不修改字符串的情况下实现对给定子字符串的搜索,因此可以直接处理给定的argv 参数,例如

int substr_cnt(const char *haystack, const char *needle)
{
    int cnt = 0;
    const char *found = haystack;
    while ((found = strstr(found, needle)) != NULL) {
        ++found;
        ++cnt;
    }

    return cnt;
}

main中的调用也一样,直接传argv就行了

arg_parametr_cnt = substr_cnt(argv[1] + 3, ",");

现在回答你的问题,除非你真的看到了

的输出
printf("malloc failed\n");

我不相信,malloc 会返回 NULL,因为当您分配更大的内存量时,sizeof(char*)sizeof(char),它会起作用。

程序崩溃的原因已在其他答案中介绍。总结一下

  • target_counting = (char *)malloc(sizeof(char)*(strlen(target))); 分配的字符少于应有的 1 个字符
  • while ((i=strindex(target_counting, source)) &gt; 0) 我不确定,当strstr 的结果是NULL 时会发生什么。 strindex 可能返回一个负数,这取决于你的内存布局,但我不确定。
  • strncpy(target_counting, target_counting + i + 1, strlen(target_counting)); 这不是一个真正的问题,但由于您复制了字符串的其余部分,您可以改用 strcpy(target_counting, target_counting + i + 1)
  • arg_parameter = (char *) malloc(sizeof(char)*(strlen(argv[1] - 3))); 这应该是 malloc(sizeof(char) * strlen(argv[1]) - 3 + 1)
  • strncpy(arg_parameter, argv[1]+3, strlen(argv[1])); 再次 strcpy(arg_parameter, argv[1]+3) 就足够了

更新:

在这个版本中

int strindex(char *target, char *source)
{
    char *idx;
    if ((idx = strstr(target, source)) != NULL) {
        return idx - target;
    } else {
        return -1;
    }
}

您对NULL 进行了明确测试并采取相应措施。

在宏版本中

#define strindex(target, source) ((size_t) strstr(target, source) - (size_t) target)

没有这样的测试。您可以通过计算strstr() 和基地址target 之间的差来确定索引。到目前为止这很好,但是当strstr() 返回NULL 时会发生什么?

指针算法是用两个指针定义的,指向同一个数组。一旦两个指针指向不同的数组,或者一个指向一个数组而另一个指向其他位置,则行为未定义。

从技术上讲,当您计算 NULL - target 时,它可能产生一个负值,但也可能不会。如果target 指向0x0f0a3a90 的地址,您可能拥有0x0 - 0x0f0a3a90 并得到一个负值。但是,如果target 指向0xfe830780,它可能会被解释为负数,然后0x0 - 0xfe830780 可能会导致正数。

但重点是,你有未定义的行为。如需进一步阅读,请查看 指针算术,例如C++: Pointer Arithmetic

【讨论】:

  • 事实证明,主要问题出在我的宏中,只是将其转换为函数,现在一切正常。 int strindex( char *target, char *source ) { char *idx; if ((idx = strstr(target, source)) != NULL) { return idx - target; } else { return -1; } }
  • 但老实说我还是不明白,为什么会这样。
【解决方案2】:

您的 malloc 没有为空终止符分配空间,您需要 malloc (strlen(string)+1)。 带有 char* 的 malloc 可以工作,因为指针(正常)4 字节长,因此您分配的内存是所需内存的 4 倍 - 减去空终止符所需的 1 字节。

【讨论】:

    【解决方案3】:

    问题可能出在这里:main 中的malloc(sizeof(char)*(strlen(argv[1] - 3))。您从argv[1] 中减去3。 我想你打算使用:

    malloc(sizeof(char)*(strlen(argv[1]) - 2)); // Allocate one more space for '\0' character

    这样做会使strlen 访问未分配的内存。

    您的程序可能不会在这里失败,但稍后会失败,因为它只是undefined behavior

    【讨论】:

      【解决方案4】:

      有几个缓冲区溢出,但我认为让你程序崩溃的错误如下:

          strncpy(target_counting, target_counting + i + 1, strlen(target_counting));
      

      注意strncpy中的字符串不能重叠!

      我建议你改用 memmove,因为 memmove 可以处理重叠的缓冲区:

          memmove(target_counting, target_counting + i + 1, strlen(target_counting + i + 1) + 1);
      

      【讨论】:

        【解决方案5】:

        我认为你的主要问题在这里:

         arg_parameter = (char *) malloc(sizeof(char)*(strlen(argv[1] - 3)));
        

        尤其是这里

         strlen(argv[1] - 3)
        

        您传递给argv[1]-3strlen 地址是无效地址。 实际上你的意思是strlen(argv[1]) - 3。正如其他人所说,您还应该为\0 添加一个字符,所以strlen(argv[1]) - 2

        【讨论】:

          猜你喜欢
          • 2019-11-18
          • 1970-01-01
          • 2023-03-19
          • 2017-05-26
          • 2015-02-08
          • 1970-01-01
          • 1970-01-01
          • 2021-12-10
          • 1970-01-01
          相关资源
          最近更新 更多