C/简单的输入输出

维基教科书,自由的教学读本
< C

机器处理东西。我们将东西喂给机器,机器给我们吐出不同的东西。锯子将树木变为木板。内燃机将汽油转化为转动的能量。计算机也一样。不过,计算机为我们处理的不是物质材料,而是信息。

我们把信息喂给计算机,告诉计算机如何处理这些信息,然后取得结果。那一部分我们输入计算机的信息称为输入,而我们从计算机收到的信息则称为输出。任何地方都可以是输入的来源。键盘击打、网络数据,抑或转换到电信号的声波都是输入的例子。输出也有众多的形式,如显示器上播放的视频、终端上显示的一段字符串,或存储在硬盘上的数据。输入的集合与输出的产生统称为输入/输出,或缩写为I/O,是计算机的一项核心功能。

有趣的是,C语言并没有内置的I/O功能,取而代之的是包含I/O函数的外部函数库。我们能把这些函数编译、链接到程序里。在章节开始时的示例“Hello, World!”中,我们已经用到一个输出库函数:printf()。你可能会想起这个函数属于stdio.h函数库文件。正如文件名所示,stdio.h包含标准化的I/O函数,可用于给我们的程序增加加输入/输出能力。本章将带你探索其中的一些函数。


使用printf()输出[编辑]

现在回忆之前的演示程序:

#include <stdio.h>

int main(void)
{
     printf("Hello, World!");
     return 0;
}

如果你把它编译运行,你就能看到下方的句子显示在屏幕上:


  Hello, World!

这项令人惊喜的成就是由 printf()函数实现的。一个函数就像一个“黑盒子”,它为你完成特定工作,而不显示自身的内部结构。我们也可以借助C语言写自己的函数,但那就是后话了。

你已经看到用printf()可以输出括号中双引号里的文本。我们称包含在引号中的文本为字面字符串(简称字符串),那个字符串则是printf的一个参数

在此澄清一下:有时为了方便起见,我们在一个函数名称后写一对圆括号,提醒我们这确实是一个函数。然而,在大家都知道讨论的对象是函数时,这并不是必需的。

正如上文的例子所言,用printf()就像打一段文字、两端加双引号一样简单(注意这是双引号,而不是两个单引号)。也就是说,你可以把任何字符串作为printf()的参数打印出来:


  printf("This sentence will print out exactly as you see it...");

而一旦它被包含在合适的main()函数中,它将会显示:


  This sentence will print out exactly as you see it...


打印数值和转义字符[编辑]

占位符[编辑]

printf()是一个强大的函数,很可能也是C程序中最常用的函数。

接下来我们用一个问题做例子。假如我们要计算:19 + 31。让我们用C语言求出答案。

现在我们就开始写

#include "stdio.h" // this is important, since printf
                   // can't be used without this header

int main(void)
{
    printf("19+31 is");

但是卡在这里了!printf()只能打印字符串!幸好printf有输出数值的方法。我们要做的就是在字符串中放一个占位格式控制符。这样写:

    printf("19+31 is %d", 19+31);

顾名思义,占位符%d给19与31相加得到的具体数值“占了位置”。

这些占位符称为格式说明符。还有很多格式说明符可以与printf()使用。如果我们有一个单精度浮点数,我们可以用%f完整输出。其他的格式说明符有:

  • %d - 整型 (同%i)
  • %ld - 长整型 (同%li)
  • %f - 单精度浮点型
  • %lf , %g - 双精度浮点型[1]
  • %c - 字符型
  • %s - 字符串型
  • %x - 十六进制型

维基百科上printf()全部格式说明符的列表。

Tabs和换行[编辑]

假使我们想输出这样的文本:

   1905 
  312 +
  -----

printf()不会在每个语句结尾输出换行符:我们必须手动操作。可是怎样做呢?

我们能做的是使用换行转义字符。转义字符是一种特殊字符,我们能写出来,但它会在屏幕上做某些特殊的事情,如发出蜂鸣声、写一个tab字符等。要想写一个换行符,可以使用\n。所有转义字符以反斜线开始。

因此,想要实现上述的输出,应该这样写:

    printf(" 1905\n312 +\n-----\n");

为了使代码更清晰,我们将这个很长的printf语句分割成几行。然后我们的程序会变成这样:

#include <stdio.h>

int main(void)
{
    printf(" 1905\n");
    printf("312 +\n");
    printf("-----\n");
    printf("%d", 1905+312);
    return 0;
}

此外还有很多其他转义字符可用。另一个常见的做法是用\t写一个tab。你还可以用\a让电脑的蜂鸣器发出响声,不过你不应该在自己的程序里过多地使用它,因为声音的过度使用对用户的感官不很友好。


其他输出方式[编辑]

puts()[编辑]

在不考虑任何占位符或变量时,puts()函数是一种在屏幕上输出字符串的非常简单的方法。它和我们在"Hello, World!"这个例子里看到的printf()函数很是相像:

    puts("Print this string.");

输出到屏幕的是:

  Print this string.

紧接着是一个换行符(前文已经说到过)。(puts函数会在输出的结尾加一个换行符。)

使用scanf()输入[编辑]

scanf()函数是与printf()输出函数等价的输入方法——它简单但强大。在最简单的调用中,scanf的格式字符串包含一个占位符,表示用户将要输入值的类型。这些占位符大抵与printf()函数相同——%d表示整数,%f表示单精度浮点数,%lf表示双精度浮点数。

然而,与printf()相比,scanf()有一点不同。scanf()函数要求你将要存储输入值的变量内存地址。 虽然这里可以使用指针(存储内存地址的变量),这毕竟是一个之后才会涉及的概念。相反,较为简单的技巧是使用取地址运算符,即&。在我们讨论指针之前,现在我们最好先把它看作"魔法"。

下面是一个典型的用例:

#include "stdio.h"

int main(void)
{
    int a;

    printf("Please input an integer value: ");
    scanf("%d", &a);
    printf("You entered: %d\n", a);

    return 0;
}

如果你要描述上面scanf()函数产生的作用,它可以读作:“读取用户输入的整数并存储到变量a所在的地址”。

假如你要用scanf输入一个字符串,你应该包含&运算符。下面的代码会产生运行时错误,很可能导致程序崩溃。

    scanf("%s", &a);

正确的用法应该是:

    scanf("%s", a);

这是因为你在使用字符串(%s)格式说明符时,你用来存储值的变量是一个数组,数组名称(在这个例子中是a)本身指向它的基地址,因此不需要取地址运算符。

注意,使用scanf()从用户键盘上获取数据可能让你的代码易受缓冲区溢出攻击,如果你不很谨慎,则可能导致其他不可预测的行为。考虑使用 fgets()而不是scanf()

参考资料[编辑]

  1. 实际上%f也能输出double型数据,但输入时%f的使用则不同。更多细节请见维基百科上对C语言数据类型的介绍