【问题标题】: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 提供的内容和答案被误解。出于这个原因,我已将帖子回滚到原来的位置。

标签: c pointers struct segmentation-fault


【解决方案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}; 中所做的那样
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-07-28
      • 2018-01-08
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多