C编程
机器处理事物。我们将物品输入机器,机器则输出不同的物品。一把锯将树木切成木板,内燃机将汽油转化为旋转能量,计算机也不例外。但与物理物料不同,计算机处理的是信息。
我们将信息输入计算机,告诉计算机如何处理它,然后得到一个结果输出。我们输入计算机的信息叫做输入(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)
函数,这可能在某些计算机上按预期工作,但它并不被视为好做法,稍后你会看到原因。这样做的弊端在于,如果你将代码移到一台不同的计算机,使用了不同的编译器,代码可能无法正常工作。