פונקציית inline מבקשת מהקומפיילר להטמיע את גוף הפונקציה ישירות במקום הקריאה. בשאלות מעקב קוד, ההתנהגות זהה לפונקציה רגילה - ה-inline משפיע רק על ביצועים, לא על הלוגיקה.
#include <stdio.h> static inline int square(int x) { return x * x; } static inline int add(int a, int b) { return a + b; } int main() { int x = 3; int y = square(x); // 3*3 = 9 int z = add(y, x); // 9+3 = 12 printf("%d %d %d\n", x, y, z); return 0; } // Output: 3 9 12
inline לא משנה את התוצאה. הקוד עובד בדיוק כמו פונקציה רגילה.
כשלוקחים כתובת של פונקציית inline, הקומפיילר חייב לייצר עותק אמיתי שלה. הקריאה דרך המצביע עובדת כרגיל.
#include <stdio.h> static inline int doubleIt(int x) { return x * 2; } int main() { int (*fp)(int) = doubleIt; // fp points to doubleIt int a = fp(5); // calls doubleIt(5) = 10 int b = doubleIt(5); // also 10 printf("%d %d\n", a, b); return 0; } // Output: 10 10
זהו נושא מרכזי במבחנים. משתנה static בתוך פונקציית inline שומר את ערכו בין קריאות - בדיוק כמו בפונקציה רגילה.
#include <stdio.h> static inline int counter() { static int c = 0; // initialized ONCE, persists! c++; return c; } int main() { printf("%d ", counter()); // c: 0→1, prints 1 printf("%d ", counter()); // c: 1→2, prints 2 printf("%d\n", counter()); // c: 2→3, prints 3 return 0; } // Output: 1 2 3
static פירושו שהמשתנה מאותחל פעם אחת בלבד ושורד בין קריאות, גם ב-inline.
הקומפיילר לא יכול באמת לבצע inline לפונקציה רקורסיבית (זה ייצור לולאה אינסופית של הטמעות). בפועל, הקוד רץ כרקורסיה רגילה.
#include <stdio.h> static inline int factorial(int n) { if (n <= 1) return 1; return n * factorial(n - 1); } int main() { printf("%d\n", factorial(5)); return 0; } // Trace: 5*4*3*2*1 = 120 // Output: 120
#include <stdio.h> static inline int inc(int x) { return x + 1; } static inline int dbl(int x) { return x * 2; } static inline int sqr(int x) { return x * x; } int main() { int r = sqr(dbl(inc(2))); // Step 1: inc(2) = 3 // Step 2: dbl(3) = 6 // Step 3: sqr(6) = 36 printf("%d\n", r); return 0; } // Output: 36
#include <stdio.h> static inline int sumArr(int *arr, int n) { int s = 0; for (int i = 0; i < n; i++) s += arr[i]; return s; } int main() { int a[] = {1, 2, 3, 4}; printf("%d\n", sumArr(a, 4)); return 0; } // Trace: 0+1+2+3+4 = 10 // Output: 10
מוסכמת cdecl היא ברירת המחדל ברוב קומפיילרי C. כל הפרמטרים עוברים דרך ה-stack, הקורא (caller) מנקה את ה-stack. בשאלות קוד, ציון __attribute__((cdecl)) לא משנה את הפלט - הוא רק מדגיש את המוסכמה.
#include <stdio.h> int __attribute__((cdecl)) add(int a, int b) { return a + b; } int __attribute__((cdecl)) mul(int a, int b) { return a * b; } int main() { int x = add(3, 4); // 7 int y = mul(x, 2); // 14 printf("%d %d\n", x, y); return 0; } // Output: 7 14
#include <stdio.h> int __attribute__((cdecl)) fact(int n) { if (n <= 1) return 1; return n * fact(n - 1); } int __attribute__((cdecl)) fib(int n) { if (n <= 1) return n; return fib(n-1) + fib(n-2); } int __attribute__((cdecl)) gcd(int a, int b) { if (b == 0) return a; return gcd(b, a % b); } int main() { printf("%d\n", fact(5)); // 5*4*3*2*1 = 120 printf("%d\n", fib(6)); // fib(6)=fib(5)+fib(4)=5+3=8 printf("%d\n", gcd(48, 18)); // gcd(48,18)→gcd(18,12)→gcd(12,6)→gcd(6,0)→6 return 0; } // Output: // 120 // 8 // 6
#include <stdio.h> int __attribute__((cdecl)) accumulate(int val) { static int total = 0; total += val; return total; } int main() { printf("%d\n", accumulate(5)); // total: 0+5=5 printf("%d\n", accumulate(3)); // total: 5+3=8 printf("%d\n", accumulate(10)); // total: 8+10=18 return 0; } // Output: // 5 // 8 // 18
#include <stdio.h> int __attribute__((cdecl)) add(int a, int b) { return a + b; } int __attribute__((cdecl)) sub(int a, int b) { return a - b; } int main() { int (__attribute__((cdecl)) *op)(int, int); op = add; printf("%d\n", op(10, 3)); // add(10,3) = 13 op = sub; printf("%d\n", op(10, 3)); // sub(10,3) = 7 return 0; } // Output: // 13 // 7
#include <stdio.h> #ifdef _MSC_VER #define CDECL __cdecl #else #define CDECL __attribute__((cdecl)) #endif int CDECL multiply(int a, int b) { return a * b; } int main() { printf("%d\n", multiply(6, 7)); return 0; } // Output: 42 (same on both compilers)
ב-fastcall, שני הפרמטרים הראשונים עוברים ברגיסטרים ECX ו-EDX (בארכיטקטורת 32-bit). בשאלות מעקב קוד, ההתנהגות הלוגית זהה - ההבדל הוא ברמת ה-assembly בלבד.
#include <stdio.h> #ifdef _MSC_VER #define FASTCALL __fastcall #else #define FASTCALL __attribute__((fastcall)) #endif int FASTCALL add(int a, int b) { // a in ECX, b in EDX (32-bit) return a + b; } int main() { printf("%d\n", add(10, 20)); return 0; } // Output: 30
כשיש יותר משני פרמטרים, הנוספים עוברים דרך ה-stack כרגיל. הפלט הלוגי לא משתנה.
#include <stdio.h> #ifdef _MSC_VER #define FASTCALL __fastcall #else #define FASTCALL __attribute__((fastcall)) #endif int FASTCALL sum4(int a, int b, int c, int d) { // a→ECX, b→EDX, c→stack, d→stack return a + b + c + d; } int main() { printf("%d\n", sum4(1, 2, 3, 4)); return 0; } // Output: 10
#include <stdio.h> #ifdef _MSC_VER #define CDECL __cdecl #define FASTCALL __fastcall #else #define CDECL __attribute__((cdecl)) #define FASTCALL __attribute__((fastcall)) #endif int CDECL slow_add(int a, int b) { return a + b; // all params on stack } int FASTCALL fast_add(int a, int b) { return a + b; // a in ECX, b in EDX } int main() { int r1 = slow_add(5, 3); // 8 int r2 = fast_add(5, 3); // 8 printf("%d %d\n", r1, r2); return 0; } // Output: 8 8 // Same result! Difference is only in assembly.
| תכונה | cdecl |
fastcall |
|---|---|---|
| העברת פרמטרים | הכל דרך ה-stack | 2 ראשונים ב-ECX/EDX, שאר ב-stack |
| ניקוי stack | Caller מנקה | Callee מנקה |
| תמיכה ב-variadic | כן (הקורא מנקה, יודע כמה דחף) | לא (ה-callee לא יודע כמה לנקות) |
| ביצועים | תקני, גישה ל-stack | מהיר יותר (רגיסטרים מהירים מ-RAM) |
| GCC syntax | __attribute__((cdecl)) |
__attribute__((fastcall)) |
| MSVC syntax | __cdecl |
__fastcall |
| שם מעוטר (mangling) | _func |
@func@N (N=bytes of params) |
| השפעה על פלט code tracing | אין - רק assembly | אין - רק assembly |
זהו הנושא הנבחן ביותר. מצביע לפונקציה מאחסן כתובת של פונקציה ומאפשר לקרוא לה באופן דינמי. התחביר הוא:
// Declaration syntax: // return_type (*pointer_name)(param_types) #include <stdio.h> int add(int a, int b) { return a + b; } int mul(int a, int b) { return a * b; } int main() { int (*fp)(int, int); // declare FP fp = add; printf("%d\n", fp(3, 4)); // add(3,4) = 7 fp = mul; printf("%d\n", fp(3, 4)); // mul(3,4) = 12 printf("%d\n", (*fp)(5, 6)); // also mul(5,6) = 30 return 0; } // Output: // 7 // 12 // 30
#include <stdio.h> int add(int a, int b) { return a + b; } int sub(int a, int b) { return a - b; } int mul(int a, int b) { return a * b; } int main() { int (*ops[3])(int, int) = { add, sub, mul }; for (int i = 0; i < 3; i++) { printf("%d ", ops[i](10, 3)); } printf("\n"); return 0; } // Trace: // i=0: ops[0](10,3) = add(10,3) = 13 // i=1: ops[1](10,3) = sub(10,3) = 7 // i=2: ops[2](10,3) = mul(10,3) = 30 // Output: 13 7 30
#include <stdio.h> int increment(int x) { return x + 1; } int doubleIt(int x) { return x * 2; } int applyTwice(int (*f)(int), int val) { return f(f(val)); } int doSteps(int (**steps)(int), int n, int val) { for (int i = 0; i < n; i++) val = steps[i](val); return val; } int main() { printf("%d\n", applyTwice(increment, 5)); // f(5) = 6, f(6) = 7 → 7 printf("%d\n", applyTwice(doubleIt, 3)); // f(3) = 6, f(6) = 12 → 12 int (*pipeline[3])(int) = { increment, doubleIt, increment }; printf("%d\n", doSteps(pipeline, 3, 4)); // Step 0: increment(4) = 5 // Step 1: doubleIt(5) = 10 // Step 2: increment(10) = 11 → 11 return 0; } // Output: // 7 // 12 // 11
תחביר מורכב אך חשוב מאוד. הפונקציה getOp מחזירה מצביע לפונקציה.
#include <stdio.h> int add(int a, int b) { return a + b; } int sub(int a, int b) { return a - b; } // Function that returns a function pointer // Syntax: return_type (*func_name(params))(fp_params) int (*getOp(int choice))(int, int) { if (choice == 0) return add; return sub; } int main() { int (*op)(int, int); op = getOp(0); printf("%d\n", op(10, 3)); // add(10,3) = 13 op = getOp(1); printf("%d\n", op(10, 3)); // sub(10,3) = 7 // Direct chained call: printf("%d\n", getOp(0)(4, 5)); // add(4,5) = 9 return 0; } // Output: // 13 // 7 // 9
#include <stdio.h> typedef struct { char *name; int (*operate)(int, int); } Operation; int add(int a, int b) { return a + b; } int mul(int a, int b) { return a * b; } int main() { Operation ops[2] = { { "add", add }, { "mul", mul } }; for (int i = 0; i < 2; i++) { printf("%s(%d,%d) = %d\n", ops[i].name, 6, 7, ops[i].operate(6, 7)); } return 0; } // Output: // add(6,7) = 13 // mul(6,7) = 42
#include <stdio.h> int countdown(int n, int (*self)(int, int (*)(int, int (*)()))) { // complex - let's use a simpler pattern: } // Simpler approach - static FP array for dispatch: int base(int n) { return 0; } int recurse(int n); int (*dispatch[2])(int) = { base, recurse }; int recurse(int n) { printf("%d ", n); return dispatch[n > 0](n - 1); // if n>0: calls recurse(n-1) = dispatch[1](n-1) // if n==0: calls base(n-1) = dispatch[0](-1) = 0 } int main() { recurse(3); printf("\n"); return 0; } // Trace: // recurse(3): print 3, n>0→dispatch[1](2)=recurse(2) // recurse(2): print 2, n>0→dispatch[1](1)=recurse(1) // recurse(1): print 1, n>0→dispatch[1](0)=recurse(0) // recurse(0): print 0, n==0→dispatch[0](-1)=base(-1)=0 // Output: 3 2 1 0
| דפוס | תחביר | דוגמה |
|---|---|---|
| מצביע פשוט | int (*fp)(int, int) |
fp = add; fp(3,4); |
| מערך של FP | int (*arr[3])(int, int) |
arr[0] = add; arr[0](3,4); |
| FP כפרמטר (callback) | void f(int (*cb)(int)) |
f(increment); |
| פונקציה מחזירה FP | int (*getOp(int))(int,int) |
getOp(0)(3,4); |
| typedef ל-FP | typedef int (*OpFunc)(int,int) |
OpFunc fp = add; |
| FP בתוך struct | struct S { int (*f)(int); }; |
s.f(5); |
| מצביע ל-מצביע-לפונקציה | int (**pp)(int, int) |
pp = &fp; (*pp)(3,4); |
פונקציות וריאדיות מקבלות מספר משתנה של ארגומנטים באמצעות stdarg.h. הזרימה: va_start → va_arg (חוזר) → va_end.
#include <stdio.h> #include <stdarg.h> int sum_all(int count, ...) { va_list args; va_start(args, count); int total = 0; for (int i = 0; i < count; i++) { total += va_arg(args, int); } va_end(args); return total; } int main() { printf("%d\n", sum_all(3, 10, 20, 30)); printf("%d\n", sum_all(5, 1, 2, 3, 4, 5)); return 0; } // Trace sum_all(3, 10, 20, 30): // i=0: va_arg → 10, total=10 // i=1: va_arg → 20, total=30 // i=2: va_arg → 30, total=60 // Output: // 60 // 15
#include <stdio.h> #include <stdarg.h> int product_all(int count, ...) { va_list args; va_start(args, count); int result = 1; for (int i = 0; i < count; i++) result *= va_arg(args, int); va_end(args); return result; } int main() { printf("%d\n", product_all(4, 2, 3, 4, 5)); return 0; } // Trace: 1*2=2, 2*3=6, 6*4=24, 24*5=120 // Output: 120
#include <stdio.h> #include <stdarg.h> int sum_even(int count, ...) { va_list args; va_start(args, count); int total = 0; for (int i = 0; i < count; i++) { int val = va_arg(args, int); if (val % 2 == 0) total += val; } va_end(args); return total; } int count_negatives(int count, ...) { va_list args; va_start(args, count); int neg = 0; for (int i = 0; i < count; i++) { if (va_arg(args, int) < 0) neg++; } va_end(args); return neg; } int main() { printf("%d\n", sum_even(5, 1, 2, 3, 4, 5)); // 1(odd),2(even→+2),3(odd),4(even→+4),5(odd) = 6 printf("%d\n", count_negatives(4, -3, 5, -1, 0)); // -3(neg),5(no),-1(neg),0(no) = 2 return 0; } // Output: // 6 // 2
#include <stdio.h> #include <stdarg.h> double average(int count, ...) { va_list args; va_start(args, count); double sum = 0.0; for (int i = 0; i < count; i++) sum += va_arg(args, double); va_end(args); return sum / count; } int main() { printf("%.1f\n", average(3, 2.0, 4.0, 6.0)); return 0; } // Trace: (2.0+4.0+6.0)/3 = 12.0/3 = 4.0 // Output: 4.0
float מקודם ל-double אוטומטית. לכן חייבים לציין va_arg(args, double) ולא va_arg(args, float).
#include <stdio.h> #include <stdarg.h> int sum_all(int count, ...) { va_list a; va_start(a, count); int s = 0; for (int i = 0; i < count; i++) s += va_arg(a, int); va_end(a); return s; } int product_all(int count, ...) { va_list a; va_start(a, count); int p = 1; for (int i = 0; i < count; i++) p *= va_arg(a, int); va_end(a); return p; } typedef int (*VarFunc)(int, ...); int main() { VarFunc funcs[2] = { sum_all, product_all }; printf("%d\n", funcs[0](3, 2, 3, 4)); // sum: 2+3+4=9 printf("%d\n", funcs[1](3, 2, 3, 4)); // prod: 2*3*4=24 return 0; } // Output: // 9 // 24
| שלב | פונקציה/מאקרו | תיאור |
|---|---|---|
| 1 | va_list args; |
הכרזה על משתנה שיחזיק את רשימת הארגומנטים |
| 2 | va_start(args, last_fixed) |
אתחול - last_fixed הוא הפרמטר הקבוע האחרון |
| 3 | va_arg(args, type) |
שליפת הארגומנט הבא מהרשימה לפי הטיפוס שצוין |
| 4 | va_end(args) |
ניקוי - חובה לקרוא לפני יציאה מהפונקציה |
va_arg לא יודע מה הטיפוס האמיתי. אם תציינו טיפוס שגוי (למשל float במקום double), תקבלו ערך שגוי ללא שגיאת קומפילציה.
משתנה static מקומי מאותחל פעם אחת בלבד, ושומר על ערכו בין קריאות לפונקציה. זהו דפוס נפוץ מאוד בשאלות מעקב קוד.
#include <stdio.h> int counter() { static int n = 0; n++; return n; } int main() { printf("%d ", counter()); // n: 0→1, return 1 printf("%d ", counter()); // n: 1→2, return 2 printf("%d ", counter()); // n: 2→3, return 3 printf("%d\n", counter()); // n: 3→4, return 4 return 0; } // Output: 1 2 3 4
#include <stdio.h> int addTo(int x) { static int sum = 0; sum += x; return sum; } int main() { printf("%d\n", addTo(10)); // sum: 0+10=10 printf("%d\n", addTo(5)); // sum: 10+5=15 printf("%d\n", addTo(20)); // sum: 15+20=35 printf("%d\n", addTo(-5)); // sum: 35+(-5)=30 return 0; } // Output: // 10 // 15 // 35 // 30
#include <stdio.h> int next_fib() { static int a = 0, b = 1; int result = a; int temp = a + b; a = b; b = temp; return result; } int main() { for (int i = 0; i < 8; i++) printf("%d ", next_fib()); printf("\n"); return 0; } // Trace (a,b → result): // Call 1: a=0,b=1 → result=0, temp=1, a=1,b=1 // Call 2: a=1,b=1 → result=1, temp=2, a=1,b=2 // Call 3: a=1,b=2 → result=1, temp=3, a=2,b=3 // Call 4: a=2,b=3 → result=2, temp=5, a=3,b=5 // Call 5: a=3,b=5 → result=3, temp=8, a=5,b=8 // Call 6: a=5,b=8 → result=5, temp=13,a=8,b=13 // Call 7: a=8,b=13→ result=8, temp=21,a=13,b=21 // Call 8: a=13,b=21→result=13,temp=34,a=21,b=34 // Output: 0 1 1 2 3 5 8 13
הפונקציה write(fd, buf, count) כותבת count בתים מ-buf ל-file descriptor fd. fd=1 הוא stdout, fd=2 הוא stderr.
#include <unistd.h> int main() { write(1, "Hello\n", 6); // fd=1 (stdout), writes 6 bytes: H,e,l,l,o,\n write(1, "ABCDEF", 3); // writes only 3 bytes: A,B,C write(1, "\n", 1); return 0; } // Output: // Hello // ABC
write(1, "Hello", 3) ידפיס רק Hel - לא את כל המחרוזת! הפרמטר השלישי קובע כמה בתים נכתבים.
#include <unistd.h> #include <stdio.h> #include <fcntl.h> int main() { int fd = open("test.txt", O_RDONLY); // Assume test.txt contains: "ABCDEFGHIJ" char buf[5]; int n = read(fd, buf, 4); // reads 4 bytes into buf: 'A','B','C','D' buf[n] = '\0'; // null-terminate printf("Read %d bytes: %s\n", n, buf); close(fd); return 0; } // Output: Read 4 bytes: ABCD
fork() הוא נושא מרכזי במבחנים. fork() יוצר עותק של התהליך. ההורה מקבל את ה-PID של הילד, הילד מקבל 0. כל fork() מכפיל את מספר התהליכים.
#include <stdio.h> #include <unistd.h> int main() { fork(); // Now 2 processes printf("A\n"); return 0; } // Output: A is printed 2 times // A // A
#include <stdio.h> #include <unistd.h> int main() { fork(); // 1 → 2 processes fork(); // 2 → 4 processes printf("B\n"); return 0; } // Output: B is printed 4 times (2^2 = 4) // B // B // B // B
#include <stdio.h> #include <unistd.h> int main() { fork(); // 1 → 2 fork(); // 2 → 4 fork(); // 4 → 8 printf("C\n"); return 0; } // Output: C is printed 8 times (2^3 = 8)
fork() רצופות יוצרות 2^N תהליכים. כל תהליך ממשיך מנקודת ה-fork ואילך.
#include <stdio.h> #include <unistd.h> int main() { pid_t pid = fork(); if (pid == 0) { // Child process printf("Child\n"); } else { // Parent process printf("Parent\n"); } printf("Both\n"); return 0; } // Output (order may vary): // Parent // Both // Child // Both // (Parent prints "Parent" + "Both", Child prints "Child" + "Both")
#include <stdio.h> #include <unistd.h> #include <fcntl.h> int main() { // Assume file contains: "ABCDEFGHIJ" (10 bytes) int fd = open("data.txt", O_RDONLY); char buf[4]; read(fd, buf, 3); // reads "ABC", pos=3 buf[3] = '\0'; printf("%s\n", buf); // ABC lseek(fd, 5, SEEK_SET); // move to position 5 read(fd, buf, 3); // reads "FGH", pos=8 buf[3] = '\0'; printf("%s\n", buf); // FGH lseek(fd, -2, SEEK_CUR); // move back 2 from current (8-2=6) read(fd, buf, 2); // reads "GH", pos=8 buf[2] = '\0'; printf("%s\n", buf); // GH close(fd); return 0; } // Output: // ABC // FGH // GH
#include <stdio.h> #include <unistd.h> int main() { printf("Before fork: PID=%d\n", getpid()); pid_t pid = fork(); if (pid == 0) { printf("Child: PID=%d, Parent PID=%d\n", getpid(), getppid()); } else { printf("Parent: PID=%d, Child PID=%d\n", getpid(), pid); } return 0; } // Output (PIDs are system-assigned, example): // Before fork: PID=1234 // Parent: PID=1234, Child PID=1235 // Child: PID=1235, Parent PID=1234
getpid() מחזיר את ה-PID של התהליך הנוכחי. getppid() מחזיר את ה-PID של ההורה. בשאלות מבחן בדרך כלל שואלים על מספר ההדפסות, לא על ערכי PID ספציפיים.
#include <stdio.h> int factorial(int n) { if (n <= 1) return 1; return n * factorial(n - 1); } int power(int base, int exp) { if (exp == 0) return 1; return base * power(base, exp - 1); } int fib(int n) { if (n <= 1) return n; return fib(n - 1) + fib(n - 2); } int main() { printf("%d\n", factorial(6)); // 720 printf("%d\n", power(2, 8)); // 256 printf("%d\n", fib(7)); // 13 return 0; } // Trace factorial(6): 6*5*4*3*2*1 = 720 // Trace power(2,8): 2*2*2*2*2*2*2*2 = 256 // Trace fib(7): fib(6)+fib(5) = 8+5 = 13 // Output: // 720 // 256 // 13
#include <stdio.h> int gcd(int a, int b) { if (b == 0) return a; return gcd(b, a % b); } int sumDown(int n) { if (n <= 0) return 0; return n + sumDown(n - 1); } int main() { printf("%d\n", gcd(54, 24)); // gcd(54,24)→gcd(24,6)→gcd(6,0)→6 printf("%d\n", sumDown(5)); // 5+4+3+2+1+0 = 15 printf("%d\n", gcd(100, 75)); // gcd(100,75)→gcd(75,25)→gcd(25,0)→25 return 0; } // Output: // 6 // 15 // 25
#include <stdio.h> static inline int mul2(int x) { return x * 2; } int __attribute__((cdecl)) recSum(int n) { if (n <= 0) return 0; return n + recSum(n - 1); } int main() { int (*fp)(int) = mul2; int val = recSum(4); // 4+3+2+1+0 = 10 int result = fp(val); // mul2(10) = 20 printf("%d\n", result); return 0; } // Output: 20
#include <stdio.h> int applyRec(int (*f)(int), int val, int times) { if (times <= 0) return val; return applyRec(f, f(val), times - 1); } int inc(int x) { return x + 1; } int dbl(int x) { return x * 2; } int main() { printf("%d\n", applyRec(inc, 0, 5)); // inc applied 5 times to 0: 0→1→2→3→4→5 printf("%d\n", applyRec(dbl, 1, 4)); // dbl applied 4 times to 1: 1→2→4→8→16 return 0; } // Output: // 5 // 16
#include <stdio.h> int g = 10; void addToG(int x) { g += x; } void mulG(int x) { g *= x; } void resetG() { g = 0; } int main() { printf("%d\n", g); // 10 addToG(5); printf("%d\n", g); // 10+5 = 15 mulG(3); printf("%d\n", g); // 15*3 = 45 addToG(-10); printf("%d\n", g); // 45+(-10) = 35 resetG(); printf("%d\n", g); // 0 return 0; } // Output: // 10 // 15 // 45 // 35 // 0
#include <stdio.h> int val = 1; void doubleVal() { val *= 2; } void addTen() { val += 10; } void subThree() { val -= 3; } int main() { void (*steps[4])() = { doubleVal, addTen, doubleVal, subThree }; for (int i = 0; i < 4; i++) { steps[i](); printf("val=%d\n", val); } return 0; } // Trace: // step 0: doubleVal() → val = 1*2 = 2 // step 1: addTen() → val = 2+10 = 12 // step 2: doubleVal() → val = 12*2 = 24 // step 3: subThree() → val = 24-3 = 21 // Output: // val=2 // val=12 // val=24 // val=21
MSVC (Visual Studio) ו-GCC משתמשים בתחביר שונה עבור calling conventions. הפתרון: #ifdef _MSC_VER מזהה את MSVC, וה-#else מטפל ב-GCC/Clang.
#include <stdio.h> #ifdef _MSC_VER #define CDECL __cdecl #define FASTCALL __fastcall #else #define CDECL __attribute__((cdecl)) #define FASTCALL __attribute__((fastcall)) #endif int CDECL addC(int a, int b) { return a + b; } int FASTCALL addF(int a, int b) { return a + b; } int main() { printf("cdecl: %d\n", addC(10, 20)); printf("fastcall: %d\n", addF(10, 20)); return 0; } // Output (identical on both compilers): // cdecl: 30 // fastcall: 30
#ifdef קובע איזה ענף ייכלל. אם השאלה מציינת "compiled with GCC", אז _MSC_VER לא מוגדר וה-#else הוא הרלוונטי. הפלט הלוגי זהה בשני המקרים.
#include <stdio.h> #ifdef _MSC_VER #define CALL_CONV __cdecl #define COMPILER_NAME "MSVC" #elif defined(__GNUC__) #define CALL_CONV __attribute__((cdecl)) #define COMPILER_NAME "GCC" #else #define CALL_CONV #define COMPILER_NAME "Unknown" #endif int CALL_CONV compute(int x) { return x * x + 1; } int main() { printf("Compiler: %s\n", COMPILER_NAME); printf("Result: %d\n", compute(7)); return 0; } // If compiled with GCC: // Output: // Compiler: GCC // Result: 50
מדריך זה כיסה את כל הנושאים הנבחנים ב-120 שאלות הקוד בנושא Functions & Calling Conventions. נקודות מפתח למעקב קוד:
static בתוך inline שורדים בין קריאות.va_arg שולף ארגומנטים בסדר. float מקודם ל-double.