Menu

הספרייה הסטנדרטית של C: מפה של קבצי ה-header ששווה להכיר

מה כל header סטנדרטי נותן לכם: stdio.h, stdlib.h, string.h, math.h, time.h, ctype.h, limits.h, stdbool.h, stdint.h ו-assert.h, עם דוגמה קצרה ועובדת לכל אחד.

בדף הזה יש עורכים שאפשר להריץ - לערוך, להריץ ולראות את הפלט מיד.

C כשפה היא זעירה: טיפוסים, אופרטורים, בקרת זרימה, פונקציות, מצביעים. היא לא יודעת להדפיס, לקרוא קובץ, להשוות מחרוזת או להקצות זיכרון. כל זה הוא הספרייה הסטנדרטית: אוסף קבצי header שכל מימוש תקני מגיע איתם.

העמוד הזה הוא מפה. כל פרק אומר למה header מסוים משמש, מציג דוגמה קצרה ועובדת, ומפנה לעמוד המלא יותר אם יש כזה.

stdio.h: קלט ופלט

ה-header שכוללים ראשון כמעט בכל תוכנית. הוא מכיל את printf ואת scanf, את הטיפוס FILE ואת פונקציות הקבצים, ואת פונקציות הקלט הבטוחות יותר שעובדות לפי שורות.

היסודות: printf, fprintf, sprintf, snprintf לפלט, scanf, fscanf, sscanf, fgets, getchar לקלט, ו-fopen, fclose, fread, fwrite, fseek לקבצים. כיסוי מעמיק יותר נמצא ב-printf, ב-scanf ובטיפול בקבצים.

כלל אחד ששווה לזכור: לעולם אל תשתמשו ב-gets(). אין לה שום דרך לדעת את גודל ה-buffer שלכם, והיא הוסרה מהתקן ב-C11. fgets(buf, sizeof buf, stdin) היא התחליף.

stdlib.h: כלים כלליים

ה-header של הכול: זיכרון, המרות, אקראיות, בקרת תהליכים, מיון.

עיקרי הדברים: malloc, calloc, realloc, free (ראו זיכרון דינמי), atoi, atof, strtol, strtod להמרות, כשעדיף להשתמש במשפחת strto*, שיכולה לדווח על כישלון במקום ש-atoi מחזירה 0 בשקט, rand ו-srand (ראו מספרים אקראיים), exit, abort, getenv, system, ו-qsort/bsearch.

string.h: מחרוזות ובלוקים של זיכרון

מחרוזות ב-C הן מערכים של char שמסתיימים ב-'\0', וכל פעולה עליהן היא קריאה לפונקציה.

שני הרגלים מונעים מה-header הזה להפוך למקור של גלישות buffer: תמיד דעו את גודל היעד, וזכרו ש-strcmp מחזירה 0 כשהמחרוזות שוות, כלומר if (strcmp(a, b)) מתקיים כשהמחרוזות שונות. פונקציות מחרוזות מסביר בפירוט את הדפוסים הבטוחים יותר עם strncpy/snprintf.

math.h: מתמטיקה

sqrt, pow, fabs, floor, ceil, round, fmod, משפחות הטריגונומטריה והלוגריתמים, INFINITY ו-NAN.

זה ה-header היחיד שעשוי לדרוש דגל ל-linker: ב-Linux, הוסיפו -lm בסוף פקודת הקומפילציה, אחרת ה-linker מדווח "undefined reference to sqrt". סיור מלא בפונקציות מתמטיות.

time.h: שעונים ותאריכים

שני ההיסטים ב-struct tm תופסים את כולם: tm_year נספר מ-1900, ו-tm_mon מתחיל מ-0. time(NULL) היא גם ה-seed הרגיל ל-srand.

ctype.h: סיווג תווים

קטן, ושימושי יותר ממה שהוא נראה. כל פונקציה מקבלת תו ועונה על שאלת כן/לא, או ממירה בין אותיות גדולות לקטנות.

הסט המלא: isalpha, isdigit, isalnum, isspace, isupper, islower, ispunct, isxdigit, isprint, ובנוסף toupper ו-tolower.

שימו לב ל-cast ל-(unsigned char). הפונקציות האלה מוגדרות לערכים שאפשר לייצג כ-unsigned char ועוד EOF. char רגיל הוא signed ברוב המערכות, כך שבית מעל 127 מגיע כמספר שלילי והקריאה הופכת ל-undefined behavior. ה-cast לא עולה כלום ומעלים את כל סוג הבעיה הזה.

והעדיפו אותן על בדיקות שכתבתם בעצמכם. c >= '0' && c <= '9' במקרה עובד, אבל isdigit(c) אומר את מה שאתם מתכוונים ונשאר נכון בכל מקום.

limits.h ו-float.h: הקצוות של הטיפוסים

כמה גדול יכול להיות int במכונה הזו? ה-headers האלה עונים על זה, והתשובות הן הסיבה שקוד נייד שואל במקום להניח.

INT_MAX הוא הדרך לבדוק גלישה לפני שהיא קורית, וזה חשוב כי גלישה של מספר עם סימן היא undefined behavior, ולא גלגול שאפשר לבדוק אחר כך.

stdbool.h ו-stdint.h: טיפוסים טובים יותר

ל-C לא היה טיפוס בוליאני עד C99. <stdbool.h> מספק אחד:

bool, true ו-false הם מאקרו עבור _Bool, 1 ו-0, אבל הם הופכים את הכוונה לברורה. (ב-C23 הם הפכו למילות מפתח, וה-header כבר לא נדרש.)

<stdint.h> נותן טיפוסים עם רוחב מובטח. זה חיוני לפורמטים של קבצים, לפרוטוקולי רשת ולכל דבר embedded, שם "int הוא כנראה 32 ביט" לא מספיק טוב:

המאקרו בסגנון PRId32 מ-<inttypes.h> מתרחבים ל-specifier הנכון של printf לכל טיפוס ברוחב קבוע בפלטפורמה הנוכחית. זו הדרך הניידת להדפיס אותם, כי %d נכון עבור int32_t במערכות מסוימות ושגוי באחרות.

assert.h: בדיקת ההנחות שלכם

assert(expr) לא עושה כלום כשהביטוי אמת, ועוצרת את התוכנית עם הודעה כשהוא שקר.

קומפילציה עם -DNDEBUG מסירה את כל ה-assertions, כך שהן לא עולות כלום ב-build של release. זו גם האזהרה: לעולם אל תשימו תופעת לוואי בתוך assert. assert(read_next() > 0) מפסיק לקרוא לגמרי ב-build של release, והבאג שנוצר מופיע רק בפרודקשן.

assertions הן בשביל הטעויות שלכם: תנאים שאמורים להיות בלתי אפשריים אם הקוד נכון. קלט שגוי של משתמש הוא לא assertion, הוא if והודעת שגיאה.

השאר, בקצרה

  • <stddef.h>: size_t, ptrdiff_t, NULL, offsetof
  • <stdarg.h>: פונקציות variadic, המנגנון שמאחורי printf
  • <errno.h>: errno, ובנוסף perror ו-strerror לדיווח למה קריאה נכשלה
  • <signal.h>: טיפול באותות כמו Ctrl-C
  • <setjmp.h>: קפיצות לא מקומיות, ולעיתים רחוקות התשובה הנכונה
  • <locale.h>, <wchar.h>, <wctype.h>: locales ותווים רחבים
  • <stdnoreturn.h>, <stdalign.h>, <threads.h>, <stdatomic.h>: תוספות של C11

שני הרגלים הופכים את השימוש בספרייה לנעים. קראו את דף ה-manual לפני שאתם מנחשים חתימה: man 3 strtol ב-macOS וב-Linux הוא סמכותי ומהיר. ובדקו ערכים מוחזרים: malloc, fopen ו-strtol כולן מדווחות על כישלון, ותוכנית שמתעלמת מהן נכשלת הרבה יותר מאוחר ובצורה הרבה פחות ברורה.

שאלות נפוצות

מה זו הספרייה הסטנדרטית של C?

אוסף הפונקציות, הטיפוסים והמאקרו שכל מימוש תקני של C מספק, מקובצים בכ-30 קבצי header. הוא מכסה קלט ופלט (<stdio.h>), זיכרון והמרות (<stdlib.h>), מחרוזות (<string.h>), מתמטיקה (<math.h>), זמן, סיווג תווים, והגבולות של הטיפוסים המספריים. ל-C אין קלט ופלט או טיפול במחרוזות מובנים: כל זה מגיע מכאן.

מה ההבדל בין stdio.h לבין stdlib.h?

<stdio.h> הוא קלט ופלט: printf, scanf, fopen, fgets, FILE. <stdlib.h> הוא כלים כלליים: malloc ו-free, atoi ו-strtol, rand ו-srand, exit, qsort, bsearch. תוכנית טיפוסית כוללת את שניהם.

מה עושה ctype.h?

הוא מסווג וממיר תווים בודדים: isdigit, isalpha, isalnum, isspace, isupper, islower, ispunct, ובנוסף toupper ו-tolower. שימוש בהם במקום השוואות ידניות כמו c >= '0' && c <= '9' ברור יותר ונכון בכל locale ובכל ערכת תווים.

האם צריך לקשר משהו כדי להשתמש בספרייה הסטנדרטית של C?

לא, עם חריג אחד: פונקציות המתמטיקה. הספרייה הסטנדרטית של C מקושרת אוטומטית, אבל ב-Linux המימושים של <math.h> נמצאים בספרייה נפרדת, ולכן sqrt וחברותיה צריכות -lm בסוף פקודת הקומפילציה. כל השאר צריך רק את ה-#include הנכון.

איור של שפות התכנות ב-Coddy

ללמוד תכנות עם Coddy

להתחיל