请解释为啥这个 C 代码给我一个分段错误?

Posted

技术标签:

【中文标题】请解释为啥这个 C 代码给我一个分段错误?【英文标题】:Please explain why this C code gives me a segmentation fault?请解释为什么这个 C 代码给我一个分段错误? 【发布时间】:2021-05-24 23:42:13 【问题描述】:

我是一名尝试学习 C 的初学者。以下代码在我运行时出现分段错误。谁能解释原因,或者告诉我我的错误?

struct frac sum(struct frac f1, struct frac f2);
struct frac 
int *numer;
int *denom;
;
struct frac sum(struct frac f1, struct frac f2) 
struct frac rv;
*rv.numer = (*f1.numer)*(*f2.denom) + (*f2.numer)*(*f1.denom);
*rv.denom = (*f1.denom)*(*f2.denom);
return rv;

int main() 
int n = 5;
int d = 10;
struct frac myFrac1 = &n, &d;
struct frac myFrac2 = &n, &d;
struct frac myFrac3 = sum(myFrac1, myFrac2);
return 0;

【问题讨论】:

你为什么在这里使用指针? rv.numerrv.denom 未初始化为任何内容,因此取消引用它们是未定义的行为。 好的,我从 rv.numer 和 rv.denom 中删除了 *,但现在我得到 警告:从 'int' 对 'int *' 的赋值使得指针从整数而不进行强制转换。 如果我尝试将表达式的右侧转换为 rv.denom =(int *) ((*f1.denom)*(*f2.denom)); 之类的指针,我会收到 警告:从不同大小的整数转换为指针。 因为您使用的是指针。您要么需要将 rv.numerrv.demon 指向某物(例如 malloc 调用的结果或指向现有 int 的指针,就像您在 main 中对其他两个对象所做的那样),或者使 numerdenom ints 而不是指针。 所以我首先需要为rv.numerrv.denom 分配内存,然后才能让它们指向指针上的一些算术结果? 请不要编辑您的原始帖子以更改代表您问题本质的内容。编辑应仅限于格式化和/或添加新信息以澄清 cmets 中的问题。更改代码会导致新查看者感到困惑,并导致已通过 cmets 提供的内容和答案被误解。出于这个原因,我已将帖子回滚到原来的位置。 【参考方案1】:

程序会在第二行崩溃

struct frac rv;
*rv.numer = (*f1.numer)*(*f2.denom) + (*f2.numer)*(*f1.denom);

因为,您在堆栈中定义了 rv。 rv.number 可以是任何东西。

在大多数情况下,*(rv.numer) 是你不能写的地方。

【讨论】:

【参考方案2】:

"...当我运行它时,代码给了我一个分段错误。谁能解释原因,或者告诉我我的错误"

首先,如果在调试模式下编译,可能会出现一些问题,如果您能够看到它们并通过解决每个问题做出响应,可能会阻止您遇到段错误:

当我编译时出现正常警告:

不初始化变量是程序员(新老)最常被忽略但代价高昂的错误之一。始终初始化。

为什么会出现段错误 - 在您的原始代码中,我在 运行 时看到了这个:

尝试取消引用未初始化的指针几乎总是会导致分段错误。

只是一些可以解决上述错误的建议: 为了在整个示例改编中保持可读性,您的代码使用typedef 创建frac_s。以原始帖子的方式使用指针需要在地方进行动态内存分配,非指针也可以很好地服务,即struct 成员。所以,注意使用非指针成员。下面的指针使用仅限于函数参数和sum(,,) 的返回类型,从而大大简化了代码,但需要动态内存分配和free()。将以下内容与您的代码进行比较以查看更改的程度...

typedef struct  
    int numer;
    int denom;
frac_s;


frac_s * sum(frac_s *f1, frac_s *f2);

frac_s * sum(frac_s *f1, frac_s *f2) 
    frac_s * rv = calloc(1, sizeof(*rv)) ;
    if(rv)
    
        rv->numer = (f1->numer)*(f2->denom) + (f2->numer)*(f1->denom);
        rv->denom = (f1->denom)*(f2->denom);
    
    return rv;


int main(void) 
    int n = 5;
    int d = 10;

    frac_s myFrac1 = n, d;
    frac_s myFrac2 = n, d;
    frac_s *myFrac3 = sum(&myFrac1, &myFrac2);
    if(myFrac3)
    
        //use myFracs;
        free(myFracs);
    

    return 0;

【讨论】:

【参考方案3】:

试试这个

struct frac sum(struct frac f1, struct frac f2);

struct frac 
    int *numer;
    int *denom;
;

struct frac sum(struct frac f1, struct frac f2) 
    struct frac rv;
    rv.numer = (int*)malloc(sizeof(int));
    rv.denom = (int*)malloc(sizeof(int));
    *rv.numer = (*f1.numer)*(*f2.denom) + (*f2.numer)*(*f1.denom);
    *rv.denom = (*f1.denom)*(*f2.denom);
    return rv;


int main() 
    int n = 5;
    int d = 10;

    struct frac myFrac1 = &n, &d;
    struct frac myFrac2 = &n, &d;
    struct frac myFrac3 = sum(myFrac1, myFrac2);

    return 0;

结果是rv.numerrv.denom最后都指向100。

【讨论】:

这些添加的括号不会改变任何东西。 *rv.numer*(rv.numer) 是等价的。 我不这么认为,*rv.numer 会先做 *rv 然后尝试获取 numer 元素。但是,*(rv.numer) 会先找到 numer 元素,然后对其执行 *。 没有。见en.cppreference.com/w/c/language/operator_precedence。 .(结构和联合成员访问)的优先级高于*(间接(取消引用))。该页面甚至给出了*p++ 等效于*(p++) 的示例,并且后缀递增运算符具有与. 相同的优先级 我不知道你想用你的编辑做什么。这是一个 C 问题,而不是 C++ 你有指针作为你的结构成员。他们需要先分配内存,然后才能存储任何内容。另一种选择是将它们指向已分配的内存,就像您在 struct frac myFrac1 = &n, &d; 中所做的那样

以上是关于请解释为啥这个 C 代码给我一个分段错误?的主要内容,如果未能解决你的问题,请参考以下文章

为啥释放内存会导致分段错误?

为啥写作主要;在 C 中给出一个段错误

为啥我在这个 c 代码上遇到分段错误?

为啥这个字符串反转 C 代码会导致分段错误? [复制]

为啥调用 glCreateShader 时这个 OpenGL 着色器分段错误?

为啥这段代码在 leetcode 运行良好,但在 geeksforgeeks 出现分段错误?