控制函數用于對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函數。