代码之家  ›  专栏  ›  技术社区  ›  JoeCool

C编程:SEG故障、printf和相关要求

  •  3
  • JoeCool  · 技术社区  · 15 年前

    像许多年轻的程序员一样,我学到了在代码的不同点插入大量的print-to-console语句“here1”、“here2”等等,以找出我的程序何时会出错。这种蛮力调试技术在我的CS学习中为我节省了很多次。然而,当我开始用C语言编程时,我偶然发现了一个有趣的问题。如果我试着跑的话

    void* test;
    
    printf("hello world");
    test[5] = 234;
    

    当然,对于testchar的内存不是malloc'ing,我会得到一个segfault。不过,您可能会认为,逻辑上,“hello world”会在SEG故障发生之前打印出来,因为这是代码流,但在我的经验中,SEG故障总是先发生的,而“hello world”永远不会打印到控制台上。(我无法测试这个确切的例子,但是我在Linux机器上使用gcc已经多次遇到这种情况。)我猜想这与编译器重新排列某些东西和/或使用某种缓冲区(这种缓冲区是异步刷新的,因此不是立即刷新的)有关。这完全是我的猜测,因为我真的不知道为什么会发生。在我使用过的任何其他语言中,无论“testchar=…”行引起了什么问题,“hello world”仍将被打印,因此我可以确定问题在哪里。

    我的问题是,当我在编程C时,为什么会发生这种情况?为什么《你好世界》没有首先印刷?其次,有没有比这更好的C编程调试技术来完成同样的基本工作?与之类似,一种简单/直观的方法来查找有问题的代码行?

    编辑:我偶然举了一个工作例子哈哈。我现在所拥有的应该会导致一个segfault。有趣的是通常我 不要 想要一个SegFault,我就得到一个,现在当我真正想要一个时,我就写了法律代码!

    7 回复  |  直到 14 年前
        1
  •  9
  •   anon    15 年前

    您发布的代码是完全合法的,不应该导致segfault-不需要malloc任何东西。您的问题必须在其他地方-请张贴导致问题的最小代码示例。

    编辑: 现在,您已经编辑了代码,使其具有完全不同的含义。不过,“hello world”没有显示的原因是输出缓冲区没有刷新。试试Addiig

    fflush( stdout );
    

    打印后。

    关于定位问题的根源,您有两个选择:

    • 使用 __FILE__ __LINE__ C宏指令
    • 学习使用调试器-如果您的平台支持核心转储,您可以使用核心映像来查找错误所在。
        2
  •  5
  •   Graeme Perrow    15 年前

    printf 写入stdout,这是缓冲的。有时,在程序崩溃之前,缓冲区不会被刷新,因此您永远看不到输出。避免这种情况的两种方法:

    1. 使用 fprintf( stderr, "error string" ); 因为没有缓冲stderr。
    2. 添加呼叫 fflush( stdout ); 在printf调用之后。

    正如尼尔和其他人所说,所写的代码是好的。也就是说,在开始修改缓冲区之前, testChar 指向。

        3
  •  3
  •   stefanB    15 年前

    “如中所述,一种简单/直观的方法来查找有问题的代码行?”

    使用gdb(或任何其他调试器)。

    要查找程序SEG的错误位置,请用编译它 -g 选项(包括调试符号)从运行应用程序 GDB ,它将在SEG故障时停止。

    然后你可以用 bt 命令查看您在哪个点出现SEG故障。

    例子:

    > gdb ./x
    (gdb) r
    Starting program: /proj/cpp/arr/x 
    Program received signal EXC_BAD_ACCESS, Could not access memory.
    Reason: KERN_PROTECTION_FAILURE at address: 0x00000000
    0x000019a9 in willfail () at main.cpp:22
    22          *a = 3;
    (gdb) bt
    #0  0x000019a9 in willfail () at main.cpp:22
    #1  0x00001e32 in main () at main.cpp:49
    (gdb) 
    
        4
  •  2
  •   Lance Richardson    15 年前

    默认情况下,输出被缓冲,segfault在输出实际写入stdout之前发生。尝试:

    fprintf(stderr, "hello, world\n");
    

    (默认情况下,stderr未缓冲。)

        5
  •  1
  •   laalto    15 年前

    这个代码不应该是segfault。您只是将一个指向文本字符串的指针赋给一个指针变量。如果你使用 strcpy 复制带有无效指针的内容。

    消息不出现可能是由于缓冲的I/O。请打印换行符 \n 或呼叫 fflush 刷新输出缓冲区。

        6
  •  0
  •   paxdiablo    15 年前

    你有两个问题。第一个问题是您的(原始)代码不会出错。将字符串常量赋给char指针是完全有效的。但我们暂时把它放在一边,假装你在那里放了什么东西 节错。

    然后通常是缓冲区的问题,一个在C运行时库中,另一个在操作系统本身中。你需要冲洗它们。

    最简单的方法是(在UNIX中,不完全确定 fsync 在Linux中,但是您应该保证这最终会发生,除非系统本身关闭):

    printf ("DEBUG point 72\n"); fflush (stdout); fsync (fileno (stdout));
    

    我经常在Unix中这样做,它确保C运行库被刷新到Unix中。( fflush )Unix缓冲区与磁盘同步( FSYNC ,如果stdout不是终端设备,或者您是为其他文件句柄执行此操作,则非常有用。

        7
  •  0
  •   Doug T.    15 年前
    void* test;
    
    printf("hello world");
    test[5] = 234;
    

    很可能“hello world”正在某个地方被系统缓冲,并没有立即打印到屏幕上。它存储在等待任何进程/线程/负责屏幕编写的任何对象都有机会处理它。当它等待(也可能缓冲其他数据输出)时,您的函数就完成了。它来自非法访问和segfaults。

    推荐文章