C 语言学习笔记

================================================================================
《C和指针》
================================================================================
1、指针的效率(P146):指针的效率高于下标,不对指针进行加减运算,sizeof接常量表达式可以在编译时求值。

2、运行时环境(P397)
主调函数从右至左压入被调函数的实参、返回地址,然后转到被调函数,被调函数在其函数序中保存相关寄存器值和局部变量,然后执行函数体,最后由被调函数转至返回地址,然后主调函数再来清除其实参。

3、void *类型不能进行指针运算,因为void的大小未知,所以编译器会报错。
函数指针也不能进行指针运算(没意义)。

================================================================================
《C专家编程》
================================================================================
1、P22 寻常算术转换(操作符的操作数类型不一致时发生):char shrot 转 int ,float 转 double, 同类型时有unsigned 就全转 unsinged
// char 转 int (类型提升)
printf("%d", sizeof('A')); // 输出为4

整型升级:如果int可以完整表示源类型的所有值,则该源类型的值就转换为int否则转为unsigned int
注:ANSI C表示如果编译器能够保证运算结果一致,也可以省略类型提升。

2、不要为了方便对结构使用typedef,因为struct关键字可以提示一些信息,不应省掉。

3、数组和指针不同(寻址方式不同):数组是变址寻址即 EA=(IX)+A, A是数组首地址,指针是间接寻址即 EA=(A), A是指针值。总之指针访存开销比数组访存多一次(如p[i]这种),如果把数组名赋给指针,那么以后对指针的任何操作就与指针同。但是如果是大量的非单纯访存的操作用指针方针可以提高效率。
// 正常,与*(p+3)完全相同。
char * p = "adsdfs";
char c = p[3];

// 错误,因为p会按照指针方式寻址,从而把字符'1'+2的偏移来作为p[2]的地址。
extern char * p;
char p[4] = "123";
char c = p[2];

4、避免interpositioning,否则不光自己代码,系统调用也会影响(5.4系统保留的关键字)
ldd命令列出可执行文件的动态依赖集

5、有用的工具:
cflow 打印源文件中函数的调用关系
sum 打印可执行文件的检验和与程序块计数,用于检查文件版本,传输出错否。
file 打印文件类型,如是二进制文件还是文本文件。
gprof 程序性能评测工具:(gcc编译时要加-pg选项,并且要在程序运行后才能评测)
time 显示程序所使用的实际时间和cpu时间。

6、进程的内存布局:
高地址
    堆栈段---------函数的局部数据
    空洞
    ------------break,堆的边界
    堆-----------用于malloc等
    数据段的BSS段-----未赋值的符号
    数据段其它-------静态存储区
    文本段---------指令区
低地址
     
相关函数:
  brk(),是系统调用。改变数据段大小(即break的位置),一般不用显式调用,malloc会自动调它
  sbrk(),是C库函数。改变数据段大小(即break的位置)。
命令:
vmstat,报告虚拟内存统计信息。
ps -lu 用户名 ,查看该用户的进程,并显示进程内存使用情况。

系统并不支持在信号处理函数内部调用库函数,因为可能陷入无尽的信号循环。

7、K&R C会在参数传递时进行类型提升,然后在函数内部再按参数类型裁减。而ANSI C的参数传递是什么类型就传什么类型,没有提升和裁减问题。
以下4种情况:
(1).K&R C 函数声明和K&R C 函数定义:能顺利调用,传递的参数会进行类型提升。
(2).ANSI C 函数声明(原型)和ANSI C 函数定义:能顺利调用,传递的参数为实际参数。
(3).ANSI C 函数声明(原型)和K&R C 函数定义:如果使用一个较窄的类型就会失败,传递的参数是实际类型,而函数期望接收的是提升后的类型。
(4).K&R C 函数声明和ANSI C 函数定义:如果使用一个较窄的类型就会失败,传递的参数是提升后的类型,而函数期望接收的是实际类型。

#include <stdio.h>
int main(void)
{
    int c;
    /* The terminal driver is in its ordinary line-at-a-time mode */
    system("stty raw");
    /* Now the terminal driver is in character-at-a-time mode */
    c = getchar();
    system("stty cooked");
    /* The terminal driver is back in line-at-a-time mode */
    return 0;
}

perror可以自动打印当前errno中的错误信息。
strerror可以把errno的值映射为错误描述信息。

8、数组和指针相同的时候:
(1).表达式中的数组名(与声明不同)被编译器当作一个指向该数组第一个元素的指针。
(2).数组下标总是与指针的偏移量相同。(事实上a[i]在编译时总是被编译器改写成*(a+i))
(3).在函数参数的声明中,数组名被编译器当作指向该数组第一个元素的指针。

9、数组转化为指针:
Argument is:                                   Matches Formal Param:
array of array       char c[8][10];            char (*c)[10]; pointer to array
array of pointer      char *c[15];            char **c; pointer to pointer
pointer to array    char (*c)[64];            char (*c)[64]; doesn't change    
pointer to pointer     char **c;                char **c; doesn't change

同型指针的加减运算,其值以其指向类型为单位,而非字节大小。如 struct S{int a; int b}x;
&x.b-&x.a==1而不是4

10、注意:
a[--j + i++] += --y;
等价于:
--j;
--y;
a[j+i] =  a[j+i] + y;
i++;

11、fgets比gets安全

getchar通常实现为宏,返回的是int,等价于getc(stdin);
fgetc和getc相似都是从输入流读入一个字符,但是getc通常实现为宏,更加高效,所以其实参不能有副作用,因为它可能求值多次。
putchar、fputc和putc同上
ungetc将字符推回指定的输入流,数据流成功使用文件定位命令(fseek、fsetpos、rewind)时放弃所有推回的字符。
c99:snprintf


12、signal的handler中不应该调用库函数,因为信号中断可能发生在库函数更新某个数据结构的过程中。
而自己写的函数则可以清楚所有数据结构的细节。
注:signal不利于可移植性。其handler应尽可能简单(如打印错误信息,然后马上exit或者longjmp)才能保证安全性。


================================================================================
《C陷阱与缺陷》
================================================================================
1、优先级:前述(调用、下标、结构成员选择,左到右)>单目(后缀最高,右到左)>双目(赋以外:左到右)

算(移)关(== !=低)(& ^ |)逻(与>或)条赋(右到左)逗
注:具有相同优先级的所有运算符具有相同的结合律

2、两个无符号数没有溢出问题。但是两个有符号数a,b的加法运算可能会溢出:可以通过把它们强制转换为无符号数计算后写INT_MAX比较
if ((unsigned)a + (unsigned)b > INT_MAX)
或者
if (a > INT_MAX - b)

3、对同一文件交替执行fwrite和fread,需要在其中间插入fseek函数。
setbuf(stdout, buf); // 设置buf被stdout的输出缓冲区

4、常见错误:
   char c;
   
   (unsigned)c是得不到与c等价的无符号整数,因为c转换为无符号整数前,首先被转换为int型整数
   
   正确方式:
   (unsigned char)c,因为unsigned char类型的字符转换为无符号整数时无需先转换为int型整数。
   
   无符号数右移,前面都是添0;有符号数右移可能添0也可能添1。
   
5、表达式"23424253525"[i]是合法的,可以用来解决字符集移植性问题,因为不是所有的字符集的数字字符都是按顺序排列的。(ansi c是这样规定的)
ascii的字母表是连续编码的,但EBCDIC的字母表不是的

6、值位为N的有符号数的表示范围一般是:-2^(N-1)到2^(N-1)-1,所以负数取负可能会溢出,而正数取负一定不会溢出。

7、除法运算的截断
q = a / b;
r = a % b;
a、b、q、r之间应该维持的关系:
1).最重要的:q*b+r==a
2).如果改变a的正负号,希望也会改变q的符号,但不会改变q的绝对值
3).当b>0时,希望保证r>=0且r<b。
在实际实现中,C语言的定义只保证了性质1),及当a>=0且b>0时,保证|r|<|b|及r>=0.

因此当a<0,b>0时,如果C语言的实现是r>0,那么可以通过r-=b;q++;来修正到对称情况。
对称情况:满足1), 2)条件


================================================================================
《C语言参考手册第五版》
================================================================================

1、逻辑源代码行,C89:>=509,C99:>=4095
   标识符字符数:C89:>=31,C99:>=63
   外部标识符字符数:C89:>=6且不区别大小写,C99:>=31且区分大小写,允许把“通用字符名“当作6个字符(最大为\U0000FFFF)或10个字符(\U00010000以上)

通用字符名: P42
   C语言也支持把通用字符名称(universal character name)作为使用扩展字符集的方法,而不管C 的实现版本采用何种编码。可以使用通用字符名称指定扩展字符,而通用字符名称就是其Unicode 的值,格式如下:

\u XXXX
或者:
\U XXXXXXXX    

其中XXXX或XXXXXXXX是十六进制表示法的Unicode 码点(code point,指的是码集合内的一个码)。小写的u 后面接着四个十六进制数字,或者大写的U 后面接着正好八个十六进制数字。如果某个码点前四个十六进制数字是零,那么其通用字符名称写成\uXXXX或\U0000XXXX都可以。

C99允许在字符(串)型常量中使用通用字符名。

2、注释大段源码用宏
#if 0
...
#endif

3、iso646.h文件中定义了展开一些运算符的宏以及一些不能完全表示美语或英语字符I/O设备的替换拼写:
如三字母词。
<% %> : { }
<: :> : [ ]
%:    : #
%:%   : ##

标准C语言保留所有以下划线开头加大写字母或另一个下划线的标识符。
保留以is和to开头的名称,以便今后在库(ctype.h)中增加相关函数。
C的关键字不能作为普通标识符但可以作为宏名,因为预处理发生在编译分词的前面。
C99新增关键字:_Bool、_Complex、_Imaginary、inline(只能在函数声明中出现)、restrict
C99新增预定义标识符:__func__
C99新增:long long  对应:ll或LL
C99保留:bool、true、false

4、C99允许十六进制浮点型常量(用字母p而不是e分开小数与指数部分,表示指数的基为2),以前则只有十进制型的。
如:0X0X0.4P2F 表示1.0
1.0+1.0*I是C99复数常量
常见浮点型常量形式:0.、3e1、3.14159、.0、1.0E-3、1e-3、1.0、.00034、2e+9
常量浮点数可以加F、L后缀,F是double,L是long double

5、mktemp函数的输入要被修改,所以不能传指向常量字符串的指针,要传数组或者malloc啥的。

宽字符串和正常字符串紧挨着接合得到宽字符串常量,C89不行,C99支持。

CTRL + D :结束输入(EOF)

6、C99支持带参数宏中的可变参数表即... ,在宏函数中用__VA_ARGS__代替...要出现的位置

无参数的带参宏可以模拟不带参数的函数
标准C语言不重新扩展自己的扩展中出现的自身宏,如
#define char unsigned char // 不会递归扩展char
标准C语言预处理器自定义对象宏,都以双下划线开头和结尾,不能被#undef或者重新定义
标准C语言禁止替换字符(串)型常量中的宏参数。
标准C语言在预处理时会将注释替换成空格

P49:
__STDC__(当编程器为ISO兼容实现时值为1)与__STDC_VERSION__(实现符合C89增补1,值为199409L,符合C99,值为199901L,否则值是未定义)宏可以编写与标准C语言和非标准C实现兼容的代码。
__STDC_HOSTED__宏是C99中引入的,用于区别宿主实现(宏值为1)和独立实现(宏值为0,大部分库函数不能用)。

7、#inlcude中的文件名形式只能是:字母与数字(以字母开头).一个字母
C89:点号前最多5个符号, C99:点号前最多8个符号
#include命令的包含嵌套至少支持8级,C99要求至少15级

8、#if或#elif的常量表达式中出现未定义宏名,则换成整数常量0.
#line 10 test.c // 指定line所在文件名为test.c的第10行。

9、#pragma 杂注 // 杂注是不会宏扩展的,内建标准杂注在C99中要在前面加 STDC
如: #pragma STDC FENV_ACCESS ON

_Pragma运算符是C99新增的,是一个表达式,后接字符串常量,支持转义字符。
_Pragma("STDC FENV_ACCESS ON") //同上式是一样的,且会扩开宏

10、标准C才有 #error tokens(可以多个) // tokens可以是字符串常量(宏名不扩展)也可以不用双引号,这样宏会被扩展。

11、C++使用C89预处理器,__cplusplus是C++实现的,__STDC__在C++中定义与否取决于实现。

12、声明的组成部分:存储类说明符(quto、register、extern、static 、typedef)、类型说明符(type)与限定符(const、volatile、restrict)、函数说明符、声明符、初始化语句。
注:禁止计算register变量的地址

####C语言中,(代码块、文件块)块开头的声明可以隐藏块外的同名声明。但同一个作用域中的同名声明是错误的,叫做冲突。####
C99不允许函数调用隐式声明函数。
C99允许块中任何地方声明。

13、C语言有5种命名空间P68:预处理宏名;语句标号(如goto后的那种,总是后面跟一个冒号);结构、联合、枚举标志;成员名(用在.或->之后,结构体、联合体);其它名称(变量、函数、typedef名称、枚举常量)。

14、goto标号之前的auto变量的初始化操作可能不会被执行。且auto块级标识符的值不从块的上次传递到下次执行中。P205

goto L; /* 在C中合法,但不提倡,在C++中非法(C++不能绕过带初始化的声明而跳转入复合语句) */
...
{
    static int vector[10] = {0};
    int sum = 0; // 在goto 转到下句时,这里的sum是不会初始化的。
L:
    ...
}

15、volatile:编译器这个变量易变。告诉编译器在使用时不要使用在寄存器中备份而是重新去读一遍RAM里的值。
有volatie修饰的变量,每次操作时遵循下面动作:
从内存取值 ---> 放入寄存器 ----> 操作 ---->写回内存
没有volatie修饰的变量,操作可能遵循(可能就是不是所有情况都如此):
从内存取值 ---> 放入寄存器 ----> 第一次操作 -----> 第二次操作(此时仍操作寄存器中的值) …… ---->第N次操作 ---->写回内存

C99:restrict用来修饰指针,即只有被其修饰的指针可以修改指向的内容,其它指向相同对象的指针的任何行为都是未定义的(这样编译器就可以放心地对restrict指针进行优化)。两个限制指针或者一个限制指针和一个非限制指针可以引用同一个对象,条件是该对象在限制指针存在期间不被修改。
如:memcpy函数要求源和目标内存区不重叠,那么像如下声明函数即可达到目的:
void * memcpy(void * restrict s1, const void * restrict s2, size_t n);
这样如果s1和s2有重叠内存区的话就会违背声明的restrict

16、C99中,结构的最后一个成员可以是灵活数组,不声明长度。(注n维数组必须把右边n-1维的长度以常量方式写出)。
C99变长数组:不能是static、extern型,且不能作为结构成员或联合成员。但是可变修改类型(如变长数组指针)可以用static修饰。
typedef声明使用变长数组时,长度表达式只在声明时求值一次。
变长数组或可变修改类型可以作函数参数类型:如果数组长度也是参数,则必须出现在数组参数之前。调用具有变长数组参数的函数时,数组参数的维长应符合函数参数声明,否则结果不确定。
在函数原型声明(而不是函数定义)中,可用[*]表示变长数组维度,但在函数定义时要提供非常量表达式。(因为在原型声明中任何非常量维度都是和*一样处理)

17、外部名称:一组同名声明中,只允许一个是定义声明,其余的都是它的引用声明。定义声明满足以下任何一条即可:
1)初始化语句(C标准采用) 2)省略存储类型(无extern)
建议:外部变量的定义点应在源文件中,并省略extern且包含初始化语句。

18、C++兼容性:
嵌套定义的struct或union在标准C中,内部定义对外是可见的,而C++的不可见。

P97 具有const限定符而没有显示存储类型说明的顶层声明在C++被认为是static的,而在C中是extern的。

C++顶层变量没有试探性定义,C中的试探性定义在C++中被看成实际定义。
如:
int i;
..
int i; // 在C中有效,在C++中会造成重复定义

19、C99支持扩展整数类型名

20、 if (ip != NULL)...比if (ip)...更好,因为NULL不一定在所有类型指针的null实现上都是值0(注:NULL是对象指针值,长度可能不适用于函数指针)

21、(位段非常不利于移植,跟硬件直接相关)结构体的位字段成员通常不在机器的可寻址边界上,困此可能无法建立指向位字段的指针,所以C语言没有提供指向位字段的指针;可以放置无名位字段,提供相邻成员之间的填充(对无名字段指定长度0有特殊含义——表示存储前一位字段的字中不能再放置更多的位字段,下一个位字段只能放到下一个字中)。
结构不能包含自身类型的实例,但可以包含自身类型的指针。
C99中,结构的最后一个成员可以是不完整数组类型,称为灵活数组成员,使得结构长度可以在运行时改变;在C99以前是在结构最后一个成员声明为只含一个元素的数组,如char s[1],现在要声明为char s[],在sizeof计算该结构大小时,忽略s的存在。

22、联合成员在标准C中可以是位字段,传统C中则不能。

23、extern int fun(void); // 有返回值
    (void)fun(); // 显式放弃返回值
    
24、typedef定义的类型(此类型并不是新类型,而是原类型的同义词)前不能加其它类型说明符(如unsigned等),但可加类型限定符(如const, restrict, volatile等)。
用typedef定义的函数类型不能继承函数性,故只能用过定义函数指针,指针数组等。如下:
typedef int func();
func * f_p, * f_array[10]; // 正确
func fun ,fun_array[10];   // 错误

25、C语言中不要求整型长度很大,使它能够表示指针,但C语言编程人员通常假设long类型长度足够表示指针。C99中,inttypes.h可能定义整型类型intptr_t与uintptr_t,保证整数类型的长度跢表示指针。

26、类型转换:
整型-->整型:
带符号(短)-->无符号(长):先位扩展为带符号的,再转为无符号的。
无符号或带符号(长)-->无符号(短):截去高位。
指针-->整型:把指针当作长度等于指针长度的无符号整数,然后再转为目标类型。
复数-->实数:放弃虚部
虚数<-->实数:结果都为0
任何-->void:让读者知道编程人员故意忽略表达式的值。
指针-->_Bool:null指针为0,否则为1

类型提升(一元转换):(标准C)
float:不转(传统C转为double)
阶>=int的整型:无转换
阶<int的带符号整型:转为int
阶<int的无符号整型:值可以用int表示的转为int,不能的转为unsigned int,(传统C都转为unsigned int)

二元转换:(假设每个操作数都已进行一元转换)
操作数一                操作数二                标准C            传统C
long double             *实数类型            long double            不适用
double                *实数类型                double            同标准
float                *实数类型                float            double
*无符号类型            *无符号类型     阶较大的无符号类型            同标准
*带符号类型            *带符号类型     阶较大的带符号类型            同标准
*无符号类型    阶较小或相等的带符号类型            无符号类型        同标准
*无符号类型        阶较大的带符号类型,能      带符号类型    带符号类型的无符号版本
                表示所有无符号类型值
*无符号类型        阶较大的带符号类型,不能        带符号类型的        同标准
                表示所有无符号类型值        无符号版本
*其它类型                *其它类型                不转换            同标准

函数参数类型转换:
无原型控制或形参为...的部分:进行一元转换,但是float都提升为double(因此在格式化字符串中%f对应的类型为double,%Lf对应的类型为long double,而没有float的对应格式字符,因此%lf是未定义行为,只是在C99中允许lf与f同义)


27、sizeof(函数名)无效,sizeof(void)无效,但值 == 1
利用null指针获得结构成员偏移量,标准C没有显式地允许或禁止;
#define OFFSET(type, field) \
    ((size_t)&((type *)0)->field) // OFFSET宏类似于stddef.h文件中的offsetof宏
    
28、C99引入了复合字面值(左值型常量):即在普通字面值前面加上类型转换。
复合字面值出现在文件顶部,则是外部类型的,且初始化表达式列表只能包含常量值。
char * temp1 = (char []){"4242423"}; // temp1指向的字符串可修改
char * temp2 = "daqewrw";

29、因为有的C实现会忽略“缩小”数值作用的类型转换,为了保证最大的可移植性,编程人员应自己实现截尾数值:将其放在变量中,或对整数进行显式掩码运算,而不是依赖于类型缩小转换。
如(double)(float)3.1415926535897932384; // 有的实现会忽略float转换,从而没有减小值的精度。

30、sizeof接表达式时,只在编译时确定其类型,也不会去求值表达式。
int i = 0;
sizeof(i++); // 这里的i++不会执行
sizeof接数组时,如果数组长度影响结果,则总是完全求值数组长度表达式,而其不影响结果时,没有确定是否求值长度表达式。
sizeof接类型名(如struct s),则副作用为声明了这个类型。// C++中无效

31、标准C中:-e是0-(e)的缩写,+e是0+(e)的缩写。
不同实现可能对带符号整数使用不同表示方法,因此对带符号数按位取反~的结果可能无法移植,为了保持可移植性,只对无符号数作~运算。
无符号数e:~e的值为MAX-e

32、运算符
实数都可以做++,--运算。
%两边只能接整型数,div,ldiv,fmod可以用于浮点数。
位运算符用于整型数(只要用于无符号数才具有可移植性)
关系运算符中,浮点数可能有NaN的情况,这样会造成“无效”异常,此时关系的值为false
结构和联合类型不能比较相等性,即使这些关系可以赋值也不行。(因为对齐造成的空洞可能包含任意值,要补偿这些空洞以比较相等性会带来巨大的开销)

33、常量表达式
宏常量:类型为C99:intmax_t、uintmax_t或C89:long、unsigned long,不能做类型转换也不能sizeof运算。
整型常量(整数、字符、枚举):可以sizeof,也可以类型转换

34、编译器对含 二元运算符+、*、&、^、|的表达式的重新布置(按结合性和交换性)的结果是不确定的。

35、P254:标准C中,如果连续序列点之间对一个对象修改多次,则结果是未定义的。
序列点:程序执行序列中发生前面执行流的所有副作用的点,此后不再发生任何副作用(副作用产生的运算包括赋值和函数调用)。
*完整表达式结束时,即初始化表达式、表达式语句、return语句中的表达式、条件迭代或switch语句中的控制表达式(包括for语句中的每个表达式)之后。
*&&、||、?:、逗号运算符的第一个操作数之后。
*函数调用表达式中求值参数和函数表达式之后。

36、switch后不一定要是复合语句,case和default标号不一定要放在内层的最顶层,可以嵌套在其它复合语句的中间。
continue语句对switch语句无效。

37、C99函数声明中:
int f(int x[const 10]); // x视为int * const类型
int g(int const y[10]); // y视为int const *类型

38、内联
任何静态函数都可以指定为inline。(因为内联声明和函数定义必须在同一单元才能正常内联)
外部函数可采用内联定义的方式实现正常内联:
// 文件square.h,实现Inline definition
inline double square(doulbe x) // 必须不用extern声明
{
    return x*x;
}

// 文件square.c
#include "square.h"
extern inline square(double x); // force an external definition using the inline code

39、C++中main函数不能递归调用,也不能取得其地址。

40、独立实现与宿主实现必有的核心库:
float.h、iso646.h、limits.h、stdarg.h、stdbool.h、stddef.h、stdint.h

41、可变参函数实现
标准C:stdarg.h
参数列表中至少含一个固定参数param,供va_start使用
void fun(type param, ...)
{
    va_list ap;
    
    va_start(ap, param); // 获得param后的地址(第一个可变参数)放在ap内部指针中
    // 循环处理va_arg
    va_arg(ap, any_type);
    ...
    va_end(ap);
}

传统C:vararg.h
参数列表必须是全可变参
void fun(va_alist)
va_dcl // 传统函数参数声明,不加;以防止空参数
{
    va_list ap;
    
    va_start(ap); // 获得第一个参数地址放到ap内部指针中
    
    // 循环处理va_arg
    va_arg(ap, any_type);
    ...
    va_end(ap);
}

42、数据流:文本流+二进制流
文本流:由分成行的字符序列组成,每行由\n结束。因此在不同实现中,都必须把文本文件中换行的实际表示映射为标准的\n
二进制流:
fclose在出错时返回EOF,否则返回0。
exit函数会刷新输出缓冲区。
freopen用于重定向stream到新的数据流,如将stdin、stdout、stderr重新关联到另一文件.
输出和输入交替进行,则中间须调用:fsetpos、fseek、rewind、fflush,这些操作会清空所有内部缓冲区。
fwide用于设置与测试流定向:根据stream为面向宽字符、面向字节、无定向,函数返回正值、负值、0。当流定向(宽字符或字节)以后就不受setlocale影响,也不能再重新定向。定向时是受当前的locale影响的。
setvbuf()的参数bufmode:_IOFBF(数据流完全缓冲)、_IOLBF(写入换行符或缓冲区满时刷新缓冲区)、_IONBF(数据流不缓冲)
stderr不带缓冲,stdin、stdout是行缓冲
setbuf(stream, buf); // 等价于下句
((buf==NULL) ?
(void)setvbuf(stream,NULL,_IONBF,0) :
(void)setvbuf(stream,buf,_IOFBF,BUFSIZ))

ftell和fseek:不能用于宽字符
对于二进制流:ftell返回文件位置指针当前位置相对于文件首的偏移字节数,该数值适用于fseek的第二个参数(此时其第三个参数应当是SEEK_SET)。而文本流ftell返回的数值跟实现有关。
标准不要求对二进制流的SEEK_END提供有意义的支持。
// 文本流只提供以下有限的调用形式
fseek(stream, 0L, SEEK_SET);         // 文件开头
fseek(stream, 0L, SEEK_CUR);         // 同一位置
fseek(stream, 0L, SEEK_END);         // 文件末尾
fseek(stream, ftell_pos, SEEK_SET);  // 在上次对stream调用ftell返回的位置

fgetpos和fsetpos:用于宽字符流定位
wint_t用来存放wchar_t的所有值以及WEOF。

43、格式字符串说明P277
#用于o、x。。输出八进制、十六进制前缀。

44、FP_NORMAL 正常浮点数(规格化),FP_SUBNORMAL 精度降低的浮点数(次规格化)
nan(char * p); // 若p指向的字符串是数字则返回其浮点值,否则返回0


P442:如果两个浮点数值中的一个或两个为NaN,则它们是无序的,对无序值使用C语言的比较运算
符时,会产生“无效”浮点数异常。
用isunordered, isgreater, isgreaterequal, isless, islessequal, islessgreater宏可以避免异常问题。除第一个在无充时返回真,其余在无序时返回0

45、sleep(), alarm()不是标准C函数






转载于:https://my.oschina.net/dake/blog/196693

猜你喜欢

转载自blog.csdn.net/weixin_33912638/article/details/91508759