控制函数用于对C语言程序的标准控制流(如if/else、switch、for等)提供扩展,在头文件assert.h、setjmp.h和signal.h中提供,分别提供表达式断言功能、非本地跳转功能、信号处理功能。
1、assert.h: 提供用于断言的assert宏。程序中若没有定义NDEBUG,则asset(exp)对表达式exp进行断言,若断言为假(即为0),则会调用__assert_fail函数打印一条“断言失败”的消息,并终止程序。若定义了NDEBUG宏,则assert通常为空语句。
-
- #ifdef _ASSERT_H
- # undef _ASSERT_H
- # undef assert
- # undef __ASSERT_VOID_CAST
- # ifdef __USE_GNU
- # undef assert_perror
- # endif
- #endif /* assert.h */
- #define _ASSERT_H 1
- #include <features.h>
- #if defined __cplusplus && __GNUC_PREREQ (2,95)
- # define __ASSERT_VOID_CAST static_cast<void>
- #else
- # define __ASSERT_VOID_CAST (void)
- #endif
-
-
-
- #ifdef NDEBUG
- # define assert(expr) (__ASSERT_VOID_CAST (0))
-
-
-
- # ifdef __USE_GNU
- # define assert_perror(errnum) (__ASSERT_VOID_CAST (0))
- # endif
- #else /* 没有定义NDEBUG */
- #ifndef _ASSERT_H_DECLS
- #define _ASSERT_H_DECLS
- __BEGIN_DECLS
-
- extern void __assert_fail (__const char *__assertion, __const char *__file,
- unsigned int __line, __const char *__function)
- __THROW __attribute__ ((__noreturn__));
-
- extern void __assert_perror_fail (int __errnum, __const char *__file,
- unsigned int __line,
- __const char *__function)
- __THROW __attribute__ ((__noreturn__));
-
-
- extern void __assert (const char *__assertion, const char *__file, int __line)
- __THROW __attribute__ ((__noreturn__));
-
- __END_DECLS
- #endif /* Not _ASSERT_H_DECLS */
-
- # define assert(expr) /
- ((expr) /
- ? __ASSERT_VOID_CAST (0) /
- : __assert_fail (__STRING(expr), __FILE__, __LINE__, __ASSERT_FUNCTION))
- # ifdef __USE_GNU
- # define assert_perror(errnum) /
- (!(errnum) /
- ? __ASSERT_VOID_CAST (0) /
- : __assert_perror_fail ((errnum), __FILE__, __LINE__, __ASSERT_FUNCTION))
- # endif
-
-
-
- # if defined __cplusplus ? __GNUC_PREREQ (2, 6) : __GNUC_PREREQ (2, 4)
- # define __ASSERT_FUNCTION __PRETTY_FUNCTION__
- # else
- # if defined __STDC_VERSION__ && __STDC_VERSION__ >= 199901L
- # define __ASSERT_FUNCTION __func__
- # else
- # define __ASSERT_FUNCTION ((__const char *) 0)
- # endif
- # endif
- #endif /* NDEBUG. */
-
- #include <assert.h>
- #include <libintl.h>
- #include <stdio.h>
- #include <stdlib.h>
- #include <sysdep.h>
- #include <unistd.h>
-
- extern const char *__progname;
- #ifdef USE_IN_LIBIO
- # include <wchar.h>
- # include <libio/iolibio.h>
- # define fflush(s) INTUSE(_IO_fflush) (s)
- #endif
-
-
-
-
- #ifdef FATAL_PREPARE_INCLUDE
- # include FATAL_PREPARE_INCLUDE
- #endif
- #undef __assert_fail
- void
- __assert_fail (const char *assertion, const char *file, unsigned int line,
- const char *function)
- {
- char *buf;
- #ifdef FATAL_PREPARE
- FATAL_PREPARE;
- #endif
-
- if (__asprintf (&buf, _("%s%s%s:%u: %s%sAssertion `%s' failed./n"),
- __progname, __progname[0] ? ": " : "",
- file, line,
- function ? function : "", function ? ": " : "",
- assertion) >= 0)
- {
- (void) __fxprintf (NULL, "%s", buf);
- (void) fflush (stderr);
-
- free (buf);
- }
- else
- {
-
- static const char errstr[] = "Unexpected error./n";
- __libc_write (STDERR_FILENO, errstr, sizeof (errstr) - 1);
- }
- abort ();
- }
- hidden_def(__assert_fail)
-
- #include <assert.h>
- #include <libintl.h>
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- #include <sysdep.h>
- #include <unistd.h>
-
- extern const char *__progname;
- #ifdef USE_IN_LIBIO
- # include <wchar.h>
- # include <libio/iolibio.h>
- # define fflush(s) INTUSE(_IO_fflush) (s)
- #endif
-
-
-
-
- #ifdef FATAL_PREPARE_INCLUDE
- # include FATAL_PREPARE_INCLUDE
- #endif
- void
- __assert_perror_fail (int errnum,
- const char *file, unsigned int line,
- const char *function)
- {
- char errbuf[1024];
- char *buf;
- #ifdef FATAL_PREPARE
- FATAL_PREPARE;
- #endif
-
- if (__asprintf (&buf, _("%s%s%s:%u: %s%sUnexpected error: %s./n"),
- __progname, __progname[0] ? ": " : "",
- file, line,
- function ? function : "", function ? ": " : "",
- __strerror_r (errnum, errbuf, sizeof errbuf)) >= 0)
- {
- (void) __fxprintf (NULL, "%s", buf);
- (void) fflush (stderr);
-
- free (buf);
- }
- else
- {
-
- static const char errstr[] = "Unexpected error./n";
- __libc_write (STDERR_FILENO, errstr, sizeof (errstr) - 1);
- }
- abort ();
- }
- libc_hidden_def (__assert_perror_fail)
-
-
- #undef NDEBUG
- #include <assert.h>
-
- void
- __assert (const char *assertion, const char *file, int line)
- {
- __assert_fail (assertion, file, line, (const char *) 0);
- }
解释:
(1)GNU还提供了一个扩展assert_perror,功能与assert类似,只不过打印指定的错误码对应的消息,通过__assert_perror_fail函数来实现。另外提供的_assert函数是为了与标准兼容,它的功能由__assert_fail取代。
(2)__assert_fail和__assert_perror_fail函数都是把指定格式的消息写入到buf中,然后打印buf中的消息,并刷新标准错误流的状态,释放消息缓冲区buf,最后要调用abort函数终止程序。
2、setjmp.h: 标准库函数setjmp和longjmp实现基本形式的非本地跳转。setjmp用于保存调用者的堆栈环境(被保存在表示跳转缓冲区的jmp_buf型数组ENV中),然后返回0。longjmp用于跳转到保存堆栈环境的地方,并从那里的setjmp调用返回,返回指定的状态码,如果状态码为0则返回1。
-
- #ifndef _SETJMP_H
- #define _SETJMP_H 1
- #include <features.h>
- __BEGIN_DECLS
- #include <bits/setjmp.h> /* 获取__jmp_buf */
- #include <bits/sigset.h> /* 获取__sigset_t */
-
-
- struct __jmp_buf_tag
- {
-
-
- __jmp_buf __jmpbuf;
- int __mask_was_saved;
- __sigset_t __saved_mask;
- };
-
- __BEGIN_NAMESPACE_STD
- typedef struct __jmp_buf_tag jmp_buf[1];
-
- extern int setjmp (jmp_buf __env) __THROW;
- __END_NAMESPACE_STD
-
-
- extern int __sigsetjmp (struct __jmp_buf_tag __env[1], int __savemask) __THROW;
- #ifndef __FAVOR_BSD
-
- extern int _setjmp (struct __jmp_buf_tag __env[1]) __THROW;
-
- # define setjmp(env) _setjmp (env)
- #else
-
-
- # define setjmp(env) setjmp (env)
- #endif /* Favor BSD. */
-
- __BEGIN_NAMESPACE_STD
-
- extern void longjmp (struct __jmp_buf_tag __env[1], int __val)
- __THROW __attribute__ ((__noreturn__));
- __END_NAMESPACE_STD
- #if defined __USE_BSD || defined __USE_XOPEN
-
-
- extern void _longjmp (struct __jmp_buf_tag __env[1], int __val)
- __THROW __attribute__ ((__noreturn__));
- #endif
-
- #ifdef __USE_POSIX
-
-
- typedef struct __jmp_buf_tag sigjmp_buf[1];
-
- # define sigsetjmp(env, savemask) __sigsetjmp (env, savemask)
-
-
- extern void siglongjmp (sigjmp_buf __env, int __val)
- __THROW __attribute__ ((__noreturn__));
- #endif /* Use POSIX. */
- __END_DECLS
- #endif /* setjmp.h */
-
- #include <errno.h>
- #include <setjmp.h>
-
- int
- __libc_sigsetjmp (jmp_buf env, int savemask)
- {
-
- __sigjmp_save (env, savemask);
- __set_errno (ENOSYS);
-
- return 0;
- }
- weak_alias (__libc_sigsetjmp, __sigsetjmp)
- stub_warning (__sigsetjmp)
- #include <stub-tag.h>
-
- #include <stddef.h>
- #include <setjmp.h>
- #include <signal.h>
-
-
- void
- __libc_siglongjmp (sigjmp_buf env, int val)
- {
-
- _longjmp_unwind (env, val);
- if (env[0].__mask_was_saved)
-
- (void) __sigprocmask (SIG_SETMASK, &env[0].__saved_mask,
- (sigset_t *) NULL);
-
- __longjmp (env[0].__jmpbuf, val ?: 1);
- }
- strong_alias (__libc_siglongjmp, __libc_longjmp)
- libc_hidden_def (__libc_longjmp)
- weak_alias (__libc_siglongjmp, _longjmp)
- weak_alias (__libc_siglongjmp, longjmp)
- weak_alias (__libc_siglongjmp, siglongjmp)
注意setjmp和longjmp函数都是直接调用内部函数来完成工作的。头文件setjmp.h中的其他部分都是BSD、XOPEN或POSIX方面的扩展。
3、signal.h: 定义了信号原子类型sig_atomic_t、信号处理函数的注册signal、发送信号的函数raise。它包含了bits/signum.h,这个Linux系统的头文件中定义了C标准中的信号及其他一些信号。标准C语言中的信号有SIGINT,SIGILL,SIGABRT,SIGFPE,SIGSEGV,SIGTERM,还要3个特殊的信号SIGERR,SIG_DFL, SIG_IGN,因此标准C语言中总共有9个信号。
signal.h代码如下:
-
- #ifndef _SIGNAL_H
- #if !defined __need_sig_atomic_t && !defined __need_sigset_t
- # define _SIGNAL_H
- #endif
- #include <features.h> /* 定义了一些编译选项 */
- __BEGIN_DECLS
- #include <bits/sigset.h> /* 获取__sigset_t和__sig_atomic_t类型 */
-
- #if defined __need_sig_atomic_t || defined _SIGNAL_H
- # ifndef __sig_atomic_t_defined
- # define __sig_atomic_t_defined
- __BEGIN_NAMESPACE_STD
- typedef __sig_atomic_t sig_atomic_t;
- __END_NAMESPACE_STD
- # endif
- # undef __need_sig_atomic_t
- #endif
- #if defined __need_sigset_t || (defined _SIGNAL_H && defined __USE_POSIX)
- # ifndef __sigset_t_defined
- # define __sigset_t_defined
- typedef __sigset_t sigset_t;
- # endif
- # undef __need_sigset_t
- #endif
- #ifdef _SIGNAL_H
- #include <bits/types.h> /* 定义了一些扩展整数类型 */
- #include <bits/signum.h> /* 定义了C标准中的信号及其他一些信号 */
- #if defined __USE_XOPEN || defined __USE_XOPEN2K
- # ifndef __pid_t_defined
- typedef __pid_t pid_t;
- # define __pid_t_defined
- #endif
- #ifdef __USE_XOPEN
- # endif
- # ifndef __uid_t_defined
- typedef __uid_t uid_t;
- # define __uid_t_defined
- # endif
- #endif /* Unix98 */
-
-
- typedef void (*__sighandler_t) (int);
-
- extern __sighandler_t __sysv_signal (int __sig, __sighandler_t __handler)
- __THROW;
- #ifdef __USE_GNU
- extern __sighandler_t sysv_signal (int __sig, __sighandler_t __handler)
- __THROW;
- #endif
-
-
- __BEGIN_NAMESPACE_STD
- #ifdef __USE_BSD
- extern __sighandler_t signal (int __sig, __sighandler_t __handler)
- __THROW;
- #else
-
- # ifdef __REDIRECT_NTH
- extern __sighandler_t __REDIRECT_NTH (signal,
- (int __sig, __sighandler_t __handler),
- __sysv_signal);
- # else
- # define signal __sysv_signal
- # endif
- #endif
- __END_NAMESPACE_STD
- #ifdef __USE_XOPEN
-
- extern __sighandler_t bsd_signal (int __sig, __sighandler_t __handler)
- __THROW;
- #endif
-
-
- #ifdef __USE_POSIX
- extern int kill (__pid_t __pid, int __sig) __THROW;
- #endif /* Use POSIX. */
- #if defined __USE_BSD || defined __USE_XOPEN_EXTENDED
-
-
- extern int killpg (__pid_t __pgrp, int __sig) __THROW;
- #endif /* Use BSD || X/Open Unix. */
- __BEGIN_NAMESPACE_STD
-
- extern int raise (int __sig) __THROW;
- __END_NAMESPACE_STD
-
- #endif /* signal.h */
- __END_DECLS
- #endif /* not signal.h */
bits/signum.h代码如下:
-
- #ifdef _SIGNAL_H
-
- #define SIG_ERR ((__sighandler_t) -1) /* 出错时返回 */
- #define SIG_DFL ((__sighandler_t) 0) /* 默认行为 */
- #define SIG_IGN ((__sighandler_t) 1) /* 忽略信号 */
- #ifdef __USE_UNIX98
- # define SIG_HOLD ((__sighandler_t) 2) /* 给信号加保留的掩码 */
- #endif
-
-
- #define SIGHUP 1 /* 挂断 (POSIX). */
- #define SIGINT 2 /* 中断 (ANSI). */
- #define SIGQUIT 3 /* 退出 (POSIX). */
- #define SIGILL 4 /* 非法指令 (ANSI). */
- #define SIGTRAP 5 /* 跟踪捕捉 (POSIX). */
- #define SIGABRT 6 /* 异常终止,如调用abort (ANSI). */
- #define SIGIOT 6 /* 图像输出终端(IOT)陷阱 (4.2 BSD). */
- #define SIGBUS 7 /* 总线错误 (4.2 BSD). */
- #define SIGFPE 8 /* 浮点数异常 (ANSI). */
- #define SIGKILL 9 /* 杀死,此信号可解锁 (POSIX). */
- #define SIGUSR1 10 /* 用户定义信号1 (POSIX). */
- #define SIGSEGV 11 /* 段错误,即无效内存访问 (ANSI). */
- #define SIGUSR2 12 /* 用户定义信号2 (POSIX). */
- #define SIGPIPE 13 /* 管道错误 (POSIX). */
- #define SIGALRM 14 /* 时钟警告 (POSIX). */
- #define SIGTERM 15 /* 终止 (ANSI). */
- #define SIGSTKFLT 16 /* 堆栈故障 */
- #define SIGCLD SIGCHLD /* 等同于SIGCHLD (System V). */
- #define SIGCHLD 17 /* 子进程状态已经改变 (POSIX). */
- #define SIGCONT 18 /* 继续 (POSIX). */
- #define SIGSTOP 19 /* 停止,此信号可解锁 (POSIX). */
- #define SIGTSTP 20 /* 键盘发的停止信号 (POSIX). */
- #define SIGTTIN 21 /* 从tty进行后台读操作 (POSIX). */
- #define SIGTTOU 22 /* 后台写入tty (POSIX). */
- #define SIGURG 23 /* socket紧急状态 (4.2 BSD). */
- #define SIGXCPU 24 /* 超出CPU限制 (4.2 BSD). */
- #define SIGXFSZ 25 /* 超出文件大小限制 (4.2 BSD). */
- #define SIGVTALRM 26 /* 时钟虚拟警告 (4.2 BSD). */
- #define SIGPROF 27 /* 时钟Profiling警告 (4.2 BSD). */
- #define SIGWINCH 28 /* 窗口大小改变 (4.3 BSD, Sun). */
- #define SIGPOLL SIGIO /* 可移植的事件发生 (System V). */
- #define SIGIO 29 /* 可以执行I/O (4.2 BSD). */
- #define SIGPWR 30 /* 重启时电源失效 (System V). */
- #define SIGSYS 31 /* 错误的系统调用 */
- #define SIGUNUSED 31
- #define _NSIG 65 /* 最大的信号值+1(包括实时信号) */
- #define SIGRTMIN (__libc_current_sigrtmin ())
- #define SIGRTMAX (__libc_current_sigrtmax ())
-
- #define __SIGRTMIN 32
- #define __SIGRTMAX (_NSIG - 1)
- #endif /* <signal.h> included. */
signal函数和raise函数的实现:
-
- #include <errno.h>
- #include <signal.h>
-
- __sighandler_t
- signal (sig, handler)
- int sig;
- __sighandler_t handler;
- {
- __set_errno (ENOSYS);
- return SIG_ERR;
- }
- weak_alias (signal, ssignal)
- stub_warning (signal)
- stub_warning (ssignal)
- #include <stub-tag.h>
-
- #include <signal.h>
- #include <errno.h>
-
- int
- raise (sig)
- int sig;
- {
- __set_errno (ENOSYS);
- return -1;
- }
- weak_alias (raise, gsignal)
- stub_warning (raise)
- stub_warning (gsignal)
- #include <stub-tag.h>
注意signal和raise函数并没有做任何特别的实现,只是处理了一下出错时的情况(返回SIG_ERR,即-1)。真正的实现使用Linux中有对应功能的函数,signal直接映射到Linux的ssignal函数,raise直接映射到Linux的gsignal函数。