extern函数调用main

时间:2017-05-03 13:33:41

标签: c unix

在阅读Maurice Bach的 Unix系统设计时,我遇到了以下代码片段。

#include < signal.h>
char *cp;
int callno;

main() {
    char *sbrk();
    extern catcher();

    signal(SIGSEGV, catcher);
    cp = sbrk(O);
    printf("original brk value %u\n", cp);
    for (;;)
    *cp++ = 1; 
}


catcher(signo) {
    int signo;
    callno++;
    printf("caught sig %d %dth call at addr %u\n", signo, callno, cp);
    sbrk(256);
    signal(SIGSEGV, catcher); 
}

我对main方法中的两个语句感到困惑

  

char * sbrk();

     

extern catcher();

我理解extern是如何运作的,我也知道sbrk()做了什么,但我无法理解他们为什么在extern之前写catcher()以及为什么char* 1}}在sbrk()来电之前写的?

在编译此代码时,我在Ubuntu上的gcc-4.8.4上遇到了编译错误,但代码在Mac中没有任何错误编译。为什么会这样?

2 个答案:

答案 0 :(得分:5)

char *sbrk();
extern catcher();

这些是函数声明,而不是函数调用。您正在阅读的代码是旧样式(ANSI之前的版本),在随后的(c99或更新版本)C标准中,它们不再有效。

您应该在catcher()的声明中添加显式返回类型。当前的隐式声明意味着它具有int返回类型。但是,信号处理程序的正确签名不指定返回值。当我们添加显式返回类型时,不再需要extern关键字,可以将其删除。

sbrk实际上是在常规标头中声明的,因此请删除声明和#include <unistd.h>。但是,sbrk是BSD(和SUSv2的一部分)而不是标准C函数,因此在包含#define _BSD_SOURCE之前,需要使用#define _XOPEN_SOURCE=500unistd.h激活声明。

Printfstdio.h中声明,因此请将其包含在内。 %u用于打印unsigned int。应使用%p格式说明符打印指针。

因此,在对代码进行一些现代化之后:

#define _BSD_SOURCE
#include <stdio.h>
#include <signal.h>
#include <unistd.h>

void catcher();

char *cp;
int callno;

int main(void) {
    signal(SIGSEGV, catcher);
    cp = sbrk(O);  // You sure this should be an O and not a 0?
    printf("original brk value %u\n", cp);
    for (;;)
        *cp++ = 1; 
}


void catcher(int signo) {
    callno++;
    printf("caught sig %d %dth call at addr %p\n", signo, callno, cp);
    sbrk(256);
    signal(SIGSEGV, catcher); 
}

请注意,您应该避免在信号处理程序中调用printf。例如,请参阅How to avoid using printf in a signal handlerSignal Handlers and Async-Signal Safety

答案 1 :(得分:0)

除了@KlasLindbäck的回答,在当前的C和POSIX标准下,此代码还存在其他问题:

#include < signal.h>
char *cp;
int callno;

main() {
    char *sbrk();
    extern catcher();

    signal(SIGSEGV, catcher);
    cp = sbrk(O);
    printf("original brk value %u\n", cp);
    for (;;)
    *cp++ = 1; 
}


catcher(signo) {
    int signo;
    callno++;
    printf("caught sig %d %dth call at addr %u\n", signo, callno, cp);
    sbrk(256);
    signal(SIGSEGV, catcher); 
}

请勿使用sbrk()

sbrk()已从POSIX标准中删除。直接使用已不再安全。来自the Linux man page

  

避免使用brk()sbrk()malloc(3)内存分配包   是分配内存的便携而舒适的方式。

为什么呢?因为许多C库函数经常在内部使用malloc() / calloc() / realloc() / ...和mixing malloc() and brk()/sbrk() calls in the same process is unsafemalloc()实现通常在内部使用brk() / sbrk(),因此在您自己的代码中使用sbrk()可能会破坏您的堆。

请勿使用signal()

signal() has significant issues。它根本不一致。请改用sigaction()