前言
在编写 C 语言程序之前,我们需要先导入程序运行所需的库,这些库也被称为头文件。本篇基础知识将以 C 语言标准输入输出库 stdio.h 为例进行讲解。
一、占位符:
与 Java 不同,在 C 语言中,占位符用于 printf、scanf 这类格式化输入输出函数,用来标记数据的位置与数据类型。在 printf 输出时,占位符所在的位置会被替换为变量的值;在 scanf 输入时,占位符则代表需要从控制台读取对应类型的数据并存入变量。
C++ 中存在两套 IO 体系:如果沿用 C 风格的 printf/scanf,占位符的用法和规则与 C 语言完全一致;如果使用 C++ 原生的 cout 与 cin 流输入输出,则无需占位符,系统会自动识别变量的数据类型。
在 C 语言中,变量必须先声明后使用,声明时需要指定其数据类型。数据类型决定了变量在内存中占用的字节数、取值范围以及可进行的运算。下面列出 C 语言中常见的变量类型:
| 数据类型 | 关键字 | 占用字节数(32位系统) | 取值范围(大致) |
|---|---|---|---|
| 短整型 | short |
2 字节 | -32768 ~ 32767 |
| 整型 | int |
4 字节 | -2147483648 ~ 2147483647 |
| 长整型 | long |
4 字节(32位)/ 8 字节(64位) | 取决于系统位数 |
| 长长整型 | long long |
8 字节 | -9223372036854775808 ~ 9223372036854775807 |
| 无符号整型 | unsigned int |
4 字节 | 0 ~ 4294967295 |
| 单精度浮点型 | float |
4 字节 | 约 3.4e-38 ~ 3.4e38 |
| 双精度浮点型 | double |
8 字节 | 约 1.7e-308 ~ 1.7e308 |
| 字符型 | char |
1 字节 | -128 ~ 127(或 0 ~ 255) |
| 字符串 | char[] |
取决于长度 | 由字符数组存储 |
| 指针 | void* / 类型* |
4 字节(32位)/ 8 字节(64位) | 内存地址 |
注意:上表中的字节数基于常见的 32 位系统,实际大小可能因编译器和操作系统而异。可以使用
sizeof(类型)运算符在程序中获取某个类型实际占用的字节数。
总而言之,在 C 和 C++ 语言中,占位符就是变量位置的替代品。不同变量对应的占位符如下表所示:
| 数据类型 | 占位符 | 说明 |
|---|---|---|
int |
%d |
输出/输入十进制整数 |
long |
%ld |
输出/输入长整型整数 |
long long |
%lld |
输出/输入长长整型整数 |
unsigned int |
%u |
输出/输入无符号十进制整数 |
float |
%f |
输出/输入单精度浮点数 |
double |
%lf |
输出/输入双精度浮点数(scanf 中必须用 %lf) |
char |
%c |
输出/输入单个字符 |
char[](字符串) |
%s |
输出/输入字符串 |
void*(指针) |
%p |
输出指针地址 |
short |
%hd |
输出/输入短整型整数 |
unsigned long |
%lu |
输出/输入无符号长整型整数 |
unsigned long long |
%llu |
输出/输入无符号长长整型整数 |
double(printf 输出) |
%e / %E |
以科学计数法输出浮点数 |
double(printf 输出) |
%g / %G |
根据数值大小自动选择 %f 或 %e 输出 |
int(八进制) |
%o |
以八进制形式输出整数 |
int(十六进制) |
%x / %X |
以十六进制形式输出整数(小写/大写字母) |
二、转义符:
在 C 语言中,转义字符以反斜杠 \ 开头,其核心作用是改变紧跟在反斜杠后面的字符原本的含义。有些字符无法直接写在字符串中,或者本身在 C 语言中具有特殊语法作用,此时就需要借助转义符来表示。
举个例子:双引号 " 是字符串的边界标记,如果直接写在字符串内部,编译器会误认为字符串提前结束,从而引发编译错误;此时使用 \",反斜杠会告诉编译器,这个双引号只是普通字符,并非字符串结束符,最终就能正常输出双引号。再比如换行操作,我们无法直接在双引号包裹的字符串中敲回车换行,使用 \n 就可以实现换行功能。
除了 \" 和 \n 之外,还有很多常用转义字符:\r 代表回车,将光标移到当前行的开头;\t 是水平制表符,相当于按下 Tab 键,用来做文本对齐;\\ 用来输出反斜杠本身;\0 是空字符,作为 C 语言字符串的结束标记,是字符串必不可少的终止符号。
简单来说,转义字符解决了两类问题:一是表示无法直接输入、不可见的控制字符(如换行、制表、字符串结束符等);二是转义 C 语言中自带特殊含义的符号(如双引号、反斜杠等),让它们不再承担语法功能,而是单纯作为普通字符输出。
| 转义符 | 含义 | 说明 |
|---|---|---|
\n |
换行 | 将光标移到下一行开头 |
\t |
水平制表符 | 将光标移到下一个制表位(相当于按 Tab 键) |
\\ |
反斜杠 | 输出一个反斜杠 \ 本身 |
\" |
双引号 | 在字符串中输出双引号 " |
\' |
单引号 | 在字符中输出单引号 ' |
\r |
回车 | 将光标移到当前行开头 |
\b |
退格 | 将光标向左移动一格 |
\f |
换页 | 将光标移到下一页开头(常用于打印机) |
\a |
响铃 | 发出提示音(部分终端有效) |
\v |
垂直制表符 | 将光标移到下一个垂直制表位 |
\0 |
空字符 | 字符串结束标志(ASCII 码为 0) |
\xhh |
十六进制转义 | 输出 ASCII 码为十六进制 hh 的字符,如 \x41 输出 A |
\ooo |
八进制转义 | 输出 ASCII 码为八进制 ooo 的字符,如 \101 输出 A |
注意:转义符必须写在双引号或单引号内才会生效。例如
printf("第一行\n第二行");会在输出时换行。
三、头文件:<stdio.h> 标准输入输出库
该库包含的函数有:printf、scanf、fgets、fputs 等。
printf
printf 是输出打印语句,一般写法为 printf("想输出的内容");。如果涉及到变量,则写为 printf("内容+变量所对应的占位符", 变量);。
下面给出几个 printf 的代码示例:
c
#include <stdio.h>
int main() {
int age = 18;
double score = 92.5;
char grade = 'A';
char name[] = "小明";
// 1. 输出纯文本
printf("Hello, World!\n");
// 2. 输出变量(占位符对应变量)
printf("年龄:%d\n", age);
// 3. 多个占位符,按顺序对应多个变量
printf("姓名:%s,成绩:%.1f,等级:%c\n", name, score, grade);
// 4. 占位符也可以直接放常量或表达式
printf("两数之和:%d\n", 10 + 5);
return 0;
}
运行结果:
text
Hello, World!
年龄:18
姓名:小明,成绩:92.5,等级:A
两数之和:15
注意:
%.1f表示保留一位小数;\n是换行符。占位符的数量、顺序必须与后面的变量一一对应,否则输出结果会错乱。
scanf
scanf 用来从键盘读取用户输入的数据并存储在变量中,格式为 scanf("变量对应的占位符", &变量);。
但是,使用 scanf 存储内容时存在一个问题:如果存储的是字符串,且输入中包含空格,则无法存储空格后面的内容。例如:
c
#include <stdio.h>
int main(){
char a[10];
scanf("%s",&a);
printf("%s",a);
}
结果:
text
hello world
hello

这时候,我们就可以使用一个新的函数来解决这个问题。
fgets
fgets 用来从输入流中读取一行字符串,并存储到指定的字符数组中。与 scanf 不同,fgets 可以读取包含空格的整行内容,因此更适合处理带空格的字符串输入。
fgets 的格式为 fgets(字符数组, 最大读取字符数, 输入流);。其中,第三个参数 stdin 表示从键盘标准输入读取数据。
下面给出一个使用 fgets 读取带空格字符串的代码示例:
c
#include <stdio.h>
int main() {
char name[50];
printf("请输入姓名(可包含空格):");
fgets(name, sizeof(name), stdin);
printf("你输入的姓名是:%s", name);
return 0;
}
运行结果:
text
请输入姓名(可包含空格):张三 李四
你输入的姓名是:张三 李四
注意:
fgets会把换行符一并读入字符串,因此输出时会在末尾多出一个换行。如果想去掉末尾的换行符,可以手动将其替换为字符串结束符\0。此外,fgets最多读取最大读取字符数 - 1个字符,剩余空间会自动补上\0,从而避免缓冲区溢出。
fputs
fputs 用于将字符串写入指定的输出流,其基本格式为 fputs(字符串, 输出流);。其中,第二个参数 stdout 表示输出到屏幕的标准输出。与 printf 不同,fputs 既不会自动添加换行符,也不会解析字符串中的占位符,而是将字符串内容原样输出。因此,fputs 更适合用于输出无需格式化的纯文本。
具体来说,fputs 的返回值是 int 类型:当写入成功时返回非负值(通常是 0),当发生错误时返回 EOF(即 -1)。这一特性使得 fputs 在需要判断写入是否成功的场景下非常实用,例如在文件写入或网络传输中,可以通过检查返回值来确认数据是否完整送达。
此外,fputs 的第二个参数不仅可以是 stdout,还可以是文件指针(如 FILE *fp),这意味着它同样适用于向文件写入字符串。与 fprintf 相比,fputs 不会对字符串中的 % 等字符做任何特殊处理,因此当字符串内容本身包含大量 % 符号(如格式化文本、日志模板)时,使用 fputs 可以避免意外的格式解析,保证内容原样输出。
从底层实现来看,fputs 会逐个字符地将字符串写入输出流,直到遇到字符串结束符 \0 为止,但并不会把 \0 本身写入。这意味着它非常适合处理以 \0 结尾的 C 风格字符串,而不需要额外指定写入长度。相比之下,fwrite 则需要显式指定每次写入的元素大小和数量,使用起来相对繁琐。因此,在需要输出一整行以 \0 结尾的字符串时,fputs 是更简洁、更直观的选择。
具体来说,fputs 的返回值是 int 类型:当写入成功时返回非负值(通常是 0),当发生错误时返回 EOF(即 -1)。这一特性使得 fputs 在需要判断写入是否成功的场景下非常实用,例如在文件写入或网络传输中,可以通过检查返回值来确认数据是否完整送达。
此外,fputs 的第二个参数不仅可以是 stdout,还可以是文件指针(如 FILE *fp),这意味着它同样适用于向文件写入字符串。与 fprintf 相比,fputs 不会对字符串中的 % 等字符做任何特殊处理,因此当字符串内容本身包含大量 % 符号(如格式化文本、日志模板)时,使用 fputs 可以避免意外的格式解析,保证内容原样输出。
c
#include <stdio.h>
int main() {
char message[] = "Hello, fputs!";
// 输出到标准输出(屏幕)
fputs(message, stdout);
fputs("\n", stdout); // 手动添加换行
// 也可以直接输出字符串常量
fputs("这是第二行内容。\n", stdout);
return 0;
}
运行结果:
text
Hello, fputs!
这是第二行内容。
注意:
fputs不会自动换行,需要手动添加\n;同时它也不会解析%d这类占位符,因此不适合用于输出变量值。若需要格式化输出变量,应改用printf。此外,fputs与fgets是一对互补函数:fgets负责从输入流读取数据,fputs负责向输出流写入数据。在实际开发中,
fputs常用于以下场景:一是输出日志信息,因为日志通常不需要格式化,直接原样写入即可;二是向文件追加内容,配合文件指针可以轻松实现;三是与fgets配合完成文件的逐行复制。需要注意的是,fputs写入文件时不会自动添加换行符,因此如果希望每条记录独占一行,需要在字符串末尾手动加上\n。另外,
fputs与puts也有明显区别:puts只能向标准输出stdout写入,并且会在字符串末尾自动追加一个换行符;而fputs可以指定任意输出流,且不会自动换行。因此,当需要向文件或其他输出流写入内容、或希望完全控制换行行为时,应优先选择fputs。
练习
1.定义不同类型的变量并输出他们的值和所占字节数
2.输入圆的半径,计算并输出圆的周长和面积
3.输入一个三位数,将其百位十位个位分别输出
1.定义不同类型的变量并输出他们的值和所占字节数
在 C 语言中,变量必须先声明后使用,声明时需要指定其数据类型。数据类型决定了变量在内存中占用的字节数、取值范围以及可进行的运算。下面列出 C 语言中常见的变量类型:
| 数据类型 | 关键字 | 占用字节数(32位系统) | 取值范围(大致) |
|---|---|---|---|
| 短整型 | short |
2 字节 | -32768 ~ 32767 |
| 整型 | int |
4 字节 | -2147483648 ~ 2147483647 |
| 长整型 | long |
4 字节(32位)/ 8 字节(64位) | 取决于系统位数 |
| 长长整型 | long long |
8 字节 | -9223372036854775808 ~ 9223372036854775807 |
| 无符号整型 | unsigned int |
4 字节 | 0 ~ 4294967295 |
| 单精度浮点型 | float |
4 字节 | 约 3.4e-38 ~ 3.4e38 |
| 双精度浮点型 | double |
8 字节 | 约 1.7e-308 ~ 1.7e308 |
| 字符型 | char |
1 字节 | -128 ~ 127(或 0 ~ 255) |
| 字符串 | char[] |
取决于长度 | 由字符数组存储 |
| 指针 | void* / 类型* |
4 字节(32位)/ 8 字节(64位) | 内存地址 |
注意:上表中的字节数基于常见的 32 位系统,实际大小可能因编译器和操作系统而异。可以使用
sizeof(类型)运算符在程序中获取某个类型实际占用的字节数。
下面给出一个完整的代码示例,演示如何定义不同类型的变量,并使用 sizeof 运算符输出它们的值和所占字节数:
c
#include <stdio.h>
int main() {
// 定义不同类型的变量
short s = 10;
int i = 100;
long l = 100000L;
long long ll = 10000000000LL;
unsigned int ui = 3000000000U;
float f = 3.14f;
double d = 3.14159265358979;
char c = 'A';
char str[] = "Hello";
int *p = &i;
// 输出变量的值
printf("short 变量 s 的值:%hd\n", s);
printf("int 变量 i 的值:%d\n", i);
printf("long 变量 l 的值:%ld\n", l);
printf("long long 变量 ll 的值:%lld\n", ll);
printf("unsigned int 变量 ui 的值:%u\n", ui);
printf("float 变量 f 的值:%f\n", f);
printf("double 变量 d 的值:%lf\n", d);
printf("char 变量 c 的值:%c\n", c);
printf("字符串 str 的值:%s\n", str);
printf("指针 p 指向的地址:%p\n", (void*)p);
// 输出各变量所占的字节数
printf("\n各变量所占字节数:\n");
printf("short 占 %zu 字节\n", sizeof(s));
printf("int 占 %zu 字节\n", sizeof(i));
printf("long 占 %zu 字节\n", sizeof(l));
printf("long long 占 %zu 字节\n", sizeof(ll));
printf("unsigned int 占 %zu 字节\n", sizeof(ui));
printf("float 占 %zu 字节\n", sizeof(f));
printf("double 占 %zu 字节\n", sizeof(d));
printf("char 占 %zu 字节\n", sizeof(c));
printf("字符串 str 占 %zu 字节\n", sizeof(str));
printf("指针 p 占 %zu 字节\n", sizeof(p));
return 0;
}
运行结果(在 64 位系统上):
text
short 变量 s 的值:10
int 变量 i 的值:100
long 变量 l 的值:100000
long long 变量 ll 的值:10000000000
unsigned int 变量 ui 的值:3000000000
float 变量 f 的值:3.140000
double 变量 d 的值:3.141593
char 变量 c 的值:A
字符串 str 的值:Hello
指针 p 指向的地址:0x7ffd12345678
各变量所占字节数:
short 占 2 字节
int 占 4 字节
long 占 8 字节
long long 占 8 字节
unsigned int 占 4 字节
float 占 4 字节
double 占 8 字节
char 占 1 字节
字符串 str 占 6 字节
指针 p 占 8 字节
注意:
sizeof返回的是size_t类型,因此使用%zu作为占位符输出。字符串str占 6 字节,是因为它包含 5 个字符加上末尾的字符串结束符\0。指针的大小取决于系统位数,在 64 位系统上为 8 字节,在 32 位系统上为 4 字节。
2.输入圆的半径,计算并输出圆的周长和面积
输入值并以这个值进行运算,这时候我们需要用到scanf。输出周长和面积则需要我们printf和数学公式

结果为:

3.输入一个三位数,将其百位十位个位分别输出
这时候我们有两种思路。
第一种,用数字运算,采用取余整除的方式:

第二种,用数组的方式,我们可以将输入的三位数看作是一个数组,然后读取即可


可能有人会问了,我们为什么要减去这个'0'呢?
我们先记住:'0' 是字符0,不是数字0。计算机内存里所有字符存的都是ASCII码。
字符'0'的ASCII码是 48
字符'1'的ASCII码是 49
字符'2'的ASCII码是 50
......
字符'9'的ASCII码是 57
当我们用%s读取输入123到字符数组里:
num0里面存的并不是整数1,而是字符'1',它对应的ASCII码是49。
如果你直接printf("%d", num0),输出的会是49,而不是我们想要的1。
'1' - '0' 等价于 49 - 48 = 1
用字符减去'0',本质就是用ASCII码相减,把数字字符转换成真正的整型数字。
下面是不写减去'0'的代码结果:

总结
这篇文章讲解了 C 语言标准输入输出库 <stdio.h> 的基础知识,核心内容可总结为三部分:
一、占位符:用于 printf/scanf 等格式化函数,标记数据位置与类型。文中给出了完整对照表,覆盖 int(%d)、long(%ld)、float(%f)、double(%lf)、char(%c)、字符串(%s)、指针(%p)等常用类型,并说明 C++ 沿用 C 风格时规则一致。
二、转义符:以反斜杠 \ 开头,用于表示无法直接输入的字符或转义特殊符号。表格列出了 \n(换行)、\t(制表符)、\(反斜杠)、"(双引号)、\0(字符串结束符)等常用转义符及其含义。
三、头文件 <stdio.h> 的四个核心函数:
printf:格式化输出,支持占位符与变量一一对应,附有完整代码示例;
scanf:从键盘读取输入,但无法读取含空格的字符串;
fgets:可读取包含空格的整行字符串,更适合处理带空格的输入;
fputs:向输出流原样写入字符串,不解析占位符、不自动换行,适合输出纯文本或写文件。