【小黑chenchen】:7预处理和文件操作

发布于:2022-11-27 ⋅ 阅读:(322) ⋅ 点赞:(0)

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

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

目录

宏定义:

常量的定义:

文件包含:

C程序的分文件编写:

文件操作:

什么是文件?

文件的概念:

指向文件的指针:

内存和外存的区别:

文件指针的定义:

文件的打开和关闭:

文件操作函数:

条件编译:


宏定义:

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

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

宏定义关键字:#define

1.无参宏定义

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

  例如:

#include<stdio.h>

#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<stdio.h>

#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;

     报错:表达式必须是可修改的左值

文件包含:

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

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

■.文件包含的基本格式

#include<系统头文件>

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

#include"自定义头文件"

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

■.头文件的重复包含

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

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

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

  例如:

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

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

  通用方式:

#ifndef 该头文件名

#define 该头文件名

......

#endif

C程序的分文件编写:

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

 即,

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

■.分文件编写的好处

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

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

文件操作:

什么是文件?

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

(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<stdio.h>

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,

   用来指向FILE中的数据,

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

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

   即,

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

   

文件的打开和关闭:

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

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

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

基本格式:

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

文件的打开方式与含义:

 例如:

int main()
{
    FILE *fp;

    fp = fopen("test.txt","r");

    fclose(fp);

    return 0;
}
打开一个文件时,会通知编译器一下三个信息:

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

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

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

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

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

基本格式:

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

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

文件操作函数:

文件操作函数都包含在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向后移动到离当前文件位置100个字节处
fseek(fp,-10,2);
将文件指针fp向前移动到离文件末尾100个字节处

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

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

条件编译:

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

可节省编译时间。

■.#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;
#els
int c=100;
#endif

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

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

本文含有隐藏内容,请 开通VIP 后查看

网站公告

今日签到

点亮在社区的每一天
去签到