# 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` | 回车 | **宽度和精度控制** ```c 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)读取格式化数据。 **基本用法** ```c int num; scanf("%d", &num); // 注意:必须使用&取地址 char str[100]; scanf("%s", str); // 字符串不需要& float f; scanf("%f", &f); ``` **scanf的返回值** ```c int result = scanf("%d %d", &a, &b); // result: 成功匹配的项数 // 返回值为EOF时表示输入结束或错误 ``` **缓冲区残留问题** ```c 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:格式化到字符串缓冲区** ```c char buffer[100]; sprintf(buffer, "Value: %d, Name: %s", 42, "test"); // buffer = "Value: 42, Name: test" ``` **snprintf:安全版本,防止缓冲区溢出** ```c char buffer[10]; int result = snprintf(buffer, sizeof(buffer), "Value: %d", 123456789); // buffer = "Value: 123" // result = 需要的字符数(不含'\0') // 如果result >= sizeof(buffer),表示输出被截断 ``` ### fprintf/fscanf **fprintf:格式化输出到文件** ```c FILE *fp = fopen("output.txt", "w"); if (fp) { fprintf(fp, "Integer: %d\n", 42); fprintf(fp, "String: %s\n", "Hello"); fclose(fp); } ``` **fscanf:从文件格式化输入** ```c FILE *fp = fopen("input.txt", "r"); if (fp) { int num; char str[100]; fscanf(fp, "%d %s", &num, str); fclose(fp); } ``` ### gets/puts(不推荐,已废弃) ```c // gets:已废弃,不安全 // char str[100]; // gets(str); // 可能导致缓冲区溢出 // puts:输出字符串并自动换行 puts("Hello, World!"); // 等同于 printf("Hello, World!\n"); ``` ### 嵌入式场景 1. **串口输出格式化** ```c printf("[UART] Received: 0x%02X\n", data); printf("[ERROR] Code: %d, Message: %s\n", err_code, err_msg); ``` 2. **日志构造** ```c char log_buffer[256]; snprintf(log_buffer, sizeof(log_buffer), "[%s] %s: %d", timestamp, function, line); ``` 3. **配置文件解析** ```c 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` | 存储已读取字符数 | - | | `%*` | 跳过输入(赋值抑制) | - | ### 格式化输出控制 ```c // 宽度和精度 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格式化输出详解 ```c // printf_example.c #include #include 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; } ``` **编译命令:** ```bash gcc -o printf_example printf_example.c ./printf_example ``` ### 示例2:scanf输入详解 ```c // scanf_example.c #include #include 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; } ``` **编译命令:** ```bash gcc -o scanf_example scanf_example.c ./scanf_example ``` ### 示例3:sprintf/snprintf安全格式化 ```c // sprintf_example.c #include #include 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; } ``` **编译命令:** ```bash gcc -o sprintf_example sprintf_example.c ./sprintf_example ``` ### 示例4:fprintf/fscanf文件操作 ```c // file_io_example.c #include #include // 学生结构体 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; } ``` **编译命令:** ```bash gcc -o file_io_example file_io_example.c ./file_io_example ``` ### 示例5:嵌入式场景应用 ```c // embedded_io_example.c #include #include #include // 模拟串口寄存器 #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; } ``` **编译命令:** ```bash gcc -o embedded_io_example embedded_io_example.c -std=c99 ./embedded_io_example ``` ## 6.4 注意事项与易错点 ### printf注意事项 1. **格式说明符与参数类型不匹配** ```c int x = 42; printf("%f", x); // 错误!undefined behavior printf("%d", (double)x); // 错误! ``` 2. **缓冲区刷新** ```c printf("Hello"); // 可能不会立即显示 fflush(stdout); // 强制刷新 // 或使用换行符 printf("Hello\n"); ``` 3. **线程安全** - printf不是线程安全的 - 多线程环境需要加锁或使用线程安全版本 ### scanf注意事项 1. **必须使用取地址符** ```c int x; scanf("%d", x); // 错误! scanf("%d", &x); // 正确 ``` 2. **缓冲区溢出风险** ```c char str[10]; scanf("%s", str); // 危险!输入可能超过9个字符 scanf("%9s", str); // 安全:限制读取长度 ``` 3. **scanf返回值** ```c int result = scanf("%d", &x); if (result == EOF) { // 输入结束或错误 } else if (result == 0) { // 输入不匹配 } else { // 成功读取 } ``` ### sprintf/snprintf注意事项 1. **sprintf缓冲区溢出** ```c char buffer[10]; sprintf(buffer, "Value: %d", 123456789); // 溢出! ``` 2. **snprintf返回值** ```c int result = snprintf(buffer, size, "..."); if (result >= size) { // 输出被截断 } ``` 3. **字符数组必须以null结尾** ```c char buffer[5]; snprintf(buffer, 5, "Hello"); // 结果:Hell\0 // buffer[4]是null字符 ``` ### 文件IO注意事项 1. **检查文件指针** ```c FILE *fp = fopen("file.txt", "r"); if (fp == NULL) { perror("Error opening file"); return 1; } ``` 2. **及时关闭文件** ```c // 使用完文件后立即关闭 fclose(fp); ``` 3. **二进制模式 vs 文本模式** ```c fopen("file.bin", "rb"); // 二进制读取 fopen("file.txt", "w"); // 文本写入 ``` ### 常见错误示例 ```c // 错误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:输入与格式不匹配 **正确处理:** ```c 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读取数字后,换行符会留在缓冲区中,影响后续的字符读取。 **解决方案:** ```c // 方案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: 如何格式化输出十六进制、八进制等特殊格式? **答:** ```c 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. **使用互斥锁**: ```c pthread_mutex_t print_mutex = PTHREAD_MUTEX_INITIALIZER; pthread_mutex_lock(&print_mutex); printf("Thread-safe message\n"); pthread_mutex_unlock(&print_mutex); ``` 2. **使用线程安全函数**: ```c // POSIX线程安全版本 flockfile(stdout); printf("Thread-safe message\n"); funlockfile(stdout); ``` 3. **使用日志库**:许多日志库提供线程安全的功能