C Primer Plus(三)

重读C Primer Plus ,查漏补缺

  重读C Primer Plus,记录遗漏的、未掌握的、不清楚的知识点html

 

文件输入/输出

  一、fgets函数在读取文件内容时会将换行符读入,但gets不会,fputs函数在写入文件时不会追加一个换行符,但puts会,应该对应配合使用。数组

  二、不一样操做系统下,以文本方式打开文件,几乎没有区别,但因为不一样操做系统文件结尾的的标识符不一样,以二进制方式打开时,可能会将结尾标识符错误输出。安全

  三、对于大型文件,有两个特殊的函数提供支持:数据结构

1 int fgetpos(FILE * restrict stream, fpos_t * restrict pos);
2 int fsetpos(FILE * stream, const fpos_t *pos);

  其中,fpos_t是经过其余类型定义的文件定位类型,在使用上述函数时,fsetpos中的pos必须是经过fgetpos函数得到的。当两个函数执行成功时,会返回0。函数

  四、其余标准IO函数flex

 1 size_t fwrite(const void * restrict ptr, size_t size, size_t nmemb, FILE* restrict fp);
 2 size_t fread(void * restrict ptr, size_t size, size_t nmemb, FILE* restrict fp);
 3 // 是否到达文件结尾
 4 int feof(FILE* fp);
 5 // 是否发生读写错误
 6 int ferror(FILE* fp);
 7 // 将字符回流进缓冲区
 8 int ungetc(int c, FILE* fp)
 9 // 马上将缓冲区内容写入文件
10 int fflush(FILE* fp)
11 // 替换缓冲区
12 int setvbuf(FILE* restrict fp, char * restrict buf, int mode, size_t size)

  固然,上述的一些函数在目前的VS Studio中会被认为是不安全的函数,已通过时。spa

  

结构和其余数据格式

   五、C99标准下支持对结构体初始化时的任意字段赋值:操作系统

1 struct book gift = {.value=25.99, .author="Harry Potter", .title="Yoo"};
2 // 此时 0.25 会被赋给定义结构体时author后的那个成员,即使那个成员已经被初始化过。
3 struct book gift = {.value=25.99, .author="Harry Potter", 0.25};

  六、对于结构体数组,数组名不是其首个元素的地址,须要引用首个元素再取地址。翻译

  七、在结构中通常使用字符数组,而不使用字符指针,结构中的字符指针没法很好的初始化地址,这样会有使用上的风险,因此结构中的字符指针最好只指向那些字符串常量或者是指向由malloc分配的内存。指针

  八、C99标准对结构也支持复合文字,同时复合文字的结构也能够做为函数参数,也能够取地址,也和普通变量有相同的生存周期,声明方式以下:

1 (struct book) {"The Idiot", "Fyodor Dostoyevsky", 6.99}

  九、C99支持一种伸缩型数组成员,这个成员必须是结构中最后一个成员,并且不是惟一一个成员,就像声明普通数组同样,但括号内为空,这个成员不会在声明后当即存在,实际上,C99但愿使用malloc为这样含有伸缩型成员的数组分配空间。

1 struct flex{
2     int count;
3     double avreage;
4     double scores[]; // 伸缩型成员
5 }
6 struct flex * pf;
7 pf = malloc(sizeof(struct flex) + 5*sizeof(double))
8 pf->count = 5;
9 pf->scores[2] = 2.99;

  十、对于C中的枚举类型,某些属性不能顺延至C++,例如C容许对枚举作++运算,但C++不容许。

1 enum spectrum {red, yellow, green, blue};
2 spectrum color;
3 for(color = red; color != blue; color++);

  十一、在C中,对于同一做用域下的标记和变量名可使用同一个名字,由于对于标记(枚举、结构,联合),他们使用的名字空间与普通变量不一样,但C++中不能够,例:

1 struct complex{double x, double y};
2 int complex; // 在C中不会引发冲突,但C++中则不容许

  十二、对于函数指针执行函数时,会出现两种语法,ANSI C把他们视为等价的。

1 void ToUpper(char *);
2 void (*pf) (char*);
3 char str[] = "hello";
4 pf = ToUpper;
5 (*pf)(str); // 语法1
6 pf(str);    // 语法2

  

位操做

  1三、为何一个字节能够表示的有符号整数的范围是-128~+127?

  看这里:https://www.cnblogs.com/Dylan7/p/12649972.html

  1四、计算机中小数是如何表示的?(一部分表示指数,一部分表示小数,有精确度问题)

  1五、对位进行操做的第二种方法就是位字段(从没用过,细节能够用到时再研究),位字段比如一个结构体,但其中的成员,表明的是某几位上的值,好处是避免了经过复杂的位运算去控制某些位上的值,声明例如:

1 struct box
2 {
3     unsigned int opaque       :1 // 总体结构的对齐补齐依据无符号整型
4     unsigned int fill_color   :3 // 数字表明须要几位来表示这个字段
5     unsigned int              :4 // 能够跳过一些位
6     unsigned int show_border  :1 // 但一个字段不能横跨两个无符号整型的边界
7 }
8 struct box b;
9 b.fill_color = 7; // 不能够超过字段所占用的位可表示的上限

  

C预处理器和C库

  1六、程序翻译的第一步,在预处理前,编译器会对代码作一些翻译,将代码中出现的字符映射到源字符集(用来处理多字节字符和使C外观更加国际化的三元字符扩展),接着查找反斜杠后紧跟换行符的实例,将其转换为一行,而后将文本划分为预处理的语言符号序列以及空白字符及注释序列(将用一个空格代替一个注释),最后进入预处理阶段,寻找每个预处理指令。

  1七、 几个宏定义

1 #define F(x) #x      // #将语言符号字符串化
2 #define F(x) F##x    // ##将两个语言符号组成一个语言符号
3 #define F(x,...)  printf("x", __VA_ARGS__)  // ...和__VA_ARGS__,可变参数(必须为最后一个参数)

  1八、#if 指令后面跟常量整数表达式,能够与 #elif 配合使用,例如:

1 #if 1 == SYS
2     ...
3 #elif 2 == SYS
4     ...
5 #endif    

  同时,还有如下新的实现方式,defined 是一个预处理运算符,若是参数使用#define定义过,defined返回1,不然返回0。

1 #if defined(INMPC)
2     ...
3 #elif defined(VAX)
4     ...
5 #endif   

  1九、#line 用于重置__LINE__,__FILE__宏所报告的行数

    #error 指令使预处理器能够发出一条错误信息

1 #line 10000
2 #line 10 cool.c"
3 #if __STD_VERSION__ != 199901L
4     #error Not C99
5 #endif

  20、C99 提供了_Pragma预处理器运算符,能够将字符串转换成常规的编译指示

1 _Pragma("c99 on") 等价于
2 #pragma c99 on

  2一、内联函数不会在调试器中显示,例如使用gdb调试时,有些内联函数没法被手动执行,同时内联函数具备内部连接属性,因此在多文件程序中,使用其余文件的内联函数时,要单独声明一次,而且在尝试获 取内联函数的地址时,编译器都会产生非内联函数,也就是说可能产生外部定义的函数。

  2三、在main()函数结束时,会隐式地调用exit()函数,同时,能够经过atexit()函数,向exit()注册在程序容许结束时执行的函数,ANSI C保证能够设置至少32个函数,按照先设置后执行的顺序执行,atexit()函数接受一个返回值为void,参数也为void的函数指针做为惟一参数。

  2四、memcpy()与memmove()两个函数的区别在于声明上,以及memcpy()会假定两个内存区没有重叠。

1 void *memcpy(void * restrict s1, const void * restrict s2, size_t n);
2 void *memmove(void *s1, void *s2, size_t n);

  2五、可变参数的相关内容包含在stdarg.h头文件中,使用起来比较复杂,包括初始化可变参数列表,遍历列表,清理列表,拷贝列表等一系列操做,须要时再研究。

 

高级数据表示

  2六、 这章没什么新奇内容,但它告诉咱们,用C能够实现不少复杂的数据结构。

 

 

  2020年4月16日,星期五,晚23点09分,首次完整读完这本书,共勉。

  学如逆水行舟,不进则退;心似平原放马,易纵难收。