1. printf 函数基础

1.1 函数原型与头文件

printf 函数是 C 语言中用于格式化输出的标准函数,其函数原型定义在头文件 <stdio.h> 中。具体原型如下:

int printf(const char *format, ...);
  • const char *format:格式控制字符串,用于指定输出的格式。它由普通字符和格式说明符组成。普通字符将按原样输出,而格式说明符则用于指定后续参数的输出格式。
  • ...:表示可变参数列表,这些参数将根据格式控制字符串中的格式说明符依次输出。

在使用 printf 函数时,必须包含头文件 <stdio.h>,否则编译器将无法识别该函数。

1.2 格式化输出的基本概念

printf 函数通过格式控制字符串来实现灵活的输出。格式控制字符串中的格式说明符以 % 开头,后跟一个或多个字符,用于指定输出数据的类型和格式。以下是一些常见的格式说明符及其含义:

格式说明符 含义
%d%i 输出有符号十进制整数。例如:printf("%d", 123); 输出 123
%u 输出无符号十进制整数。例如:printf("%u", 456); 输出 456
%f 输出浮点数,默认保留 6 位小数。例如:printf("%f", 3.14159); 输出 3.141590
%c 输出单个字符。例如:printf("%c", 'A'); 输出 A
%s 输出字符串。例如:printf("%s", "Hello"); 输出 Hello
%p 输出指针地址。例如:printf("%p", &var); 输出变量 var 的地址。
%% 输出一个百分号 %。例如:printf("%%"); 输出 %

格式说明符还可以包含一些可选的修饰符,用于控制输出的宽度、精度、对齐方式等。以下是一些常见的修饰符及其用法:

  • 宽度修饰符:指定输出的最小宽度。如果实际输出的宽度小于指定的宽度,则会在左侧或右侧填充空格(默认为右侧)。例如:

    printf("%5d", 123); // 输出 "   123",总宽度为 5,左侧填充空格
    printf("%-5d", 123); // 输出 "123   ",总宽度为 5,右侧填充空格
    
  • 精度修饰符:对于浮点数,指定小数点后的位数;对于字符串,指定最大输出长度。例如:

    printf("%.2f", 3.14159); // 输出 "3.14",小数点后保留 2 位
    printf("%.3s", "Hello"); // 输出 "Hel",字符串的最大长度为 3
    
  • 对齐修饰符- 用于左对齐,+ 用于强制输出正负号,空格用于在正数前输出空格。例如:

    printf("%-5d", 123); // 输出 "123   ",左对齐
    printf("%+d", 123); // 输出 "+123",强制输出正号
    printf("% d", 123); // 输出 " 123",正数前输出空格
    

通过合理使用格式说明符和修饰符,printf 函数可以实现灵活多样的格式化输出,满足各种编程需求。# 2. 格式说明符详解

2.1 常见格式说明符

printf 函数的格式说明符是实现格式化输出的核心,以下是一些常见的格式说明符及其详细用法:

整数格式说明符

  • %d%i:用于输出有符号十进制整数。这是最常用的整数格式说明符,适用于正负整数。
    printf("Value: %d\n", -123); // 输出:Value: -123
    
  • %u:用于输出无符号十进制整数。它适用于非负整数,输出时不会显示负号。
    printf("Value: %u\n", 456); // 输出:Value: 456
    
  • %o:用于输出无符号八进制整数。输出时不会显示前缀 0
    printf("Octal: %o\n", 255); // 输出:Octal: 377
    
  • %x%X:用于输出无符号十六进制整数。%x 输出小写字母,%X 输出大写字母。
    printf("Hex: %x\n", 255); // 输出:Hex: ff
    printf("Hex: %X\n", 255); // 输出:Hex: FF
    

浮点数格式说明符

  • %f:用于输出十进制浮点数,默认保留 6 位小数。可以通过精度修饰符指定小数点后的位数。
    printf("Float: %f\n", 3.1415926); // 输出:Float: 3.141593
    printf("Float: %.2f\n", 3.1415926); // 输出:Float: 3.14
    
  • %e%E:用于输出科学计数法表示的浮点数。%e 输出小写字母 e%E 输出大写字母 E
    printf("Scientific: %e\n", 3.1415926); // 输出:Scientific: 3.141593e+00
    printf("Scientific: %E\n", 3.1415926); // 输出:Scientific: 3.141593E+00
    

字符和字符串格式说明符

  • %c:用于输出单个字符。
    printf("Char: %c\n", 'A'); // 输出:Char: A
    
  • %s:用于输出字符串。可以通过精度修饰符指定最大输出长度。
    printf("String: %s\n", "Hello, World!"); // 输出:String: Hello, World!
    printf("String: %.5s\n", "Hello, World!"); // 输出:String: Hello
    

其他格式说明符

  • %p:用于输出指针地址。
    int var = 10;
    printf("Address: %p\n", &var); // 输出指针地址
    
  • %%:用于输出一个百分号 %
    printf("Percentage: %%\n"); // 输出:Percentage: %
    

2.2 格式说明符的高级用法

格式说明符还可以结合多种修饰符来实现更复杂的格式化输出,以下是一些高级用法:

宽度修饰符

宽度修饰符用于指定输出的最小宽度。如果实际输出的宽度小于指定的宽度,则会在左侧或右侧填充空格(默认为右侧)。可以通过以下方式指定宽度:

  • 固定宽度:直接指定一个整数。
    printf("%10d\n", 123); // 输出:         123
    
  • 动态宽度:使用 * 表示宽度由后续参数指定。
    int width = 10;
    printf("%*d\n", width, 123); // 输出:         123
    

精度修饰符

精度修饰符用于控制输出的精度,对于浮点数和字符串有不同的含义:

  • 浮点数:指定小数点后的位数。
    printf("%.*f\n", 3, 3.1415926); // 输出:3.142
    
  • 字符串:指定最大输出长度。
    printf("%.*s\n", 5, "Hello, World!"); // 输出:Hello
    

对齐修饰符

对齐修饰符用于控制输出的对齐方式:

  • 左对齐:使用 - 修饰符。
    printf("%-10d\n", 123); // 输出:123
    
  • 右对齐:默认对齐方式。
    printf("%10d\n", 123); // 输出:         123
    

符号修饰符

符号修饰符用于控制输出的符号:

  • 强制显示符号:使用 + 修饰符。
    printf("%+d\n", 123); // 输出:+123
    
  • 正数前加空格:使用空格修饰符。
    printf("% d\n", 123); // 输出: 123
    

填充修饰符

填充修饰符用于指定填充字符:

  • 零填充:使用 0 修饰符。
    printf("%010d\n", 123); // 输出:0000000123
    

组合修饰符

可以将多种修饰符组合使用,以实现复杂的格式化需求:

printf("%-10.3f\n", 3.1415926); // 输出:3.142
printf("%010.3f\n", 3.1415926); // 输出:000003.142
printf("%+010.3f\n", 3.1415926); // 输出:+00003.142

通过合理使用这些高级修饰符,printf 函数可以实现更加灵活和精确的格式化输出,满足复杂的编程需求。# 3. 输出控制与格式化

3.1 宽度与对齐

printf 函数通过宽度修饰符和对齐修饰符来控制输出的宽度和对齐方式,从而实现更加整齐美观的输出效果。

宽度修饰符

宽度修饰符用于指定输出的最小宽度。如果实际输出的宽度小于指定的宽度,则会在左侧或右侧填充空格(默认为右侧)。可以通过以下方式指定宽度:

  • 固定宽度:直接指定一个整数。例如:

    printf("%10d\n", 123); // 输出:         123
    printf("%10s\n", "Hello"); // 输出:     Hello
    
  • 动态宽度:使用 * 表示宽度由后续参数指定。这种方式更加灵活,可以根据实际需求动态调整宽度。例如:

    int width = 10;
    printf("%*d\n", width, 123); // 输出:         123
    printf("%*s\n", width, "Hello"); // 输出:     Hello
    

对齐修饰符

对齐修饰符用于控制输出的对齐方式,主要分为左对齐和右对齐:

  • 左对齐:使用 - 修饰符。左对齐时,输出内容会靠左对齐,右侧填充空格。例如:

    printf("%-10d\n", 123); // 输出:123
    printf("%-10s\n", "Hello"); // 输出:Hello
    
  • 右对齐:默认对齐方式。右对齐时,输出内容会靠右对齐,左侧填充空格。例如:

    printf("%10d\n", 123); // 输出:         123
    printf("%10s\n", "Hello"); // 输出:     Hello
    

实际应用示例

在实际编程中,宽度和对齐修饰符常用于对齐表格数据,使输出更加整齐。例如,输出一个简单的成绩表:

printf("%-10s%-10s%-10s\n", "Name", "Score", "Grade");
printf("%-10s%-10d%-10s\n", "Alice", 85, "A");
printf("%-10s%-10d%-10s\n", "Bob", 78, "B");
printf("%-10s%-10d%-10s\n", "Charlie", 92, "A");

输出结果如下:

Name       Score     Grade    
Alice      85        A        
Bob        78        B        
Charlie    92        A        

通过合理使用宽度和对齐修饰符,可以实现更加整齐和美观的输出效果,提升程序的可读性和用户体验。

3.2 精度控制

精度修饰符用于控制输出的精度,对于浮点数和字符串有不同的含义。通过精度修饰符,可以实现更加精确的格式化输出,满足不同的需求。

浮点数精度

对于浮点数,精度修饰符指定小数点后的位数。默认情况下,printf 输出浮点数时会保留 6 位小数,但可以通过精度修饰符来调整。例如:

printf("%f\n", 3.1415926); // 默认输出:3.141593
printf("%.2f\n", 3.1415926); // 输出:3.14
printf("%.5f\n", 3.1415926); // 输出:3.14159

如果精度值大于实际小数位数,多余的位数会用 0 填充。例如:

printf("%.5f\n", 3.14); // 输出:3.14000

字符串精度

对于字符串,精度修饰符指定最大输出长度。如果字符串的实际长度大于指定的精度值,则会截断输出。例如:

printf("%s\n", "Hello, World!"); // 默认输出:Hello, World!
printf("%.5s\n", "Hello, World!"); // 输出:Hello
printf("%.10s\n", "Hello, World!"); // 输出:Hello, Worl

如果精度值大于字符串的实际长度,则不会进行截断。例如:

printf("%.20s\n", "Hello, World!"); // 输出:Hello, World!

动态精度

精度值也可以通过 * 动态指定,这种方式更加灵活。例如:

int precision = 3;
printf("%.*f\n", precision, 3.1415926); // 输出:3.142
printf("%.*s\n", precision, "Hello, World!"); // 输出:Hel

实际应用示例

在实际编程中,精度控制常用于格式化浮点数输出,以满足不同的精度需求。例如,输出一个包含小数的计算结果:

double result = 3.141592653589793;
printf("The result is: %.6f\n", result); // 输出:The result is: 3.141593

对于字符串,精度控制可以用于截取字符串,避免输出过长的内容。例如,输出用户输入的字符串时,限制输出长度:

char input[] = "This is a very long string.";
printf("Input: %.10s\n", input); // 输出:Input: This is a

通过合理使用精度修饰符,可以实现更加精确和灵活的格式化输出,满足各种复杂的编程需求。# 4. printf 的返回值

4.1 返回值的含义

printf 函数的返回值是一个整数,表示成功输出的字符数。这包括所有普通字符、格式化后的参数值以及换行符等。例如:

int count = printf("Hello, %s! %d\n", "World", 123);

假设输出的内容为 Hello, World! 123,则 count 的值为 15,因为总共输出了 15 个字符(包括换行符)。

需要注意的是,如果 printf 出现错误(如格式说明符与参数类型不匹配导致的输出失败),返回值为负值。例如:

int count = printf("%d\n", "Hello"); // 错误:格式说明符与参数类型不匹配

在这种情况下,count 的值可能为 -1,表示输出失败。

4.2 返回值的应用场景

printf 的返回值在实际编程中具有多种应用场景,以下是一些常见的例子:

4.2.1 输出校验

通过检查 printf 的返回值,可以判断输出是否成功。如果返回值为负数,说明输出过程中发生了错误,可以据此进行错误处理。例如:

int result = printf("The value is: %d\n", 42);
if (result < 0) {
    fprintf(stderr, "Error occurred during printf\n");
    // 进一步的错误处理
}

这种方式可以有效避免因输出失败而导致的潜在问题,提高程序的健壮性。

4.2.2 动态调整输出

根据 printf 的返回值,可以动态调整后续的输出逻辑。例如,在打印一个表格时,可以根据每列的实际宽度动态调整对齐方式。假设需要打印一个包含姓名和年龄的表格:

printf("%-10s%-5s\n", "Name", "Age");
printf("%-10s%-5d\n", "Alice", 25);
printf("%-10s%-5d\n", "Bob", 30);

如果需要根据实际输出宽度动态调整表格的对齐方式,可以先通过 printf 输出一行数据并获取返回值,然后根据返回值调整后续的输出格式。例如:

int width = printf("%s\n", "Name");
if (width < 10) {
    printf("%-10s\n", "Name");
} else {
    printf("%s\n", "Name");
}

4.2.3 调试与日志记录

在调试过程中,printf 的返回值可以作为日志记录的一部分,帮助开发者了解程序的运行状态。例如:

int result = printf("Debug: The value is %d\n", some_variable);
if (result < 0) {
    fprintf(stderr, "Error in printf during debugging\n");
} else {
    fprintf(stderr, "Debug message printed successfully\n");
}

这种方式可以将调试信息和错误信息区分开来,便于后续的分析和排查问题。

4.2.4 嵌套调用中的应用

在嵌套调用 printf 的场景中,返回值可以作为参数传递给外层的 printf,从而实现复杂的输出逻辑。例如:

printf("The length of the message is: %d\n", printf("Hello, World!\n"));

在这个例子中,内层的 printf 输出 "Hello, World!",返回值为 13(包括换行符)。外层的 printf 将返回值作为参数输出,显示 "The length of the message is: 13"。这种嵌套调用的方式在某些特殊场景下可以简化代码逻辑,但需要注意可读性和维护性。# 5. printf 的安全问题与注意事项

5.1 格式化字符串漏洞

printf 函数在使用过程中存在一些安全隐患,尤其是格式化字符串漏洞。这种漏洞主要源于 printf 函数对格式化字符串的处理机制。printf 函数会根据格式化字符串中的格式说明符依次从堆栈中读取参数并输出。如果格式化字符串中的格式说明符数量多于实际提供的参数数量,printf 函数会继续从堆栈中读取未定义的数据,从而可能导致信息泄露或程序崩溃。

格式化字符串漏洞的成因

  • 用户可控的格式化字符串:如果程序允许用户输入格式化字符串,攻击者可以通过精心构造的格式化字符串读取堆栈中的敏感信息或修改程序的执行流程。例如:

    char user_input[100];
    scanf("%s", user_input);
    printf(user_input);
    

    如果用户输入的是类似 %s %d 的格式化字符串,而程序没有提供相应的参数,printf 函数会从堆栈中读取未定义的数据并输出,可能导致信息泄露。

  • 格式说明符与参数不匹配:如果格式化字符串中的格式说明符与实际提供的参数类型或数量不匹配,可能会导致未定义行为。例如:

    printf("%d\n", "Hello");
    

    这里格式说明符 %d 期望一个整数参数,但实际提供的是一个字符串,这会导致程序行为异常。

格式化字符串漏洞的利用

攻击者可以通过以下方式利用格式化字符串漏洞:

  • 信息泄露:通过格式化字符串读取堆栈中的数据,攻击者可以获取程序的内存布局、指针地址等敏感信息。例如:

    printf("%p %p %p\n");
    

    这会输出堆栈中的前几个指针地址,攻击者可以利用这些信息进一步构造攻击。

  • 修改程序执行流程:通过格式化字符串中的 %n 格式说明符,攻击者可以向指定的内存地址写入数据,从而修改程序的执行流程。例如:

    int *ptr = (int *)0x12345678;
    printf("%100d%n", 0, ptr);
    

    这会将 100 写入地址为 0x12345678 的内存位置,从而可能改变程序的控制流。

格式化字符串漏洞的实例

以下是一个典型的格式化字符串漏洞实例:

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

void print_message(const char *msg) {
    printf(msg);
}

int main() {
    char user_input[100];
    printf("Enter your message: ");
    scanf("%99s", user_input);
    print_message(user_input);
    return 0;
}

如果用户输入的是类似 %s %d 的格式化字符串,printf 函数会从堆栈中读取未定义的数据并输出,可能导致信息泄露。如果用户输入的是类似 %n 的格式化字符串,攻击者可以向指定的内存地址写入数据,从而修改程序的执行流程。

5.2 安全使用建议

为了避免格式化字符串漏洞,建议在使用 printf 函数时遵循以下安全使用建议:

1. 避免用户可控的格式化字符串

尽量避免将用户输入直接用作格式化字符串。如果需要根据用户输入动态生成格式化字符串,可以使用 snprintf 函数进行安全的格式化输出。例如:

char format[100];
snprintf(format, sizeof(format), "Value: %d", user_value);
printf(format);

2. 确保格式说明符与参数匹配

在使用 printf 函数时,确保格式化字符串中的格式说明符与实际提供的参数类型和数量完全匹配。例如:

printf("Value: %d\n", 123);
printf("Name: %s, Age: %d\n", "Alice", 25);

3. 使用安全的格式化函数

在某些情况下,可以使用更安全的格式化函数,如 snprintfasprintf。这些函数提供了更多的安全机制,可以有效防止缓冲区溢出和格式化字符串漏洞。例如:

char buffer[100];
snprintf(buffer, sizeof(buffer), "Value: %d", 123);
printf("%s\n", buffer);

4. 检查 printf 的返回值

通过检查 printf 的返回值,可以判断输出是否成功。如果返回值为负数,说明输出过程中发生了错误,可以据此进行错误处理。例如:

int result = printf("The value is: %d\n", 42);
if (result < 0) {
    fprintf(stderr, "Error occurred during printf\n");
    // 进一步的错误处理
}

5. 避免使用 %n 格式说明符

%n 格式说明符允许向指定的内存地址写入数据,这可能会被攻击者利用来修改程序的执行流程。因此,尽量避免使用 %n 格式说明符。如果必须使用,应确保目标地址是安全的。

6. 使用编译器的安全检查功能

现代编译器提供了多种安全检查功能,可以检测格式化字符串漏洞。例如,GCC 编译器提供了 -Wformat-Wformat-security 选项,可以检测格式化字符串中的潜在问题。启用这些选项可以提前发现潜在的安全问题。例如:

gcc -Wformat -Wformat-security -o program program.c

通过以上安全使用建议,可以有效避免格式化字符串漏洞,提高程序的安全性和稳定性。# 6. printf 的变体

6.1 fprintf

fprintfprintf 的一个变体,用于将格式化后的输出写入指定的文件流,而不是标准输出。其函数原型如下:

int fprintf(FILE *stream, const char *format, ...);
  • FILE *stream:指定输出的目标文件流。可以是标准输入输出流(如 stdoutstderr),也可以是通过 fopen 打开的文件流。
  • const char *format:格式控制字符串,与 printf 的格式字符串相同。
  • ...:可变参数列表,与 printf 的参数列表相同。

使用场景

  • 文件写入:将数据写入文件时,fprintf 是非常有用的工具。例如,将日志信息写入日志文件:
    FILE *logfile = fopen("log.txt", "a");
    if (logfile) {
        fprintf(logfile, "Error occurred at %s: %d\n", "2024-05-10 12:00:00", 404);
        fclose(logfile);
    }
    
  • 标准错误输出:向标准错误输出流(stderr)写入错误信息:
    fprintf(stderr, "Error: %s\n", "File not found");
    

返回值

fprintf 的返回值与 printf 相同,表示成功输出的字符数。如果发生错误,返回值为负数。

6.2 sprintf

sprintf 用于将格式化后的字符串写入指定的字符数组中,而不是输出到标准输出或文件流。其函数原型如下:

int sprintf(char *str, const char *format, ...);
  • char *str:目标字符数组,用于存储格式化后的字符串。
  • const char *format:格式控制字符串,与 printf 的格式字符串相同。
  • ...:可变参数列表,与 printf 的参数列表相同。

使用场景

  • 字符串拼接:将多个变量的值拼接成一个字符串。例如,生成一个包含用户信息的字符串:
    char buffer[100];
    sprintf(buffer, "User: %s, Age: %d, Score: %.2f", "Alice", 25, 85.5);
    printf("%s\n", buffer);
    
  • 动态生成字符串:根据运行时的变量值动态生成字符串。例如,生成一个包含时间戳的日志消息:
    char timestamp[20];
    time_t now = time(NULL);
    strftime(timestamp, sizeof(timestamp), "%Y-%m-%d %H:%M:%S", localtime(&now));
    char logmsg[100];
    sprintf(logmsg, "[%s] Error occurred: %s", timestamp, "File not found");
    printf("%s\n", logmsg);
    

返回值

sprintf 的返回值表示成功写入的字符数(不包括终止符 \0)。如果目标数组的大小不足以容纳格式化后的字符串,可能会导致缓冲区溢出,从而引发安全问题。

安全问题

由于 sprintf 不检查目标数组的大小,容易导致缓冲区溢出。为了避免这种情况,建议使用 snprintf

6.3 snprintf

snprintfsprintf 的安全版本,它允许指定目标缓冲区的最大大小,从而避免缓冲区溢出。其函数原型如下:

int snprintf(char *str, size_t size, const char *format, ...);
  • char *str:目标字符数组,用于存储格式化后的字符串。
  • size_t size:目标缓冲区的最大大小,包括终止符 \0
  • const char *format:格式控制字符串,与 printf 的格式字符串相同。
  • ...:可变参数列表,与 printf 的参数列表相同。

使用场景

  • 安全的字符串拼接:在拼接字符串时,确保不会超出目标缓冲区的大小。例如:
    char buffer[50];
    snprintf(buffer, sizeof(buffer), "User: %s, Age: %d", "Alice", 25);
    printf("%s\n", buffer);
    
  • 动态生成字符串:根据运行时的变量值动态生成字符串,同时确保不会超出缓冲区大小。例如:
    char buffer[50];
    int age = 25;
    snprintf(buffer, sizeof(buffer), "User: %s, Age: %d", "Alice", age);
    printf("%s\n", buffer);
    

返回值

snprintf 的返回值表示在不考虑缓冲区大小限制的情况下,格式化后的字符串长度(包括终止符 \0)。如果返回值大于目标缓冲区的大小,说明目标缓冲区不足以容纳完整的格式化字符串。例如:

char buffer[10];
int len = snprintf(buffer, sizeof(buffer), "Hello, World!");
printf("Buffer: %s, Length: %d\n", buffer, len);

输出结果为:

Buffer: Hello, W, Length: 13

安全性

snprintf 是最安全的字符串格式化函数之一,因为它允许指定目标缓冲区的最大大小,从而有效防止缓冲区溢出。在实际编程中,建议优先使用 snprintf,而不是 sprintf。# 7. 实际应用示例

7.1 日常编程中的使用场景

printf 函数在日常编程中有着广泛的应用,以下是一些常见的使用场景及其示例代码:

1. 输出调试信息

在开发过程中,printf 是一种非常方便的调试工具,可以帮助开发者快速定位问题。例如,输出变量的值以检查程序的运行状态:

int x = 10;
printf("The value of x is: %d\n", x);

2. 输出用户友好的消息

printf 可以用于向用户显示友好的提示信息或错误消息。例如,提示用户输入数据:

printf("Please enter your name: ");

3. 格式化输出数据

printf 可以用于格式化输出各种类型的数据,包括整数、浮点数、字符串等。例如,输出一个学生的成绩信息:

int id = 1001;
char name[] = "Alice";
float score = 89.5;
printf("Student ID: %d, Name: %s, Score: %.2f\n", id, name, score);

4. 输出表格数据

printf 可以通过宽度和对齐修饰符来输出整齐的表格数据。例如,输出一个包含姓名、年龄和成绩的表格:

printf("%-10s%-5s%-5s\n", "Name", "Age", "Score");
printf("%-10s%-5d%-5.2f\n", "Alice", 20, 89.5);
printf("%-10s%-5d%-5.2f\n", "Bob", 22, 95.0);
printf("%-10s%-5d%-5.2f\n", "Charlie", 19, 78.9);

5. 输出日志信息

printf 可以用于生成日志信息,记录程序的运行情况。例如,输出一条带有时间戳的日志信息:

#include <time.h>
time_t now = time(NULL);
char *time_str = ctime(&now);
printf("Log: %s - Program started\n", time_str);

7.2 复杂格式化输出示例

printf 函数的强大之处在于其能够实现复杂的格式化输出,以下是一些复杂格式化输出的示例及其代码:

1. 输出多行格式化数据

在某些情况下,需要输出多行格式化数据,每行包含不同的信息。例如,输出一个包含多个字段的报告:

printf("Report:\n");
printf("Name: %s\n", "Alice");
printf("Age: %d\n", 25);
printf("Score: %.2f\n", 89.5);
printf("Grade: %s\n", "A");

2. 动态格式化输出

在某些情况下,格式化字符串和参数可能在运行时动态生成。例如,根据用户输入动态生成格式化字符串并输出:

char format[100];
snprintf(format, sizeof(format), "Name: %s, Age: %d, Score: %.2f");
char name[] = "Bob";
int age = 22;
float score = 92.0;
printf(format, name, age, score);

3. 输出带有颜色的文本

在某些终端中,可以通过转义序列输出带有颜色的文本。例如,输出红色的错误信息:

printf("\033[31mError: File not found\033[0m\n");

4. 输出带有条件的文本

在某些情况下,需要根据条件输出不同的文本。例如,根据成绩输出不同的评价:

float score = 85.0;
if (score >= 90) {
    printf("Score: %.2f - Excellent\n", score);
} else if (score >= 80) {
    printf("Score: %.2f - Good\n", score);
} else {
    printf("Score: %.2f - Needs improvement\n", score);
}

5. 输出带有时间戳的日志信息

在日志系统中,通常需要在每条日志信息前加上时间戳。例如,输出带有时间戳的日志信息:

#include <time.h>
time_t now = time(NULL);
struct tm *tm_info = localtime(&now);
printf("[%04d-%02d-%02d %02d:%02d:%02d] Log: Program started\n",
       tm_info->tm_year + 1900, tm_info->tm_mon + 1, tm_info->tm_mday,
       tm_info->tm_hour, tm_info->tm_min, tm_info->tm_sec);

2 条评论

  • 1