有没有办法在segfault的情况下打印一些东西? C

时间:2015-11-17 02:36:35

标签: c segmentation-fault

例如,如果我的程序是segaults,而不是gcc打印到控制台" Segmentation Fault"我可以打印出来吗?雅顿傻瓜"?

2 个答案:

答案 0 :(得分:1)

段错误通常是由解除引用垃圾指针引起的。因此,虽然你所问的字面答案是,正如kaylum所说,你可以在一个信号处理程序中捕获SIGSEGV,更好的答案是,在你使用指针之前,你应该问自己,“我怎么知道这个指针是有效的,我是否在我的数组的范围内?"

如果您不知道,您的程序有错误。如果你认为你这样做,你可以把假设变成一个断言,因为你的指针是有效的,它将永远通过。例如:

void fill_array( unsigned fill_this_many,
                 size_t array_size,
                 int a[array_size] )
{
  assert(a);
  assert( array_size >= fill_this_many );
  for ( unsigned i = 0; i < fill_this_many; ++i )
    a[i] = f(i);

  return;
}

现在,当您要取消引用空指针或写入数组末尾时,您将收到一条详细消息,其中包含更多有用的调试信息,而且#34;某处存在段错误, &#34;它甚至可以使您免于沉默的内存损坏。

如果要编写自己的消息,可以定义一个包装器,例如:

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

void fatal_error_helper( const char* file, int line, const char* restrict message )
{
  fflush(stdout);  // Don’t cross the streams!
  fprintf( stderr, "\nError in %s, line %d: %s\n", file, line, message );
  exit(EXIT_FAILURE);
}

#define fatal_error(message) fatal_error_helper( __FILE__, __LINE__, (message) )


int main(void)
{
  int *big_array = calloc( 1073741824UL, sizeof(int) );
  if (!big_array)
    fatal_error("Not enough memory.");

  return EXIT_SUCCESS;
}

一个人为的例子,说明如何在编译时进行边界检查,以便在常量发生变化时优雅地失败:

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

#define LENGTH 14U
#define M      5U

int main(void)
{
    char message[LENGTH] = "hello, world!";

    static_assert( M < LENGTH, "Tried to capitalize more letters than the array can hold." );

    for ( unsigned i = 0; i < M; ++i )
      message[i] = toupper(message[i]);

    printf( "%s\n", message );

    return EXIT_SUCCESS;
}

答案 1 :(得分:0)

#include <signal.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>

void segv_handler(int sig)
{
    (void)sig;
    const char *msg = "Hello signal handler!";
    size_t len = strlen(msg);
    write(STDERR_FILENO, msg, len);
    abort();
}

int main()
{
    struct sigaction act;

    act.sa_handler = segv_handler;
    sigemptyset(&act.sa_mask);
    act.sa_flags = 0;

    sigaction(SIGSEGV, &act, NULL);

    int *nullint = 0;

    *nullint = 4;

    return 0;
}
编辑:我很难解释如何做到这一点。当然,在编写信号处理程序时需要考虑很多细节。

基本限制是信号处理程序无法访问任何未被原子写入的变量/结构,因为可以在程序中的任意两条指令之间调用处理程序。这意味着没有调用堆内存管理,像printf那样缓冲等等。

可以在手册页stdoutsigactionwrite中找到代码所做的更多详细信息。