Skip to content

输入 & 输出

输入和输出是程序与用户交互的重要方式。在C语言中,标准库提供了丰富的输入输出函数,用于从键盘读取数据和向屏幕输出数据。在本章节中,我们将学习C语言中的输入输出函数及其使用方法。

1. 标准输入输出流

在C语言中,有三个标准流:

  1. 标准输入流(stdin):通常对应于键盘,用于读取用户输入
  2. 标准输出流(stdout):通常对应于屏幕,用于输出数据
  3. 标准错误流(stderr):通常对应于屏幕,用于输出错误信息

这些流在<stdio.h>头文件中定义,是程序启动时自动打开的。

2. 格式化输出函数:printf

printf是C语言中最常用的输出函数,用于向标准输出流输出格式化的数据。

2.1 基本语法

c
int printf(const char *format, ...);

2.2 说明

  • format:格式控制字符串,包含普通字符和格式说明符
  • ...:可变参数列表,对应格式说明符
  • 返回值:成功输出的字符数

2.3 格式说明符

格式说明符含义示例
%d输出十进制整数printf("%d", 123);123
%i输出十进制整数(与%d相同)printf("%i", 123);123
%o输出八进制整数printf("%o", 10);12
%x输出十六进制整数(小写)printf("%x", 10);a
%X输出十六进制整数(大写)printf("%X", 10);A
%u输出无符号十进制整数printf("%u", 123);123
%f输出浮点数printf("%f", 3.14);3.140000
%e输出科学计数法表示的浮点数(小写)printf("%e", 123.45);1.234500e+02
%E输出科学计数法表示的浮点数(大写)printf("%E", 123.45);1.234500E+02
%g自动选择%f或%e中较短的形式printf("%g", 123.45);123.45
%G自动选择%F或%E中较短的形式printf("%G", 123.45);123.45
%c输出单个字符printf("%c", 'A');A
%s输出字符串printf("%s", "Hello");Hello
%p输出指针地址printf("%p", &x);0x7ffc...
%%输出百分号本身printf("%%");%

2.4 修饰符

格式说明符可以包含修饰符,用于控制输出的宽度、精度等:

修饰符含义示例
%nd输出宽度为n的整数,右对齐printf("%5d", 123); 123
%-nd输出宽度为n的整数,左对齐printf("%-5d", 123);123
%0nd输出宽度为n的整数,不足部分用0填充printf("%05d", 123);00123
%n.mf输出浮点数,总宽度为n,小数部分为m位printf("%8.2f", 3.14); 3.14
%.mf输出浮点数,小数部分为m位printf("%.2f", 3.14159);3.14
%ns输出宽度为n的字符串,右对齐printf("%10s", "Hello"); Hello
%-ns输出宽度为n的字符串,左对齐printf("%-10s", "Hello");Hello

2.5 示例

c
#include <stdio.h>

int main() {
    int a = 123;
    float b = 3.14159;
    char c = 'A';
    char d[] = "Hello, World!";
    int *p = &a;
    
    printf("整数: %d\n", a);  // 整数: 123
    printf("浮点数: %f\n", b);  // 浮点数: 3.141590
    printf("字符: %c\n", c);  // 字符: A
    printf("字符串: %s\n", d);  // 字符串: Hello, World!
    printf("指针地址: %p\n", p);  // 指针地址: 0x7ffc...
    
    // 使用修饰符
    printf("\n使用修饰符:\n");
    printf("右对齐,宽度5: %5d\n", a);  // 右对齐,宽度5:   123
    printf("左对齐,宽度5: %-5d\n", a);  // 左对齐,宽度5: 123  
    printf("补0,宽度5: %05d\n", a);  // 补0,宽度5: 00123
    printf("浮点数,保留2位小数: %.2f\n", b);  // 浮点数,保留2位小数: 3.14
    printf("浮点数,宽度8,保留2位小数: %8.2f\n", b);  // 浮点数,宽度8,保留2位小数:    3.14
    printf("字符串,宽度15,左对齐: %-15s结束\n", d);  // 字符串,宽度15,左对齐: Hello, World! 结束
    
    return 0;
}

3. 格式化输入函数:scanf

scanf是C语言中最常用的输入函数,用于从标准输入流读取格式化的数据。

3.1 基本语法

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

3.2 说明

  • format:格式控制字符串,包含格式说明符
  • ...:可变参数列表,对应格式说明符,都是指针类型
  • 返回值:成功读取并赋值的参数个数

3.3 格式说明符

格式说明符含义示例
%d读取十进制整数scanf("%d", &a);
%i读取整数(自动识别十进制、八进制、十六进制)scanf("%i", &a);
%o读取八进制整数scanf("%o", &a);
%x读取十六进制整数scanf("%x", &a);
%u读取无符号十进制整数scanf("%u", &a);
%f读取浮点数scanf("%f", &b);
%e读取科学计数法表示的浮点数scanf("%e", &b);
%c读取单个字符scanf("%c", &c);
%s读取字符串(遇到空白字符停止)scanf("%s", s);
%p读取指针地址scanf("%p", &p);

3.4 示例

c
#include <stdio.h>

int main() {
    int a;
    float b;
    char c;
    char d[50];
    
    printf("请输入一个整数: ");
    scanf("%d", &a);
    printf("你输入的整数是: %d\n", a);
    
    printf("请输入一个浮点数: ");
    scanf("%f", &b);
    printf("你输入的浮点数是: %.2f\n", b);
    
    // 注意:在读取字符前,需要清除输入缓冲区的换行符
    while (getchar() != '\n');
    
    printf("请输入一个字符: ");
    scanf("%c", &c);
    printf("你输入的字符是: %c\n", c);
    
    printf("请输入一个字符串: ");
    scanf("%s", d);  // 遇到空白字符停止
    printf("你输入的字符串是: %s\n", d);
    
    return 0;
}

3.5 注意事项

  • 地址传递scanf的参数必须是变量的地址,使用&运算符
  • 字符串读取%s会在遇到空白字符(空格、制表符、换行符)时停止读取
  • 输入缓冲区:输入的数据会先存储在输入缓冲区中,scanf从缓冲区中读取数据
  • 换行符处理:读取字符时,需要注意输入缓冲区中的换行符
  • 返回值检查:应该检查scanf的返回值,确保数据读取成功

4. 字符输入输出函数

4.1 字符输出函数:putchar

c
int putchar(int c);
  • 功能:向标准输出流输出一个字符
  • 参数:要输出的字符(int类型,实际是char类型的ASCII码)
  • 返回值:成功输出的字符,失败返回EOF

4.2 字符输入函数:getchar

c
int getchar(void);
  • 功能:从标准输入流读取一个字符
  • 参数:无
  • 返回值:成功读取的字符(int类型),失败返回EOF

4.3 示例

c
#include <stdio.h>

int main() {
    char c;
    
    printf("请输入一个字符: ");
    c = getchar();
    printf("你输入的字符是: ");
    putchar(c);
    printf("\n");
    
    // 读取并输出多个字符,直到遇到换行符
    printf("请输入一行字符: ");
    while ((c = getchar()) != '\n') {
        putchar(c);
    }
    printf("\n");
    
    return 0;
}

5. 字符串输入输出函数

5.1 字符串输出函数:puts

c
int puts(const char *s);
  • 功能:向标准输出流输出一个字符串,并自动添加换行符
  • 参数:要输出的字符串
  • 返回值:成功返回非负数,失败返回EOF

5.2 字符串输入函数:gets(不推荐使用)

c
char *gets(char *s);
  • 功能:从标准输入流读取一行字符串,直到遇到换行符
  • 参数:存储字符串的字符数组
  • 返回值:成功返回s,失败返回NULL

注意gets函数不检查数组边界,可能导致缓冲区溢出,因此不推荐使用。

5.3 字符串输入函数:fgets(推荐使用)

c
char *fgets(char *s, int size, FILE *stream);
  • 功能:从指定流读取最多size-1个字符,直到遇到换行符或EOF
  • 参数
    • s:存储字符串的字符数组
    • size:数组大小
    • stream:输入流,标准输入为stdin
  • 返回值:成功返回s,失败返回NULL

5.4 示例

c
#include <stdio.h>
#include <string.h>

int main() {
    char s1[50] = "Hello, World!";
    char s2[50];
    
    // 输出字符串
    puts(s1);  // 输出Hello, World!并自动换行
    
    // 输入字符串(使用fgets)
    printf("请输入一行字符串: ");
    fgets(s2, 50, stdin);
    
    // 移除fgets读取的换行符
    size_t len = strlen(s2);
    if (len > 0 && s2[len - 1] == '\n') {
        s2[len - 1] = '\0';
    }
    
    printf("你输入的字符串是: %s\n", s2);
    
    return 0;
}

6. 其他输入输出函数

6.1 格式化输出到字符串:sprintf

c
int sprintf(char *str, const char *format, ...);
  • 功能:将格式化的数据输出到字符串中
  • 参数
    • str:存储结果的字符数组
    • format:格式控制字符串
    • ...:可变参数列表
  • 返回值:成功输出的字符数

6.2 示例

c
#include <stdio.h>

int main() {
    char s[100];
    int a = 123;
    float b = 3.14;
    
    // 格式化输出到字符串
    sprintf(s, "整数: %d, 浮点数: %.2f", a, b);
    printf("格式化后的字符串: %s\n", s);  // 格式化后的字符串: 整数: 123, 浮点数: 3.14
    
    return 0;
}

6.3 从字符串读取格式化数据:sscanf

c
int sscanf(const char *str, const char *format, ...);
  • 功能:从字符串中读取格式化的数据
  • 参数
    • str:要读取的字符串
    • format:格式控制字符串
    • ...:可变参数列表,都是指针类型
  • 返回值:成功读取并赋值的参数个数

6.4 示例

c
#include <stdio.h>

int main() {
    char s[] = "123 3.14 A";
    int a;
    float b;
    char c;
    
    // 从字符串读取格式化数据
    sscanf(s, "%d %f %c", &a, &b, &c);
    printf("a = %d, b = %.2f, c = %c\n", a, b, c);  // a = 123, b = 3.14, c = A
    
    return 0;
}

7. 输入输出的注意事项

7.1 输入缓冲区

当使用scanfgetchar等函数读取输入时,输入的数据会先存储在输入缓冲区中,然后由这些函数从缓冲区中读取。如果输入缓冲区中有剩余的数据,下一次读取会直接从缓冲区中获取,而不会等待用户输入。

7.2 示例:输入缓冲区问题

c
#include <stdio.h>

int main() {
    int a;
    char c;
    
    printf("请输入一个整数: ");
    scanf("%d", &a);
    printf("你输入的整数是: %d\n", a);
    
    printf("请输入一个字符: ");
    c = getchar();  // 会读取输入缓冲区中的换行符
    printf("你输入的字符是: '%c'\n", c);  // 输出换行符
    
    return 0;
}

7.3 解决方法

c
#include <stdio.h>

int main() {
    int a;
    char c;
    
    printf("请输入一个整数: ");
    scanf("%d", &a);
    printf("你输入的整数是: %d\n", a);
    
    // 方法1:使用getchar清除换行符
    while (getchar() != '\n');
    
    printf("请输入一个字符: ");
    c = getchar();
    printf("你输入的字符是: '%c'\n", c);
    
    return 0;
}

7.4 安全性

  • 缓冲区溢出:使用gets函数时,可能会导致缓冲区溢出,应该使用fgets代替
  • 格式字符串漏洞:使用printf时,应该避免使用用户提供的格式字符串
  • 类型匹配:确保scanf的格式说明符与变量类型匹配

8. 示例:综合运用

让我们看一个综合运用输入输出函数的例子:

c
#include <stdio.h>
#include <string.h>

// 学生结构体
typedef struct {
    int id;
    char name[50];
    int age;
    float score;
} Student;

int main() {
    Student students[3];
    int i;
    
    // 输入学生信息
    for (i = 0; i < 3; i++) {
        printf("请输入第%d个学生的信息:\n", i + 1);
        
        printf("ID: ");
        scanf("%d", &students[i].id);
        
        // 清除输入缓冲区的换行符
        while (getchar() != '\n');
        
        printf("姓名: ");
        fgets(students[i].name, 50, stdin);
        
        // 移除fgets读取的换行符
        size_t len = strlen(students[i].name);
        if (len > 0 && students[i].name[len - 1] == '\n') {
            students[i].name[len - 1] = '\0';
        }
        
        printf("年龄: ");
        scanf("%d", &students[i].age);
        
        printf("成绩: ");
        scanf("%f", &students[i].score);
        
        printf("\n");
    }
    
    // 输出学生信息
    printf("学生信息表:\n");
    printf("%-5s %-20s %-5s %-6s\n", "ID", "姓名", "年龄", "成绩");
    printf("----------------------------------------\n");
    
    for (i = 0; i < 3; i++) {
        printf("%-5d %-20s %-5d %-6.1f\n", 
               students[i].id, students[i].name, students[i].age, students[i].score);
    }
    
    // 计算平均成绩
    float sum = 0;
    for (i = 0; i < 3; i++) {
        sum += students[i].score;
    }
    float average = sum / 3;
    
    printf("----------------------------------------\n");
    printf("平均成绩: %.2f\n", average);
    
    return 0;
}

9. 小结

输入和输出是程序与用户交互的重要方式,C语言提供了丰富的输入输出函数。在本章节中,我们学习了:

  1. 标准输入输出流stdinstdoutstderr
  2. 格式化输出函数printf,用于向标准输出流输出格式化的数据
  3. 格式化输入函数scanf,用于从标准输入流读取格式化的数据
  4. 字符输入输出函数putchargetchar,用于输入输出单个字符
  5. 字符串输入输出函数putsfgets,用于输入输出字符串
  6. 其他输入输出函数sprintfsscanf,用于字符串的格式化操作
  7. 输入输出的注意事项:输入缓冲区问题、安全性问题等

通过合理使用这些输入输出函数,可以编写出更加友好、交互性更强的C程序。在实际编程中,应该根据具体的需求选择合适的输入输出函数,并注意处理输入缓冲区、安全性等问题。