05——预处理........标准输入输出

第一部分:预处理命令
预处理命令是C语言编译系统的一个组成部分,是在编译前由预处理程序对源文件的预处理文件进行加工
预处理是在C语言编译的4个阶段(预处理、编译、汇编、链接)的第一个阶段。C语言的预处理功能有以下3种:
1、宏定义
2、文件包含
3、条件编译
分别用宏定义命令、文件包含命令、条件编译命令实现。为了与其他的C语句区分,预处理命令以符号#开头

1、宏定义
1)不带参数宏定义
用法:
#define 宏名 宏值
说明:
在之前我们已经使用过不带参数的宏定义,如:
#define PI 3.141592
它的作用是使用标识符PI来代替数字3.141592。在预处理阶段,将该程序中所有出现的PI(宏名)全部替换成3.141592(宏值)。
将宏名替换成宏值的过程我们称之为“宏展开”
注意:
⒈宏名一般使用大写字母表示以便与变量名区别
⒉使用宏名来代替宏值可以减少一个程序中重复书写某些值的操作的工作量,同时也避免了写错的可能性。同时使用宏名来代替宏值能够将宏值赋予一定意义,增强代码的可读性
⒊宏定义是简单的替换,不做语法正确性检查。语法正确性检查是下个阶段(编译)所做的工作
⒋宏定义不是C语句,因此不要在末尾加分号;
⒌通常#define写在文件开头,函数之前,这样宏的使用范围是全文件有效。若要指定宏的有效范围,则可使用#undef命令来终止宏的作用域。
#define G 9.8
int main()
{
}
#undef G
int f1()
{
}
则宏的使用范围是#undef G之前
⒍在进行宏定义时,可以引用已定义的宏名进行层层置换。如:
#define R 3
#define PI 3.141592
#define CIRCLE 2*PI*R
#define AREA PI*R*R

2)带参数宏定义
带参数的宏定义不是简单的用字符串去代替标识符,还要进行参数替换。
用法:
#define 宏名(参数列表) 字符串
说明:
带参数的宏定义是这样展开置换的:如果要替换的字符串中有宏名中的参数,则将相应的参数代替宏名的参数列表中的参数,如果宏定义中的字符串不是参数字符(如a*b中的*号)则保留
示例:
#include<stdio.h>
#define PI 3.141592
#define S(r) PI*r*r
int main()
{
    float a = 3.6;
    float area = S(a);
    printf("%f\n",area);
    return 0;
}
float area = S(a);在宏展开后变成了:
float area = 3.141592*a*a;
注意:
⒈对带参数的宏的展开仅仅是进行简单的字符串替换。如果有多个参数,推荐在每个参数都加上()以防止出现错误结果
示例:
#include<stdio.h>
#define FUN1(a,b) a * b
#define FUN2(a,b) (a)*(b)
int main()
{
    int a=2;
    int b=3;
    printf("%d\n",FUN1(a+b,b+a));
    printf("%d\n",FUN2(a+b,b+a));
    return 0;
}
FUN1与FUN2的宏代替结果是不一样的。
⒉在宏定义的时候,在宏名与参数列表之间不要加空格,否则空格后的字符都会作为替代字符串的一部分。如:
#define S (r) PI*r*r//注意S与(r)之间有空格
则在
area = S(a);
会被展开成:
area = (r) PI*r*r(a);
这显然是不对的。

2、文件包含
文件包含是指将其他文件的内容全部包括进来,即将另外的文件内容包含到本文件中。
用法:
#include <文件名>

#include "文件名"
使用<>和""的区别:使用尖括号时,系统到存放C库函数的头文件的目录中去找要包含的文件,这称为标准方式。使用引号时,系统优先在用户当前目录下查找要包含的文件,若找不到,再按标准方式查找。
一般来说,包括系统库函数使用<>以节省时间,包括用户自定义的文件使用""
注意:
⒈一个#include命令只能包含一个文件,如若要包含多个文件需使用多个#include命令
⒉被包含文件与其所在文件在预处理后已变成同一个文件。因此被包含文件中的全局静态变量在所在文件中也同样有效,不必再次使用extern声明

3、条件编译
一般情况下,源程序中所有的文件都要参加编译。但有时只需要程序的一部分代码进行编译,这时就要指定编译条件,这就是条件编译
条件编译有以下几种形式:
用法1:
#ifdef 标识符
程序段1
#else
程序段2
#endif
说明:当定义过标识符,则编译程序段1,否则编译程序段2。#else部分可以省略,即:
#ifdef
程序段1
#endif

用法2:
#ifndef 标识符
程序段1
#else
程序段2
#endif
说明:与上一种类似,只不过将ifdef换成ifndef。当未定义过标识符,则编译程序段1,否则编译程序段2。#else部分可以省略

用法3:
#if 表达式
程序段1
#else
程序段2
#endif
说明:当指定的表达式是真(非零)的时候,编译程序段1,否则编译程序段2。#else部分可以省略

第二部分:标准输入输出
所谓的输入输出是针对计算机为主体而言的。计算机向外部设备(显示器、打印机等)输出数据称为输出,输入设备(如鼠标键盘等)向计算机输入数据称为输入。
C语言本身不提供输入输出语句,输入输出操作是C函数库中的函数来实现的。诸如printf、scanf等不是C语言的关键字,而是函数名
C语言函数库提供一批“标准输入输出函数”,它是以标准输入输出设备作为输入输出对象。其中有:putchar()、getchar()、printf()、scanf()、puts()、gets()。在这里主要讲解前4个函数,后2个函数在讲解字符串之后再深入讲解。
在使用C库函数的时候,需要先将有关头文件包含进来,标准输入输出的头文件:
#include<stdio.h>
stdio是standard input&output的缩写,.h是表示这是个头文件(head)

格式化输入输出:是指在用户控制下的各种按照预指定格式输入/输出数据。其中的格式控制符指定了输入/输出设备的数量和格式
格式化输入输出有printf()和scanf()两个函数
有关printf()与scanf()的格式控制详见PPT上的表格
1、printf()
函数原型:
int printf("格式控制",参数列表……);
返回值:正确:输出的字符数量;错误:返回EOF(end of file)
说明:
1)格式控制:是用""引起来的字符串,包括
①格式说明:由"%"和格式字符组成的(如%d、%f等)。它的作用是把输出的数据按指定的格式输出。格式说明总是由%开头的
②普通字符:需要原样输出的字符
2)参数列表:一个参数需要一个格式说明,参数列表与格式说明需要在数量、类型、顺序上相匹配
3)printf()用于在标准输出设备上显示数据
4)printf()的宽度修饰符:
宽度修饰符用于为输出的数据(整型、浮点型、字符型、字符串等)设置输出的宽度。如果输出数据不足宽度则在左(或右)补齐(空格或0等),如果输出数据超过宽度则原样输出。
用法:整型:printf("%md",整型变量);
①m是正整数,则输出宽度是m,数据靠右,不足的在左侧补空格
②m是负整数,则输出宽度是m,数据靠左,不足的在右侧补空格
若要空白的部分不补空格而是补0的话可以:printf("%0md",整型变量);
浮点型:printf("%m.nf",浮点型变量);
与整型类似,指定整数部分与小数部分的宽度。区别是n的值不能指定负数
5)其他修饰符:
⒈"l":可借助此修饰符将整型或浮点型输出成长整型或双精度浮点型。用法:printf("%ld",a);printf("%lf",f);
⒉"h":可借助此修饰符将整型输出成短整型。用法:printf("%hd",a);
⒊"*":如果用户不希望预先指定输出宽度,而是通过程序指定,则可以使用*修饰符。用法:printf("[%*d]",5,123);在这里*的位置会变成5
练习:输入以下程序,查看输出结果
#include <stdio.h>
int main()
{
    printf("The number 555 in various forms:\n");
    printf("Without any modifier: \n");
    printf("[%d]\n",555);
    printf("With – modifier :\n");
    printf("[%-d]\n",555);
    printf("With digit string 10 as modifier :\n");
    printf("[%10d]\n",555);
    printf("With 0 as modifier : \n");
    printf("[%0d]\n",555);
    printf("With 0 and digit string 10 as modifiers :\n");
    printf("[%010d]\n",555);
    printf("With -, 0 and digit string 10 as modifiers: \n");
    printf("[%-010d]\n",555);
    return 0;
}
2、scanf()
函数原型:
int scanf("格式控制",参数列表……);
返回值:正确:输入成功的参数数量;错误:0
说明:
1)格式控制:与printf()基本相同,由"%"和格式字符组成,作用是指定输入的数据的格式和类型
2)参数列表:scanf()使用地址值作为接收输入数据。因此当接收数据的变量为基本数据类型时,要加&(&:取地址运算符,取出这个变量所在的内存的地址。不是按位与)。如果是数组、指针等,直接写变量名即可不要加&
3)scanf()使用非打印字符来判断输入数据的开始和结束,因此它可以忽略空白区域和行界限来获取数据。如
scanf("%d%d%d",&a,&b,&c);
以下3中输入方式都是合法的:
①3空格4空格5(多个空格也可)
②3tab4tab5
③3回车4回车5
4)如果在格式控制中指定了数据分界,则需要按规定输入分界才算输入有效数据。如:
scanf("%d,%d,%d",&a,&b,&c);
则输入3,4,5才是合法的,输入3空格4空格5是非法的。同样还有:
scanf("a=%d,b=%d,c=%d",&a,&b,&c);
则输入a=3,b=4,c=5才是合法的

缓冲输入/输出:
缓冲区:在程序与和输入/输出设备间建立起来的用于缓冲的空间,是一块临时建立起来的存储区域,可以在内存中或在设备控制卡上
缓冲区的作用:暂时存放输入/输出数据,等待CPU(或其他的运算设备)来处理数据。
设立缓冲区的作用是为了解决高速设备和低速设备读写速率不吻合的问题。
输入设备--->输入缓冲区stdin--->程序
程序--->输出缓冲区stdout--->输出设备

字符数据输入输出:
1、putchar()
函数原型:
int putchar(int c);//c的范围:0~127
返回值:正确:输出的字符的unsigned char值;错误:返回EOF
说明:
putchar()是从标准输出流stdout中输出一个字符。其中参数c可以是0~127之间的整数、字符变量、转义字符等。以下的用法都是合法的:
putchar('A');
putchar(65);
putchar('\n');
2、getchar()
函数原型:
int getchar(void);
返回值:正确:输入字符的ASCII码;错误:返回-1
说明:
getchar()是从标准输入流stdin中输入一个字符。当程序运行到getchar()时,程序会阻塞等待用户输入。用户的输入的字符会先暂存在stdin中直至输入回车为止(回车也放入缓冲区中)。之后getchar()会读出缓冲区的第一个字符并将该字符回显至屏幕。
用户可以在回车前输入多个字符,但是每个getchar()函数只读取第一个字符,剩余的字符会继续存储在缓冲区中,等待后续的getchar()调用。
练习:
int main()
{
    char a,b;
    a = getchar();
    b = getchar();
    putchar(a);
    putchar(b);
    return 0;
}
运行程序,输入x回车y回车,会发现输出的并不是xy,而是
x

思考这是为什么。
答案:第一次输入的回车还存在缓冲区中,第二个putchar()会读取这个回车而不会读取y
/**********fgets()简介**************/
gets()函数不推荐使用,因为gets()函数非常容易造成数据的溢出。fgets()函数属于gets()函数的替代品。
从指定流中获取字符串:
fgets()
函数原型:char* fgets(char *s,int n,FILE stream);
参数列表:
char *s:将读取出的数据存入的字符串的地址
int n:要读取的长度
FILE stream:文件结构体指针,需要读取的文件流
返回值:当读到第n-1个字符或读到换行符的时候,fgets()停止操作,在s末尾添加'\0'。执行成功返回字符串的首地址s,执行失败返回EOF
有关fgets()函数的用法将在接下来的课程(字符串、文件IO操作)中详细讲解,这里只简单理解即可。
/**********fgets()简介end***********/

猜你喜欢

转载自blog.csdn.net/qq_34427165/article/details/81218513