为什么malloc没有分配足够的内存?

时间:2014-03-18 11:15:17

标签: c segmentation-fault malloc

我真的很难用一段非常简单的代码。 该程序采用./a.out -t=1,32,45,2之类的参数,并在stdout中打印逗号数量。但有时执行工作正常,并且更经常抛出分段错误。

我在这个函数行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;
}

5 个答案:

答案 0 :(得分:3)

这里有几个问题,重点是,你根本不需要分配内存。您可以实现搜索给定的子字符串而无需修改字符串,因此可以直接使用给定的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*) vs sizeof(char),它就可以了。

其他答案已经涵盖了程序崩溃的原因。总结

  • target_counting = (char *)malloc(sizeof(char)*(strlen(target)));分配一个少于它的字符
  • while ((i=strindex(target_counting, source)) > 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

答案 1 :(得分:2)

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

答案 2 :(得分:2)

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

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

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

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

答案 3 :(得分:2)

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

    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);

答案 4 :(得分:1)

我认为你的主要问题在于:

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

特别是在这里

 strlen(argv[1] - 3)

您传递到strlen的{​​{1}}地址,该地址不是有效地址。 实际上你的意思是argv[1]-3。正如其他人所说,你也应该为strlen(argv[1]) - 3添加一个字符\0