سوال ها
- حجم یک کره
- جمع دو عدد
- کوچکترین از دو عدد
- کوچکترین از سه عدد
- چاپ یک رشته
- چاپ یک آرایه
- جمع محتوای یک آرایه
مقدمه
شکل کلی تعریف یک ماکرو به اینصورته:
#define MACRO (argument1, argument2, …, argumentN) replacement-expression
که در عبارت فوق یک ماکرو به نام MACRO با N آرگومان توسط دستورالعمل define تعریف شده و عبارت replacement-expression جایگزین نام ماکرو در برنامه قبل از کامپایل شدن میشود.
توجه: اسامی ثابت ها و ماکرو ها را با حروف بزرگ مینویسیم تا متمایز بشن با سایر شناسه های بکار رفته در برنامه.
مثال: تعریف یک ماکرو به نام MULTIPLY با دو آرگومان x و y که کار ضرب را انجام میدهد:
#define MULTIPLY (x, y) ( ( x ) * ( y ) )
شاید در نگاه اول پرانتز ها غیرضروری و اضافه بنظر برسد اما این چنین نیست. تنها کاری که برنامه پیش پردازنده انجام میدهد کپی کردن آرگومان ها در عبارت جایگزین، و قرار دادن عبارت جایگزین بجای ماکرو در کد برنامه است. و آرگومان ها و عبارت جایگزین هنگام بسط ماکرو به هیچ وجه ارزیابی نمیشوند. بنابراین دو نکته از این پرانتزها میتوان استخراج کرد:
نکته ۱: بهتر است کل عبارت جایگزین داخل پرانتز بیاید. این به این خاطر است که فرض کنید میخواهید ماکرو را درون یک عبارت محاسباتی بکار ببرید آیا اگر کل عبارت جایگزین داخل پرانتز نباشد برنامه نتیجه ی مورد نظر را تولید خواهد کرد؟ مثلا در عبارت محاسباتی زیر اگه عبارت جایگزین داخل پرانتز نباشد نتیجه مطلوب حاصل نمیشود.
۷ / MULTIPLY(3, 5) => 7 / 3 * 5 = ( 7 / 3 ) * 3
اما نتیجه مطلوب:
۷ / ( ۳ * ۵)
نکته ۲: بهتر است آرگومان های ماکرو را در عبارت جایگزین درون پرانتز قراردهیم. چون ممکن است آرگومان های ماکرو همیشه یک لیترال یا متغیر نباشند. مثلا آرگومان میتواند یک تابع باشد یا یک عبارت محاسباتی. در نتیجه اگر آرگومان داخل پرانتز نباشد ممکن است نتیجه مطلوب بدست نیاید. مثال:
MULTIPLY( 2 + 5, 7 ) => 2 + 5 * 7 = 2 + ( 5 * 7 )
اما نتیجه مطلوب:
( ۲ + ۵ ) * ۷
توجه: نشانه گذاری ‘({…})’ باعث میشود بلاکی از دستورات به عنوان یک عبارت عمل کند. در این حالت حاصل عبارت، مقدار آخرین دستور بلاک است. این نشانه گذاری در تعریف ماکرو ها بسیار بکار میرود. مثال هایی از موارد استفاده از این نشانه گذاری را در جواب تمرین ها خواهید دید.
مساله: حجم یک کره
برنامه ای بنویسید توسط یک ماکرو (macro) با یک آرگومان حجم یک کره را حساب کند. برنامه باید حجم کره ای هایی از شعاع ۱ تا ۱۰ را حساب کند و نتیجه را در قالب جدول چاپ کند. فرمول حجم یک کره به اینصورته:
( ۴٫۰ / ۳ ) * PI * r3
که PI هست ۳٫۱۴۱۵۹
حل- این تمرین میخواهد نشان دهد که میشه از ثابتی که قبلا با define تعریف شده در یک ثابت یا ماکروی دیگر استفاده کرد. ابتدا ثابت PI را توسط define تعریف میکنیم و بعد از اون یک ماکرو به نام VOLUME با یک آرگومان تعریف میکنیم که و عبارت فوق را جلوی آن قرار میدهیم. دو مدل جدول میشه برای نمایش حجم کره ها با شعاع های ۱ تا ۱۰ بکار برد. یک جدول که شامل دو سطر هست که در ستون های سطر اول شعاع ۱ تا ۱۰ اومده و در ستون های سطر دوم حجم (شکل ۱). و جدول دیگه شامل دو ستونه که در هر سطر از آن در ستون اول شعاع و در ستون دوم حجم آمده (شکل ۲). کد فرض میگیریم منظور از طراح سوال شکل ۲ است.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | #include <stdio.h> #define PI 3.14159 #define VOLUME(r) ( 4.0 / 3 * PI * ( r ) * ( r ) * ( r ) ) int main(void) { int r; //print header of table printf("%8s%12s\n", "Radius", "Volume"); //print data for(r = 1; r <= 10; r++) printf("%8d%12.3lf\n", r, VOLUME(r) ); return 0; } /* result Radius Volume 1 4.189 2 33.510 3 113.097 4 268.082 5 523.598 6 904.778 7 1436.754 8 2144.659 9 3053.625 10 4188.787 */ |
سوال: جمع دو عدد
یک برنامه بنویسید که یک ماکرو به نام SUM تعریف کنه با دو آرگومان x و y و توسط آن خروجی زیر تولید شود:
The sum of x and y is 13
1 2 3 4 5 6 7 8 9 10 11 12 | #include <stdio.h> #define SUM(x, y) ( (x) + (y) ) int main(void) { printf("%s%d", "The sum of x and y is ", SUM(10, 3) ); return 0; } /* result The sum of x and y is 13 */ |
سوال: کوچکترین از دو عدد
یک برنامه بنویسید که یک ماکرو به نام MINIMUM2 تعریف کند که توسط آن از بین دو مقدار عددی کوچکترین اشان رابدست آورد. دو عدد از صفحه کلید گرفته شود.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | #include <stdio.h> #define MINIMUM2(a, b) ( ( a ) < ( b ) ? ( a ) : ( b ) ) int main(void) { //integer values int a, b; printf("%s", "Enter two integer numbers: "); scanf("%d %d", &a, &b); printf("The minimum of %d and %d is %d\n\n", a, b, MINIMUM2(a, b) ); //float values float x, y; printf("%s", "Enter two numbers: "); scanf("%f %f", &x, &y); printf("The minimum of %.2f and %.2f is %.2f\n", x, y, MINIMUM2(x ,y) ); return 0; } /* Enter two integer number: 5 10 The minimum of 5 and 10 is 5 Enter two float number: 1.5 0.99 The minimum of 1.50 and 0.99 is 0.99 */ |
سوال: کوچکترین از سه عدد
یک برنامه بنویسید که یک ماکرو به نام MINIMUM3 تعریف کند و توسط ماکرو MINIMUM2 تمرین قبل کوچکترین مقدار را از بین سه مقدار عددی بدست آورد.
حل – هدف از این تمرین اینه که نشان بده میشه از ماکرویی که قبلا تعریف شده در یک ماکروی دیگر استفاده کرد. اگر ماکرو MINIMUM3 را بصورت MINIMUM2(a, MINIMUM(b, c)) تعریف کرده باشیم عبارتی که جایگزین این ماکرو میشود این است:
MINIMUM2(a, MINIMUM2(b, c) )
- ( ( a ) < ( MINIMUM2(b, c) ) ? ( a ) : ( MINIMUM2(b, c) ) )
- ( ( a ) < ( ( ( b ) < ( c ) ? ( b ) : ( c ) ) ) ? ( a ) : ( ( ( b ) < ( c ) ? ( b ) : ( c ) ) ) )
که همانطور در بسط ماکرو دیده میشه. ماکروی MINIMUM2(b,c) دومرتبه بسط داده شده، یعنی عبارتی یکسان دو مرتبه محاسبه شده. اما میتوان با نشانه گذاری ({}) همراه با typeof پردازش ماکروی فوق را یکبار انجام داد. یعنی اینکه ابتدا یک متغیر از نوع آرگومان ورودی تعریف میکنیم و آن را مساوی MINIMUM(b,c) قرار میدیم سپس این متغیر را جای دومین آرگومان ماکرو قرار میدیم (هنگام کامپایل کد از گزینه –std=gnu99 برای کار با typeof استفاده کنید.)
توجه: قرار دادن عبارت جایگزین ماکروی MINIMUM3 در بین پرانتز الزامی نیست. چون کل ماکروی MINIMUM2 داخل پرانتز قرار گرفته.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | #include <stdio.h> #define MINIMUM2(a, b) ( ( a ) < ( b ) ? ( a ) : ( b ) ) #define MINIMUM3(x, y, z) MINIMUM2(x, MINIMUM2(y, z) ) //another version of MINIMUM3 #define MINIMUM(a, b, c) \ ({ typeof(a) min2 = MINIMUM2(b, c); \ MINIMUM2(a, min2); }) int main(void) { //integer values int a, b, c; printf("%s", "Enter three integer numbers: "); scanf("%d %d %d", &a, &b, &c); printf("The minimum of %d, %d and %d is %d\n\n", a, b, c, MINIMUM(a, b, c) ); //float values float x, y, z; printf("%s", "Enter three numbers: "); scanf("%f %f %f", &x, &y, &z); printf("The minimum of %.2f, %.2f and %.2f is %.2f\n", x, y, z, MINIMUM(x ,y, z) ); return 0; } /* Enter three integer numbers: 15 2 18 The minimum of 15, 2 and 18 is 2 Enter three numbers: 1 5 0.2 The minimum of 1.00, 5.00 and 0.20 is 0.20 */ |
سوال – چاپ رشته
برنامه ای بنویسید که مقدار رشته ای را توسط تعریف و استفاده ی ماکرو PRINT چاپ کنید
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | #include <stdio.h> #define PRINT(str) printf("%s", str) int main(void) { char string[50]; printf("%s","Enter a string value: "); scanf("%s", string); PRINT("your text is: "); PRINT(string); PRINT("\n"); PRINT("OK. Good Bye!"); return 0; } /* Enter a string value: Hi your text is: Hi OK. Good Bye! */ |
سوال – چاپ یک آرایه
یک برنامه بنویسید که با تعریف و استفاده از یک ماکرو به نام PRINTARRAY یک آرایه عددی را بگیرد. این ماکرو دو آرگومان میگیرد یکی آرایه و دیگری تعداد عناصر آن.
حل – عناصر آرایه را در یک خط چاپ میکنیم. احتمالا منظور از طرح این سوال این بوده که میشه ماکرو را در چند خط معرفی کرد. اگر توجه کرده باشید یک ماکرو را در حالت عادی نمیتوانید در چند خط تعریف کنید. در حالت تعریف چند خطی یک ماکرو انتهای خط های میانی یک backslash میذاریم. در کد زیرین دو ماکرو یکی یک خطی و دیگری چند خطی تعریف کردیم. اما اگه بخواهیم یک new line در انتها چاپ کنیم آنگاه تعداد دستوراتمون بیش از ۱ خواهد شد و در این حالت بهتر است که دستورات در یک بلاک قرار گیرند و از نشانه گذاری ({}) استفاده کنیم.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | #include <stdio.h> //single-line macro #define SINGLE_LINE_PRINTARRAY(arr, n) for(int i = 0; i < (n); i++) printf("%4d", arr[i] ); //multi-line macro #define PRINTARRAY(arr, n) for(int i = 0; i < (n); i++) \ printf("%4d", arr[i] ); //multi-command macro #define MULTI_COMMAND_PRINTARRAY(arr, n) \ ({int i; \ for( i = 0; i < ( n ); i++ ) \ printf("%4d", arr[i]); \ printf("%c",'\n');}) int main(void) { int a[] = {10, 20, 30}; puts("Values of the array:\n"); printf("%40s", "print using MULTI_COMMAND_PRINTARRAY"); MULTI_COMMAND_PRINTARRAY(a, 3); printf("%40s", "print using SINGLE_LINE_PRINTARRAY"); SINGLE_LINE_PRINTARRAY(a, 3); printf("%c",'\n'); //new line printf("%40s", "print using PRINTARRAY"); PRINTARRAY(a, 3); } /* output Values of the array: print using MULTI_COMMAND_PRINTARRAY 10 20 30 print using SINGLE_LINE_PRINTARRAY 10 20 30 print using PRINTARRAY 10 20 30 */ |
سوال – جمع محتوای یک آرایه
از ما خواسته شده برنامه ای بنویسیم که توسط تعریف و استفاده از یک ماکرو به نام SUMARRAY جمع عناصر یک آرایه عددی را بدست آوریم. این ماکرو باید آرایه و تعداد عناصر آن را بعنوان آرگومان بگیره.
حل – احتمالا این سوال از بین تمرین ها نکته دار ترین است. چون سوال خیلی دقیق نیست میشه به شکل های مختلف جواب مساله را داد. در ادامه سه برداشت مختلف ذکر شده که کد هر سه مورد بررسی میشن.
برداشت ۱ – فرض میگیریم که یک متغیر به نام total قبل از استفاده از ماکرو تعریف کردیم. حالا یک ماکرو تعریف میکنیم که دو آرگومان داره یکی نام آرایه و دیگری تعداد عناصر آن، کار این ماکرو جمع زدن عناصر آرایه با متغیر total است . اسم این ماکرو را میذاریم SUMARRAY1
برداشت ۲ – ضعف ماکروی SUMARRAY1 اینه که وابسطه به نام total است. اگر در برنامه نام دیگری برای متغیر انتخاب کنیم با مشکل مواجه میشیم اما اگر نام متغیری که مجموع در آن قرار میگیرد را به عنوان سومین آرگومان به ماکرو بدیم ماکروی SUMARRAY2 را یک گام کاربردی تر کردیم. پس یک آرگومان سومی برای ماکرو درنظر میگیریم که اسم متغیری است که قرار هست حاصل جمع عناصر آرایه قرار گیرد. این ماکرو را SUMARRAY2 مینامیم.
برداشت ۳ – همانطور که گفته شد اگر دستورات را داخل ({}) قرار دهیم همانند یک عبارت عمل میکند. که حاصل آن نتیجه آخرین دستور است. پس کافیه جمع عناصر را توسط حلقه بدست بیاریم و در انتها متغیری که حاوی مجموع است را بنویسیم. در نتیجه بسط ماکرو مساوی با حاصل جمع آرایه است. در این ماکرو میخواهیم سه آرگومان داشته باشیم که دوتای آن طبق معمول نام آرایه و تعداد عناصر آن و سومین آرگومان نوع آرایه باشه (مثل float یا int – جالبه بدونیم که میشه این کلمات کلیدی را به عنوان آرگومان به ماکرو داد). با این کار دیگه نیازی به تعریف متغیر قبل از استفاده از ماکرو نداریم و درون ماکرو متغیری از نوع آرگومان سوم برای نگهداری مجموع تعریف میکنیم (هرچند همانند همان تکنیکی در ماکروی MINIMUM3 بکار رفت میتوان توسط typeof یک متغیر از نوع عناصر آرایه تعریف کرد اما ترجیح میدیم از این تکنیک که گفته شد برای تعیین نوع متغیر استفاده کنیم.)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 | #include <stdio.h> #define SUMARRAY1(arr, n) \ ({total = 0; \ for(int i = 0; i < (n); i++) \ total += arr[i]; }) #define SUMARRAY2(arr, n, result) \ ({result = 0; \ for(int i = 0; i < (n); i++) \ result += arr[i]; }) #define SUMARRAY3(arr, n, type) \ ({type total = 0; \ for(int i = 0; i < ( n ); i++) { \ total += arr[i]; \ } \ total;}) int main(void) { int const SIZE = 3; float a[3] = {1.0, 2.5, 3.5}; //print array printf("%25s ", "Values of the array"); for(int i = 0; i < SIZE; i++) printf("%6.2f", a[i]); puts(""); //usage of SUMARRAY1 float total; SUMARRAY1(a, SIZE); printf("%25s %6.2f\n", "Total using SUMARRAY1", total); //usage of SUMARRAY2 float ans; SUMARRAY2(a, SIZE, ans); printf("%25s %6.2f\n", "Total using SUMARRAY2", ans); //usage of SUMARRAY3 printf("%25s %6.2f\n", "Total using SUMARRAY3", SUMARRAY3(a, SIZE, float)); } /* output Values of the array 1.00 2.50 3.50 Total using SUMARRAY1 7.00 Total using SUMARRAY2 7.00 Total using SUMARRAY3 7.00 */ |
1 دیدگاه
بازخورد: حل تمرین های کتاب دایتل C | پی سی آموز | آموزش ساده تا حرفه ای