6. 格式化IO.md 26 KB

6 格式化IO

6.1 完整概念讲解

printf详解

printf是C语言中最常用的格式化输出函数,用于将格式化的数据输出到标准输出(stdout)。

格式化输出说明符

说明符 类型 示例
%d 有符号十进制整数 printf("%d", 42);42
%u 无符号十进制整数 printf("%u", 42u);42
%f 浮点数(默认6位小数) printf("%f", 3.14);3.140000
%c 字符 printf("%c", 'A');A
%s 字符串 printf("%s", "Hello");Hello
%p 指针地址 printf("%p", ptr);0x7fff5fbff8d0
%x 十六进制(小写) printf("%x", 255);ff
%X 十六进制(大写) printf("%X", 255);FF
%o 八进制 printf("%o", 8);10
%ld 长整型 printf("%ld", 1234567890L);
%lu 无符号长整型 printf("%lu", 1234567890UL);
%zu size_t类型 printf("%zu", sizeof(int));
%e 科学计数法 printf("%e", 3140000.0);3.140000e+06
%g 自动选择%f或%e printf("%g", 0.00001);1e-05

转义字符

转义字符 含义
\n 换行
\t 制表符
\\ 反斜杠
\" 双引号
\0 空字符
\r 回车

宽度和精度控制

printf("%10d", 42);      // 右对齐,宽度10:"        42"
printf("%-10d", 42);     // 左对齐,宽度10:"42        "
printf("%010d", 42);     // 零填充:"0000000042"
printf("%.2f", 3.14159); // 2位小数:"3.14"
printf("%10.2f", 3.14);  // 宽度10,2位小数:"      3.14"

scanf详解

scanf用于从标准输入(stdin)读取格式化数据。

基本用法

int num;
scanf("%d", &num);  // 注意:必须使用&取地址

char str[100];
scanf("%s", str);   // 字符串不需要&

float f;
scanf("%f", &f);

scanf的返回值

int result = scanf("%d %d", &a, &b);
// result: 成功匹配的项数
// 返回值为EOF时表示输入结束或错误

缓冲区残留问题

int num;
char str[100];

// 问题示例
scanf("%d", &num);     // 输入"123\n"
scanf("%s", str);      // 可能读取到换行符

// 解决方案
scanf("%d", &num);
while (getchar() != '\n');  // 清除缓冲区
scanf("%99s", str);          // 限制读取长度

sprintf/snprintf

sprintf:格式化到字符串缓冲区

char buffer[100];
sprintf(buffer, "Value: %d, Name: %s", 42, "test");
// buffer = "Value: 42, Name: test"

snprintf:安全版本,防止缓冲区溢出

char buffer[10];
int result = snprintf(buffer, sizeof(buffer), "Value: %d", 123456789);
// buffer = "Value: 123"
// result = 需要的字符数(不含'\0')
// 如果result >= sizeof(buffer),表示输出被截断

fprintf/fscanf

fprintf:格式化输出到文件

FILE *fp = fopen("output.txt", "w");
if (fp) {
    fprintf(fp, "Integer: %d\n", 42);
    fprintf(fp, "String: %s\n", "Hello");
    fclose(fp);
}

fscanf:从文件格式化输入

FILE *fp = fopen("input.txt", "r");
if (fp) {
    int num;
    char str[100];
    fscanf(fp, "%d %s", &num, str);
    fclose(fp);
}

gets/puts(不推荐,已废弃)

// gets:已废弃,不安全
// char str[100];
// gets(str);  // 可能导致缓冲区溢出

// puts:输出字符串并自动换行
puts("Hello, World!");  // 等同于 printf("Hello, World!\n");

嵌入式场景

  1. 串口输出格式化

    printf("[UART] Received: 0x%02X\n", data);
    printf("[ERROR] Code: %d, Message: %s\n", err_code, err_msg);
    
  2. 日志构造

    char log_buffer[256];
    snprintf(log_buffer, sizeof(log_buffer),
            "[%s] %s: %d", timestamp, function, line);
    
  3. 配置文件解析

    FILE *config = fopen("config.txt", "r");
    if (config) {
       fscanf(config, "baudrate=%d", &baudrate);
       fscanf(config, "timeout=%d", &timeout);
       fclose(config);
    }
    

6.2 核心API/语法

printf格式说明符速查表

说明符 类型 大小修饰符 示例
%d int %hd(short), %ld(long), %lld(long long) %ld
%u unsigned int %hu, %lu, %llu %lu
%f double %Lf(long double) %Lf
%c char - %c
%s char* - %s
%p void* - %p
%x unsigned int %hx, %lx, %llx %lx
%n int* - 存储已输出字符数

scanf格式说明符速查表

说明符 用途 示例输入
%d 读取int 123
%u 读取unsigned int 123
%f 读取float 3.14
%lf 读取double 3.14
%c 读取char(包括空格) a
%s 读取字符串(遇空格停止) hello
%[^\n] 读取整行 hello world
%n 存储已读取字符数 -
%* 跳过输入(赋值抑制) -

格式化输出控制

// 宽度和精度
printf("%10d", 42);        // 右对齐,宽度10
printf("%-10d", 42);       // 左对齐
printf("%010d", 42);       // 零填充
printf("%.2f", 3.14159);   // 2位小数
printf("%10.2f", 3.14);    // 宽度10,2位小数

// 动态精度
printf("%.*f", precision, value);  // precision是变量

// 特殊格式
printf("%#x", 255);        // 输出前缀:0xff
printf("%#o", 8);          // 输出前缀:010
printf("%+d", 42);         // 显示正号:+42
printf(" %d", 42);         // 空格填充正数: 42

6.3 代码示例(完整可编译,附gcc命令)

示例1:printf格式化输出详解

// printf_example.c
#include <stdio.h>
#include <stdint.h>

int main() {
    int int_val = 42;
    unsigned int uint_val = 255;
    float float_val = 3.14159;
    double double_val = 2.718281828;
    char char_val = 'A';
    char str_val[] = "Hello, World!";
    void *ptr_val = &int_val;

    printf("=== Basic Format Specifiers ===\n");
    printf("%%d:  %d\n", int_val);
    printf("%%u:  %u\n", uint_val);
    printf("%%f:  %f\n", float_val);
    printf("%%e:  %e\n", float_val);
    printf("%%g:  %g\n", float_val);
    printf("%%c:  %c\n", char_val);
    printf("%%s:  %s\n", str_val);
    printf("%%p:  %p\n", ptr_val);
    printf("%%x:  %x\n", uint_val);
    printf("%%X:  %X\n", uint_val);
    printf("%%o:  %o\n", uint_val);

    printf("\n=== Size Modifiers ===\n");
    printf("%%hd:  %hd\n", (short)12345);
    printf("%%ld:  %ld\n", 1234567890L);
    printf("%%lld: %lld\n", 123456789012345LL);
    printf("%%hu:  %hu\n", (unsigned short)12345);
    printf("%%lu:  %lu\n", 1234567890UL);
    printf("%%llu: %llu\n", 123456789012345ULL);
    printf("%%zu:  %zu\n", sizeof(int));

    printf("\n=== Width and Precision ===\n");
    printf("%%10d:    [%10d]\n", 42);
    printf("%%-10d:   [%-10d]\n", 42);
    printf("%%010d:   [%010d]\n", 42);
    printf("%%.2f:    [%.2f]\n", float_val);
    printf("%%10.2f:  [%10.2f]\n", float_val);
    printf("%%-10.2f: [%-10.2f]\n", float_val);
    printf("%%10.5f:  [%10.5f]\n", float_val);

    printf("\n=== Escape Characters ===\n");
    printf("Newline:    \"Line1\\nLine2\"\n");
    printf("Tab:        \"Col1\\tCol2\"\n");
    printf("Backslash:  \"C:\\\\path\"\n");
    printf("Quote:      \"Say \\\"Hello\\\"\"\n");

    printf("\n=== Special Formats ===\n");
    printf("%%#x:  %#x\n", uint_val);
    printf("%%#X:  %#X\n", uint_val);
    printf("%%#o:  %#o\n", uint_val);
    printf("%%+d:  %+d\n", 42);
    printf("%%+d:  %+d\n", -42);
    printf(" %%d:  % d\n", 42);

    return 0;
}

编译命令:

gcc -o printf_example printf_example.c
./printf_example

示例2:scanf输入详解

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

int main() {
    int int_val;
    unsigned int uint_val;
    float float_val;
    double double_val;
    char char_val;
    char str_val[100];
    char line_val[100];

    printf("=== Basic scanf Usage ===\n");
    printf("Enter an integer: ");
    scanf("%d", &int_val);
    printf("You entered: %d\n", int_val);

    printf("Enter an unsigned integer: ");
    scanf("%u", &uint_val);
    printf("You entered: %u\n", uint_val);

    printf("Enter a float: ");
    scanf("%f", &float_val);
    printf("You entered: %f\n", float_val);

    printf("Enter a double: ");
    scanf("%lf", &double_val);
    printf("You entered: %f\n", double_val);

    // 清除缓冲区中的换行符
    while (getchar() != '\n');

    printf("\n=== Character and String Input ===\n");
    printf("Enter a character: ");
    scanf("%c", &char_val);
    printf("You entered: %c\n", char_val);

    // 清除缓冲区
    while (getchar() != '\n');

    printf("Enter a word (no spaces): ");
    scanf("%99s", str_val);  // 限制长度防止溢出
    printf("You entered: %s\n", str_val);

    // 清除缓冲区
    while (getchar() != '\n');

    printf("Enter a line: ");
    scanf("%99[^\n]", line_val);  // 读取整行
    printf("You entered: %s\n", line_val);

    printf("\n=== Return Value ===\n");
    int a, b, result;
    printf("Enter two integers: ");
    result = scanf("%d %d", &a, &b);
    printf("Successfully matched %d items\n", result);
    printf("Values: %d and %d\n", a, b);

    printf("\n=== Multiple Values ===\n");
    int x, y, z;
    printf("Enter three integers separated by spaces: ");
    if (scanf("%d %d %d", &x, &y, &z) == 3) {
        printf("Sum: %d\n", x + y + z);
    } else {
        printf("Error reading input\n");
    }

    return 0;
}

编译命令:

gcc -o scanf_example scanf_example.c
./scanf_example

示例3:sprintf/snprintf安全格式化

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

int main() {
    char buffer[200];
    char small_buffer[10];
    int num = 42;
    float pi = 3.14159;
    char name[] = "World";

    printf("=== sprintf Examples ===\n");

    // 基本sprintf
    sprintf(buffer, "Number: %d, Float: %.2f, String: %s", num, pi, name);
    printf("Result: %s\n", buffer);

    // 格式化到字符串
    sprintf(buffer, "Hex: 0x%08X", 0xDEADBEEF);
    printf("Result: %s\n", buffer);

    printf("\n=== snprintf Examples ===\n");

    // 安全格式化,防止溢出
    int result = snprintf(small_buffer, sizeof(small_buffer),
                         "Value: %d", 123456789);
    printf("Small buffer: '%s'\n", small_buffer);
    printf("Required length: %d\n", result);

    if (result >= sizeof(small_buffer)) {
        printf("WARNING: Output was truncated!\n");
    }

    // 正常情况
    result = snprintf(small_buffer, sizeof(small_buffer),
                     "Value: %d", 42);
    printf("Small buffer: '%s'\n", small_buffer);
    printf("Required length: %d\n", result);

    printf("\n=== Practical Examples ===\n");

    // 构造日志消息
    char log_msg[256];
    int line_num = 42;
    const char *func_name = "main";
    snprintf(log_msg, sizeof(log_msg),
            "[%s:%d] Processing data...", func_name, line_num);
    printf("Log: %s\n", log_msg);

    // 构造文件路径
    char filepath[512];
    int file_id = 1001;
    snprintf(filepath, sizeof(filepath),
            "/data/logs/log_%04d.txt", file_id);
    printf("Path: %s\n", filepath);

    // 构造URL
    char url[1024];
    const char *host = "api.example.com";
    const char *endpoint = "/users";
    int user_id = 123;
    snprintf(url, sizeof(url),
            "https://%s%s?id=%d", host, endpoint, user_id);
    printf("URL: %s\n", url);

    return 0;
}

编译命令:

gcc -o sprintf_example sprintf_example.c
./sprintf_example

示例4:fprintf/fscanf文件操作

// file_io_example.c
#include <stdio.h>
#include <stdlib.h>

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

int main() {
    const char *filename = "students.txt";
    FILE *fp;

    printf("=== Writing to File with fprintf ===\n");
    fp = fopen(filename, "w");
    if (fp == NULL) {
        perror("Error opening file for writing");
        return 1;
    }

    // 写入标题
    fprintf(fp, "# Student Records\n");
    fprintf(fp, "# ID\tName\tScore\n");

    // 写入数据
    Student students[] = {
        {1, "Alice", 95.5},
        {2, "Bob", 87.3},
        {3, "Charlie", 92.1}
    };

    for (int i = 0; i < 3; i++) {
        fprintf(fp, "%d\t%s\t%.1f\n",
                students[i].id, students[i].name, students[i].score);
    }

    fclose(fp);
    printf("Data written to %s\n", filename);

    // 显示文件内容
    printf("\n=== File Contents ===\n");
    fp = fopen(filename, "r");
    if (fp) {
        char line[256];
        while (fgets(line, sizeof(line), fp)) {
            printf("%s", line);
        }
        fclose(fp);
    }

    printf("\n=== Reading from File with fscanf ===\n");
    fp = fopen(filename, "r");
    if (fp == NULL) {
        perror("Error opening file for reading");
        return 1;
    }

    // 跳过注释行
    char line[256];
    while (fgets(line, sizeof(line), fp)) {
        if (line[0] != '#') {
            break;
        }
    }

    // 读取数据
    Student read_student;
    while (fscanf(fp, "%d %49s %f",
                  &read_student.id, read_student.name, &read_student.score) == 3) {
        printf("Read: ID=%d, Name=%s, Score=%.1f\n",
               read_student.id, read_student.name, read_student.score);
    }

    fclose(fp);

    printf("\n=== Writing Formatted Data ===\n");
    const char *data_file = "data.txt";
    fp = fopen(data_file, "w");
    if (fp) {
        // 格式化写入多种数据类型
        fprintf(fp, "Integer: %d\n", 42);
        fprintf(fp, "Float: %.3f\n", 3.14159);
        fprintf(fp, "String: %s\n", "Hello");
        fprintf(fp, "Char: %c\n", 'A');
        fprintf(fp, "Pointer: %p\n", (void*)fp);
        fprintf(fp, "Hex: 0x%08X\n", 0xDEADBEEF);
        fclose(fp);
        printf("Formatted data written to %s\n", data_file);
    }

    return 0;
}

编译命令:

gcc -o file_io_example file_io_example.c
./file_io_example

示例5:嵌入式场景应用

// embedded_io_example.c
#include <stdio.h>
#include <stdint.h>
#include <string.h>

// 模拟串口寄存器
#define UART_DR  (*(volatile uint32_t*)0x40011004)
#define UART_SR  (*(volatile uint32_t*)0x40011000)

// 日志级别
typedef enum {
    LOG_DEBUG,
    LOG_INFO,
    LOG_WARN,
    LOG_ERROR
} LogLevel;

// 日志函数
void log_message(LogLevel level, const char *function, int line,
                const char *format, ...) {
    char buffer[256];
    char *level_str;

    switch (level) {
        case LOG_DEBUG: level_str = "DEBUG"; break;
        case LOG_INFO:  level_str = "INFO"; break;
        case LOG_WARN:  level_str = "WARN"; break;
        case LOG_ERROR: level_str = "ERROR"; break;
        default:        level_str = "UNKNOWN"; break;
    }

    // 构造日志前缀
    int prefix_len = snprintf(buffer, sizeof(buffer), "[%s] %s:%d: ",
                             level_str, function, line);

    // 格式化实际消息
    va_list args;
    va_start(args, format);
    vsnprintf(buffer + prefix_len, sizeof(buffer) - prefix_len, format, args);
    va_end(args);

    // 输出到串口(模拟)
    printf("%s\n", buffer);
}

// 简化版日志宏
#define LOG(level, ...) log_message(level, __func__, __LINE__, __VA_ARGS__)

// 传感器数据结构
typedef struct {
    uint32_t timestamp;
    int16_t temperature;
    uint16_t humidity;
    uint32_t pressure;
} SensorData;

// 格式化传感器数据为CSV格式
int format_sensor_csv(const SensorData *data, char *buffer, size_t size) {
    return snprintf(buffer, size, "%u,%d,%u,%u",
                   data->timestamp, data->temperature,
                   data->humidity, data->pressure);
}

// 格式化传感器数据为JSON格式
int format_sensor_json(const SensorData *data, char *buffer, size_t size) {
    return snprintf(buffer, size,
                   "{\"timestamp\":%u,\"temperature\":%d,\"humidity\":%u,\"pressure\":%u}",
                   data->timestamp, data->temperature,
                   data->humidity, data->pressure);
}

// 解析配置文件
int parse_config(const char *filename, int *baudrate, int *timeout) {
    FILE *fp = fopen(filename, "r");
    if (!fp) {
        return -1;
    }

    char line[128];
    while (fgets(line, sizeof(line), fp)) {
        // 跳过注释和空行
        if (line[0] == '#' || line[0] == '\n') {
            continue;
        }

        // 解析key=value格式
        if (sscanf(line, "baudrate=%d", baudrate) == 1) {
            continue;
        }
        if (sscanf(line, "timeout=%d", timeout) == 1) {
            continue;
        }
    }

    fclose(fp);
    return 0;
}

// 模拟硬件寄存器格式化输出
void format_register_dump(uint32_t *registers, int count) {
    printf("=== Register Dump ===\n");
    for (int i = 0; i < count; i++) {
        printf("REG[0x%02X]: 0x%08X (bin: ", i * 4, registers[i]);
        // 输出二进制表示
        for (int bit = 31; bit >= 0; bit--) {
            printf("%d", (registers[i] >> bit) & 1);
            if (bit % 4 == 0 && bit > 0) {
                printf(" ");
            }
        }
        printf(")\n");
    }
}

int main() {
    printf("=== Embedded IO Examples ===\n\n");

    // 1. 日志格式化
    printf("--- Log Messages ---\n");
    LOG(LOG_INFO, "System initialized");
    LOG(LOG_DEBUG, "Sensor value: %d", 25);
    LOG(LOG_WARN, "Low battery: %d%%", 15);
    LOG(LOG_ERROR, "Failed to read sensor at address 0x%02X", 0x48);

    // 2. 传感器数据格式化
    printf("\n--- Sensor Data Formatting ---\n");
    SensorData sensor = {
        .timestamp = 1234567890,
        .temperature = 256,  // 25.6°C (放大10倍)
        .humidity = 650,     // 65.0% (放大10倍)
        .pressure = 101325   // 1013.25 hPa (放大100倍)
    };

    char csv_buffer[128];
    format_sensor_csv(&sensor, csv_buffer, sizeof(csv_buffer));
    printf("CSV: %s\n", csv_buffer);

    char json_buffer[256];
    format_sensor_json(&sensor, json_buffer, sizeof(json_buffer));
    printf("JSON: %s\n", json_buffer);

    // 3. 配置文件解析(模拟)
    printf("\n--- Configuration Parsing ---\n");
    int baudrate = 0, timeout = 0;
    // 注意:实际使用时需要真实的配置文件
    // parse_config("config.txt", &baudrate, &timeout);
    printf("Default baudrate: %d, timeout: %d\n", 9600, 30);

    // 4. 寄存器转储格式化
    printf("\n--- Register Dump ---\n");
    uint32_t regs[] = {0x00000001, 0xDEADBEEF, 0x12345678, 0xFFFFFFFF};
    format_register_dump(regs, sizeof(regs) / sizeof(regs[0]));

    // 5. 串口输出格式化(模拟)
    printf("\n--- UART Output Simulation ---\n");
    char uart_msg[64];
    snprintf(uart_msg, sizeof(uart_msg), "ACK:CMD%04X:LEN%02X", 0x1234, 16);
    printf("UART TX: %s\n", uart_msg);

    return 0;
}

编译命令:

gcc -o embedded_io_example embedded_io_example.c -std=c99
./embedded_io_example

6.4 注意事项与易错点

printf注意事项

  1. 格式说明符与参数类型不匹配

    int x = 42;
    printf("%f", x);  // 错误!undefined behavior
    printf("%d", (double)x);  // 错误!
    
  2. 缓冲区刷新

    printf("Hello");  // 可能不会立即显示
    fflush(stdout);    // 强制刷新
    // 或使用换行符
    printf("Hello\n");
    
  3. 线程安全

    • printf不是线程安全的
    • 多线程环境需要加锁或使用线程安全版本

scanf注意事项

  1. 必须使用取地址符

    int x;
    scanf("%d", x);     // 错误!
    scanf("%d", &x);    // 正确
    
  2. 缓冲区溢出风险

    char str[10];
    scanf("%s", str);   // 危险!输入可能超过9个字符
    scanf("%9s", str);  // 安全:限制读取长度
    
  3. scanf返回值

    int result = scanf("%d", &x);
    if (result == EOF) {
       // 输入结束或错误
    } else if (result == 0) {
       // 输入不匹配
    } else {
       // 成功读取
    }
    

sprintf/snprintf注意事项

  1. sprintf缓冲区溢出

    char buffer[10];
    sprintf(buffer, "Value: %d", 123456789);  // 溢出!
    
  2. snprintf返回值

    int result = snprintf(buffer, size, "...");
    if (result >= size) {
       // 输出被截断
    }
    
  3. 字符数组必须以null结尾

    char buffer[5];
    snprintf(buffer, 5, "Hello");  // 结果:Hell\0
    // buffer[4]是null字符
    

文件IO注意事项

  1. 检查文件指针

    FILE *fp = fopen("file.txt", "r");
    if (fp == NULL) {
       perror("Error opening file");
       return 1;
    }
    
  2. 及时关闭文件

    // 使用完文件后立即关闭
    fclose(fp);
    
  3. 二进制模式 vs 文本模式

    fopen("file.bin", "rb");  // 二进制读取
    fopen("file.txt", "w");   // 文本写入
    

常见错误示例

// 错误1:printf格式说明符不匹配
printf("%d", 3.14);  // undefined behavior
printf("%f", 42);    // undefined behavior

// 错误2:scanf缺少取地址符
int x;
scanf("%d", x);     // undefined behavior

// 错误3:sprintf缓冲区溢出
char buffer[10];
sprintf(buffer, "Very long string that exceeds buffer");  // 溢出

// 错误4:未检查返回值
FILE *fp = fopen("nonexistent.txt", "r");
fscanf(fp, "%d", &x);  // fp可能为NULL

// 错误5:未关闭文件
void func() {
    FILE *fp = fopen("temp.txt", "w");
    fprintf(fp, "data");
    // 忘记fclose(fp) - 内存泄漏
}

6.5 面试要点(3-5个Q&A)

Q1: printf和sprintf有什么区别?什么时候用snprintf?

答:

  • printf:输出到标准输出(stdout)
  • sprintf:输出到字符串缓冲区
  • snprintf:带长度限制的sprintf,防止缓冲区溢出

使用snprintf的场景:

  1. 构造字符串时需要防止缓冲区溢出
  2. 需要知道格式化后的字符串长度
  3. 嵌入式系统中内存受限时

Q2: scanf的返回值表示什么?如何正确处理?

答:

  • 返回值:成功匹配的输入项数
  • 返回EOF:输入结束或读取错误
  • 返回0:输入与格式不匹配

正确处理:

int result = scanf("%d", &x);
if (result == EOF) {
    // 处理输入结束或错误
} else if (result == 0) {
    // 输入不匹配,清除无效输入
    while (getchar() != '\n');
} else {
    // 成功读取
}

Q3: 为什么在嵌入式开发中常用snprintf而不是sprintf?

答:

  1. 安全性:snprintf可以防止缓冲区溢出
  2. 可控性:返回格式化后的字符串长度
  3. 嵌入式环境:内存受限,必须精确控制缓冲区大小
  4. 调试:可以安全地截断过长的字符串

Q4: 如何处理scanf的缓冲区残留问题?

答: 缓冲区残留问题:当使用scanf读取数字后,换行符会留在缓冲区中,影响后续的字符读取。

解决方案:

// 方案1:使用getchar()清除
scanf("%d", &num);
while (getchar() != '\n');

// 方案2:使用scanf跳过空白字符
scanf("%d ", &num);  // 注意格式字符串中的空格

// 方案3:使用fgets读取整行
fgets(buffer, sizeof(buffer), stdin);
sscanf(buffer, "%d", &num);

Q5: fprintf和printf有什么区别?在嵌入式开发中有什么应用?

答:

  • printf:输出到标准输出(通常是串口终端)
  • fprintf:输出到指定文件流

嵌入式应用:

  1. 日志记录:将日志写入文件而非直接输出
  2. 数据存储:将传感器数据保存到文件
  3. 配置管理:读取和写入配置文件
  4. 调试:将调试信息输出到特定文件

Q6: 如何格式化输出十六进制、八进制等特殊格式?

答:

int value = 255;

// 十六进制
printf("Hex: %x\n", value);      // ff
printf("Hex: %#x\n", value);     // 0xff
printf("Hex: %#X\n", value);     // 0XFF
printf("Hex: %08x\n", value);    // 000000ff

// 八进制
printf("Octal: %o\n", value);    // 377
printf("Octal: %#o\n", value);   // 0377

// 二进制(需要自定义函数)
for (int i = 31; i >= 0; i--) {
    printf("%d", (value >> i) & 1);
}

Q7: 在多线程环境中使用printf需要注意什么?

答: printf不是线程安全的,多个线程同时调用printf可能导致输出混乱。

解决方案:

  1. 使用互斥锁

    pthread_mutex_t print_mutex = PTHREAD_MUTEX_INITIALIZER;
    pthread_mutex_lock(&print_mutex);
    printf("Thread-safe message\n");
    pthread_mutex_unlock(&print_mutex);
    
  2. 使用线程安全函数

    // POSIX线程安全版本
    flockfile(stdout);
    printf("Thread-safe message\n");
    funlockfile(stdout);
    
  3. 使用日志库:许多日志库提供线程安全的功能