欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

《C和指针》总结

程序员文章站 2022-07-09 22:12:48
链接属性 1.extern 关键字用于标识符第二次或以后的声明并不会改变第一次声明所指定的属性。 2.static 防止被访问 (和java完全不同) 存储类型...

链接属性

1.extern 关键字用于标识符第二次或以后的声明并不会改变第一次声明所指定的属性。
2.static 防止被访问 (和java完全不同)

存储类型

1.变量存储地方:普通内存,堆栈,硬件寄存器
2.代码块外声明的是静态变量,存于静态内存(普通内存),程序运行前存在,始终存在
3.自动变量
4.代码块内变量 + static --> 静态变量

运算符

1. i+++ ++i 这种5个加号的存在,gcc中间有空格可以通过,甚至可以是6个,i+++ + ++i,一个表示正数

指针

1.定义:
    #define NULL (void*) 0
    #define EOF -1
2. int *a; *a=1;
    unix:段违例 segmentation violation ,内存错误:memory fault,总线错误:bus error
3.对NULL指针间接访问,有的编译器访问内存0,有的引发一个错误并终止程序
4.尽量显示初始化指针
5.指针与整型值的相互转换罕见
6.*100=25  不是对100内存地址的地方赋值25,是非法的,100是整型, *(int *)100=25;
    访问硬件本身,很少用
7. * 从右向左的结合性
8.后缀 ++ -- 顺序点   ()不是顺序点!
     ;  ,  ||  &&  ?:  还有函数的所有的赋值之后,第一条语句执行之前。
     函数返回值已拷贝给调用者但在该函数外代码执行之前
     每个基类和成员初始化之后
9. *cp++  *++cp  ++*cp  (*cp)++ ++*++cp  ++*cp++  *(*cp)++  后缀++优先级大于*   *不是++顺序点
10.for(cp=&arr[0];cp<&arr[LEN];) *cp++;
    for(cp=&arr[LEN];cp>&arr[0];) *--cp;
    for(cp=&arr[LEN-1];cp>=&arr[0];cp--) *cp;//都是作为左值操作
    第三句cp到达&arr[0]之后还减1,再进行比较 cp>=&arr[0]值是未定义的,因为cp移动到了数组边界之外
    标准允许数组元素的指针与指向数组最后一个元素后面的那个内存进行比较,但不允许与数组第一个元素之前!
11.指针的加减法会根据类型自动调整

函数

1.函数原型:向编译器提供一些关于函数的特定信息,更为安全
2.函数原型参数不是必须
3.没有参数的原型 int *func(void); void提示没有参数,而不是表示一个void类型的参数
4.函数的缺省认定:程序调用一个无法见到原型的函数是,编译器认为该函数返回整型值
5.stdarg 宏
    定义在stdarg.h中,这个文件声明了一个类型 va_list和三个宏 va_start va_arg va_end
    float average(int values,...){
        va_list args;
        int sum=0;
        va_start(args,values);//准备访问可变参数
        for(int count=0;count

数组

1.数组名是指针常量,而不是指针变量,编译器用数组名来记住数组属性
2.程序在完成链接之后,内存中的数组的位置是固定的,不能移动,所以数组名是指针常量
3.两种情况下数组名并不用指针常量来表示:
    1)作为sizeof的操作数:返回整个数组长度
    2)作为单目操作符&的操作数:取一个数组名的地址返回指向数组的指针,
    而不是指向一个指针常量的指针!!
4.array[subscript]等同于*(array+(subscript))  subscript甚至可以为负
    2[array]=*(2+array)=array[2]  对编译器来说无差别
5.下标绝不会比指针更有效率,但指针有时比下标更有效率
    int array[10] a; for(a=0;a<10;a++) array[a]=0;//每次都会有a*4
    int array[10] *ap; for(ap=array;ap

字符串,字符和字节

1.包含string.h,它里面所包含的原型可以是编译器更好的执行错误检测
2.自己编写字符串函数,保留以str开头的函数名,用于标准库的扩展
3. size_t strlen(char const *string)
    size_t 在stddef.h中定义,是一个unsigned int
    程序保护不会溢出
4.在表达式中无符号数可能导致不可预料的结果
    if(strlen(x) >= strlen(y))....
    if(strlen(x) - strlen(y) >= 0)....
    看似相等,事实上第二局永远是真,无符号数不可能为负
5.标准库函数有的是用汇编实现的
6.char *strcpy(char *dst,char const *src);
    src和dst出现重叠,结果是未定义的
    如果src比dst长,多余的字符仍将复制!必须程序控制
7.char *strcat(char *dst,char const *src);
    src和dst出现重叠,结果是未定义的
    和strcpy一样,必须保证剩余的空间大于src
8.int strcmp(char const *s1,char const *s2);
    s1小于s2,返回小于0的数,大于返回大于0的数,等于返回0
    不等 if( strcmp(a,b) )
9.char *strncpy(char *dst,char const *src,size_t len);
  char *strncat(char *dst,char const *src,size_t len);
  char *strncmp(char const *dst,char const *src,size_t len);
  strncpy 总是复制的长度为len,如果strlen(src)小于len,dst用额外的NUL字节填充到len长度
  如果大于len,只有len长度被复制,但是!他的结果不会以NUL结尾!
  strncat 与 strncpy 不同,它总是在结果字符串后面加NUL,不会进行NUL填充,
  最多复制len长度外加一个NUL,不管空间够不够
10.char *strchr(char const *str,int ch);
   char *strrchr(char const *str,int ch);
    strchr查找ch第一次出现的位置,返回指向其的指针,strrchr返回最后一个
11.char *strpbrk(char const *str,char const *group);
    返回一个指向str中第一个匹配group中任何一个字符的字符位置
12.char *strstr(char const *s1,char const *s2);
13.size_t strspn(char const *str,char const *group);//不再group内
    size_t strcspn(char const *str,char const *group);
14.char *strtok(char *str,char const *seq);
    找到下一个由seq组成的字符合集分割str的标记,并将其用NUL结尾,返回指向这个标记的指针
    没有const修饰str,会被修改,会被重复调用,知道返回NUL
    static char whitespace[]=" \t\f\r\v\n";//有个空格  char *token;
    for(token=strtok(line,whitespace);token!=null;token=strtok(NULL,whitespace))
    可以在每次调用strtok时使用不同的seq,字符串的不同部分用不同的分割
    strtok保存了函数的局部状态信息,不能同时解析多个字符串,
    在for循环体内调用内部调用,strtok的函数将导致程序失败
15.char *strerror(int error_number)
    调用函数或者请求操作系统功能如打开文件出错时,操作系统通过设置并返回一个外部整型
    变量errno进行错误代码报告,此函数接受error并返回描述
16.字符分类:ctype.h 接受一个字符的ASCII码,函数测试这个字符,并返回整形值表示真假
    iscntrl isspace isdigit isxdigit islower isupper isalpha isalnum前三个合集 
    ispunct标点符号 isgraph图形字符 isprint图形字符和空格
17.字符转换:ctype.h   int tolower(int ch)   int toupper(int ch)
18.直接测试或测试会降低可移植性 if(ch>='A'||ch<='Z')在ASCII字符集机器上可以
    但使用EBCDIC字符集的机器不可以,if(isupper(ch)) 都可以!
19.内存操作:以长度作为操作终止标志,解决字符串\0结尾问题。void指针,其他数据类型也可使用
    void *memcpy(void *dst,void const *src,size_t length)
    void *memmove(void *dst,void const *src,size_t length)
    相比于memcpy,dst和src可以重叠,它利用了一个临时空间复制,速度慢
    void *memcmp(void const *a,void const *b,size_t length)
    根据无符号字节进行比较,如果比较的不是单字节数据,结果不可测
    void *memchr(void *a,int ch,size_t length)
    void *memset(void *a,int ch,size_t length)从a开始设置length个ch
    length是操作的字节数,不是一字节的数据类型要数量乘长度,例如int 结构体

结构体

1.struct{....}x;struct{....}*y;这两个声明被编译器当成不同的声明,y=&x也是非法的,即使成员列表相同
2.想在多个源文件中使用定义的结构体,放在头文件中
3.间接访问运算符*的优先级小于成员访问 .     ->大于&
4.可以包含自身的引用,但不能在没定义别名前用别名。不能包含自身,因为递归不能终止
5.相互引用用不完整声明
6.如果不存在将相关成员放在一起的要求,应该根据边界进行重排结构体的成员存储结构
7.sizeof返回结构体的整体长度,offsetof宏(stddef.h)返回指定成员开始存储的位置距离结构体开始
    存储的位置偏移几个字节,返回size_t,offset(type,member)type是结构名,member是成员名
8.位段:
    只能声明为int,signed int,unsigned int(用作表达式时自动升级的类型),
    用int并不好,它究竟是解释为有符号还是无符号由编译器决定
    段中的最大位数限制在一个整型之内,成员的内存分配从左到右还是从右到左未知
    后面的位段不能够容纳与前面一个位段剩余空间时,可能直接接在前面的位段后面(内
    存位置边界上形成重叠),也可能放在内存的下一个字。
9.位段实现的功能都可以用移位和屏蔽来实现,在目标代码中,移位和屏蔽是必需的,
    位段唯一好处源代码简洁,但是可移植性较弱

联合体

1.初始化必须是第一个元素,必须位于一对花括号内,如果类型不符,会转换(如果可能)并赋给第一个

动态内存分配

1.void *calloc(size_t num_elements,size_t element);void *malloc(size_t size);
    void *realloc(void *ptr,size_t new_size);void free(void *pointer);
    calloc 会初始化为0。free,realloc传递NULL作为参数无作用。分配不成功返回NULL
    定义在stdlib.h。NULL定义在stdio.h 

高级指针

1.指向指针的指针
2.理解声明方法 1)推论声明  2)用于声明变量的表达式和普通的表达式在求值时使用相同的规则
    int *f()   int (*f)()  int *(*f)()   int *f[]  int(*f[])()  int *(*f[])()
    int f()[] f是一个函数,它的返回值是一个整形数组。它是非法的,函数不能返回数组
    int f[]() f是一个数组,它的元素类型是返回整型的函数。非法的,数组元素长度相同,不可能是函数
    unix系统cdecl程序可以转换声明和英语
3.函数指针
    初始化:int f(int );  int (*pf)(int) = &f;
    调用函数: f(12)  (*pf)(12) 或者 pf(12)
    f(12)简单调用函数。过程:函数名f首先被转换成一个函数指针,该指针指向函数在内存中的位置。
    然后,函数调用操作符调用该函数,执行开始与这个地址的代码。所以,(*pf)(12)函数指针转换
    成函数编译器又将其转换回去,pf(12)才是正道
4.回调函数,java中策略模式,接口实现,例如Comparator,但java中可以用实现接口和泛型解决
    类型的问题,C中只能是 void*,灵活但不安全。
    在写用于函数指针调用的函数时一般参数为void*,注意强制转换。
    强制类型转换会躲过一般的类型检测,必须格外注意类型的问题。
    函数指针调用库函数,例如strcmp,编译器一般有警告,因为参数是void*,而不是char*
5.转换表。汇编中TAB也经常这样用
6.字符串是指针常量,字符串出现在表达式中的时候就是指针常量
    转换二进制为字符 "0123456789ABCDEF"[value % 16];  *"xyz" 就是 x

预处理

1.文本性质的操作
2.预处理符号 _FILE_  _LINE_  _DATE_  _TIME_  _STDC_
3.#define 可以多行,除了最后一行,其他行行尾要加反斜杠。
4.宏定义中如果调用函数不要再末尾加上分号。有些语句还好,但例如if中,两个分号的出现会出问题
5.#define name(parameter-list) stuff。参数列表左括号和name必须紧邻,否则变成stuff
6.对数值进行求值得宏定义各个参数都要加上括号,最外层还要括号
7.宏参数和#define定义可以包含其他#define定义的符号,宏不可以出现递归
8.预处理对字符串常量内容并不进行进检测。把宏参数插入字符串常量中两种方法
    1)邻近字符串自动连接特性
        #define PRINT(FORMAT,VALUE) printf("result:" FORMAT "\n",VALUE) PRINT("%d",1+1);
    2.使用预处理器把宏参数转换成字符串  #argument会被翻译成 "argument"
        #define PRINT(FORMAT,VALUE) printf(#VALUE "is" FORMAT "\n",VALUE) PRINT("%d",1+1);
9.## 把位于它两边的符号连接成一个符号
    #define ADD_T0_SUM(sum_num,value) sum ## sum_num +=value   
    ADD_T0_SUM(5,25) sum5变量加上25
10.宏与函数:
    定义简单的工作 #define MAX(a,b) ((a)>(b)?(a):(b))
    考虑函数调用的消耗,与宏类似内联函数占用的空间
    函数有数据类型的限制,例如上面的。
    一些任务函数无法实现,例如当参数是一种数据类型。
11.宏参数的副作用,宏参数在宏定义中出现超过一次时,宏参数副作用可能出现危险。
    common lisp 宏也有这个问题,但它的宏实在强大,可以很轻松解决
    z=MAX(x++,y++)  z=((x++) > (y++) ? (x++) : (y++))
    小的值增加了一次,大的增加了两次。
    不仅限于改变变量的值,例如getchar()也有副作用。
12.条件编译 #if constant-expression  不想出现在产品中,调试,维护却需要
                statement
            #elif ......    #else  ....    #endif  ....  
        constant-expresion(常量表达式) 由预处理器进行求职,非0就statement。例如ASSIGN宏
    #if defined(symbol)  等同于 ifdef symbol , if !defined(symbol) 等同于 ifndef symbol
    #if 功能更强大 #if x>0 || defined(ABC) && defined(BCD)
    可以嵌套,
13.编译器定义一系列标准位置查找函数库头文件,/user/include,可以添加
14.标准要求头文件嵌套至少支持8层,但没有理由让嵌套超过两层,很难判断源文件之间真正依赖关系
    unix的make实用工具必须知道这些依赖关系决定文件修改后哪些要重新编译。
15.#error  #line  #progma   # 无效指令

输入输出函数

1.ANSI编译器允许在函数库基础上增加函数,但如果关心可移植性,应该避免
2.void perror(char const *message);  stdio.h ;会在message后面加个冒号和错误内容
3.标准库函数在一个外部整型变量errno(errno.h中定义)中保存错误代码后把这个信息传递给用户程序
4.只有当一个库函数失败时errno才会被设置,所以我们不能通过测试errno值来判断是否有错误发生
5.void exit(int status);stdlib.h; status值返回个操作系统,用于提示程序是否正常,这
    个值和main返回的整型状态相同。经常在perror后调用,实在出错的不能忍了。无处可返回
6.绝大多数流失完全缓冲的,意味着读取和写入实际上是从一块缓冲区内存区域来复制数据
7.printf分布于程序中用于调试。但这些函数的输出结果被写入缓冲区,并不会立刻显示。
    如果程序失败,缓冲输出可能不会被实际写入,会得到错误出现的位置不正确的结论
    解决办法:printf("something");fflush( stdout ) ;立即 fflush
8.对于那些文本行的外在表现形式与换行符表示文本结束的标准不同的系统上,库函数负责翻译
9.stdio.h声明了FILE结构,用于访问一个流。每个流对应一个FILE。
10.每个程序运行,系统至少提供三个流,stdin,stdout,stderr,都是指向FILE结构的指针
11.标准输入输出和错误是缺省的位置,因编译器而异,通常键盘和终端
    可以重定向 $ program < data > answer  DOS和UNIX都支持
12.FILE *fopen( char const *name , char const *mode ); FILE *变量的名字用于保存fopen的返回值,它并不影
    响哪个文件被打开。name为字符串,避免在各系统中的差异。文本:r w a 二进制:rb wb ab 更新:a+,可读写
    失败返回NULL指针,errno会提示问题的性质,应该始终检查返回值
13.FILE *freopen(char const *filename,char const *mode,FILE *stream);
    打开或重新打开stream,首先试图关闭stream,然后用指定的filename和mode重新打开这个流,失败返回NULL
14.int flose(FILE *f);成功返回0,否则返回EOF
15.fopen和fclose直接的程序可能导致FILE*的改变,造成fclose失败
16.int fgetc(FILE *stream); int getc(FILE *stream); int getchar(void);
    读取一个字符,没有返回EOF。返回值是int而不是char,是因为EOF
17.int fputc(int character,FILE *stream);int putc(int character,FILE *stream);int putchar(int character);
    第一个参数是要被打印的字符,在打印前,函数把这个整型参数裁剪为一个无符号字符值
    putchar('abc');只打印一个字符,至于是哪一个由编译器决定。写入到已关闭流或其他失败,返回EOF
18.fgetc和fputc是函数,getc,putc,getchar,putchar,是宏
19.int ungetc(int character,FILE *stream);把character返回到流中
20.char *fgets(char *buffer,int buffer_size,FILE *stream);
    缓冲区到达buffer_size-1或者读到换行符,停止读取。下一次调用从流下一个字符开始,无论如何末尾都是NIL
    如果在任何字符读取前就到达了文件尾,缓冲区没改变,fgets返回NULL,否则返回指向缓冲区的指针
21.int fputs(char const *buffer,FILE *stream);buffer必须包含一个字符串,但它不像fgets最多只能读取一行。
    写入错误时,返回EOF,否则返回非负值
22.char *gets(char *buffer);int puts(char const *buffer);和fgets,fputs类似,保留它们是为了向后兼容
    不同在于gets读取一行时,并不在行尾保留换行符,puts写入时会再添加一个换行符。
    而且,gets没有缓冲长度参数,多出来的字符会被写入缓冲区后面的内存!!
23.int fscanf(FILE *stream,char const *format,...);
    int scanf(char const *format,...);int sscanf(char const *string,char const *format,...);
    省略号代表一个可变长度的指针列表,从输入转换而来的值逐个存储到这些指针指向的内存。
    会跳过空白字符。当格式化字符串到达末尾或者读取的输入不再匹配格式字符串所指定的类型时,
    输入就停止,输入值的数目被作为返回值,没有任何输入就到结尾返回EOF。
    格式代码format都是以%开头,后面可以是一个可选星号(丢弃不存储这个字符),一个可选的宽度(非负整数,
    限制被读取用于转换的长度,在类型长度长于或短于缺省长度时,不指定宽度将出错,总是指定更具移植性),
    一个可选限定符(h,l,L),和一个格式代码
24.                 h                   l                  L
    d,i,n       short                   long        
    o,u,x       unsigned short      unsigned long   
    e,f,g                           double              long double
25.格式码:
    c(char *) 读取单个字符,如果给出宽度,就读取宽度个,末尾不会加NUL,
    i,d(int *) 有符号,i根据第一个字符决定基数,
    u,o,x(unsigned *) u十进制,o八进制,x十六进制
    e,f,g(float *) 
    s(char *) 一串非空字符,发现空白停止,自动加NUL
    [xxx](char *) 一圈字符,发现第一个不在给定组合内的字符停止,自动加NUL,列表头可加^,表示不在组合内,
        是否支持横杠表示范围内字符因编译器而异,例如 %[a-z]
    p(void *) 输入预期为一串字符,转换方式因编译器而异,但转换结果用p打印产生的字符和输入是相同的
    n(int *) 到目前为止通过scanf从输入读取的字符数被返回。%n转换的字符并不计算在scanf返回值之内。
        它本身不消耗输入
    % 与输入中的%相匹配,该%被丢弃
26.int fprintf(FILE *stream,char const *format,...);
    int printf(char const *format,...);  int sprintf(char *buffer,char const *format,...);
27.size_t fread(void *buffer,size_t size,size_t count,FILE *stream);
    size_t fwrite(void *buffer,size_t size,size_t count,FILE *stream);
    size是缓冲区每个元素的字节数,count是读取或写入的元素数,buffer的空间要有size*count这么大
    返回子是实际吸入或读取的元素(不是字节)数目。可以实现java中对象流的操作
28.int fflush(FILE *stream);迫使一个输出流的缓冲区的数据进行物理写入。
29.一般情况数据室线性写入,
    long ftell(FILE *stream);返回流的当前位置,在二进制流中,这个值就是当前位置距离文件起始位置的
        字节数偏移量,在文本流中,并不一点准确地代表字符数偏移量,因为有些系统将对行末字符进行翻译
        但ftell的返回值总可以用于fseek函数中。
    int fseek(FILE *stream,long offset,int from);定位一个流,改变下一次操作的位置。
    from:SEEK_END,从尾部起offset个字节,可正可负。SEEK_CUR,offset可正可负。SEEK_SET,起始位置,offset非负
30.试图定位到文件起始位置之前是错误,定位到文件尾之后并进行写入会扩展这个文件,试图访问会导致返回
    一条“到达文件尾”的信息。二进制流可能不支持SEEK_END,文本流中SEEK_CUR,SEEK_END,offset必须是0,
    如果from是SEEK_SET,offset必须是ftell的返回值
31.fseek副作用:行末指示字符被清除。如果fseek之前ungetc,那么被退回的字符会被丢弃,因为定位操作后,它不
    再是下一个字符。定位允许从写入模式切换到读取模式,或者回到打开的流以便更新
32.void rewind(FILE *stream);定位到流的起始位置,同时清楚流的错误标识
    int fgetpos(FILE *stream,fpos_t *position);int fsetpos(FILE *stream,fpos_t const *position);
    ftell与fseek的替代方法,fgetpos存储当前位置到fpos_t,fsetpos设置文件到fpos_t
    fpos_t表示文件位置不是标准定义的,可能是偏移量也可能不是,
33.void setbuf(FILE *stream,char *buf);int setvbuf(FILE *stream,char *buf,int mod,size_t size);
    必须在流打开但并没有执行任何其他操作前执行,setbuf设置一个数组对流进行缓冲,长度为BUFSIZ,
    在stdio.h中的定义。为流自行制定缓冲区可以防止I/O函数库为它动态分配缓冲。buf为NULL则关闭缓冲。
    为流使用自动数组缓冲可能导致流还没关闭,但数组已经出了代码快,内存被别的函数使用,流也继续使用
    setvbuf:mode用于指定缓冲的类型,_IOFBF指定一个完全缓冲的流,_IONBF指定一个不缓冲的流,_IOLBUF指定一个
    行缓冲流,就是每当一个换行符写入缓冲区,缓冲区进行刷新。buf为NULL,size必须为0,一般为BUFSIZ
34.流错误函数;int feof(FILE *stream);流处于文件尾返回真。int ferror(FILE *stream);报告流的错误状态,
    如果出现任何读写错误就返回真。void clearerr(FILE *stream);对流的错误标志进行重置
35.临时文件: FILE *tmpfile(void);以wb+模式打开,这使它可用于二进制和文本数据。如果文件必须由其他模式打开
    或者由另一个程序打开,必须用fopen,而且不再需要时必须使用remove删除。
    char *tmpnam(char *name);创建临时文件的名字。参数为NULL,返回一个指向静态数组的指针,该数组包含
    文件名,否则指定一个长度至少为L_temnam的数组,文件名在返回的指针代表的数组中。会保证不重名,TMP_MAX
36.int remove(char const *filename);int rename(char const *oldname,char const *newname);
    成功返回0,否则返回非0。remove文件已经打开,结果取决编译器。rename存在newname,结果编译器,oldname仍可用

标准库函数

1.stdlib.h   int abs(int value); long int labs(long int value);长整形的abs
    p_t p(int numerator,int denominator);第二个参数是分母,第一个是分子。产生商和余数,
    p_t的结构 int quot;//商,int rem;//余数
    lp_t lp(long int number,long int denom);
2.stdlib.h   下面函数产生的是伪随机数 int rand(void);0-32767 为了避免每次调用rand获得相同的随机列表,用
    void srand(unsigned int seed);常用每天的时间做种子 srand( (unsigmaned int ) time(0));
3.math.h   double sin(double angle);....cos....tan.....double asin(double value);..atan....
    double atan2(double x,double y);y/x的正切值。都是弧度表示
    double sinh(double angle);.....cosh....tanh.....
4.math.h  double exp(double x); double log(double x);  double log10(double x);
5.math.h  浮点表示形式  double frexp(double value,int *exponent);计算一个指数exponent和小数fracion使
    fraction*pow(2,exponent)的值等于value,其中0.5<=fraction<1。
    double ldexp(double fraction,int exponent);返回fraction*pow(2,exponent);
    double modef(double value,double *ipart);把浮点数value分成整数*ipart和小数返回值
6.math.h  double pow(double x,double y); x的y次方
    double sqrt(double x);
7.定义域错误demain error,实参不再函数定义域内。范围错误range error数过大或过小超过表示范围
8.double floor(double x);double ceil(double x);double fabs(double x);double fmod(double x,double y);
    fmod返回x除以y产生的余数,商被限制为整数值。用double只是为了表示更大的范围
9.stdlib.h  字符串转换
    int atoi(char const *string);
    long int atol(char const *string);
    long int strtol(char const *string,char **unused,int base);
    unsigned long int strtoul(char const *string,char **unsed,int base);
    double atof(char const *string);
    double strtod(char const *string,char **unused);
    任何一个函数的第一个参数包含前导空白字符将被忽略,任何非法后缀,它们也将被忽略
    atoi和atol执行基数是10的运算。strtol保存一个指向转换值后面第一个字符的指针,如果函数的第二个参数
    并非NULL,这个指针便保存在第二个参数所指向的位置。这个指针允许字符串的剩余部分进行处理而无需推测
    转换在哪个位置终止。base是转换的基数,如果基数是0,任何用于书写整数字面量的形式都被接受,包括指定
    数字基数的形式,如0x2af4,0377。否则,基数值应该在2到36直接,对于基数11-36,字母A/a-Z/z被解释为10-35
    如果string无法表示合法的数值,返回0,函数在errno中存储ERANGE
10.time.h  clock_t clock(void);程序开始运行处理器消耗时间,近似值,想得到精确值在main开始调用,相减。
    返回的是处理器时钟滴答次数,想得到秒要除以常量CLOCKS_PER_SEC。太大返回-1
11.time_t time(time_t *returned_value);太大返回-1,标准没规定结果用秒表示,不能用于判断时间流逝,
12.char *ctime(time_t const *time_value);返回字符串,例如 Sun Jul 4 04:02:28 1976\n\0;
    字符串内的空格是固定的,
    double difftime(time_t time1,time_t time2);计算差,并转化成秒
13.struct tm *gmtime(time_t const *time_value);把时间转换成格林尼治时间,世界协调时间
    struct tm *localtime(time_t const *time_value);转换成当地时间,但标准没有UTC和当地时间关系
    strutc tm{int tm_sec;0-61;int tm_min;0-59;int tm_hour;0-23;int tm_mday;1-31;
    int tm_mon;0-31;int tm_year;0-?;要加1900;int wday;0-6;int tm_yday;0-356;int tm_isdat;夏令时标志}
14.char *asctime(struct tm const *tm_ptr);返回的格式和ctime相同
    size_t strftime(char *string,size_t maxsize,char const *format,struct tm const *tm_ptr);
    非常灵活的tm结构转换成字符串
15.time_t mktime(struct tm *tm_ptr);
16.setjmp.h   int setjmp(jmp_buf state); void longjmp(jmp_buf state,int value); 类似goto机制
    首先声明jmp_buf,并调用setjmp对它初始化,返回值是0,setjmp把程序状态保存到缓存区,调用处的函数
    称为顶层函数,以后调用longjmp,会导致保存的状态恢复,setjmp返回longjmp的参数value
17.信号 signal.h
18.void abort(void); void atexit(void (func)(void)); void exit(int status);
    abort不正常终止程序,引发SIGABRT信号。atexit注册退出信号,当eixt调用时,注册的函数将按注册顺序
    反序被调用,然后流的缓冲区被刷新,文件关闭,tmpfile文件被删除
19.assert.h  void assert(int expression);假时打印信息并终止,  #define NEBUG禁用断言
20.stdlib.h  char *getenv(char const *name);返回操作系统维护的name对应的信息
21.stdlib.h  void system(char const *command);系统执行,存在可用,返回非零值,否则返回零
22.void qsort(void *base,size_t n_elements,size_t el_size,int (*compare)(void const *,void const *));
    void bsearch(void const *key,void const *base,size_t n_element,size_t el_size,
        int (*compare)(void const*,void const *));

运行时环境

1.-S保留汇编代码
2.参数按参数列表相反的顺序压入堆栈,这样第一个参数位于堆栈中参数的顶部
    如果相反顺序,第一个参数距离帧指针的偏移量就和压入堆栈的参数数量有关。虽然编译器可以计算,但
    可变实参列表就无法计算偏移量了
3.函数三部分:函数序prologue,函数启动需要的工作。函数体body。函数跋epilogue,函数返回前清理堆栈