目录
第一章:<assert.h>
#ifdef NDEBUG
#define assert(test) ((viod)0)
#else
#define assert(test) ((test)?(void)0:_ASSERT{__FILE__":"__STR(__LINE__)""#test})
#endif
宏assert向程序中加入诊断,当它执行的时候,如果表达式为假(也就是说和0相等),assert宏就按照实现定义的格式向标准错误文件写入特定调用的失败信息(包括参数文本,源文件名字__FILE__,源文件行数__LINE__),然后它调用abort函数
打开断言:
#undef NDEBUG
#include <assert.h>
assert(signal(SIGANRT, &field_abort) != SIG_ERR);
关闭断言:
#define NDEBUG
第二章:<ctype.h>
宏带来的意外:C程序员更喜欢宏,因为ie宏可以让程序员编写出和函数调用一样可读但效率更高的代码,注意
宏比函数调用快,但展开代码比函数调用大。
宏可能会展开为一个子表达式,可以用圆括号消除这种弊端。
宏展开后,某些参数可能执行不止一次(++操作),或根本不执行。
转换表:以islower()为例
#define _LO 0x10
extern const short *Ctype;
#define islower(c) (_Ctype[(int)(c)] & _LO)
假设传进来的c='a'
#define islower('a') (_Ctype[(int)('a')] & 0x10)
(_Ctype[97] & 0x10)
查_Ctype表的 _Ctype[97] == 0x10
islower('a') = (0x10 & 0x10) = 1
ctype.h中的函数
int isalnum (int c) 测试字符是否为英文或数字,在标准c中相当于使用“isalpha(c) || isdigit(c);”
int isalpha (int c) 检查参数c是否为英文字母 ,在标准c中相当于使用“isupper(c)||islower(c);”
int isascii(int c); 检查参数c是否为ASCII码字符,也就是判断c的范围是否在0到127之间。
int iscntrl(int c); 检查参数c是否为ASCII控制码,也就是判断c的范围是否在0到30之间。
int isdigit(int c); 检查参数c是否为阿拉伯数字0到9。
int isgraph (int c); 检查参数c是否为可打印字符,若c所对映的ASCII码可打印,且非空格字符则返回TRUE。
int islower(int c); 检查参数c是否为小写英文字母。
int isprint(int c); 检查参数c是否为可打印字符,若c所对映的ASCII码可打印,其中包含空格字符,则返回TRUE。
int isspace(int c); 检查参数c是否为空格字符,也就是判断是否为空格('')、定位字符('\t')、CR('\r')、换行('\n')、垂直定位字符('\v')或翻页('\f')的情况。
int ispunct(int c); 检查参数c是否为标点符号或特殊符号。返回TRUE也就是代表参数c为非空格、非数字和非英文字母。
int isupper(int c); 检查参数c是否为大写英文字母。
int isxdigit (int c);检查参数c是否为16进制数字,只要c为下列其中一个情况则返回TRUE。16进制数字:0123456789ABCDEF。
int tolower(int c); //把大写字母转换为小写字母
int toupper(int c); //把小写字母转换为大写字母
第三章:<errno.h>
define errno (*__errno_location ())
宏errno可以被扩展为一个可更改的左值,可以扩展像_Erfun()这样的表达式,检查错误的时候,它会调用一个函数告诉程序去哪里找
errno测试
perror("NO error reported as");
assert(errno == ERANGE)
每次在使用系统调用(如ioctl())之后,检查一下全局变量errno是一种很好的做法,它隶属于ANSI C标准
#define EPERM 1
#define ENOENT 2
#define ENOFILE ENOENT
#define ESRCH 3
#define EINTR 4
#define EIO 5
#define ENXIO 6
#define E2BIG 7
#define ENOEXEC 8
#define EBADF 9
#define ECHILD 10
#define EAGAIN 11
#define ENOMEM 12
#define EACCES 13
#define EFAULT 14
#define EBUSY 16
#define EEXIST 17
#define EXDEV 18
#define ENODEV 19
#define ENOTDIR 20
#define EISDIR 21
#define ENFILE 23
#define EMFILE 24
#define ENOTTY 25
#define EFBIG 27
#define ENOSPC 28
#define ESPIPE 29
#define EROFS 30
#define EMLINK 31
#define EPIPE 32
#define EDOM 33
#define EDEADLK 36
#define ENAMETOOLONG 38
#define ENOLCK 39
#define ENOSYS 40
#define ENOTEMPTY 41
第四章:<float.h>
定义double, float, long double类型的宏
#define DBL_EPSILON 2.2204460492503131E-16
#define FLT_EPSILON 1.19209290E-07F
#define LDBL_EPSILON 1.084202172485504E-19
第五章:<limits.h>
定义数据类型的上限值和下限值的宏,例:
#define CHAR_BIT 8
#define SHRT_MIN (-32768)
#define SHRT_MAX 32767
#if __WORDSIZE == 64
#define LONG_MAX 9223372036854775807L
#else
#define LONG_MAX 2147483647L
#endif
#define LONG_MIN (-LONG_MAX - 1L)
#define INT_MAX 2147483647
#define INT_MIN (-INT_MAX - 1)
第六章:<locale.h>
不同国家书写格式不一样,如:月-日-年。区域设置定义了区域环境,主要有以下几类
#define LC_CTYPE __LC_CTYPE (字符分类)
#define LC_NUMERIC __LC_NUMERIC (其他数字格式)
#define LC_TIME __LC_TIME (时间)
#define LC_COLLATE __LC_COLLATE (控制整理顺序)
#define LC_MONETARY __LC_MONETARY (货币格式)
区域设置控制
char *setlocale(int category, const char *locale);
数字格式习惯查询
struct lconv *localeconv(void);
第七章:<math.h>
HUGE_VAL ——这个宏通常会展开一个非常大的double常量,在那些对无穷大(Inf)不提供特殊编码的机器上,这是警告值域错误的最好方式。
1、 三角函数
double sin (double);正弦
double cos (double);余弦
double tan (double);正切
2、反三角函数
double asin (double); 结果介于[-PI/2, PI/2]
double acos (double); 结果介于[0, PI]
double atan (double); 反正切(主值), 结果介于[-PI/2, PI/2]
double atan2 (double, double); 反正切(整圆值), 结果介于[-PI, PI]
3、双曲三角函数
double sinh (double);
double cosh (double);
double tanh (double);
4、指数与对数
double frexp(double value,int *exp);这是一个将value值拆分成小数部分f和(以2为底的)指数部分exp,并返回小数部分f,即f*2^exp。其中f取值在0.5~1.0范围或者0。
double ldexp(double x,int exp);这个函数刚好跟上面那个frexp函数功能相反,它的返回值是x*2^exp
double modf(double value,double *iptr);拆分value值,返回它的小数部分,iptr指向整数部分。
double log (double); 以e为底的对数
double log10 (double);以10为底的对数
double pow(double x, double y);计算以x为底数的y次幂
float powf(float x, float y); 功能与pow一致,只是输入与输出皆为浮点数
double exp (double);求取自然数e的幂
double sqrt (double);开平方
5、取整
double ceil (double); 取上整,返回比x大的最小整数
double floor (double); 取下整,返回比x小的最大整数,即高斯函数 [x]
6、绝对值
int abs(int i); 求整型的绝对值
double fabs (double);求实型的绝对值
double cabs(struct complex znum) ;求复数的绝对值
7、标准化浮点数
double frexp (double f, int *p); 标准化浮点数, f = x * 2^p, 已知f求x, p ( x介于[0.5, 1] )
double ldexp (double x, int p); 与frexp相反, 已知x, p求f
8、取整与取余
double modf (double, double*); 将参数的整数部分通过指针回传, 返回小数部分
double fmod (double, double); 返回两参数相除的余数
9、其他
int rand(void) 这是随机函数,产生的是从-90到32767的随机值
double hypot(double x, double y);已知直角三角形两个直角边长度,求斜边长度
double ldexp(double x, int exponent);计算x*(2的exponent次幂)
double poly(double x, int degree, double coeffs [] );计算多项式
int matherr(struct exception *e);数学错误计算处理程序
第八章:<setjmp.h>
goto语句不能进行非本地跳转,C库用setjmp实现
typedef int jmp_buf[_NSETJMP], 可以把它当成标号数据对象类型看待
#define setjmp(env) _Setjmp(env), 把当前调用的上下文信息存储到一个jmp_buf类型数据对象中,并在你想把控制权传递给相应的longjmp调用的地方作标记
void longjmp(jmp_buf env, int val), 实现非本地跳转
setjmp的两个危险
- 包含宏setjmp的表达式
- 在执行setjmp的函数中声明动态存储空间
/*测试*/
static jmp_buf b0;
static void jmpto(int n)
{
longjmp(b0, n);
}
static int tryit(void)
{
switch(setjmp(b0)){
case 0:
....
jmpto(0);
break;
case 1:
....
}
}
第九章:<signal.h>
#define SIGHUP 1
#define SIGINT 2
#define SIGQUIT 3
#define SIGILL 4
#define SIGTRAP 5
#define SIGABRT 6
#define SIGIOT 6
#define SIGSTKFLT 7
#define SIGFPE 8
#define SIGKILL 9
#define SIGBUS 10
#define SIGSEGV 11
#define SIGXCPU 12
#define SIGPIPE 13
#define SIGALRM 14
#define SIGTERM 15
#define SIGUSR1 16
#define SIGUSR2 17
#define SIGCHLD 18
#define SIGPWR 19
#define SIGVTALRM 20
#define SIGPROF 21
#define SIGIO 22
#define SIGPOLL SIGIO
#define SIGWINCH 23
#define SIGSTOP 24
#define SIGTSTP 25
#define SIGCONT 26
#define SIGTTIN 27
#define SIGTTOU 28
#define SIGURG 29
#define SIGXFSZ 30
#define SIGUNUSED 31
#define SIGSYS 31 /* Linux doesn't use this */
指定信号处理
void (*signal(int sig, void (*func)(int)))(int);
发送信号
int raise(int sig);
发送信号到进程
int kill(pid_t pid, int sig);
测试
static void field_fpe(int sig)
{
assert(sig == SIGFPE);
puts("SUCCESS testing <signal.h>");
exit(EXIT_SUCCESS);
}
int main()
{
assert(signal(SIGFPE, &field_fpe) == SIG_DEL);
raise(SIGFPE);
puts("FAILURE testing <signal.h>");
}
第十章:<stdarg.h>
va_list 是一个适合保存宏va_start、va_arg、va_end所需要信息的类型。
//一个可变参数函数至少声明一个固定参数,宏va_start引用最后一个固定参数,所以它能够对可变参数表进行定位。
void va_start(va_list ap, parmN);
//一个函数在返回它的调用者之前一定要调用va_end。
void va_end(va_list ap);
type va_arg(va_list ap, type);
测试
static int tryit(const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
for(; *fmt; ++fmt){
switch(*fmt){
case 'd':
va_arg(ap, int);
break;
case 'c':
....
}
}
va_end(ap);
}