一个C语言的基本教程—IO篇

10.与用户交互的关键——IO篇

  至此我们学了很多很多东西了,最早各种程序设计语言的出现都是为了计算服务的,像是过去使用的DOS、UNIX,他们都没有图形界面,一来是机能限制,二来是用户的交互在没有那么重要

  当然,后来随着家庭计算机的普及,也有越来越多软件开始考虑与用户的交互,这一篇我们就要介绍一下在没有图形界面的情况下如何利用IO设计更好的用户交互。

(1).I/O是什么

  I/O是Input/Output(输入/输出)的缩写,标准I/O是从用户键盘输入,输出到用户的屏幕上。没错,stdio.h这个头文件就是standard input & output的缩写,在stdio.h中有很多标准IO函数,在其中还定义了stdin(标准输入), stdout(标准输出)和stderr(标准错误),我们之后会和stdin和stdout打交道,stderr可能会简单提一下。

(2).换个办法操作字符

  我们之前好像都是用的这个办法输入和输出字符:

#include <stdio.h>
int main()
{
    char a = 0;
    scanf("%c", &a);
    printf("%c", a);
    return 0;
}
#1.新的朋友——getchar和putchar

  即用scanf()和printf()这一对函数来完成,其实还有更简单、更单纯的用于单字符输入输出的函数:getchar()和putchar()

#include <stdio.h>
int main()
{
    char a = getchar();
    putchar(a);
    return 0;
}

  相当简单的两个函数哈,我们来看看他们的原型:

  • int getchar(void);
  • int putchar(int char);

  getchar()函数无参数,返回一个从stdin中读取的字符,putchar则会将传入的字符输出到stdout中,若输出失败则返回EOF,否则返回输出字符转换为unsigned int的值。当然,一般不怎么用putchar()的返回值。

#2.getchar的妙用

  你可能会想,我都有scanf()和printf()了,这俩函数有啥用呢?举个简单的例子,你在Dev-C++中运行一个程序到最后会有这样一个提示:
p85

  在你按下任意键之后,这个黑框框才会退出,这对于想要看运行结果的你来说是非常方便的,我们都知道,C语言程序完成编译之后会产生一个可执行程序,在Windows下一般是一个.exe文件,假设你写了下面这一段代码:

#include <stdio.h>
int main()
{
    for (int i = 0; i < 10; i++) {
        printf("%d ", i);
    }
    printf("\n");
    return 0;
}

  你可以试试编译一下这段源代码,然后找到同目录下的.exe文件执行一下,然后就会发现:这个黑框框一闪而过,假设这个时候你回到IDE去运行,结果还是正常的,这就说明其实程序本身是没有问题的,那为什么会这样呢?
  问题就出在结束的时候,因为没有任何语句阻断main函数执行完毕,一旦main函数return 0了,这个程序的运行就结束了,窗口就会自动退出,所以我们需要一些不影响程序正常运行,但是又可以帮助我们暂停程序执行的语句,这时候getchar()就起作用了,我们试试看:

#include <stdio.h>
int main()
{
    for (int i = 0; i < 10; i++) {
        printf("%d ", i);
    }
    printf("\n");
    getchar();
    return 0;
}

p86

  很好,程序没有直接退出,这时候如果我们按一下回车,就会退出了。getchar()在这里一直等待从stdin中获取一个字符,一旦获取到了,它的任务也就完成了,程序也就结束了。这是我们构建一个对用户友好的界面的第一步,首先至少要让用户在结束前能看到运行结果是吧?
  你现在知道这个了,不过好像还有点小问题,假设你的程序在程序最后的getchar()之前有用户输入scanf()或getchar(),程序好像还是会一闪而过:

#include <stdio.h>
int main()
{
    char a = 0;
    a = getchar();
    putchar(a);
    getchar();
    return 0;
}

  这又是为什么呢?我先不说,你先在getchar()的后面再加一个getchar():

#include <stdio.h>
int main()
{
    char a = 0;
    a = getchar();
    putchar(a);
    getchar();
    getchar();
    return 0;
}

p87

  成功了,这是为什么呢?还记得我们说getchar()是从stdin中取字符吗?stdin和stdout是一种流(stream),你可以把他们看成两条河,stdin(标准输入流)这条河上游是用户的键盘输入下游是各种从stdin中取东西的函数,当getchar()发现这条河里啥玩意儿没有的时候,它就会要求你在上游给它输入点什么东西进来。

  比如你输入了字符a,显然你只打一个a是没用的,这个a会一直停在那个地方,只有你按一下回车,程序才会继续下去,你在键盘上做的一切操作都不是白做的,回车是’\n’,因此这样一下你就往stdin这条河里丢了两个字符,一个是’a’,一个是’\n’,假设下游只有一个getchar(),那么按顺序来,它会把’a’取走,所以这段代码中我们输入a之后,变量a的值就是字符a了,这没问题,不过这条河里还有一个’\n’,之前代码末尾只有一个getchar()的时候,它会把河里的’\n’给取过来,所以最后的getchar()也直接完成了工作了,程序就退出了,现在你明白了吗?为了解决这个问题,我们多用了一个getchar(),把stdin里的’\n’也给捞走了,最后河里什么都不剩了,你就得输入点什么才能让程序退出了。
  我们使用getchar()防止程序退出的先决条件是:stdin(标准输入流)中没有字符,必须要用户完成一次输入操作

  所以我们会发现,无论是输入还是输出,我们都不是直接和程序完成数据交互的,输入通过stdin完成,输出通过stdout完成。

(3).重新认识一下——printf()与scanf()

#1.更好用的printf()

  printf()可以说是我们最熟悉的函数之一了。不过其实我们之前只是很简单地应用了printf()完成了输出,其实printf()还有很多其他的使用方法。
  我们先来复习一下各种格式化占位符:

关键字中文名基本数据类型格式占位符
short短整型整型%d
int整型整型%d
long长整型整型%ld
long long超长整型整型%lld
unsigned int无符号整型整型%u
unsigned long无符号长整型整型%lu
unsigned long long无符号超长整型整型%llu
float单精度浮点型浮点型%f
double双精度浮点型浮点型%lf
char字符型字符型%c

  这些格式化占位符主要是帮助我们把一些基本数据类型变为字符串以便输出,每种数据都有自己的特性,例如整数可以有八进制、十六进制表示,浮点数打印有位数差距、可以选择是否使用科学计数法等等,这样的需求C语言当然是可以实现的,我们来介绍一下:
  格式占位符的一般格式为:

%[标志符][最小宽度][.精度][长度指示符]type

标志符含义长度指示符含义
-左对齐hh单字节
+在前方补+/-号h以short类型输出
(空格)正数留空(正数前保留一个符号的位置)l以long类型输出
最小宽度/.精度含义ll以long long类型输出
number(最小宽度)最小字符数(字符不足时补空格)L以long double类型输出
.number(精度)小数的位数
type用于type用于
i/dinta/A十六进制浮点数
uunsignede/E科学计数法
o八进制f/Ffloat
x十六进制X十六进制大写字母

  上面的表格可以自己写一写试试看,这是一些比较常用的输出格式占位符,还有一个比较特殊的标志符是 #,在o/O,x/X之前使用#表示在输出的八进制/十六进制数字前加上0或0x标记:

#include <stdio.h>
int main()
{
	printf("%o\n", 330);
	printf("%#o\n", 330);
	printf("%x\n", 330);
	printf("%X\n", 330);
	printf("%#x\n", 330);
	printf("%#X\n", 330);
	return 0;
}

p88

  当#后面跟着0和最小宽度数字时,可以表示用0填充直到达到最小宽度:

#include <stdio.h>
int main()
{
	printf("%#07d\n", 330);
	return 0;
}

p89

  这么一来,我们就可以设计一个相对整齐的界面了:

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
int main()
{
	time_t timep;
	struct tm *p;
	time (&timep);
	p = gmtime(&timep);
	double discount = 7.5, origin = 199, after = origin * discount/10;
	
	printf("Thanks for purchase!!\n");
	printf("Here is your script!\n\n");
	printf("+-----------------\n");
	printf("+ Time:\t%d/%d/%d\n", 1900+p->tm_year, 1+p->tm_mon, p->tm_mday);
	printf("+ Origin:%+9.2lf\n", origin);
	printf("+ Discount:%7.1lf\n", discount);
	printf("+ Cost:%+11.2lf\n", after);
	printf("+-----------------\n");
	printf("Looking forward to your next visit.\n");
	return 0;
}

p90

  以上是一张购物收据的输出样例,可能不太好看哈,不过还是比较整齐的,你可以依靠我们刚刚说的格式化输出来构造一个更好看的输出格式。

#2.printf()的一个奇技淫巧

  我相信假设你有做一些C语言的练习总会遇到这样的输出要求:

输出的数字间以空格分割,最后一个数字后没有空格

  一般来说大家都会选择在第一个或者最后一个单独打印作为一个特例进行特别处理,不过依托于printf中这个""字符串字面量的特殊性,我们可以写出这样的代码来:

#include <stdio.h>
int main()
{
    int a[10] = {4123,22,1,131,332,12};
    for (int i = 0; i < 10; i++) {
        printf(" %d"+!i, a[i]);
    }
    printf("\n");
    return 0;
}

p91

  WTF,这个+!i是在干什么?这是一个很好玩的技巧:首先我们知道字符串对应的是字符数组,对数组名进行加减操作其实就是对指针进行加减操作
  i从0到9,!i仅在i=0时会等于1,这时候假设" 4123"的地址是0x00123456,那么一次+1的操作就会让地址跳转为0x00123457,对于字符串的读取我们只需要到\0截止即可,初始位置并不重要,+1之后正好可以忽略掉一个字符,也就是第一个元素打印的时候前面的空格,然后之后的元素因为都是非0的数字,取逻辑非之后值都为0,所以空格就被保留下来了。
  以上这个技巧对于分隔符是一个字符的情况都是有效的,如果是多个字符你可能就要开动一下自己的脑子想想有没有类似的办法了。

#3.printf()的本质

  putchar()是把一个字符放进stdout里面,这我们已经知道了,那printf()呢?它当然也是了,不过因为printf()支持字符串的输出,所以printf()是一次性将一大串内容在合适的时机一起加入stdout,之后输出到用户的屏幕上。

#4.更好用的scanf()

  刚刚我们介绍了printf()的不同用法,那scanf()有吗?当然有啊,不过我们先来看看scanf()函数的原型:

int scanf(const char* restrict format,...);

  你发现,scanf()这个函数居然还有返回值,的确是这样的,有的时候如果你在OJ上做题,使用了scanf函数的话可能还会收到这样一条警告信息:

warning: ignoring return value of ‘scanf’, declared with attribute warn_unused_result [-Wunused-result]
     scanf("%d",&D);
     ^~~~~~~~~~~~~~

  这个警告的意思是你忽略了scanf()函数的返回值,当然,一般来说我们是用不到scanf()函数的返回值的,scanf()函数的返回值与前面的格式化字符串有关,scanf()函数读入了多少个变量,scanf()就会返回多少,而当读取到了"文件末尾"的时候,scanf()会返回EOF(End Of File,一个stdio.h定义的宏,值为-1).
  关于scanf()和EOF,我们一般在不指定输入规模的时候更容易用到,比如这个例子:

先输入一个数字a,再求之后输入的一串数字中,这个数字a出现的次数

  很简单的题,不过你看这就觉得奇怪,这道题,没有说明会有多少个数字啊!这我就不能用for循环了,如果用while循环的话,什么时候才能停止呢?这时候就要用到EOF了,一般这种题在数据输入结束的时候会再输入一个EOF作为停止标记,那我们只需要这么做就好了:

#include <stdio.h>
int main()
{
    int cnt = 0;
    int a = 0, input = 0;
    scanf("%d", &a);
    while (scanf("%d", &input) != EOF) {
        if (input == a) cnt++;
    }
    printf("%d\n", cnt);
    return 0;
}

  很好的程序,你运行之后发现怎么样都结束不了,当然不是程序有问题,当你的数字输入结束之后,你需要输入一个EOF,在Windows下,同时按Ctrl+Z即可,在Linux环境下则需要按下Ctrl+D,然后再敲回车,你就发现程序成功终止了。
  那么接下来我们就如同printf()一样,介绍一下scanf()函数的格式化字符串吧。它的基本形式是:

%[标志符]类型

标志符含义类型用于
*跳过字符dint
数字最大字符数i整数(可能为十六/八进制)
hhcharuunsigned int
hshorto/x八进制/十六进制
llong, doublea,e,f,gfloat
lllong longcchar
Llong doubles无空白字符的字符串
[…]允许的一系列字符
p指针

  其余的你可以自己尝试,我想提一提[…],这个[…]事实上是一种正则表达式,当然更深入的我就不提了,我说说简单的,这个中括号可以表示一个集合,当我们往里输入什么东西的时候,scanf会匹配满足这个集合的所有内容,直到匹配到不符合的为止,这很抽象,我们举个例子:

#include <stdio.h>
int main()
{
    int cnt = 0;
    char str[1000];
    scanf("%[a-zA-Z]", str);
    printf("%s\n", str);
    return 0;
}

  其中[a-zA-Z]代表匹配a到z和A到Z的所有字符
p92

  不错,按照我们想的来了,输入了一大串字符,中间有两个数字,scanf一读到3就停止读入了,还有一个符号^,代表否定,假设我们写[^a-zA-Z],那就是读到a-z和A-Z之间的字符的时候就停止读入,这在一定条件下可以说是相当好用的。
  有的时候我们的程序可能要定期读入一定量的规律字符串,一系列的数据通过逗号分隔开,我们要把每个数据都从中抽取出来,全部读取再for循环遍历拆解是可以的,但是很麻烦,不如我们在输入的时候就直接赋值到各个变量,例如:

//$GPRMC,004319.00,A,3016.98468,N,12006.39211,E,0.047,,130909,,,D*79
scanf("%*[^,],%[^,],%[^,],%[^,],%[^,],%[^,],%[^,],%[^,],%[^,],%[^,]",
sTime,sAV,sLati,&sNW,sLong,&sEW,sSpeed,sAngle,sDate);

  上述输入的是一串GPS信息1,我们可以把这些信息精准的赋值给特定的变量,这可就方便多了。

#5.Whitespace与scanf()

  whitespace(空白字符)包含" “(空格),”\t"(制表符),“\n”(换行符),当scanf()接受到空白字符的时候,对于一个变量的接收就终止了,而我们现在所有的输入输出函数只要与屏幕和键盘交互,那就一定要和stdin或stdout打交道,所以scanf()这个函数也一样是从stdin中获取字符,这时候就有一个问题:假设输入的是一串字符,而且字符之间以空格分割:

// c d a e f
#include <stdio.h>
int main()
{
    char a[5];
    for (int i = 0; i < 5; i++) {
        scanf("%c", &a[i]);
    }
    for (int i = 0; i < 5; i++) {
        printf("%c ", a[i]);
    }
    return 0;
}

p93

  这个输出结果跟我们的输入还真是完全不一致呢!产生这个结果的原因就是:scanf()接受字符的情况下,接收到了一个空白字符即停止,但下一次接收的时候stdin中还存在空白字符,scanf()就会把这个字符再收入进来,这么下去最终就会有两个字符没有被传入。而在收集数字类型的时候就不会有这个问题——收集数字类型时如果stdin中存在空白字符,stdin会被刷新,空白字符会消失。
  因为我们如果要输入一连串字符,就一定要注意:要么把字符连在一起输入,要么每次输入字符后用一个getchar()捞走空白字符:

#include <stdio.h>
int main()
{
    char a[5];
    for (int i = 0; i < 5; i++) {
        scanf("%c", &a[i]);
        getchar();
    }
    for (int i = 0; i < 5; i++) {
        printf("%c ", a[i]);
    }
    printf("\n");
    return 0;
}

p94

  这就是我们希望的样子了。还有一个关于字符串的事情:我们都知道字符串可以直接用%s接收,那假设说我们要输入这样一个句子:This is a sentence.会怎么样呢?

#include <stdio.h>
int main()
{
    char str[100];
    scanf("%s", str);
    printf("%s\n", str);
    return 0;
}

p95

  str只成功收入了一个This,这又是为什么呢?就如我们之前所说的一样,scanf()函数在接受到了空白字符之后就会停止接受,所以这种带有空白字符的句子,用scanf()是没有办法接受的,那有没有什么办法可以解决呢?有的!

(4). 通吃的gets()和配套的puts()

  gets函数可以解决scanf()在输入字符串的过程中可能遇到空格的问题,例如:

#include <stdio.h>
int main()
{
    char sentence[100];
    gets(sentence);
    printf("%s\n", sentence);
    return 0;
}

p96

  非常好,就算有空格也还是读取成功了,gets()函数的截止条件是接收到一个换行符’\n’,这样我们就可以把一整行的内容全部接收进来了,不过有一个问题:gets()函数是有危险的,比如上面这个程序,我们输入一个很长的句子试试看:

//Haha I'm trying to input a long sentence to test the gets function. My mother always say, life was like a box of chocolate, you're nerver know what you're going to get. I believe this sentence is long enough to raise some exceptions.

p97

  我们把一个字符数非常多的句子输入了进来,虽然后面它还是成功打印了出来,不过到最后return了3221225477,那肯定是出异常了。
  我相信你也可以看得出来:sentence这个字符串最大只能接受99个字符,但是我们输入的句子远远长于99个字符,gets()函数的问题就在这里:gets()函数完全不检查输入内容和目标字符数组的长度,这样就可能引发越界访问等等问题,所以我们一般有fgets()与gets_s()两个备选的函数用于取代gets()。
  fgets()函数的原型如下:

char* fgets(char* str, int n, FILE* stream);

  三个参数:str是要存入的字符串,n是最大接收的字符数(一般设为str数组的长度),stream是一个文件指针,文件指针我们之后会在文件篇细讲,在此我们先引入一点基本的内容:

  • 文件指针是一个指向一个叫做FILE的结构体(struct)的指针,不懂不要紧,结构体和FILE* 我们之后都还会继续提到,你只要知道,文件指针是C语言中用来处理文件的一个工具,有了文件指针之后我们就可以在C语言中与外界的文件交互,例如用C语言读写一个.txt文件,就需要用到文件指针。
  • 我们之前提到的三个IO流:stdin,stdout,stderr,这三者都是stdio.h定义的宏,那既然是宏,肯定是基于C语言中一个已经存在的类型进行的重新定义,事实上,这三个宏对应的变量的类型都是文件指针,在C语言中把这三者也看成了文件,这样做的好处是我们可以设计一系列通用的函数,例如我们之后要介绍的fprintf()函数与fscanf()函数

  当然,其实现在可以不用了解那么多,你只需要知道:C语言把从键盘读取(stdin)的流和向屏幕输出内容(stdout)或错误(stderr)的流都当做文件来处理,一系列对文件使用的函数一样可以对标准输入输出流使用

  那么我们回到fgets()函数,第三个参数FILE* stream是一个文件指针,既然是一个输入的函数,那我们应该传入stdin以让fgets()从stdin读取字符,不过毕竟是文件指针,之后我们在学习文件的时候,fgets()函数也可以对于文件使用。说了这么多,我们来写个代码试着用一用看:

#include <stdio.h>
int main()
{
    char a[100];
    fgets(a, 100, stdin);
    printf("%s", a);
    return 0;
}

p98

  你看,结果非常正常,在合适的位置截断了输入,最终程序也return了0。不过这个函数挺麻烦的,还得传一个stdin进来。另一个叫做gets_s()的函数就不需要传入这个stdin,因为它是gets_safety,是gets()函数的安全版本(MSVC可喜欢_s了),它的原型如下:

char* gets_s(char* buffer, size_t sizeInCharacters);

  这个函数的确是少了个stdin,但它是C11标准引入的可选支持内容,我的gcc甚至过不了编译,所以我换Visual Studio完成下面代码的运行:

#include <stdio.h>
int main()
{
    char str[100];
    gets_s(str, 100);
    // sample1: This is a sentence.
    // sample2: Haha I'm trying to input a long sentence to test the gets function. My mother always say, life was like a box of chocolate, you're nerver know what you're going to get. I believe this sentence is long enough to raise some exceptions.
    printf("%s\n", str);
    return 0;
}

p99

  这个短句子还是挺正常的,下面看sample2:
p100

  这回直接给我弹Debug断言错误,事实上gets_s这个函数有一个不太好的特性:如果读取到最大数量都没有读到换行符,首先会把目标数组的首字符设置为’\0’,读取并丢弃随后的输入,然后返回空指针,之后还有可能会中止或结束程序。 这可不太好,读入内容超过了最大大小,直接把程序给我中止掉了,而且很多编译器可能都不支持,所以一般是不用这个函数的。

  所以我们一般在输入字符串这件事情上有两个选择:gets()与fgets(),一般我们这么选择:gets()函数用一些确定输入规模的情况,例如你在oj上做题,oj确认输入的数据都是成一定规模而且最大长度受限的,你可以开一个比较大的数组,然后直接存入。 fgets()就用于一些与用户交互的程序上,因为不能保证用户输入是可信的,这个时候就要以安全为主

  puts()函数是与gets()函数对应的输出函数,puts()的用法很简单,往里传入一个字符串str就行了:

#include <stdio.h>
int main()
{
    char* str = "Haha I'm trying to input a long sentence to test the gets function. My mother always say, life was like a box of chocolate, you're nerver know what you're going to get. I believe this sentence is long enough to raise some exceptions.";
    puts(str);
    return 0;
}

p101

  轻轻松松,是吧?puts()函数有一个比较好的特性,你可能也发现了,我们这个字符串的末尾是没有换行符的,但是用puts()函数在最后会补一个换行符
  刚说了fgets()是对文件操作的,那么对应的还有一个对文件操作的fputs()函数,与fgets()一样,我们这次要向其中传入一个stdout作为目标文件,这个函数不会在输出末尾补充换行符:

#include <stdio.h>
int main()
{
    char* str = "Haha I'm trying to input a long sentence to test the gets function. My mother always say, life was like a box of chocolate, you're nerver know what you're going to get. I believe this sentence is long enough to raise some exceptions.";
    fputs(str, stdout);
    return 0;
}

p102

  就像这样,puts()与fputs()还是比较简单的。

(5). 对stdin和stdout操作的fprintf()和fscanf()函数

  fprintf()和fscanf()两个函数和printf()、scanf()长得很像,相差只有一个f,事实上这两个函数也是对文件进行操作的函数,不过因为我们的stdin和stdout也是"文件",所以我们也可以用这两个函数完成输出与输入的操作,他们的原型如下:

int fprintf(FILE* stream, const char* format [,argument...]);
int fscanf(FILE* stream, char* format [,argument...]);

  后面的格式字符串与printf()和scanf()一样,只要传入stdin或stdout即可,在此我就不细讲了,在文件篇中我们再来讲一讲。

(6). 用sprintf()操作字符串

  在python中,我们可以通过格式化的方式构造一个字符串出来,比如:

a = 123
b = 456
c = 123.4556789
s = f"a = {a}, b = {b}, c = {c:.7f}"
print(s)

  在C中我们也可以利用sprintf()函数完成对字符串的构造,比如下面这样:

#include <stdio.h>
int main()
{
    char str[1000];
    int a = 123, b = 456;
    double x = 123.4556612;
    sprintf(str, "a = %d, b = %d, x = %.7lf\n", a, b, x);
    printf("%s", str);
    return 0;
}

p103

  很生动形象,把一串字符串输入进一个字符数组中形成一个新字符串。

(7). <ctype.h>头文件中的字符操作

函数原型作用函数原型作用
int isalnum(int c)判断传入字符是否为字母和数字int isalpha(int c)判断传入字符是否为字母
int iscntrl(int c)判断传入字符是否为控制字符int isdigit(int c)判断传入字符是否为十进制数字
int isgraph(int c)判断传入字符是否有图形表示int islower(int c)判断传入字符是否为小写字母
int isprint(int c)判断传入字符是否为可打印字符int ispunct(int c)判断传入字符是否为标点符号
int isspace(int c)判断传入字符是否为空白字符int isupper(int c)判断传入字符是否为大写字母
int isxdigit(int c)判断传入字符是否为十六进制数字--
int tolower(int c)把大写字母转换为小写字母int toupper(int c)把小写字母转换为大写字母

(8). 来试试做一个界面吧!

  有这样的需求:一个漂亮的选单界面,用户输入了选项后要检测输入的内容是否正确,还是挺简单的吧?

#include <stdio.h>
#include <ctype.h>
int main()
{
	int choice = 0;
	char target[1024];
	printf("Here are all choices: \n");
	printf("+----------------------\n");
	for (int i = 1; i < 7; i++) {
		printf("| choice %d : Content %d\n", i, i);
	}
	printf("+----------------------\n");
	printf("Please input your choice: ");
	int check = scanf("%d", &choice);
	if (check != EOF && check == 1) {
		if (choice < 7 && choice >= 1) {
			printf("...\n");
		}
		else {
			printf("%d is a wrong choice!\n", choice);
		}
	}
	else {
		printf("Input Error! Try again later!\n");
	}
	return 0;
}

p104

  依旧是很简单的界面,在你学完了这一节的内容之后,你可以自己去尝试构建一个更好看的界面。

小结

  这一章是相对比较短的一章,因为很多函数和文件联系紧密,我会在文件篇具体介绍,不过这次介绍的printf和scanf的各种输入格式需要重点掌握一下,它对于你之后更加熟练地完成格式化输入是非常有利的。
  下一章我们会介绍可以构造自己的数据类型结构体与联合


  1. ECNU Online Judge 1168. GPS数据处理 ↩︎

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值