【问题标题】:Get segmentation fault when manipulating with malloc in c在 c 中使用 malloc 操作时出现分段错误
【发布时间】:2022-01-20 21:57:42
【问题描述】:

我正在实现一个程序,将a 数组中的所有值除以100,然后使用malloc 将它们存储在b 数组中。问题是在main 中打印b 的值时出现分段错误。

这是我的代码

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

void divide(int *a, int n, double *b){
    b=malloc(n*sizeof(double));
    
    for(int i=0; i<n; i++){
        b[i]=(double)a[i]/100.0;
    }

    //check: values still remain in b
    for (size_t i = 0; i < 5; i++)
    {
        printf("%.2f ", b[i]);
    }
}

int main(){
    int a[]={1,2,3,4,5};
    double *b;

    divide(a,5,b);
    
    //check: lost value and cause segmentation fault
    for (size_t i = 0; i < 5; i++)
    {
        printf("%.2f ", b[i]);
    }
    free(b);
    return 0;
}

那么导致这个问题的原因以及如何解决呢?

提前致谢。

【问题讨论】:

  • b=malloc(n*sizeof(double));divide 的调用者没有任何意义。 main 中的 b 保持不变。 ,并且在此过程中,您还会在divide 中泄漏本地b 分配的内存。通过地址传递b 参数(因此是指向指针的指针)或利用函数的其他未使用的返回结果。 Thiis 是一个非常常见的 C 初学者问题,并且这个问题有 数百 个重复项,但可惜的是白话和问题描述如此多样化,以至于很难搜索为了。如果我找到链接,我会发布它。
  • malloc 与核心问题无关。核心问题是对函数内部参数的赋值不会修改调用函数中的参数。调用函数中的参数b和函数divide中的参数b是不同的对象。
  • @DavidRanieri 关于内存管理,这完全无关紧要。对于 any 程序也可以这么说,它充斥着内存泄漏或其他情况,它们并非旨在无限运行(例如永久服务进程)。这种习惯是可怕的,应该尽早在年轻工程师的学习路径中消除。
  • @WhozCraig 我同意,但是年轻的工程师也应该区分什么是“真正的”内存泄漏和什么不是:stackoverflow.com/a/274433/1606345 OP sn-p 不会泄漏内存。
  • @DavidRanieri 每次调用函数divide 时都会分配内存,并且一旦函数返回,就无法访​​问该内存,即丢失。在我看来,这确实是内存泄漏。

标签: c malloc dynamic-memory-allocation pass-by-value function-definition


【解决方案1】:

您正在将指针 b 按值传递给函数 divide

divide(a,5,b);

即该函数处理原始指针的副本。更改副本不会影响原始指针。

您需要通过指向它的指针通过引用传递指针,或者重新设计函数,使其返回指向函数内动态分配的内存的指针。

例如,函数可以通过以下方式声明和定义

double * divide( const int *a, size_t n )
{
    double *b = malloc( n * sizeof( double ) );

    if ( b != NULL )
    {
        for ( size_t i = 0; i < n; i++ )
        {
            b[i] = a[i] / 100.0;
        }

        //check: values still remain in b
        for ( size_t i = 0; i < n; i++ )
        {
            printf("%.2f ", b[i]);
        }
    }

    return b;
}

在 main 你可以写

double *b = divide( a, sizeof( a ) / sizeof( *a ) );

否则函数可能看起来像

void divide( const int *a, size_t n, double **b )
{
    *b = malloc( n * sizeof( double ) );

    if ( *b != NULL )
    {
        for ( size_t i = 0; i < n; i++ )
        {
            ( *b )[i] = a[i] / 100.0;
        }

        //check: values still remain in b
        for ( size_t i = 0; i < n; i++ )
        {
            printf("%.2f ", ( *b )[i]);
        }
    }
}

并像这样称呼

divide( a, sizeof( a ) / sizeof( *a ), &b );

【讨论】:

  • 非常感谢您的回答。但是如果我不想更改函数,那么如何通过引用传递b?我以为b是一个指针,那么把b传给函数就够了?
  • @Becker 查看我的附加帖子。
【解决方案2】:

divide 函数接收的是指针b 的副本。这意味着函数 main 中的变量 b 在调用后没有改变。一个更简单的例子来说明这一点是

void f(int n)
{
   n = 1;
}

f 被调用后,n 在调用方没有改变。

最简单的代码修正是让divide 接收指向指针b 的指针。 divide 的签名将是

void divide(int *a, int n, double **b);

但是,最好在同一个函数中分配和释放内存,因为很容易忘记 divide 分配内存。在处理数组时,我总是使用两个方便的宏函数 NEW_ARRAY 和 LEN 来简化代码。下面是我的建议。如果 a 的长度改变了,其余的代码会很好地遵循。

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

#define LEN(array) (sizeof (array) / sizeof (array)[0])

#define NEW_ARRAY(pointer, length) \
    { \
        (pointer) = malloc(((size_t) length) * sizeof (pointer)[0]); \
        if ((pointer) == NULL) { \
            fprintf(stderr, "Allocating memory with malloc failed: %s\n", strerror(errno)); \
            exit(EXIT_FAILURE); \
        } \
    }

void divide(int *a, int n, double *b)
{
    for (int i = 0; i < n; i++) {
        b[i] = a[i] / 100.0;
    }
}


int main(void)
{
    int a[] = {1, 2, 3, 4, 5};
    double *b;

    NEW_ARRAY(b, LEN(a));
    divide(a, LEN(a), b);
    for (size_t i = 0; i < LEN(a); i++) {
        printf("%.2f ", b[i]);
    }
    printf("\n");
    free(b);

    return 0;
}

【讨论】:

  • Karistrom 感谢您的建议,这真的很有帮助。抱歉回复晚了。
猜你喜欢
  • 2022-01-13
  • 2017-06-06
  • 1970-01-01
  • 2018-09-24
  • 1970-01-01
  • 2010-11-06
  • 2021-03-18
  • 1970-01-01
相关资源
最近更新 更多