【问题标题】:Function receives a pointer to double, allocates memory and fills resulted array of doubles [duplicate]函数接收指向双精度的指针,分配内存并填充结果的双精度数组[重复]
【发布时间】:2019-06-06 07:22:26
【问题描述】:

我的目标是将指向 double 的指针传递给函数,在函数内部动态分配内存,用 double 值填充结果数组并返回填充数组。在 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(),但是,所有值都归零。问题是什么?至于第二个程序,分割错误的原因是什么?

【问题讨论】:

  • if(count = 0) 你的意思是if(count == 0)?您的编译器是否发出任何警告?它可能会给你一个警告。
  • 为什么“把戏”没用?除非有你没有提到的东西,否则它正是你问题的正确解决方案。
  • @Blaze,对不起“==”运算符。至于其他部分,编译器没有警告。
  • @rtoijala,这就是我的代码中的全部内容。没有其他功能或操作。
  • @tenghiz 使用双指针是正确的方法。

标签: c pointers malloc


【解决方案1】:

正确的做法是这样的:

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 调用。值得一提的是,如果您希望在 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;
}

您在下面的 cmets 中提到,您通常遇到指针问题。这并不罕见。这可能有点棘手,需要一些练习才能习惯。我最好的建议是了解*&amp; 的真正含义并认真思考问题。 * 是解引用运算符,所以*p 是地址p 中存在的值。 **p 是地址 *p 中存在的值。地址运算符&amp;* 的逆运算符,因此*&amp;xx 相同。还要记住,用于索引的[] 运算符只是语法糖。它的工作原理是这样的:p[5] 转换为 *(p+5),其有趣的效果是 p[5]5[p] 相同。

在上述代码的第一个版本中,我使用了p = tmp 而不是*p = tmp,当我构建了一个完整的示例来查找该错误时,我还使用了*p[count] 而不是(*p)[count]。很抱歉,但这确实强调了我的观点。在处理指针,尤其是指向指针的指针时,真的要考虑你在写什么。 *p[count] 等价于*(*(p+count))(*p)[count] 等价于*((*p) + count) 这是完全不同的东西,不幸的是,即使我使用-Wall -Wextra -std=c18 -pedantic-errors 编译,这些错误都没有被发现。

您在下面的 cmets 中提到您需要转换 realloc 的结果。这可能意味着您正在使用 C++ 编译器,在这种情况下您需要进行强制转换,它应该是 (double *)。在这种情况下,更改为:

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

*p = tmp;

请注意,我还更改了指针的类型。在 C 中,tmp 是什么类型的指针并不重要,但在 C++ 中,它要么必须是 double*,要么您需要进行另一个转换:*p = (double*)tmp

【讨论】:

  • realloc 中使用(count+1)*sizeof(**p),因为count 是已经使用的元素数量。我想一定是scanf("%lf", &amp;(*p)[count]);
  • 我应该使用 *tmp 还是 **tmp?
  • 那么强制转换为 malloc 和 realloc 呢? (double*) 还是 (double**)?
  • @tenghiz 你的意思是在tmp的声明中吗?其实没关系。只要不取消引用它们,就可以安全地相互分配指针。
  • 我用纯文本写了你不应该使用malloc。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-10-27
  • 1970-01-01
  • 2018-06-23
  • 2016-08-17
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多