《C语言点滴》学习笔记

GCC compiler–static
gcc -static hello.c -o hello
生成静态链接文件

1.这个世界上只有两种产品,一种是没人用的,另一种是被人骂的。
2.No reason is needed to love C.Anything can be a reason not to love C.
3.静若瘫痪,动若癫痫。。。-_-!
4.老师只是一个点着的火柴,如果你是汽油,老师会点燃你,如果你是水,你会浇灭老师。
5.C语言一般都用来完成程序核心的功能,一般我们不用C语言编写界面或编写一个完整的面向最终用户的应用。
6.世界上有10种人,一种懂二进制,一种不懂二进制。
7.scanf就像你的女朋友,突然间就不再工作,不会给你任何提示也不会告诉你为什么——————说的好像我真有一个一样…

版本控制工具

代码风格:

1.大多数有错误的函数都大于500行。
2.一个函数内部的变量最好不要超过7个。

第3章 数据类型

(1)C语言四种数据类型

整型,浮点型,指针型和聚合类型。

(2)原码,反码和补码

原码:符号位为0表示正数,符号位为1表示负数。
反码:若为正数则与原码一样,若为负数则对其原码的非符号位各位取反
补码:若为正数则与原码一样,若为负数则对其原码的非符号位各位取反,并在末位加1.
无符号整形数的原码,反码,补码都一致。

(3)用原码表示有符号数有两个问题

1.正负相加不等于零。
2.有两个零

(4)整数的极限值宏定义:

打印无符号数时勿忘’%u’,注意有符号与无符号的区别
limits.h
char int short long long long
SCHAR_MAX INT_MAX SHRT_MAX LONG_MAX LLONG_MAX
SCHAR_MIN INT_MIN SHRT_MIN LONG_MIN LLONG_MIN
UCHAR_MAX UINT_MAX USHRT_MAX ULONG_MAX ULLONG_MAX
CHAR_MIN
CHAR_MAX

(5)char&int getchar()

char有的时候是无符号,有的时候是有符号。每种编译器对char的符号都有自己的定义。
int getchar(),读到文件末尾或错误的时候,会返回EOF,EOF在stdio.h中被定义为-1。

(6)float 有效位

float的有效位通常为6位或7位
1. inf:infinity (linux) INF:infinity (windows)
nan:not a number (linux) IND:indeterminate (windows)
inf 一般是得到的数值超出浮点数表示范围(溢出,即阶码超出能表示的最大值)
nan 一般是对浮点数进行了未定义的操作,如对-1开方。
nan==nan 结果为false,0。即不能和nan比较
可以用函数: int isNumber(double d){return (d==d);}来判断d是否为nan,若d是nan则返回0,否则返回非零值。
3、1.0/0.0等于inf,-1.0/0.0等于-inf,0.0+inf=inf;
4、对负数开方sqrt(-1.0)、对负数求对数(log(-1.0))、0.0/0.0、0.0*inf、inf/inf、inf-inf这些操作都会得到nan。(0/0会产生操作异常;0.0/0.0不会产生操作异常,而是会得到nan)
5、得到inf时就查看是否有溢出或者除以0,得到nan时就查看是否有非法操作。
6、C语言的头文件

(7)float limits–float.h

float double long double
FLT_MAX DBL_MAX LDBL_MAX
FLT_MIN DBL_MIN LDBL_MIN
FLT_DIG DBL_DIG LDBL_DIG
FLT_EPSILON DBL_EPSILON LDBL_EPSILON
FLT_MAX_EXP DBL_MAX_EXP LDBL_MAX_EXP
FLT_MIN_EXP DBL_MIN_EXP LDBL_MIN_EXP

(8)判断两个浮点数相等—— FLT_EPSILON

if(fabs(a-b) < FLT_EPSILON)
#include < math.h >
double fabs(double) 求浮点数绝对值
int abs(int) 求整型数绝对值

(9)常量与常量后缀

UL unsigned long
f float

(10)sizeof

sizeof是C语言的一个关键字。返回类型为size_t的无符号的整型数。
因此在判断长度时应注意是否出现溢出。str2-str1>0。
用来计算结构体大小。对数组名利用sizeof得到的是整个数组占用的字节数。指针则为指针变量所占字节数。

第4章 表达式和运算符

(1)自增(减)运算符

可以把自增(减)运算符分为两个步骤。一个步骤是复制一份变量的拷贝用于周围的表达式,还有一个步骤是增加自身的值。
*p++ 由于单目运算符都是右结合的,运算时都是从右向左开始算。
++*p++ ++(*p++)

(2)左值和右值

在内存中有一个确定位置的叫左值。左值又分为可修改左值与不可修改左值。C中前缀运算符和后缀运算符都是复制一份变量的值拷贝,我们无法确定拷贝在哪里。

(3)布尔值

零值为假,非零为真。用浮点类型来保存一个逻辑值是无法准确定义真假的。
if(a==b==c) <-> if(0==c)

(4)数据类型转换

float->double
char,short->int->unsigned->long->double

(5)运算顺序问题

1.运算顺序固定:&& || ?: ,三个半,当用’,’来分开函数的实参时,运算顺序是不固定的。eg.printf(“%d %d”,m,m=7);
2.if(5>3||i++);if(5<3&&i++); 逻辑运算中的短路效应,将不计算i++;
3.good example: if(d!=0&&n/d>0);
4.另,如f()+g()*h(),函数的调用顺序无法确定。

(6)模运算

1.只能用在整型数上。
2.余数的符号与被除数相同。
用于:判断整数;映射到某一范围;得到后n位数;分别计算每位上的值;
/*将a的二进制表示打印出来*/
while(a)
{
printf(“%d\n”,a%2);
a=a/2;
}

(7)模运算与hash

((a mod q * b mod q) mod q) instead of ((a*b) mod q)
霍纳法则用于高效的计算多项式
Pn(x)=An*X^n + An-1*X^n-1 +…+ A0*X^0;
Pn(X)=(…((An*X+An-1)*X+An-2)*X+…A1)X+A0
/coding/
Pn=0;
while(n!=0)
{
Pn=Pn*x+a[n–];
}

(8)位运算

~ 按位取反
& 按位与
| 按位或
^ 异或
<< 左移
>> 右移

第5章 输入输出

(1)流:

1.流是一种特殊的数据结构,它是动态和线性的。动态是指数据的内容和时间相关,线性是指流只在纵向上有长度,在横向上没有宽度即——流每次只能读入一字节。C语言用一个“流”来抽象代表所有的设备。分为1.I/O流 2.文本流 3.二进制流
2.文本流的特点是流由文本行组成,每一文本行由0个或多个某种字符集的字符组成,以’\n’结束。一个文本流读入与写出时可能会对其内容做更改,因为字符是有一定意义的,系统可以识别并在适当的时候解释。例如,在输出文本流中遇到’\b’时,系统的操作是将输入流中的前一字符删除。
3.流和一个文件或设备通过opening操作来关联,并通过closing操作来断开这种关联。C语言中用FILE*类型的指针来定义一个流。C语言中文件的概念和流的概念有的时候可以互换。

(2)输入输出流:

1.输入流stdin,默认指向键盘。两个输出流stdout和stderr,默认都指向屏幕。任何一个输入输出函数,如果函数的参数没有制定输入流和输出流则默认从stdin读取字节,将字节输出到stdout。
2.stdout的内容首先要保存到缓冲区内,而输出到stderr的内容则直接输出到屏幕。fprintf(stderr,…)
3.stdin与stdout都是常量,不能修改。C语言提供freopen函数可以将stdin和stdout重定义。
4.a.exe <_input 1>output 2>&1 著名的重定向,stdin被重定向为input文件,1代表stdout,与2代表的stderr重定向到output文件。重新定义文件标识符可以用i>&j命令,标识把文件标识符i重新定向到j,可以把’&’理解为取地址。

(3)单个字符的输入与输出

1.空白字符:本身没有显示,但占据一定水平和垂直距离的字符。\t,\n,enter
2.int getchar() putchar();在键盘上的所有输入都首先保存在输入缓存区內,直到我们输入一个回车——输入暂时结束,输入函数到输入缓冲区中读数据。getchar()每次读入任意字符,包括’\n’。getchar()的实现:fgetc(stdin)。
3.getch(),getche();都为非标准函数,不等待用户按回车,getch不回显而getche回显。返回值都为int,与EOF(-1)作比较判断是否结束。

(4)字符串输入输出

1.gets(),puts();gets读入字符串时遇到回车或EOF为字符串的终止符,同时把回车读走。( warning: ‘gets’ is deprecated )
2.char *fgets(char *s, int size, FILE *stream); Never use gets,use fgets instead.

(5)格式化输入输出

1.int scanf(const char *format, …); return the number of input items successfully matched and assigned.
2.格式控制字符串包含三类:1)空白字符 2)非空白字符 3)格式说明符——%[*][width][modifiers]type. ‘*’代表忽略读入的数据。width代表最多读入数据的宽度(列数),modifiers主要有h和l,分别用来输入短整形,长整形和double类型。%hd %ld %lf 分别对应short long double
3.格式字符串中有空白字符,scanf函数会读入并忽略所有stdin的空白字符
4.当格式控制字符串中出现一个非空白字符的时候scanf函数会从stdin中读取下一个字符,然后把读取的字符与非空白字符比较,若一致,会舍弃读入的字符,不一致则函数失败。同时stdin缓冲区的字符串不读入。
5.三种情况认为是数据输入结束:在stdin缓冲区遇到空白符,遇到非法字符输入,达到输出域宽
6.scanf函数必须要改变传入函数实参的值,因此传入变量的地址。
7.当scanf读入一个整数的时候,输入中的空格,回车,tab键会被忽略。
8.与gets不同,scanf会忽略输入字符串前后面的空格,tab和回车等空白字符。
9.scanf函数按照格式控制字符串给定的格式读取,如果读取失败,scanf退出,但是不会从缓冲区读走不匹配数据。
10.the ‘f’ in scanf means format.

(6)清空缓冲区

1.
int a;
do
{
printf(“*\n”);
scanf(“%d”,&a);
while((c=getchar())!=’\n’&&c!=EOF)
;
if(111==a)
{
break;
}
}while(1);

(7)fgets work with sscanf

fgets(line,sizeof(line),stdin)

if(sscanf(line,”%d %s %d”,…)==3)
printf(“…”);
else if……
else ……

(8)格式化输出printf函数

1.printf可以用%f输出float和double两种类型;scanf必须使用%f输入float,%lf输入double。
2.对’*’的解释与scanf不同。scanf中,*表示忽略该处的变量输入,例如:%*2s 将忽略两个字符。而在printf中,*可以代替%后面的数字,如%*d代表从参数表读取*的值。
3.scanf格式控制字符串中不使用’\n’ ‘\n’翻译为回车符发生在编译阶段。但是键盘上敲击与文件中出现的’\n’不会被翻译为回车符
4.printf不会根据格式转换符强转。

第6章 控制结构

(1)跳出嵌套循环

for(i=0;i<10&&flag;i++)
    for(j=0;j<20&&flag;j++)
        if(condition)
            flag=0;

第7章 函数,模块和宏定义

(1)预处理指令

常见为三种,文件包含,条件编译,宏替换;

(2)文件包含

#include

(3)条件编译

#if #else #ifndef #ifdef #endif #undef… 用处:
1.调试语句
#ifdef DEBUG
printf(“we are debugging\n”);
#endif
2.使程序支持跨平台编译
#ifdef linux
# include < ext/hash_map>
#else
# include < hash_map>
#endif
3.防止头文件的重复包含

(4)宏替换#define

/*object-like macro*/
#define <identifier> <replace token list>
/*function-like macro*/
#define <identifier>(<parameter list>)\
            <replacement token list>
eg.
#define PI 3.1415926
#define PERIMETER(r) (2*PI*(r)) 

(5)main函数:

1.关于main()
int main()
int main(void)
int main(int argc,char *argv[])
argc 与 argv 可以换成别的名字。argc代表传入参数的个数,argv为一个指针数组,其中每一个指针指向了传入参数的具体的值。argc[0]志向的是可执行程序的名字。也写作int main(int argc,char** argv)
exit函数与return;

(6)命令行解析:

Linux下提供了getopt函数解析命令行的参数。而windows下对此并不支持。

(7)static 和 const

1.const char* p;和char* const p;前者指针所指数据为常量,后者指针本身为常量。

2.static两个特性:静态,隐藏;
static声明一个静态变量。静态变量和全局变量存储在静态内存区,与栈不同,不会伴随函数的推出而消失。静态区变量和程序的寿命一样长。static类型的变量若未初始化其值为零。定义在一个函数里也不会虽函数退出而消失。典型用法是用来统计函数被调用多少次。
static变量只在定义它的范围内可见。
变量类型 声明位置 存储位置 作用域 作用
static全局变量 语句块之外 静态存储区 整个文件 不允许其他文件访问
static局部变量 语句块中 静态存储区 整个语句块 值在程序执行时一直保持

(8)编译和链接

    #include<stdio.h>
    int foo(int i);
    void main(void)
    {
        foo(5);
    }

编译时不会出错。只在链接时需要把foo的引用链接到真实的foo的实现上回收到链接错误:”error LNK2019……”
编译器——单元编译,每次只编译一个.c文件。

(9)声明和定义

编译器并不申请内存,只是保留一个引用;当执行链接时,把正确的内存地址链接到那个引用上。
声明一个变量要用extern,由此不为其申请内存。注:extern关键字只用于声明数据类型,不用于声明函数。extern int f(); <==> int f();
而定义变量和函数时会为其分配一段内存。
无论是变量还是函数,不分配内存的声明可以声明多次,可以放在头文件中也可以放在源文件中;但是分配内存的声明只能声明一次,且一定要在.c中。

(10)头文件

1.避免头文件的重复包含——避免错误,提高效率
1)
#ifndef _A_H
#define _A_H
… …
#endif
2)
在头文件里只应该包含不申请内存的声明语句。由于C语言编译器的单元编译,在编译下一个.c文件时,编译器不知道已经定义过此变量/函数并分配内存。此后链接时会无法确定链接哪一块。

2.头文件内容
1)用于避免重复包含的#ifndef…#endif
2)宏定义
3)struct,union,enum等类型的typedef类型定义
4)全局变量及函数不申请内存的声明(变量前带extern)

3.一个项目中,最好把所有的声明放到一个统一的global.h文件中而不要重复放在不同的.c中。以免改动。
#ifndef GLOBAL_H
#define GLOBAL_H
#include “other.h”
… …
typedef …
int f(int i);
extern int foo;//全局变量
#endif

(11)C和C++混合项目

1.编译器的工作
编译器编译.cpp文件与.c文件时分别使用C++标准与C标准。而在C++标准编译文件时为了支持函数重载,编译器执行一项name mangle的过程会改变函数的名字,因此在链接器工作时不能把两者的同一函数链接到一起。

2.为了支持混合编程
使用extern”C”关键字。但是由于C不支持extern”C”:
#ifdef __cplusplus
extern “C” {
#endif
Sum(int,int);
#ifdef __cplusplus
}
#endif
由此使.c与.cpp可以包含同一头文件。但是并不在.c和.cpp中使用。且extern只对函数有效。

第8章 库函数

(1)数学相关

绝对值函数: abs,fabs
幂函数: exp,pow
对数函数: log,log10
开平方函数: sqrt
上下界函数: ceil,floor
三角函数: sin,cos,tan 以弧度为参数,45*pi/180==>45度
反三角函数: asin,acos,atan

(2)字符串相关

字符类型判断函数: isalnum,isalpha,islower,isupper,isspace ==>ctype.h
处理函数: String manipulation: strcpy,strcat ==>会改变传入字符串的内容,需保证字符串的足够控件。源地址目的地址不重叠。
为改进引入strncpy,strncat。
String examination: strlen,strcmp ==>

strtok==>char* strtok(char* str,const char* delimiters),tok代表tokenizing,用于将一个字符串按照分割符分割。
1.第二次调用要传递NULL作str。
2.它会修改str内容,把每一个delimiters替换成’\0’。

(3)字符串与数字相互转换

1.atof,atoi,atol==>double,int,long? stdlib.h

2.sprintf(str,”…”,…) ==>> sscanf

(4)时间函数

1.系统时间和日历时间

2.time_t通过typedef定义,为long型。为从1970年1月1日0时0分0秒到此时的秒数,32位机器最多记到2038年1月18日19时14分07秒。

3.time_t time(time_t* timer) time(&now); | now=time(NULL);

4.可用ctime得到时间字符串但是时间固定,可以通过localtime的日历时间或gmtime得到格林威治时间。然后把日历时间传入strftime函数。从日历时间通过mktime也能得到系统时间time_t。

(5)随机数探讨

1.随机函数的实现
unsigned long int next=1;
int rand(void)
{
next=next * 1103515245 + 12345;
return (unsigned int)(next/65536)%32768;
}
void srand(unsigned int seed)
{
next=seed;
}

2.RAND
RAND_MAX rand()返回的随机整数的最大值。

(6)系统相关函数

1.exit()用于终止当前程序的运行。与return相比有几个优点:1)可以在任何一个地方调用 2)调用时可以执行额外操作,如,刷新缓冲区,关闭打开的文件。还可以通过atexit注册一些退出函数,当调用exit函数时这些函数会被执行。

2.system()把传入的字符串参数传送给宿主操作系统。由宿主操作系统的shell执行。

3.signal函数在程序收到制定信号时指定调用的回调函数。

第九章 Array

1.a[],a <==> &a[0],don’t do a++

2.Initialize the array
1) int array[3]={1}; make array[0] = 1,the other element will be 0;
2) memset(array,0,sizeof(array));
3) memcpy(b,a,sizeof(a))

3.Array is passed to a function as a address.if you do not want it be changed use fun(const b_array[]);

4.Do not return a array if you declare it in a function. It’s declared on the stack,will be meaningless after the function is executed.

5.Differences between “debug” and “release”:In debug mode,it will add two additional elements after array to check array bounds.But it will bu useless if array bounds happends after those two elements.

6.Each array has a type.When the type of element is a array.We got a two-dimensional array.To initialize it:
1)int a[2][3]={1,2,3,4,5,6}
2)int a[][3]={{1,2,3},{4,5,6}}; //can not initialize it as int a[2][],the length of the element must be sure.

7.Addressing a two-dimensional array:
1)int a[M][N]; a[m][n] <==> *(a+m*N+n) <==> a[m*N+n];

第十章 指针

(1)内存

1.硬盘保存数据基于物理的磁性,访问靠机械运动;而内存保存数据是基于电信号;
2.内存的基本单位是字节(byte),每一个字节有独一无二的地址。

(2)指针基础

1.char p1,p2 Instead of char p1,p2.
2.*与&相对应。
3.任何指针类型变量都有两个属性:本身保存的地址和指向变量的类型。

(3)void AND NULL

1.void一般应用于两种情况
1)函数没有返回值或参数。
2)void类型的指针只保存一个地址,不包含指向变量的类型信息。任何类型的指针都可以直接赋值给它而无需类型转换。
2.void类型指针进行算数运算和进行取值操作都是不允许的。
3.如果将void类型指针赋给其他类型的指针,则需要进行强制类型转换。使其包含类型(长度)信息。
4.void应用与函数的参数和返回值中以达到泛型的目的,使函数可以处理更多的类型。如malloc返回的就是一个void指针。memcpy和memset也如此。————它操作的对象仅仅是一片内存,不关心这片内存中保存的是什么类型。
void * malloc(size_t size);
void * memcpy(void *dest, const void *src,size_t len);
void * memset(void *buffer,int c,size_t num);
5.NULL描述指针的值。一般被定义为0。也用于返回指针的一些函数中表示调用没有成功。

(4)指针与数组

1.a[i]用在表达式中时,自动转换为指针加偏移量 *(a+i)。i[a]也同样有效。
2.当sizeof 或者取地址运算符&作用于数组变量a的时候,a并不转化为指针常量。除此之外,与指针不同之处在于:
3.不能修改a的地址;
当指针pa指向一个单独的变量b的时候;
如果在一个文件中定义了int a[5],在另外一个文件中却声明为extern int *a;是不正确的。
4.
指针的指针 int **PP <==> 指针的数组 int *pa[5]
数组的指针 int (*ap)[5] <==> 数组的数组 int aa[2][3]
5.
一个XX型的指针指向一个XX型的地址。
XX型数组变量是一个XX型的地址。
6.[]的优先级比高,int *p[3] 会被解释为int (p[3])。故此写成int(*p)[3] 时的(*p)是一个指针,指针指向的类型为int[3]。

(5)C语言的内存映像

1.堆,栈,静态存储区,常量存储区,代码段。
栈中存储函数内的局部变量,函数参数,返回数据等,函数结束后自动释放。
堆中存储malloc分配的内存,用free释放。静态存储区存储全局变量及静态变量。
常量存储区存储字符串常量,与代码段同样是只读的。
2.静态存储区在程序编译的时候已经分配好,在程序的运行期间都存在。

(6)动态内存分配

1.malloc AND calloc #include

(7)动态数组

1.动态一维数组。
2.动态二维数组
用指向数组的指针模拟
int ()p[3] = (int ()[3])calloc(n,sizeof(int)*3);
用指向指针的指针模拟
int p=(int )calloc(n,sizeof(int *));
for(i=0;i

(8)字符串

1.字符串不同的存储位置
char *gp=”hello”; //常量存储区
char ga[]=”hello”; //静态存储区
char *foo()
{
char *p=”hello”; //常量存储区
char a[]=”hello”; //栈
p[0]=’z’; //运行时出错
gp[0]=’z’; //运行时出错
return a; //a在函数结束后释放,故无效。
}

(9)函数和指针

1.接口定义
使用后需要free来清理内存的函数是不合理的。
可以返回传入地址以便用在表达式中。

(10)函数指针

1.int (*pf)(),pf指向一个返回整数的函数。这个函数接受什么参数在声明时并不重要,可以忽略;通过pf() 或 (*pf)() 来调用pf指向的函数。

2.回调函数
eg.《C语言点滴》P220
比较函数comp_double是sort函数通过函数指针“回调”的。调用哪个函数并没有固化在sort函数中,而是运行时根据传入的函数指针动态的决定。
windows操作系统中,利用回调函数来连接多个动作,如移动鼠标和点击鼠标后,系统会回调用户程序中某个特定的函数。

3.C++通过模板来达到泛型目的,它是类型安全的。而P220例是类型不安全的,归结于int (ptr)(const void ,const void*)函数中的void指针丢失了类型信息。

4.函数指针用于定义接口规范。在C++中可用虚拟基类来定义。虚拟基类的物理实现:虚拟基类维护一个vtbl虚拟函数表。在这个表里保存着类中所有对应虚函数的函数指针。所有的虚拟基类的派生类都维护(或者说遵守)这个vtbl的规范。

5.复杂声明:
eg.1 void ( ap[10])(void ()() )
eg.2 int (* frp(int))(char* char*)
eg.3 void (*fp)()

6.复杂声明定义:
eg.1 void(ap[10]) (void ()());
//利用typedef定义
typedef void (*pfv)();
typedef void (*pf_taking_pfv) (pfv);
pf_taking_pfv ap[10];

7.复杂声明用于强制类型转换:
1)如何显示调用地址为0的一个程序
((void ()())0)();
2)

第十一章 结构体

1.自定义数据类型
C语言是一种强类型语言,定义任何一个变量,都需要准确,唯一的指定这个变量的数据类型。唯一的一个泛型类型就是void,只是一个中间类型。
C到C++带来了处理问题角度的转变,把C++看成带class的C是狭隘和片面的。

2.结构体
结构体的读写
1)用fwrite和fread可以整块读写 fwrite(&s,sizeof(s),1,fp);
优点:速度快。
缺点:生成的文件由于有空洞会比较大,不同机器对齐方式可能不同,移植问题。
2)分次读写
结构体遵循单向值传递。但基于效率问题的考虑应避免单向值传递结构体。通常传递的是地址,即函数的型参使用指针。

3.枚举
enum DAY {MON=1,TUE,WED,THU,FRI,SAT,SUN};
只能通过枚举符赋值。

第十二章 文件

(1)文件指针的解释

FILE 是C语言中定义的一个结构类型,定义在stdio.h,用fopen打开一个文件的时候,返回一个FILE*指针变量。每个FILE* 指针变量标识一个特定的磁盘文件,用户不应修改FILE *指针变量所指向的结构类型和内部的所有数据。

    typedef struct
    {
        short level;        /*缓冲区‘满’或‘空’的程度*/
        unsigned flags;     /*文件状态标志*/
        char fd;        /*文件描述符*/
        usigned char hold;  /*如缓冲区不读字符*/
        short bsize;        /*缓冲区的大小*/
        unsigned char *buffer;  /*数据缓冲区的位置*/
        unsigned char *curp;    /*指针当前的指向*/
        unsigned istemp;    /*临时文件指示器*/
        short token;        /*用于有效性检查*/
    }FILE;

路径名:windows用’\’,linux用’/’;
r+与w+:r代表读,w代表写,+代表读写;若文件不存在,r+失败,w+新建文件;若文件存在,r+不清空文件,w+清空文件。
fopen(“file”,”r”);

文件交替读写

    do
    {
        c=fgetc(fp);
        if(islower(c)!=0)
        {
            fseek(fp,-1L,SEEK_CUR);
            fputc(toupper(c),fp);
            fseek(fp,0L,SEEK_CUR);  /*说是不能少,少了没区别*/
        }
    }while(!feof(fp));

“rw”第一个字符死循环
“r+”正常工作
“w+”清空文件

(2)断行标志符

回车 (return) 0D
换行 (line feed) 0A
UNIX/Linux下: “换行”
DOS/Window下: “回车+换行”
Mac下: “回车”
因此,UNIX/Mac下的文本在Windows下打开会变成一行。Windows的文件在UNIX和Mac下打开可能在每行的结尾多出一个符号。
/r 覆盖当前行之前内容;

(3)文本格式和二进制格式

当以文本模式打开文件时,写’\n’到一个文件,或者从一个文件读取换行符,都会进行对应的转换;而以二进制模式b打开的文件不进行这种转换。
由于Linux系统中,’\n’就是0x0A,文本模式与二进制模式是相同意义的。

(4)不同的文件读写函数

按字符读写: fputs,fgets,fputc,fgetc等。
按数据块读写: fread,fwrite。
通常按字符读写是以文本方式打开文件。按数据块写则按二进制方式(b)打开文件。

(5)文件末尾与feof函数

1.从缓冲区读取文件内容的时候,缓冲区中文件的末尾处会添加一个EOF标志。这个EOF标志值只出现在缓冲区中而不出现在硬盘中。
2.如果feof返回真,文件位置指针一定指向末尾的EOF,反之不一定成立。
3.fgetc,fgets,fscanf,fread读取到了EOF符的时候会设置标志位,由此决定feof返回值。
4.几乎所有的读函数当遇到文件末尾或者读取出错时返回相同的值。判断:ferror()返回真,错误;feof()返回真,读到末尾;

        int c;
        while(c=fgetc(fp))
        {
            if(c==EOF)
            {
                if(feof(fp)!=0)
                    break;
                if(ferror(fp)!=0)
                    error_handel;
            }
            else
            {
                balabala;
            }
        }

5.文件操作错误
1)文件不存在
2)文件已存在
3)磁盘满
4)写一个只读文件
5)读一个只写文件
6)设备损坏

第十三章 BUG和错误处理

(1) void assert(int expression);

判断expression的逻辑值,如果其值为假,先向stderr打印一条错误信息,然后调用abort终止程序。
可以在#include< assert.h> 语句前插入#define NDEBUG来禁用或关闭assert();
asser用处:
1.在函数开始处检验传入参数的合理性
2.在库函数调用前检查传入参数的合理性
3.检查变量的合理性或一致性
4.其他的用于验证应该出现的假设的情况

(2)相应函数

fprintf
perror
strerror 输入一个errno值,打印出与之对应的错误原因字符串

(3)errno

无法利用返回值通知错误的情况

        #include<errno.h>
        errno=0;
        lib_function();
        if(errno>0)
            perror("asd");

(4)面向对象的异常处理机制

优点1.把错误处理代码从正常代码中分离出来,使整个代码的逻辑变得清晰。
优点2.异常可以高效率的在调用的堆栈上传递。函数中的异常会自动在调用的堆栈中向上传递,直到在某一个特定的调用层次上被一个对应的catch语句捕获为止。
NOTICE:任何抛出的异常都要处理,否则沿着调用堆栈一直到调用的最上层,然后终止程序。异常底层的实现借助于栈回退(Stack Unwind)技术。
避免使用空的catch语句。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值