【问题标题】:Initializing iterator pointer : Segmentation Fault初始化迭代器指针:分段错误
【发布时间】:2014-08-28 16:26:45
【问题描述】:

我试图在 C 中创建队列(作为一个类项目)。他们提供的演示代码适用于 Borland Turbo C。我正在尝试通过gcc 重建程序。虽然代码在 Turbo C 中完美运行,但它在运行时以 Segmentation Fault (core dumped) 的形式在 gcc 中抛出错误。

我没有包含不必要的代码部分。逐行尝试和测试。

struct node {
    int data;
    struct node *link;
};

struct queue {
    struct node *front;
    struct node *rear;
};

void initQ(struct queue *q) {
    q->front = q->rear = NULL;  //      Error : Segmentation Fault! (core dumped)
}


void main() {
    struct queue *Q;
    initQ(Q);
}

我确定问题与编译器中的 C 版本有关。由于 Turbo C 非常古老,它不支持最新的修复。我在代码的其他各个部分遇到类似的Segmentation Fault 错误,例如:

void displayQ(struct queue *q) {
        struct node *temp;
        temp->link = q->front;  //      Error : Segmentation Fault! (core dumped)
 }

问题 1:为什么 gcc 会给出这样的运行时错误? (在此代码中)

问题 2:为什么代码在 Turbo C 中可以正常工作,但在 gcc 中不行?

问题 3:这种编程风格是否有替代方案?

【问题讨论】:

  • 广告问题 2:代码在每个版本的 C 中都是无效的,检查为什么它似乎与 Turbo C 一起工作将取决于其他几个信息。例如。平台。另请注意,它可能似乎适用于小队列,但对于较大的输入会失败,或者如果再次运行可能会立即失败,依此类推。看来,Q 恰好包含一些可以取消引用的随机值(它指向的内容以及您可能覆盖的其他数据是另一回事)。另请注意,使用 Gcc 编译时它似乎也可以工作。

标签: c pointers gcc memory


【解决方案1】:

您需要预留空间(使用malloc):

struct queue *Q = malloc(sizeof(*Q));
initQ(Q);

或者更好calloc:

struct queue *Q = calloc(1, sizeof(*Q));
/* initQ(Q); you don't need this, calloc set all members to NULL */

最后别忘了打free(Q);

【讨论】:

  • +1 使用malloc(sizeof *Q);malloc(sizeof(struct queue)); 进行良好的malloc() 通话。
  • 跳过free(Q)可能会导致程序抛出Segmentation Fault!错误?
  • 不,跳过free a memory leak 发生
【解决方案2】:
void initQ(struct queue *q) {
    q->front = q->rear = NULL;
}

在您的代码中,q 在您使用它时未初始化并指向某个随机地址。试试:

struct queue Q;
initQ(&Q);

【讨论】:

  • initQ() 中没有更多错误,但我在displayQ() 函数中遇到了同样的错误。
【解决方案3】:

Q 没有被初始化为特别指向任何地方,这意味着它是一个 invalid 指针值。当您将它传递给initQ 时,您尝试使用-> 运算符取消引用它。尝试取消引用无效指针会导致未定义的行为,这可能意味着从按预期工作的代码到崩溃到损坏数据的任何事情。

无论Q 在 Turbo C 下具有什么初始的、不确定的值,它都位于内存的可访问区域中(不过,它仍然是一个 invalid 指针),所以你不会得到你想要的段错误在 gcc 中做。

当您调用initQ 时,q 必须指向一个有效的对象。你可以打电话给initQ 一个现有的队列实例,如下所示:

struct queue Q; // Q is an instance of struct queue, not a pointer to it
initQ( &Q );

或者,您可以创建一个伪构造函数,为新的动态分配内存 队列对象并在其上调用initQ

struct queue *create_queue_element( )
{
  struct queue *q = malloc( sizeof *q );
  if ( q )
    initQ( q );
  return q;
}
...
struct queue *Q = create_queue_element();

【讨论】:

    【解决方案4】:

    问题 1:为什么 gcc 会给出这样的运行时错误? (在这段代码中)

    因为代码不正确 - 你取消引用一个初始化的指针。


    问题 2:为什么代码在 Turbo C 中可以正常工作,但在 gcc 中不行?

    它不能正常工作,它根本没有检测到错误。 16 位 DOS 代码没有内存保护和虚拟内存的好处。它无法区分属于您的进程的实际内存(在 DOS 中没有进程内存,因为它不是多任务处理。整个 DOS 子系统在其自己的受保护 VM 中运行。您可能拥有的唯一保护是取消引用 NULL 指针。指针的值将解析为某个不确定的内存位置,并且可能看起来可以正常工作,直到其他东西将同一内存位置用于其他目的——这可能永远不会发生,有时可能会发生,或者在执行时可能会发生在另一台机器上 - 你无法分辨。

    大多数情况下,当您的应用程序变得更大并使用越来越多的内存时,问题会在编写代码很久之后才显现出来。如果引用的位置恰好在您的代码空间中,您将看到代码在执行时随机更改的有趣前景。这样的错误很难找到,因为它们的原因和结果通常被时间和代码接近分开。

    你写的:

    struct queue *Q = NULL ;
    

    您可能收到了运行时错误报告。这不会使代码更正确,但确实更有可能检测到此类错误。


    问题 3:这种编程风格是否有替代方案?

    这不是风格问题,而是正确性问题。但是,如果您始终在声明中初始化指针以指向有效实例或 NULL,则可以避免该问题或至少实现对错误的早期检测。

    即使在这种情况下,在 32 位代码中,错误是在运行时检测到的,但这绝不是保证 - 您的初始化指针驻留在堆栈上,在其他情况下,它可能包含在您的进程中是有效地址的值,而您只会踩踏自己的数据。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-07-26
      • 2019-03-27
      • 1970-01-01
      • 2019-08-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多