机器处理事物。我们将物品输入机器,机器则输出不同的物品。一把锯将树木切成木板,内燃机将汽油转化为旋转能量,计算机也不例外。但与物理物料不同,计算机处理的是信息。

我们将信息输入计算机,告诉计算机如何处理它,然后得到一个结果输出。我们输入计算机的信息叫做输入(input),从计算机获得的信息叫做输出(output)。输入可以来自几乎任何地方。例如,键盘上的按键输入、网络连接传输的数据或转化为电信号的声音波形,都是输入的例子。输出也可以有很多形式,例如显示在显示器上的视频、终端中显示的一串文本,或我们保存到硬盘上的数据。输入和输出的集合通常被称为输入/输出,简称 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() 只需要在括号中输入被引号包围的文本。我们将这种被引号包围的文本称为字面字符串(literal string),而这个字符串是 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> // 这个很重要,因为 printf 需要这个头文件

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 - 双精度浮点型
  • %c - 字符
  • %s - 字符串
  • %x - 十六进制

有关所有 printf() 格式说明符的完整列表,请参考 Wikipedia。

标签和换行符

如果我们想让输出看起来像这样:

1905 
312 +
-----

printf() 不会在每条语句后自动加上换行符:我们必须自己加上。那么该怎么做呢?

我们可以使用换行符转义字符。转义字符是一个特殊字符,它在屏幕上做出一些特别的效果,比如发出蜂鸣声、输入制表符等等。要输入换行符,我们写 \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 来输入制表符。你还可以使用 \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("请输入一个整数值:");
    scanf("%d", &a);
    printf("你输入的是:%d\n", a);

    return 0;
}

如果你描述上面 scanf() 函数调用的效果,可以这样说:“从用户输入一个整数,并将其存储到变量 a 的地址中”。

如果你尝试使用 scanf() 输入一个字符串时,不应该加上 & 符号。下面的代码会导致运行时错误,程序可能会崩溃:

scanf("%s", &a);

正确的用法是:

scanf("%s", a);

这是因为,每当你使用字符串格式说明符(%s)时,用于存储该值的变量将是一个数组,而数组名(在这个例子中是 a)本身就指向数组的基地址,因此不需要取地址符 &

注意,使用 scanf() 从键盘收集用户输入可能会使代码容易受到缓冲区溢出问题的影响,并且如果不小心,可能会导致其他不希望的行为。考虑使用 fgets() 来替代 scanf()

关于输入的注意事项

当在键盘上输入数据时,信息并不会直接传递给正在运行的程序。数据首先会存储在一个称为缓冲区(buffer)的地方——这是一小段为输入源保留的内存空间。有时,当程序想要从输入源读取数据时,缓冲区中可能还会残留一些数据,而 scanf() 函数会读取这些数据,而不是等待用户输入某些内容。有些人可能建议你使用 fflush(stdin) 函数,这可能在某些计算机上按预期工作,但它并不被视为好做法,稍后你会看到原因。这样做的弊端在于,如果你将代码移到一台不同的计算机,使用了不同的编译器,代码可能无法正常工作。

Last modified: Sunday, 12 January 2025, 1:23 PM