【黄色手套22】7话:预处理和文件操作

预处理是在编译前所做的工作,编译器自动调用预处理程序对源码中以 ‘#’ 开头的预处理部分进行处理;处理完毕后,进入源码的编译阶段。

宏定义、文件包含、条件编译三者都属于预处理部分。

-------------------

目录

宏定义:

常量的定义:

文件操作:

什么是文件?

文件的概念:

指向文件的指针:

内存和外存的区别:

文件指针的定义:

文件的打开和关闭:

文件操作函数:

文件包含:

C程序的分文件编写:

条件编译:


宏定义:

宏定义,又称宏替换,自定义一个宏名(符合标识符的命名规则)
用来替换任意数据、标识符或者表达式。

即,使用某些定义的标识符,在编译之前对源代码进行替换处理

1. 无参宏定义

    宏定义关键字:#define

定义无参宏的基本格式:#define 宏名 宏替换

    // 末尾无分号

    // 可以没有宏名

例如:

#include

#define I int              //没有分号
#define Pi 3.1415926
#define N 10
#define M (10+20)
#define Enter putchar('\n')

int main()
{
	I a=1;
	printf("%d\n", a);     // 输出1
    printf("%d\n",N*M);    // 输出300
    Enter;
	return 0;
}

■.  不能给宏定义的常量进行赋值操作:

     如:N=6;× (报错:表达式必须是可修改的左值)

2. 带参宏定义

定义带参宏的基本格式:#define 宏名(参数列表) 宏替换

  ■. 带参宏可以像函数一样调用:

#include

#define MAX(x,y) x>y?x:y
#define ADD(x,y) (x+y)
#define Cir_C(r) 2*Pi*r    // 求圆的周长
#define Cir_S(r) Pi*r*r    // 求圆的面积

int mian()
{
    printf("%d\n", MAX(10, 20));  // 输出20
    printf("%d\n", ADD(20, 30)*ADD(30, 30));  // 输出3000 
	printf("%f\n", Cir_C(4));      
	printf("%f\n", Cir_S(4));
    return 0;
}

  ■. 宏定义是一种替换操作,其在替换完成前并不会计算。

  ■. 宏定义尽量用大写,使其在程序中容易辨别区分。

  ■. typedef只给数据类型取别名(后期处理)

      define什么都可以(预先处理)

常量的定义:

关键字const

定义常量的基本格式:

const 数据类型 常量名 = 常量值;

 定义成常量后,其值不可被改变:

 如,const int a=10;

        a=100;(报错:表达式必须是可修改的左值)

___________

文件操作:

什么是文件?

文件有不同的类型,在程序设计中,主要有两种文件。

(1)程序文件:

                包括源程序文件(.c)、目标文件(.obj)、可执行文件(.exe)等。

                这一类型的文件主要用于存储程序代码。

(2)数据文件:

                此文件的内容不是程序,而是程序运行时读写的数据。

                比如程序运行过程中输出到磁盘或其他设备上的数据,或在程序运行过程中供程序读                      取的数据。

■.C语言的文件操作主要是对数据文件的操作;

    之前,程序中所处理的数据输入和输出都是以终端为对象的,是从键盘输入数据,运行结果输出到终端显示器上;

    实际上,我们可以将一些数据(程序运行的最终结果或中间数据)保存起来,方便以后需要时再调用;而这时,就需要用到磁盘文件。

   

文件的概念:

(1)文件名:

                每一个文件都需要一个唯一的文件标识,以便用户使用。

                文件标识也称为文件名,它由三部分组成:

                其一是文件路径----表示文件在外存设备中的存储位置,文件路径是唯一标识文件在外存中的位置;

                其二是文件名主干----表示文件的名字,可由用户自定义,命名规则遵循标识符的命名规     则;

                其三是文件后缀----表示文件的性质,也称为文件的格式,用于描述文件的类型。

(2)文件的分类:

                根据数据的组成形式,数据文件可分为ASCII文件和二进制文件。

                数据在内存中是以二进制形式存储的,如果不加转换输出到外存中,就是二进制文件。

                     (可以认为它是存储在内存的数据的映像,所以又称为映像文件)

                如果要求在外存上以ASCII码形式存储,就需要在存储前进行转换。

                     (每一个字节存放一个字符的ASCII码,ASCII文件又称为文本文件)

(3)文件存储方式的区别:

                一个数据在磁盘上存储,字符一律以ASCII形式存储,数值型数据可以兼用二进制形式存储(如,整数10,000,用ASCII形式存储在磁盘上占五个字节(一个字符一个字节);用二进制行书存储在磁盘上只占四个字节(00000000 00000000 00100111 00010000))。

                用ASCII形式存储时字符与字节一一对应,一个字符一个字节,便于逐个处理,但占用存储空间较多,而且处理时要花费转换时间(ASCII码与二进制的转换)。

                用二进制形式存储就相当于把内存中的数据内容直接存储在磁盘上,由于不需要转换,所以二进制文件便于计算机处理。

______________

指向文件的指针:

        C语言要想操作内存,需要用到各种数据类型的指针。

      (如,int *p;char *p;float *p等)

  例如,整型数据类型的指针:

#include
void swap(int *x, int *y)
{
	*x = *x + *y;
	*y = *x - *y;
	*x = *x - *y;
}

int main()
{
	int a = 5, b = 6;
	swap(&a,&b);
	printf("%d %d\n",a,b);     //输出 6 5
	return 0;
}

        而文件也是需要调用到内存中才能够使用,

        所以就相应地需要用到“指向文件的指针”,

        即“文件类型的指针”,简称“文件指针”,(如,FILE *fp)

     

         每一个被使用的文件都在内存中开辟一个相应的文件信息区,用来存放文件的相关信息

       (如,文件的名字、文件的状态、文件的位置等),

     这些信息保存在一个结构体变量中,此结构体类型是由系统自动声明的,取别名为FILE

         其被包含在stdio.h头文件中。

       

内存和外存的区别:

内存和外存是两个概念

        CPU能直接访问的存储器称之为内存储器(内存),它包括cache和主存器;

        CPU不能直接访问的存储器称为外存储器(外存);

        CPU通过内存储器调入外存储器上的信息来间接访问外存储器;

        为什么CPU要有内外之分呢?保障CPU自身的高运行效率。

文件指针的定义:

■. 文件的结构体类型别名为 FLIE

    文件类型在stdio.h头文件中已有声明,所以我们不需要另外单独声明,直接使用即可。

■. 文件类型变量的定义基本格式:

FILE 文件变量名;

   例如:

FLIE f1;

    定义了一个结构体变量f1,

    f1中存放一个文件的相关信息,

    这些信息在打开文件时由系统根据文件的实际情况自动放入。

    然而,

★ 我们一般不用文件变量来访问文件,而是使用文件指针来访问文件。

■. 文件指针的定义基本格式:

FILE *fp;

   定义了一个文件类型指针fp,

   用来指向FILE中的数据,

   fp指向某一文件在内存中的文件信息区(结构体变量),

   通过此文件信息区能够访问此文件,

   即,

  ★ 文件指针变量fp可以找到并操作其指向的文件。   

   

________________

文件的打开和关闭:

fopen() 和fclose() 都包含在stdio.h头文件中

■. 文件打开函数:fopen()

fopen()函数返回的是该文件的起始地址,我们通常将其返回值赋值给一个文件指针,用文件指针指向此文件的地址。

基本格式:

文件指针 = fopen("文件名","文件打开方式");

文件的打开方式与含义:

【黄色手套22】7话:预处理和文件操作_第1张图片

 例如:

int main()
{
    FILE *fp;
    fp = fopen("test.txt","w+");
    fclose(fp);
    return 0;
}

    打开一个文件时,会通知编译器以下三个信息:

    打开的文件名称、文件的打开方式、使用哪个文件指针指向被打开的文件。

■. 文件关闭函数:fclose()

    在使用完一个文件之后,为了防止它被误用,应该关闭它。

    “关闭”就是撤销文件信息区和文件缓冲区,使指针不再指向此文件,即无法再操作此文件。

     除非重新打开此文件,使指针指向此文件。

基本格式:

fclose(文件指针);

   养成在每次程序终止之前关闭所有打开的文件的良好习惯。

   当fclose()函数成功关闭文件时,返回0;否则返回EOF(-1)。

   最后,将指针指向空 :p=NULL;


_____________  

文件操作函数:

文件操作函数都包含在stdio.h头文件里。

文件操作函数
fgetc()
fputc()
fgets()
fputs()

fscanf()

fprintf()
fread()
fwrite()
rewind()
fseek()

 ■. 文件操作流程:

     定义文件指针 -》打开文件 -》操作文件 -》关闭文件

     文件打开完成后就可以对其进行读写操作,即可使用一些常用的文件操作函数。

■. 字符输入和输出函数:

①使用字符读取函数fgetc()从文件读取一个字符

char ch;
ch = fgetc(fp);
putchar(ch);
putchar('\n');

  从文件指针fp指向的位置读取一个字符存入字符变量ch中,

  读取成功返回所读的字符,失败则返回为文件结束标志EOF(-1)。

②使用字符写入函数fputc()从文件写入一个字符:

   基本格式:

 fputc('字符',文件指针);

■. 字符串输入和输出函数:

①字符串读取函数fgets()

基本格式:

fgets(字符数组,字符个数,文件指针);

例如:

char str1[10]="";
fgets(str1,3,fp);
puts(str1);

   从文件指针fp指向的位置读取一个长度为2的字符串

 (最后一位赋值为'\0',用作字符串结束标志),

   存放在字符数组str1中,读取成功返回地址str1,失败则返回NULL。

②字符串写入函数fputs()

   基本格式:

 fputs("字符串",文件指针);

  例如:

  fputs("123\n",fp);       

■. 格式化写入和读取函数:

fprintf() 基本格式:

 fprintf(文件指针,"格式化占位符",写入列表);

  例如:

fprintf(fp,"%s,%d,%c\n",str1,x,y);
将变量以相应的格式写入fp指向的位置

  fscanf()基本格式:

fscanf(文件指针,"%s,%d,%c,%f",str2,x,y,z);
从fp指向的位置“读”相应类型的数据,“取”到相应的变量中

   fscanf()函数遇空格、回车、tab键停住  

   读取和写入的格式应该保持一致

■.  以二进制形式写入和读取数据:

fread()函数:

fread(buffer,size,count,fp);
buffer是一个地址(数组),用于存储从文件读取出来的数据,
size为需要读取的字节单位大小,
count为需要取出的数据项的个数(每个数据项的大小为size)。

fwrite()函数:

fwrite(buffer,size,count,fp);
从fp指向的文件中位置读入count个size大小的数据,放入到数组butter中

随机读写文件:

(1)文件指针位置标记及定位

         文件指针打开文件后默认指向文件的开头,也就是第一个数据的位置,

         此后每读取或写入一次,文件指针自动向后移动到与数据大小相对应的文件位置。

(2)强制使文件指针指向文件开头

         使用rewind()函数强制使文件指针fp指向文件开头的位置:

 rewind(fp);

(3)使文件指针指向文件中任意位置

        使用fseek()函数使文件指针指向文件中任意位置

 fseek(fp,位移量,起始点);

        起始点用0、1、2代替表示,0为文件开头位置,2为文件末尾位置,1为文件当前位置;

        位移量是指以起始点为基点,向前(-)/后(+)移动的字节数,位移量为long型数据:

fseek(fp,100,0);
将文件指针fp向后移动到离文件开头100个字节处
fseek(fp,50,1);
将文件指针fp向后移动到离当前文件位置50个字节处
fseek(fp,-10,2);
将文件指针fp向前移动到离文件末尾100个字节处

       fseek()函数一般用于二进制文件。

       fseek()和rewind()函数可以实现文件的随机读写。

____________

 文件包含:

如果,我们想要用库函数就需要包含其头文件,也就是文件包含,

当然,我们也可以编写自定义头文件,包含自己编写的头文件。

■. 文件包含的基本格式

#include<系统头文件>

  包含系统头文件用 < > ,只会在系统头文件中找

#include"自定义头文件"

  包含自定义头文件用 " " ,会先在自定义头文件中找,找不到再到系统头文件中找

■. 头文件的重复包含

  --文件包含允许嵌套,即在一个(被包含)文件中可以包含其他文件。

  --头文件的嵌套包含可能会引起头文件的重复包含,从而出现函数和变量的重定义问题,

     所以需要避免头文件重复包含,某些宏定义语句可以防止头文件重复包含。

  例如:

  #pragma once  
  //  防止头文件重复包含,不让文件的内容被包含两次,在头文件最前面添加

  #pragma once 是独有的,有使用平台(如vs)的限制,其他平台可能不存在

  通用方式:

#ifndef 该头文件名

#define 该头文件名

......

#endif

C程序的分文件编写:

多文件编程就是把多个头文件(.h文件)和源文件(.c文件)组合在一起构成一个程序。
多文件编程一般用来开发中大型项目。
  
可以用头文件封装函数的声明,在源文件里完成函数功能的定义

 即,C语言头文件中只写函数声明和数据类型的声明,不在头文件中写函数的定义和变量的定义。

■. 分文件编写的好处

  1.代码的复用性:代码可以重复使用

  2.代码的封装性:保护代码不被他人盗取篡改

___________

条件编译:

在预处理阶段,编译之前,根据不同的条件来编译不同的代码段,

可节省编译时间。

■. #if......#else的使用

  #define M 1
  #if M
  int a=10;
  #else
  int a=100;
  #endif

■. #ifdef.....#endif的使用

#define B   //宏定义可以只有宏名没有宏替换
#ifdef B
int b=10;
#else
int b=100;
#endif

■. #ifndef......#endif的使用

#define C
#ifndef C
int c=10;
#else
int c=100;
#endif

#ifndef......#endif可以用来处理头文件重复包含问题

#ifndef xxxxx
#define xxxxx
.
.
.
.
.
.
#endif

________#

你可能感兴趣的:(1:C语言,c语言)