函数接收一个指向double的指针,分配内存并填充生成的double数组

时间:2019-06-06 07:22:26

标签: c pointers malloc

我的目标是向函数传递双精度指针,在函数内部动态分配内存,用双精度值填充结果数组并返回填充数组。在StackOverflow中潜心研究之后,我发现了两个相关的主题,即Initializing a pointer in a separate function in CC dynamically growing array。因此,我尝试编写自己的代码。但是,结果与上述主题中描述的结果不同。该程序是同时使用gcc和Visual Studio运行的。

第一次审判。

int main()
{
    double *p;
    int count = getArray(&p);
    <...print content of p...>
    return 0;
}

int getArray(double *p)
{
    int count = 1;
    while(1)
    {
        if(count == 1)
            p = (double*)malloc(sizeof(double));
        else 
            p = (double*)realloc(p, count*sizeof(double));
        scanf("%lf", &p[count-1]);
        <...some condition to break...>
        count++;
    {
    <... print the content of p ...>
    return count;
}

(这是编译器发出的有关不兼容的参数类型的警告。请忽略它。)

输入:

1.11
2.22
3.33

输出:

1.11
2.22
3.33

0.00
0.00
0.00

第二次审判。

int main()
{
    double *p;
    int count = getArray(&p);
    <...print content of p...>
    return 0;
}

int getArray(double **p)
{
    int count = 1;
    while(1)
    {
        if(count == 1)
            *p = (double*)malloc(sizeof(double));
        else 
        {
            double ** temp = (double*)realloc(*p, count*sizeof(double));
            p = temp;
        }
        scanf("%lf", &(*p)[count-1]);
        <...some condition to break...>
        count++;
    {
    <... print the content of p ...>
    return count;
}

输入:

1.11
2.22
Segmentation error.

我在几种不同的* nix机器上尝试了此方法,当循环使用realloc时,该方法失败。出乎意料的是,此代码可以在Visual Studio中完美运行。

我的问题是:第一个代码允许分配和重新分配内存,甚至将所有分配的内存传递给main(),但是,所有值都为零。问题是什么?至于第二个程序,分割错误的原因是什么?

1 个答案:

答案 0 :(得分:3)

正确的做法是这样的:

int getArray(double **p)
{
    int count = 0;
    while(1)
    {
        if(count == 0)
            *p = malloc(sizeof(**p));
        else 
            *p = realloc(*p, (count+1)*sizeof(**p));
        scanf("%lf", &((*p)[count]));
        <...some condition to break...>
        count++;
    {
    <...print content of p...>
    return count;
}

如果将指针传递给函数,并且不仅要更改其指向的值,还希望更改其指向的地址,则必须使用双指针。否则根本不可能。

通过使用sizeof(var)而不是sizeof(type)节省一些麻烦。如果您写int *p; p = malloc(sizeof(int));,那么您将写两次相同的东西(int),这意味着如果它们不匹配,您可能会弄乱东西,这正是您所经历的。这也使得以后更改代码变得更加困难,因为您需要在多个位置进行更改。如果您改写int *p; p = malloc(sizeof(*p));,那风险就消失了。

加上don't cast malloc。完全没有必要。

分配(和重新分配)时,您始终应该做的另一件事是检查分配是否成功。像这样:

if(count == 0) 
    *p = malloc(sizeof(**p));
else 
    *p = realloc(*p, (count+1)*sizeof(**p));

if(!p) { /* Handle error */ }

还请注意,可以重新分配NULL指针,因此在这种情况下,malloc是不必要的。仅在不使用if语句的情况下使用realloc调用。值得一提的是,如果您希望在重新分配失败时能够继续执行,则不应将p赋值给返回值。如果realloc失败,您将失去以前的一切。改为这样:

int getArray(double **p)
{
    int count = 0;

    // If *p is not pointing to allocated memory or NULL, the behavior
    // of realloc will be undefined.
    *p = NULL;

    while(1)
    {
        void *tmp = realloc(*p, (count+1)*sizeof(**p));
        if(!tmp) {
            fprintf(stderr, "Fail allocating");
            exit(EXIT_FAILURE);
        }

        *p = tmp;

        // I prefer fgets and sscanf. Makes it easier to avoid problems
        // with remaining characters in stdin and it makes debugging easier

        const size_t str_size = 100;
        char str[str_size];
        if(! fgets(str, str_size, stdin)) {
            fprintf(stderr, "Fail reading");
            exit(EXIT_FAILURE);
        }

        if(sscanf(str, "%lf", &((*p)[count])) != 1) {
            fprintf(stderr, "Fail converting");
            exit(EXIT_FAILURE);
        }

        count++;

        // Just an arbitrary exit condition
        if ((*p)[count-1] < 1) {
            printf("%lf\n", (*p)[count]);
            break;
        }
    }
    return count;
}

您在下面的评论中提到,您通常在使用指针时遇到麻烦。这并不稀奇。这可能有点棘手,并且需要一些练习才能习惯。我最好的建议是学习*&的真正含义,并认真思考。 *是取消引用运算符,因此*p是地址p上存在的值。 **p是地址*p上存在的值。地址运算符&*有点相反,因此*&xx相同。还请记住,用于索引的[]运算符只是语法糖。它的工作方式如下:p[5]转换为*(p+5),其有趣的效果是p[5]5[p]相同。

在上述代码的第一个版本中,我使用p = tmp而不是*p = tmp,并且在构建一个完整的示例来查找该错误时,我还使用了*p[count]而不是{{1 }}。抱歉,但这确实强调了我的观点。当处理指针,尤其是指向指针的指针时,请真正考虑您在写什么。 (*p)[count]等效于*p[count],而*(*(p+count))等效于(*p)[count],这是完全不同的,而且不幸的是,即使我使用{{1 }}。

您在下面的评论中提到,您需要转换*((*p) + count)的结果。这可能意味着您正在使用C ++编译器,在这种情况下,您需要进行强制转换,并且应该为-Wall -Wextra -std=c18 -pedantic-errors。在这种情况下,请更改为:

realloc

请注意,我还更改了指针的类型。在C语言中,指针类型(double *)并不重要,但是在C ++中,它要么必须是double *tmp = (double*)realloc(*p, (count+1)*sizeof(**p)); if(!tmp) { fprintf(stderr, "Fail allocating"); exit(EXIT_FAILURE); } *p = tmp; ,要么您需要执行另一种强制转换:tmp