0Pricing
C Academy · 课时

实际示例

日志记录函数

实际示例 是 CoddyKit 上的免费 C Academy 课时。 这是第 4 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 C Academy 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 C Academy 课程共包含 4 节课。

日志函数

记录器是可变参数在现实中的完美应用。我们希望像调用 printf 一样调用 log_msg("user %s scored %d", name, score)。

本课将构建一个小巧实用的日志工具。

#include <stdio.h>
#include <stdarg.h>

void log_msg(const char *fmt, ...) {
    va_list a; va_start(a, fmt);
    vprintf(fmt, a);
    va_end(a);
    printf("\n");
}

int main(void) {
    log_msg("user %s scored %d", "Ann", 90);
    return 0;
}

添加严重性级别

实际的日志记录器会使用 INFO 或 ERROR 这样的级别标记消息。在格式字符串前添加一个表示级别的固定参数。

#include <stdio.h>
#include <stdarg.h>

void logf(const char *level, const char *fmt, ...) {
    printf("[%s] ", level);
    va_list a; va_start(a, fmt);
    vprintf(fmt, a);
    va_end(a);
    printf("\n");
}

int main(void) {
    logf("INFO", "started with %d workers", 4);
    return 0;
}

为消息添加时间戳

转发格式字符串之前,您可以为每行添加额外上下文。这里我们使用一个虚构的计数器代替时间戳。

#include <stdio.h>
#include <stdarg.h>

static int line_no = 0;

void logf(const char *fmt, ...) {
    printf("#%d ", ++line_no);
    va_list a; va_start(a, fmt);
    vprintf(fmt, a);
    va_end(a);
    printf("\n");
}

int main(void) {
    logf("first %s", "event");
    logf("value %d", 7);
    return 0;
}

记录到 stderr

错误和诊断信息通常会输出到 stderr。使用 vfprintf(stderr, fmt, args),这样日志输出就不会与程序的正常输出混在一起。

#include <stdio.h>
#include <stdarg.h>

void err(const char *fmt, ...) {
    fprintf(stderr, "ERROR: ");
    va_list a; va_start(a, fmt);
    vfprintf(stderr, fmt, a);
    va_end(a);
    fprintf(stderr, "\n");
}

int main(void) {
    err("failed to open %s", "data.txt");
    return 0;
}

有条件地记录日志

详细程度阈值可以让您静默低优先级消息。在打印前,将消息级别与配置的最低级别进行比较。

#include <stdio.h>
#include <stdarg.h>

static int min_level = 2;

void logf(int level, const char *fmt, ...) {
    if (level < min_level) return;
    va_list a; va_start(a, fmt);
    vprintf(fmt, a);
    va_end(a);
    printf("\n");
}

int main(void) {
    logf(1, "debug hidden");
    logf(3, "important %d", 42);
    return 0;
}

构建消息字符串

有时您需要将格式化文本放入缓冲区,而不是直接打印。使用 vsnprintf 生成文本,然后再决定如何处理。

#include <stdio.h>
#include <stdarg.h>

void capture(const char *fmt, ...) {
    char buf[128];
    va_list a; va_start(a, fmt);
    vsnprintf(buf, sizeof(buf), fmt, a);
    va_end(a);
    printf("captured: %s\n", buf);
}

int main(void) {
    capture("id=%d name=%s", 5, "box");
    return 0;
}

可变参数最大值函数

数学辅助函数同样可以使用可变参数。下面的函数使用计数参数,返回多个整数中的最大值。

#include <stdio.h>
#include <stdarg.h>

int max_of(int count, ...) {
    va_list a; va_start(a, count);
    int best = va_arg(a, int);
    for (int i = 1; i < count; i++) {
        int v = va_arg(a, int);
        if (v > best) best = v;
    }
    va_end(a);
    return best;
}

int main(void) {
    printf("%d\n", max_of(4, 3, 9, 2, 7));
    return 0;
}

拼接字符串

使用 NULL 哨兵,我们可以将任意数量的字符串连接到一个缓冲区中。

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

void join(char *out, const char *first, ...) {
    out[0] = '\0';
    va_list a; va_start(a, first);
    const char *s = first;
    while (s) { strcat(out, s); s = va_arg(a, const char *); }
    va_end(a);
}

int main(void) {
    char buf[64];
    join(buf, "a", "b", "c", (char *)NULL);
    printf("%s\n", buf);
    return 0;
}

统计可变参数整数

基于哨兵的计数器展示了这种模式的灵活性。我们会持续统计值,直到遇到终止值 0。

#include <stdio.h>
#include <stdarg.h>

int count_until_zero(int first, ...) {
    va_list a; va_start(a, first);
    int n = 0, v = first;
    while (v != 0) { n++; v = va_arg(a, int); }
    va_end(a);
    return n;
}

int main(void) {
    printf("%d\n", count_until_zero(5, 6, 7, 0));
    return 0;
}

将其封装为宏

一种常见技巧是使用宏自动填充级别,从而减少调用者需要编写的内容。该宏会将参数转发给真正的可变参数函数。

#include <stdio.h>
#include <stdarg.h>

void real_log(const char *lvl, const char *fmt, ...) {
    printf("[%s] ", lvl);
    va_list a; va_start(a, fmt);
    vprintf(fmt, a);
    va_end(a);
    printf("\n");
}

#define LOG_INFO(...) real_log("INFO", __VA_ARGS__)

int main(void) {
    LOG_INFO("ready in %d ms", 50);
    return 0;
}

使用日志记录器

这里有一个完整的小型日志记录器,它结合了级别和时间戳计数器,可以直接加入程序使用。

#include <stdio.h>
#include <stdarg.h>

static int seq = 0;

void mylog(const char *lvl, const char *fmt, ...) {
    printf("%04d [%s] ", ++seq, lvl);
    va_list a; va_start(a, fmt);
    vprintf(fmt, a);
    va_end(a);
    printf("\n");
}

int main(void) {
    mylog("INFO", "boot ok");
    mylog("WARN", "low memory: %d MB", 64);
    return 0;
}

快速检查

测试您对可变参数日志设计的理解。

回顾

您构建了实用的可变参数工具:

  • 一个将格式字符串转发给 vprintf/vfprintf 的日志记录器。
  • 添加了级别、计数器、stderr 输出和条件过滤。
  • 使用 vsnprintf 将消息捕获到缓冲区中。
  • 了解了基于哨兵的辅助函数和便捷的宏包装器。

常见问题解答

「实际示例」课时是免费的吗?

是的 — 「实际示例」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 C Academy 课程的其余内容,请升级到 CoddyKit PRO。 C Academy 课程共包含 4 节课。

「实际示例」这节课中我会学到什么?

日志记录函数 你通过在浏览器中直接运行的动手代码来练习 C Academy,全天候 AI 导师会在你学习这节课的过程中回答你的问题。

学习 C Academy 需要有经验吗?

无需任何先前经验。CoddyKit 上的 C Academy 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 4 节课,共 4 节。

「实际示例」课时需要多长时间?

大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。

我能在这节 C Academy 课中编写并运行代码吗?

能。每节 C Academy 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。

此课程中的所有课时

  1. stdarg 宏
  2. 编写类似 printf 的函数
  3. 类型安全问题
  4. 实际示例
← 返回 C Academy