在阅读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中没有任何错误编译。为什么会这样?
答案 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=500
或unistd.h
激活声明。
Printf
在stdio.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 handler或Signal 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 unsafe。 malloc()
实现通常在内部使用brk()
/ sbrk()
,因此在您自己的代码中使用sbrk()
可能会破坏您的堆。
请勿使用signal()
signal()
has significant issues。它根本不一致。请改用sigaction()
。