读书笔记:C语言程序设计_现代方法

《C程序设计-现代方法》这本C语言书是一本不错的C语言的书。其中的讲解挺有条理,并且每章后面的释疑部分真的解答了我的很多疑问。

前十章转自:http://www.cnblogs.com/xkfz007/archive/2012/02/26/2369030.html
第2章 C语言基本概念
1. 在某些C语言的书中,main函数的结尾使用的是exit(0),而不是return 0,二者是否一样?
    当出现在main函数中时,这两种语句是完全等价的:二者都终止程序执行,并且向操作系统返回0值.
2. float类型的名字是由何而来的?
   float是floating-point的缩写形式.有些语言中称为real类型

第3章 格式化输入/输出
1. 转换说明%i也可以用于读写整数,%i和%d之间的区别?
   在printf格式串中使用时,二者没有区别.但是,在scanf格式串中%d只能与十进制形式的整数相匹配,而%i则可以匹配八进制,十进制和十六进制.
2.printf中显示%的话可以使用%%
3. 如果v有副作用,那么v+=e和v=v+e是不等价的。
   如:
   a[i++]+=2与a[i++]=a[i++]+2
4. C语言从Ken Thompson早期的B语言中继承了++和--操作。Thompson是从BCPL语言进行改进创造了B语言,而他创建++操作符因为在B语言的编译器中++i比i=i+1可以产生更简洁的翻译。

第6章 循环
1. 哪个无限循环格式更可取,while(1)还是for(;;)?
   C程序员传统上喜欢for(;;)的高效性,因为早期的编译器经常强制程序在每次执行while循环体时测试条件1.但是对现代编译器来说,在性能上两种无限循环应该没有差别.
2. 听说程序员应该永不使用continue语句,这种说法对吗?
   continue语句确实很少使用.尽管如此, continue语句有时候还是非常方便的.
第7章 基本类型
1. 浮点常量为什么存储成double格式而不是float格式?
   由于历史的原因,C语言更倾向于使用double类型,float类型则被看成"二等公民".
2. 为什么使用%lf读取double类型的值,而用%f进行显示呢?
   这是一个十分难回答的问题.首先,注意,scanf函数和prinf函数都是不同寻常的函数,因为它们都没有将函数的参数限制为固定数量.scanf函数和printf函数有可变长度的参数列表.当调用带有可变长度参数列表的函数时,编译器会安排float参数自动转换成double类型,其结果是printf函数无法区分float类型和double类型的参数.这解释了在printf函数调用中为何可以用%f既表示float类型又表示double类型的参数.
   另一方面,scanf函数是通过指针指向变量的.%f告诉scanf函数在所传地址位置上存储一个float类型的值,而%lf告诉scanf函数在该地址上存储一个double类型值.这里float和double的区别是非常重要的.如果给出了错误的转换说明,那么scanf函数将可能存储错误的字节数量(没有提到的是,float类型的位模式可能不同于double类型的位模式)

第8章 数组
1. 下面的程序有可能导致无限循环.
  int a[10],i;
  for(i=0;i<=10;i++)
     a[i]=0;
2. 这个数组的初始化合法吗?
   int a[]={4,9,1,8,[0]=5,7};
  合法.工作原理:编译器在处理初始化式列表时,会记录下一个待初始化的数组元素的位置.正常情况下,下一个元素是刚刚被初始化的元素后面那个.但是当列表中出现初始化式时,下一个元素会被强制为指示符对应的元素,即使该元素已经被初始化了.
  该初始化的效果类似:int a[]={5,7,1,8};
  数组长度是4

3. 将一个数组复制到另一个数组中的方法?
  (1) for循环的方式
      for(i=0;i<N;i++)
         a[i]=b[i];
  (2) 利用<string.h>头的函数memcpy.该函数是一个底层函数,它把字节从一个地方简单的复制到另一个地方.为了把数组b复制到数组a中,使用函数memcpy的格式如下:
      memcpy(a,b,sizeof(a));
4.在函数调用f(a,b)中,编译器如何知道逗号是标点符号还是运算符号呢?
   函数调用中的实际参数不能是任意的表达式,而必须是"赋值表达式".在赋值表达式中,不能用逗号作为运算符,除非逗号是在圆括号中.换句话说,在函数调用f(a,
   b)中,逗号是标点符号;而在f((a,b))中,逗号是运算符.

5. 如果几个函数具有相同的返回类型,能否把他们的声明合并?例如void print_pun(void),print_count(int n);合法吗?
   合法的.事实上,C语言甚至允许把函数声明和变量声明合并在一起:
   double x,y,average(double a,double b);
6. 为什么可以留着数组中第一维的参数不进行说明,但是其他维数必须说明呢?
   首先,需要知道C语言是如何传递数组的.在把数组传递给函数时,是把指向数组第一个元素的指针给了函数.
   其次,需要知道取下标运算符是如何工作的.C语言是按照行主序存储数组的,如果要计算下一个元素的指针,必须知道一个元素的大小,所以必须给出其他维的大小,这样才能知道一行的大小.
7. 间接递归:f1调用f2,f2调用f1,这其中涉及到一个地方:提前进行函数的声明.

第10章 程序结构
1. 局部变量
  特性:(1)自动存储期限:局部变量的存储单元是在包含该变量的函数被调用时自动分配的,函数返回时收回分配,所以称这种变量具有自动的存储期限.
       (2) 块作用域
  静态局部变量:
   静态存储期限的变量拥有永久的存储单元,在整个程序执行期间都会保留变量的值.即在程序执行期间它所占据的内存单元是不变的.
   但是它是块作用域,即它相对于其他函数是隐藏的,但是它会为将来同一个函数的再调用保留这些数据.
  形式参数:
  形式参数拥有和局部变量一样的性质,即自动存储期限和块作用域.事实上,形式参数和局部变量的唯一真正的区别是,在每次函数调用时对形式参数自动进行初始化.
2. 外部变量(全局变量)
  特性:(1)静态存储期限:
       (2)文件作用域:从变量被声明的点开始一直到所在的文件的末尾

 
3. 具有静态存储期限的局部变量会对递归函数产生什么影响?

  当函数是递归函数时,每次调用它都会产生其自动变量的新副本.静态变量就不会发生这种情况,相反,所有的递归函数都共享一个静态变量.当函数是递归函数时,每次调用它都会产生其自动变量的新副本.静态变量就不会发生这种情况,相反,所有的递归函数都共享一个静态变量.


第11章指针

1.const 是一个C语言的关键字,它限定一个变量不允许被改变。例如:用一个const变量來初始化数组,ANSI C的编译器会报告一个错误。

const int n = 5;
int a[n];

分析:这个问题讨论的是“常量“与“只读变量“的区别。常量肯定是只读的,例如5,“abc“等,肯定是只读的,因为程序中根本没有地方存放它的值,当然也就不能够去修改它。而“只读变量“则是在内存中开辟一个地方來存放它的值,只不过这个值由编译器限定不允许被修改。C语言关键字const就是用来限定一个变量不允许被改变的修饰符(Qualifier)。上述代码中变量n被修饰为只读变量,而不是常量。而ANSI C规定数组定义时维数必须是“常量“,”只读变量“也是不可以的。(常量不等于不可变的变量,但在标准C++中,这样定义的是一个常量,这种写法是对的)。在ANSI C云烟中用enum类型和#define宏來定义常量。

2.指针总是和地址一样么?

    通常是,但不总是。在一些计算机上,指针可能是“偏移量“而不完全是地址。

3.如果指针可以只想程序中的数据,那么是指针指向程序代码是否可能?

    可能。例如函数指针。

4.是否存在显示指针的值的的方法?

    调用printf函数,在格式串中采用转换%p。


第12章指针和数组

1.C语言支持3种(而且只有3种)格式的指针的算术运算(或者地址算术运算):

  • 指针加上整数。
  • 指针减去整数。
  • 两个指针相减。

    注:只有p只想数组元素时,指针p上的算术运算才会获得有意义的结果。此外,只有在两个指针指向同一个数组时,指针相减才有意义。对于并非指向数组的指针,指针的运算是“未定义的“。这并不意味着不能这样做,知识意味着不能保证会发生什么。

2.可以用关系运算符(<、<=、>、>=)和判等运算符(==和!=)进行指针比较。只有在两个指针指向同一数组时,用关系运算符进行的指针比较才有意义。

3.在标准C中,即使元素a[N]不存在(数组a的下标从0到N-1),但是对它使用取地址运算符是合法的。因为循环不会尝试检查a[N]的值,所以上述方式下用a[N]是非常安全的。

4.用多维数组作为指针。

int a[10], b[10][10];
注: 使用a作为指针指向元素a[0],但b是指向b[0]而不是b[0[0],C语言认为b不是二维数组而是作为一维数组,且这个一维数组的每个元素又是一维数组。在类型项中,a可以用作是int *型的指针,而b用作指针时则是具有int **型的指针

5.i[a]与a[i]是一样的。对于编译器而言i[a]等同于*(i+a),也就是*(a+i)(像普通加法一样,指针加法也是可以交换的)。而*(a+i)也就是a[i]。

6.说明了如何使用指针处理二维数组的行中的元素。用相似的方法处理列中的元素是否可行?

    是可行的,但是不像行处理那样容易。因为数组是按行存储的,而不是按列。下面循环清楚的表明数组a中列i的元素:

int a[NUM_ROWS][NUM_COLS], i, (*p)[NUM_COLS];
for (p = a; p <= &a[NUM_ROW - 1]; p++)
(*P)[i] = 0;
已经声明了p是指向长度为NUM_COLS的数组的指针,且此数组的元素为整型的。在表达式(*p)pNUM_COLS]中要求*p周围有圆括号。 如果没有圆括号,那么编译器将会把p作为指针的数组而不是指向数组的指针來处理了。

第13章字符串

1.根据C语言的标准,当两条或更多挑字符串字面量相连时(仅用空白字符分割),编译器必须把它们合并成单独一条字符串。例:

printf("Put a disk in drive A, then"
          "press any key to continue\n");

2. C语言把字符串字面量作为字符数组來处理。当C语言编译器在程序中遇到长度为n的字符串字面值时,它会为字符串字面量分配长度为n+1的内存空间。

3.C语言允许对指针添加下标,因此可以给字符串字面量添加下标:

char ch;
ch = "abc"[1];
ch的新值将是字母b。

4.允许改变字符串字面量中的字符,但不推荐这么做:

char *p = "abc";
*p = 'b';    /*string literal is now "bbc" */
注:对于一些编译器而言,改变字符串字面量可能会导致程序运行异常。
5.用printf函数和puts函数写字符串
   printf函数会逐个写字符串中的字符,直到遇到空字符才停止.(如果空字符丢失,printf函数会越过字符串的末尾继续写,知道最终在内存的某个地方找到空字符为止).

   puts(str); puts函数只有一个参数,此参数就是需要显示的字符串,参数中没有格式串。在写完字符串后,puts函数总会添加一个额外的换行符,因此显示会移至下一输出的开始。

6.scanf函数和gets函数读字符串

    scanf("%s", str);  在scanf函数调用中,不需要在str前添加运算符&。因为str是数组名,编译器会自动把它当作指针來处理。调用时,scanf函数会跳过空白字符,然后读入字符,并且把读入的字符存储到str中,直到遇到空白字符为止。scanf函数始终会在字符串末尾存储一个空字符。用scanf函数读入字符串永远不会包含空白字符。赢此,scanf函数通常不会读入一整行输入。

    gets(str); gets函数同scanf函数类似,把读入的字符放到数组中,然后存储一个空字符。然而,在其他方面gets函数有些不同于scanf函数:

  • gets函数不会在开始读字符串之前跳过空白字符(scanf函数会跳过)。
  • gets函数会持续读入知道找到换行符才停止(scanf 函数会在任意空白字符处停止)。此外,gets函数会忽略掉换行符,而不会把它存储到数组中,用空白字符代替换行符。

gets函数和puts函数通常比scanf函数和printf函数运行更快。

7.在strcpy(str2, str1)的调用中,strcpy无法检查str1指向的字符串的大小是否真的合适str2指向的数组。如果str1指向更长的字符串,那么结果就无法预测了。(由于strcpy函数会一直复制到str1的第一个空字符为止,所以它会越过str2指向的数组的边界继续复制。无论原来存放在数组后面内存中的是什么,都将被覆盖)。strcat有同样的问题。

8.在strcmp中字符是按ASCII的字符顺序:

  • 所有的大学字母都小于所有的消协字母
  • 数字小于字母
  • 空格符小于所有打印字符。


第14章预处理器

1.预处理指令:大许哦书预处理指令属于下面3中类型:

  • 宏定义。#define指令定义一个宏,#undef指令删除一个宏定义。
  • 文件包含。#include指令导致一个指定文件的内容被包含到程序中。
  • 条件编译。#if、#ifdef、#ifndef、#elif、#else和#endif指令可以跟互编译器可以测试的条件來将一段文本快包含到程序中或排除在程序之外。
  • #error、#line和#pragma指令是更特殊的指令,较少用到。

2.几条应用与所有预处理指令的规则:

  • 指令都以#开始。#符号不需要在一行的行首,只要它之前只有空白字符就行
  • 在指令的符号之间可以插入任意数量的空格或横向制表符。
  • 指令总是在第一个换行符处结束,除非明确知名要继续。如果想在下一行继续指令,必须在当前行的末尾使用\字符。
  • 指令可以出现在程序中的任何地方。
  • 注释可以与指令放在同意行。

3.带参数的宏:格式如下:#define 标识符(x1,x2,…,xn) 替换列表。

    在宏的名字和左括号之间必须没有空格。如果有空格,预处理器会认为是在定义一个简单的宏。

4.宏定义包含两个运算符:#和##。编译器不会识别这两种运算符,相反,它们会在预处理是被执行。

    #运算符将一个宏的参数转换为字符串字面量。它仅允许出现在带参数的宏的替换列表中。

    ##运算符可以将两个记号(例如标识符)“粘“在一起,成为一个记号。如果其中一个操作数是宏参数,“粘合“会在当形式参数被相应的实际参数替换后发生。

5.宏的通用属性

  • 宏的替换列表可以包含另一个宏的调用
  • 预处理器只会替换完整的记号,而不会替换记号的片段。因此,预处理器会忽略嵌在标识符名、字符常量、字符串字面量之中的宏名。
  • 一个宏定义的作用范围通常到出现这个宏的文件末尾。一个定义在函数中的宏并不是仅在函数内起作用,而是作用到文件末尾。
  • 宏不可以被定义两遍,除非信的定义与旧定义是一样的
  • 宏可以使用#undef指令“取消定义“。

6.宏定义中添加圆括号的两条规则

  • 如果宏的替换列表中有运算符,那么始终要将替换列表放在括号中
  • 如果宏有参数,每次参数在替换列表中出现时都要放在圆括号中  

7.在C语言中预定义了一些宏,这些宏主要是提供当前编译的信息。宏__LINE__和__STDC__是整型常量,其他3个宏是字符串字面量。

    __LINE__        被编译的文件中__LINE__所在的行号

    __FILE__        被编译的文件的名字

    __DATE__      编译的日期(格式“Mmm dd yyyy“)

    __TIME__       编译的时间(格式“hh:mm:ss“)

    __STDC__      如果编译器接受标准C,那么值为1


第15章编写大规模程序

1.

int i; /* declares i and defines it as well */
extern int i; /*declares i without defining it */
extern提示编译器变量i是在程序中的其他位置定义的(大多数可能是在不同的源文件中),因此不需要为i分配空间。顺便说一句,extern可以用于所有的类型的变量。在数组的声明中使用extern时,可以忽略数组的长度

2.头文件存在是为了给编译器而不是为连接器提供信息。


第16章结构、联合和枚举

1.结构(structure)是可能具有不同类型的值(成员(member))的集合。

    联合(union)和结构很类似,不同之处在于联合的成员共享同意存储空间。即:联合可以每次存储一个成员,但是无法同时存储全部成员。

    枚举(enumeration)是一种整数类型,它的值由程序员命名。

2.编译器只为联合中最大的成员分配足够的内存空间。联合的成员在这个空间内彼此覆盖。

3.两个或多个枚举常量具有相同的值也是合法的。

4.通常可以选择使用标记或者用typedef來定义一周内跟特殊的结构类型的名字,但是,在结构有一个指向相同结构类型的成员时,要求使用结构标记。没有结构标记,就没有办法声明指向该结构类型的成员


第17章指针的高级应用

1.内存分配函数:这些函数都是声明在<stdlib.h>中的:

  • malloc函数——分配内存块,但是不对内存块进行初始化。
  • calloc函数——分配内存块,并且对内存块进行清除。
  • realloc函数——调整先前分配的内存块。

2.当为申请内存块而调用内存分配函数时,由于函数无法知道计划存储在内存块中的数据是什么类型的,所以不能返回普通的int型指针或char型指针或其他。取而代之的,函数会返回void*型的值。void*型的值是“通用“指针,本质上它知识内存地址

3.C标准列出几条关于realloc函数的规则:

  • 当扩展内存块时,realloc函数不会对添加进内存块的字节进行初始化。
  • 如果realloc函数不能按要求扩大内存块,那么它会返回空指针,并且子原有的内存块中的数据不会发生改变
  • 如果realloc函数调用是以空指针作为第一个实际参数,那么它的行为就像malloc函数一样。
  • 如果realloc函数调用以0作为第二个实际参数,那么它会释放掉内存块。

4.如果无法扩大内存块(因为内存块后边的字节已经用于其他目的),realloc函数会在别处分配新的内存块,然后把旧块中的内容复制到新块中。一旦realloc函数返回,一定要对指向内存块的所有指针进行更新,因为可能realloc函数移动到了其他地方的内存块

5.free(p)函数会释放p指向的内存块,但是不会改变p本身。

6.当函数名后边没跟着圆括号时,C语言编译器会产生指向函数的指针來代替产生函数调用的代码。


第18章声明

1.声明说明符分为以下3大类:

  • 存储类型。存储类型一共有4种:auto、static、extern和register。 在声明中最多可以出现一种存储类型。如果表示存储类型,则必须把它放在声明中的首要位置。
  • 类型限定符。只有两种类型限定符。const和volatile。声明可以指明一个限定符、两个都有或者一个也没有。
  • 类型说明符。关键字void、char、short、int、long、float、double、signed和unsigned全部都是类型说明符。这些单词出现的顺序不是问题。类型说明符也包括结构、联合和枚举的说明。用typedef创建的类型名也是类型说明符。

2.C程序中的每个变量都具有3个性质:

  • 存储期限。变量的存储期限决定了为变量预留和释放内存的时间。具有自动存储期限的变量在所属块被执行时获得内存单元,并在块终止时释放内存单元,从而会导致变量失去值。具有静态存储期限的变量在程序运行期间占有同样的存储单元,也就是可以允许变量无限期地保留它的值。
  • 作用域。变量的作用域是指引用变量的那部分程序文本。变量可以有块作用域(变量从声明的地方一直到闭合块的末尾都是可见的)或者文件作用域(变量从声明的地方一直到闭合文件的末尾都是可见的)。
  • 链接。变量的链接确定了程序的不哦哪个部分可以共享此变量的范围。具有外部链接的变量可以被程序中的几个(或许全部)文件共享。具有内部链接的变量只能属于单独一个文件,但是此文件中的函数可以共享这个变量)。无链接的变量属于单独一个函数,而且根本不能被共享。

3.变量的默认存储期限、作用域和链接都依赖于变量声明的位置:

  • 在块内部(包括函数体)声明的变量具有自动存储期限、块作用域,并且无连接。
  • 在程序的最外层,任意块外部声明的变量具有静态存储期限、文件作用域和外部链接。

4.auto存储类型支队属于块的变量有效。auto类型的变量具有自动存储期限、块的作用域,并且无链接。

5.static存储类型可以用于全部变量,而不需考虑变量声明所在的位置。但是块外部声明的变量和块内部声明的变量会有不同的效果。当用在块外部时,单词static说明变量具有内部链接。当用在块内部时,static把变量的存储期限从自动变成了静态的

6.extern存储类型使几个源文件可以共享一个变量。extern声明中的变量始终具有静态存储期限。变量的作用域依赖于声明的位置。如果声明在块内部,那么变量具有块作用域;否则,变量具有文件作用域。

7.变量在程序中可以有多次声明,但只能有一次定义

8.声明变量具有register存储类型就要编译器把变量存储在寄存器中,而不是像其他变量一样保留在内存中。register存储类型只对声明在块内的变量有效。register类型的变量具有自动存储期限、块的作用域,并且无链接。由于寄存器没有地址,所以对register类型变量使用取地址与算符&是非法的

9.函数的存储类型:和变量的声明一样,函数的声明(和定义)可以包含存储类型,但是选项只有extern和static。在函数声明开始处的单词extern说明函数具有外部链接,也就是允许其他文件调用此函数。static说明内部链接,也就是说只能在定义函数的文件内部调用此函数。如果不指明函数的存储类型,那么会假设函数具有外部链接。

10.解释复杂声明有如下两条规则:

  • 始终从内往外读声明符。
  • 在作选择时,始终先是[]和()后市*。

例如:
int *(*x[10])(void);

11.C语言允许在声明变量时为它们指定初始值。为了初始化变量,可以在声明符的后边书写符号=,然后再跟上初始化式。( 不要把声明中的符号=和复制运算符相混淆;初始化和复制不一样)

12.空值初始化式的额外规则:

  • 具有静态存储期限的变量的初始化式必须是常量。
  • 如果变量具有自动存储期限,那么它的初始化式不需要是常量。
  • 用大括号闭合的数组、结构或联合的初始化式必须只能包含常量表达式,不允许有变量或函数调用。
  • 针对自动类型的结构或联合,它们的初始化

    式可以是另外一个结构或联合。

13.变量的初始化值依赖于变量的存储期限

  • 具有自动存储期限的变量没有默认的初始值。
  • 具有静态存储期限的变量默认情况下的值为零。用calloc分配的内存是简单的给字节的位置零,而静态变量不同于此,它是基于类型的正确初始化:即整形变量初始化为0,浮点变量初始化为0.0,而指针则初始化为空指针。

14.C语言规定对编译器而言每个数组的长度都必须是已知的。


第20章低级程序设计

1.移位运算符:

    i<<j的值是将i中的位左移j位后的结果。每次从i的最左端溢出一位,在i的最右端补一个0位。i>>j的值是将i中的位右移j位后的结果。如果i是无符号数或非负值,则需要在i的左端补0.如果i是负值,其结果是由实现定义的。

2.可移植性技巧:为了更好的保留可移植性,最好仅对无符号数进行移位运算。

3.按位运算符

符号含义优先级
按位求反最高级
&按位与 
^按位异或 
|按位或最低级
4.可移植性技巧:将所有的位于声明为unsigned int或signed int。例

struct file_date {
    unsigned int day:5;
    unsigned int month:4;
    unsigned int year:7;
};

5.由于通常意义上讲位域没有地址,C语言不允许将&运算符用于位域。

6.长度为0的位域是给编译器一个信号,告诉编译器将下一个位域放在一个存储单元的起始位置。


第22章输入/输出

1.C程序中的流的访问是通过文件指针(file pointer)实现的。此指针拥有的类型为FILE*(在<stdio.h>中定义了FILE的类型)。

2.在DOS操作系统中,文本文件和二进制文件之间存在两方面的差异:

  • 行的结尾。当文本文件中写入换行符时,此换行符会扩展成一对字符,即回行符和跟随的回车符。与之对应的转换发生在输入过程中。然而,把换行符写入二进制文件中字符就是单独一个字符(换行符)。
  • 文件末尾。在文本文件中把字符Ctrl+Z(\xla)设定为文本的结束标记。而在二进制文件中字符Ctrl+Z没有特别的含义,处理它就像其他任何字符一样。

   与此相反的,UNIX操作系统对文本文件和二进制文件不进行区分。两者会以相同的方式进行存储。一个UNIX文本文件在每行的结尾只有单独一个换行符,而且没有特殊字符用来标记文件末尾。

3.当编写用来读或写文件的程序时,需要考虑是文本文件还是二进制文件。在无法确定文件是文本形式还是二进制形式时,安全的做法是把文件设定为二进制文件。

4.打开文件

FILE *fopen(const char *filename, const char *mode);
永远不能假设可以打开文件。为了确保不会返回空指针,需要始终测试fopen函数的返回值。

用于文本文件的模式字符串
字符串含义
“r“打开文件用于读
“w“打开文件用于写(文件不需要存在)
“a“打开文件用于追加(文件不需要存在)
“r+“打开文件用于读和写,从文件头开始
“w+“打开文件用于读和写(如果文件存在就截去)
"a+"打开文件用于读和写(如果文件存在就追加)


用于二进制文件的模式字符串
字符串含义
”rb“打开文件用于读
”wb“打开文件用于写(文件不需要存在)
“ab“打开文件用于追加(文件不需要存在)
”r+b“或者“”rb+“打开文件用于读和写,从文件头开始
”w+b“或者“wb+“打开文件用于读和写(如果文件存在就截去)
”a+b"或者“ab+“

打开文件用于读和写(如果文件存在就追加)

5.关闭文件

int fclose(FILE *stream);
注:如果成功关闭了文件,那么fclose函数会返回零。否则,它将会返回错误代码EOF。

6.按照C程序员的编写习惯,通常不会把fopen函数的调用和fp的声明组合在一起使用。

7.为流附加文件

FIEL *freopen(const char *filename, const char *mode, FIEL*stream);
注:freopen 函数为已经打开的流附加上一个不同的文件。freopen 函数最常见的应用是把文件和其中一个标准流相关联,这些标准流包括:stdin、stdout或stderr。 freopen 函数通常返回的值是它的第三个实际参数(一个文件指针)。如果无法打开新的文件,那么freopen函数会返回空指针。(如果无法关闭旧的文件,那么freopen函数会忽略掉错误)。

8.文件缓冲

int fflush(FILE *stream);
void setbuf(FILE *stream, char *buffer);
void setvbuf(FILE *stream, char *buffer, int mode, size_t size);

9.其他文件操作

int remove(const char *filename);
int rename(const char *old, const char *new);
注:remove函数和rename函数对文件名而不是文件指针进行处理。如果调用成功,两个函数都返回零。否则,都返回非零值。

要确信已经关闭了要移除的文件。如果打开了要换名的文件,那么一定要确保在调用rename函数之前此文件是关闭的。如果文件是打开的,则无法对文件进行换名。

10....printf类函数

int fprintf(FILE *stream, const char *format, ...);
int printf(const char *format, ...);
注:这两个函数的返回值是写入的字符数。若出错则返回一个负值。

11....scanf类函数

int fscanf(FILE *stream, const char *format, ...);
int scanf(const char *format, ...);
注:如果在能读取任何数据项之前发生输入失败,那么会返回EOF。

12.检测文件末尾和错误条件

void clearerr(FILE *stream);
int feof(FILE *stream);
int ferror(FILE *stream);
13.字符的输入/输出

注:这些函数用于文本流和二进制流是等效的。这些函数把字符作为int型而非char型來处理。这样做的原因之一就是由于输入函数是通过返回EOF來说明一个文件末尾(或错误)情况的,而EOF又是一个负的整型常量。

输出函数
int fputc(int c, FILE *stream);
int putc(int c, FILE *stream);
int putchar(int c);
虽然putc函数和fputc函数做的工作相同,但是putc函数经常作为宏來实现,而fputc函数则作为函数使用。putchar函数通常也作为宏來使用。

如果出现错误,那么上述这三中函数都会为流设置错误指示器并且返回EOF。否则,它们哦读会返回写入的字符。

输入函数
int fgetc(FILE *stream);
int getc(FILE *stream);
int getchar(void);
int ungetc(int c, FILE *stream);
注:当对文件进行读取时,始终要把fgetc函数、getc函数或getchar函数的返回值存储在int型的变量中,而不是char型的变量中。把char型变量与EOF进行比较可能会产生错误的结果。

14.行的输入/输出

输出函数
int fputs(const char *s, FILE *stream);
int puts(const char *s);
注:在雪乳字符串中的字符以后,puts函数总会添加一个换行符。而fputs函数不会自己写入换行符。当出现错误时,上面这两种函数都会返回EOF,否则,他们都返回一个非负的书。

输入函数
char *fgets(char *s, int n, FILE *stream);
char *gets(char *s);
15.块的输入/输出

size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream);

16.文件的定位

int fgetpos(FILE *stream, fpos_t *position);
int fseek(FILE *stream, long int offset, int origin);
int fsetpos(FILE *stream, const fpos_t *pos);
long int ftell(FILE *stream);
void rewind(FILE *stream);


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值