0Pricing
C Academy · บทเรียน

การเขียนฟังก์ชันแบบ printf

อาร์กิวเมนต์จำนวนไม่แน่นอน

การเขียนฟังก์ชันแบบ printf เป็นบทเรียน C Academy ฟรีบน CoddyKit นี่คือบทเรียนที่ 2 จากทั้งหมด 4 บทเรียน คุณสามารถอ่านบทเรียนทั้งหมดด้านล่างฟรี — จากนั้นลองปฏิบัติด้วยตัวคุณเองในเบราว์เซอร์พร้อมตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7 บทเรียนนี้เป็นส่วนหนึ่งของเส้นทางการเรียน C Academy และความก้าวหน้าของคุณจะซิงค์ข้ามเว็บและแอป CoddyKit คอร์ส C Academy มีบทเรียนทั้งหมด 4 บทเรียน

ฟังก์ชันที่รับสตริงรูปแบบ

การใช้งานอาร์กิวเมนต์แบบแปรผันที่ทรงพลังที่สุดคือการสร้างฟังก์ชันรูปแบบเดียวกับ printf ที่ตีความ สตริงรูปแบบ

รูปแบบจะบอกฟังก์ชันว่ามีอาร์กิวเมนต์ตามมาอีกกี่ตัว และแต่ละตัวมีชนิดใด

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

void myprint(const char *fmt, ...) {
    printf("format was: %s\n", fmt);
}

int main(void) {
    myprint("%d items\n", 5);
    return 0;
}

การไล่อ่านสตริงรูปแบบ

ตรวจสอบสตริงรูปแบบทีละอักขระ เมื่อพบ % อักขระถัดไปจะเป็นตัวระบุการแปลง ซึ่งบอกว่าต้องอ่านข้อมูลชนิดใด

ส่วนอื่นทั้งหมดให้พิมพ์ออกมาตามตัวอักษร

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

void scan(const char *fmt) {
    for (const char *p = fmt; *p; p++) {
        if (*p == '%') printf("[spec:%c]", *(p+1));
        else putchar(*p);
    }
    putchar('\n');
}

int main(void) {
    scan("x=%d y=%s");
    return 0;
}

การอ่านชนิดข้อมูลที่ตรงกัน

เมื่อตัวระบุเป็น %d ให้อ่านเป็น int ส่วน %s ให้อ่านเป็น char * รูปแบบต้องตรงกับอาร์กิวเมนต์จริงทุกประการ

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

void mini(const char *fmt, ...) {
    va_list args;
    va_start(args, fmt);
    for (const char *p = fmt; *p; p++) {
        if (*p == '%' && *(p+1) == 'd') {
            printf("%d", va_arg(args, int));
            p++;
        } else putchar(*p);
    }
    va_end(args);
}

int main(void) {
    mini("count=%d\n", 42);
    return 0;
}

การจัดการตัวระบุหลายตัว

ขยายตัวสแกนให้รองรับการแปลงหลายรูปแบบ เพิ่มกรณีสำหรับ %d, %s และ %c โดยอ่านชนิดข้อมูลที่ถูกต้องในแต่ละครั้ง

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

void fmt_print(const char *fmt, ...) {
    va_list args;
    va_start(args, fmt);
    for (const char *p = fmt; *p; p++) {
        if (*p != '%') { putchar(*p); continue; }
        p++;
        if (*p == 'd') printf("%d", va_arg(args, int));
        else if (*p == 's') printf("%s", va_arg(args, char *));
        else if (*p == 'c') putchar(va_arg(args, int));
    }
    va_end(args);
}

int main(void) {
    fmt_print("%s is %d\n", "age", 30);
    return 0;
}

การใช้อักขระเปอร์เซ็นต์แบบเอสเคป

ฟังก์ชันรูปแบบจริงจะถือว่า %% เป็นเครื่องหมายเปอร์เซ็นต์ตามตัวอักษร เพิ่มกรณีนี้เพื่อให้ผู้ใช้พิมพ์ % จริงได้

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

void fp(const char *fmt, ...) {
    va_list args;
    va_start(args, fmt);
    for (const char *p = fmt; *p; p++) {
        if (*p == '%') {
            p++;
            if (*p == '%') putchar('%');
            else if (*p == 'd') printf("%d", va_arg(args, int));
        } else putchar(*p);
    }
    va_end(args);
}

int main(void) {
    fp("%d%% done\n", 75);
    return 0;
}

การส่งต่อไปยัง vprintf

บ่อยครั้งคุณไม่จำเป็นต้องสร้างระบบจัดรูปแบบขึ้นมาใหม่ ไลบรารีมาตรฐานมี vprintf ซึ่งรับ va_list โดยตรง

ฟังก์ชันห่อหุ้มของคุณเพียงส่งต่ออาร์กิวเมนต์ไป

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

void logline(const char *fmt, ...) {
    va_list args;
    va_start(args, fmt);
    vprintf(fmt, args);
    va_end(args);
}

int main(void) {
    logline("value = %d, name = %s\n", 7, "box");
    return 0;
}

การเพิ่มคำนำหน้า

แนวทางใช้ vprintf ทำให้เพิ่มการทำงานรอบการจัดรูปแบบมาตรฐานได้ง่าย เช่น พิมพ์ป้ายกำกับก่อนทุกข้อความ

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

void info(const char *fmt, ...) {
    printf("[INFO] ");
    va_list args;
    va_start(args, fmt);
    vprintf(fmt, args);
    va_end(args);
}

int main(void) {
    info("server started on port %d\n", 8080);
    return 0;
}

การเขียนลงบัฟเฟอร์ด้วย vsnprintf

หากต้องการจัดรูปแบบลงในสตริงแทนการพิมพ์ ให้ใช้ vsnprintf(buf, size, fmt, args)

ฟังก์ชันนี้จำกัดผลลัพธ์ด้วย size จึงป้องกันการล้น และเติมเครื่องหมายจบสตริงเสมอ

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

void format_into(char *buf, size_t n, const char *fmt, ...) {
    va_list args;
    va_start(args, fmt);
    vsnprintf(buf, n, fmt, args);
    va_end(args);
}

int main(void) {
    char out[64];
    format_into(out, sizeof(out), "id-%d", 123);
    printf("%s\n", out);
    return 0;
}

การคืนค่าความยาว

vsnprintf จะคืนค่าจำนวนอักขระที่ จะถูกเขียนหากมีพื้นที่เพียงพอ คุณสามารถใช้ค่านี้ตรวจพบการตัดทอนหรือคำนวณขนาดบัฟเฟอร์ได้

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

int build(char *buf, size_t n, const char *fmt, ...) {
    va_list args;
    va_start(args, fmt);
    int len = vsnprintf(buf, n, fmt, args);
    va_end(args);
    return len;
}

int main(void) {
    char b[10];
    int needed = build(b, sizeof(b), "%d-%d", 11, 22);
    printf("%s (needed %d)\n", b, needed);
    return 0;
}

การเลือกแนวทาง

มีกลยุทธ์สองแบบ: แยกวิเคราะห์รูปแบบด้วยตนเองเพื่อควบคุมได้เต็มที่ หรือส่งต่อไปยัง vprintf/vsnprintf เพื่อความถูกต้องและลดจำนวนโค้ด

สำหรับโค้ดที่ใช้งานจริงส่วนใหญ่ การส่งต่อเป็นทางเลือกที่ปลอดภัยและง่ายกว่า

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

void say(const char *fmt, ...) {
    va_list args;
    va_start(args, fmt);
    vfprintf(stdout, fmt, args);
    va_end(args);
}

int main(void) {
    say("%s = %.1f\n", "pi", 3.14);
    return 0;
}

ชนิดไม่ตรงกันทำให้เกิดพฤติกรรมที่ไม่กำหนด

หากสตริงรูปแบบระบุชนิดที่ไม่ตรงกับอาร์กิวเมนต์จริง พฤติกรรมที่เกิดขึ้นจะไม่ถูกกำหนด

printf("%d", "text") อาจทำให้โปรแกรมหยุดทำงาน ควรรักษาให้รูปแบบและอาร์กิวเมนต์สอดคล้องกันเสมอ

#include <stdio.h>

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

ตรวจสอบความเข้าใจอย่างรวดเร็ว

ทดสอบความเข้าใจเกี่ยวกับฟังก์ชันห่อหุ้มรูปแบบเดียวกับ printf ของคุณ

สรุปทบทวน

คุณได้เรียนรู้การสร้างฟังก์ชันรูปแบบเดียวกับ printf:

  • รับ สตริงรูปแบบ เป็นพารามิเตอร์ที่มีชื่อ จากนั้นตามด้วย ...
  • แยกวิเคราะห์ตัวระบุ % ด้วยตนเองและอ่านชนิดข้อมูลที่ตรงกัน หรือ
  • ส่งต่อ va_list ไปยัง vprintf / vsnprintf เพื่อการจัดรูปแบบที่ปลอดภัยและถูกต้อง
  • รักษาให้รูปแบบและชนิดของอาร์กิวเมนต์สอดคล้องกัน เพื่อหลีกเลี่ยงพฤติกรรมที่ไม่กำหนด

คำถามที่พบบ่อย

บทเรียน “การเขียนฟังก์ชันแบบ printf” ฟรีหรือไม่

ใช่ — ข้อความเต็มของ “การเขียนฟังก์ชันแบบ printf” ฟรีให้อ่านที่นี่บนเว็บ เพื่อปฏิบัติแบบโต้ตอบ (ตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7) และปลดล็อคส่วนที่เหลือของคอร์ส C Academy ให้อัปเกรดเป็น CoddyKit PRO คอร์ส C Academy มีบทเรียนทั้งหมด 4 บทเรียน

คุณจะเรียนรู้อะไรในบทเรียน “การเขียนฟังก์ชันแบบ printf”

อาร์กิวเมนต์จำนวนไม่แน่นอน คุณปฏิบัติ C Academy ด้วยโค้ดที่ใช้งานได้จริงที่คุณเรียกใช้โดยตรงในเบราว์เซอร์ และติวเตอร์ AI ตลอด 24/7 ตอบคำถามของคุณขณะที่คุณไปผ่านบทเรียน

คุณต้องมีประสบการณ์ก่อนที่จะเริ่มเรียน C Academy หรือไม่

ไม่จำเป็นต้องมีประสบการณ์มาก่อน C Academy บน CoddyKit ออกแบบมาสำหรับผู้เริ่มต้นไปจนถึงผู้เรียนขั้นสูง คุณสามารถเริ่มต้นที่นี่หรือเริ่มจากตัวแรกและเรียนด้วยความเร็วของคุณเอง นี่คือบทเรียนที่ 2 จากทั้งหมด 4 บทเรียน

บทเรียน “การเขียนฟังก์ชันแบบ printf” ใช้เวลานานแค่ไหน

บทเรียน CoddyKit ส่วนใหญ่ใช้เวลาประมาณ 5–10 นาที แต่ละบทเรียนจึงสั้นและเป็นแบบโต้ตอบ คุณสามารถก้าวหน้าอย่างต่อเนื่องและกลับมาเรียนต่อจากตรงที่เพิ่งหยุดบนเว็บและแอปได้เลย

ฉันเขียนและรันโค้ดในบทเรียน C Academy นี้ได้ไหม

ได้ บทเรียน C Academy ทุกบทมีตัวแก้ไขโค้ดในตัว คุณจึงเขียนและรันโค้ดจริงได้เลยในเบราว์เซอร์ และได้รับข้อเสนอแนะจาก AI ในทันที — ไม่ต้องติดตั้งในเครื่องของคุณ

บทเรียนทั้งหมดในหลักสูตรนี้

  1. มาโคร stdarg
  2. การเขียนฟังก์ชันแบบ printf
  3. ข้อควรระวังด้านความปลอดภัยของชนิด
  4. ตัวอย่างการใช้งานจริง
← กลับไปที่ C Academy