الفريق العربي للبرمجةأرشيف المنتديات · 2000 – 2023
نسخة أرشيفية للقراءة فقط — التسجيل والمشاركة مغلقان، والمحتوى محفوظ كما كان.

MACROS

بدأه مصطفى 36a2 في 25 مايو 2012 · 12 رد · 3,822 مشاهدة · في لغة C و ++C
مشاركة: واتساب X فيسبوك تيليجرام
#1 صاحب الموضوع

Macro

الماكرو هي كتلة برمجية يحتفظ المترجم بشكلها طوال مدة الترجمة على أنها من المسلمات ...

كأن يعتبر كل حرف z يكافئ الرقم 5 مثلاً ..

نعرف الماكرو عن طريق التعليمة #define

وهناك عدة أشكال لهذه التعليمة لها فوائد كثيرة ... سنتحدث عن شكلين .. فوائدهما ومضارهما ..

وأنتظر من الخبراء تنقيح المعلومات وإفادتنا بالمزيد ...

الشكل الأول :

#define NewWord KnownWord

NewWord : هي المتحول الذي نريده أن يصبح معرفاً بالنسبة للمعالج ...

KnownWord :هي اسم تعليمة أو تابع أو حتى عدة تعليمات معاً .. ولكن يجب أن تكون معرفة عند استعمالها ..

أمثلة :

#define number int

#define loop for

#define infinite 999999

#define PI 3.14

نلاحظ أهمية هذه الطريقة بحيث أن المعالج يعمل بطريقة سلسة جداً عندما يرى الكلمة المعرفة في الماكرو فهو يستبدلها بكل أريحية دون أي اشكال

مثال بسيط شامل للشكل الأول ...

ما رأيكم أن نستخدم بدل تعليمة cout تعليمة جديدة دون أن نضطر إلى إنشاء كائن جديد مثلاً : Write

وأن نجعل البرنامج "]يكتب جملة واحدة بتعليمة من حرف واحد] !!!

#include<iostream.h>
#define Write cout

#define number int
#define loop for
#define infinite 999999
#define PI 3.14
#define M Write<<"This Is Amazing Thing!!\n";
#define endline <<endl
#define pulsplus ++
void main()
{
number x=infinite;
float y=PI;
loop(number i=0;i<y;i pulsplus)
M
Write<<"Welocme To Macro++"endline;
}

لاحظ أنه يمكننا كتابة أي شيء نريده في الجزء الأيمن (المعرف بالنسبة للمعالج )

أما القسم الأيسر (الذي نريد تعريفه ) فهو يخضع لق%u

تم تعديل هذه المشاركة بواسطة مصطفى 36a2 في 25 مايو 2012 في 17:28

2
#2

بما أنك تحدث عن الماكرو و عن طريقة استخدماها و فهمك لها فهما صحيحا

فأتمنى أن ألقى بعض المساعدة منك في بعض المشاكل التي واجهتني فيها

مثلا طريقة استعمال _VA_ARGS

هذا كود لم أفهمه جيدا إذا أمكنك أن تشرح لنا كيفيه استعمال macro to find a number of params

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

#define NUMARGS(...)  (sizeof((int[]){__VA_ARGS__})/sizeof(int))
#define SUM(...)  (sum(NUMARGS(__VA_ARGS__), __VA_ARGS__))

void sum(int numargs, ...);

int main(int argc, char *argv[]) {

	SUM(1);
	SUM(1, 2);
	SUM(1, 2, 3);
	SUM(1, 2, 3, 4);

	return 1;
}

void sum(int numargs, ...) {
	int 	total = 0;
	va_list ap;

	printf("sum() called with %d params:", numargs);
	va_start(ap, numargs);
	while (numargs--)
    	total += va_arg(ap, int);
	va_end(ap);

	printf(" %d\n", total);

	return;
}

و نسأل الله لنا ولك التوفيق

تم تعديل هذه المشاركة بواسطة مومو (momo) في 26 مايو 2012 في 00:35

1 −1

i love you mama

#3

أخي مومو هذا الموضوع يتعلق بميراث لغة ال C والوسطاء الغير منتهية ... أكثر مما يتعلق بالماكرو

لا أظن أن بإمكاني مساعدتك ولكن ارجع إلى الموضوع الذي يتحدث عن دالة printf والوسطاء اللانهائية

وأترك الإجابة لمن هو أعلم ... والسلام عليكم

#4
مصطفى 36a2 كتب:

أخي مومو هذا الموضوع يتعلق بميراث لغة ال C والوسطاء الغير منتهية ... أكثر مما يتعلق بالماكرو

لا أظن أن بإمكاني مساعدتك ولكن ارجع إلى الموضوع الذي يتحدث عن دالة printf والوسطاء اللانهائية

وأترك الإجابة لمن هو أعلم ... والسلام عليكم

يأخى ما هى الا Function-Like Macros أو Macro Parameter in C

وبما ان الانتخابات انتهت ومرسي الاول ان شاء سنحاول تفسير هذا

والله من وراء القصد

1 −1

post-15367-027053900%201340345097.gifpost-15367-087183200%201340344597.gif
post-247365-0-10080400-1379403554_thumb.

#5

الماكرو الاول كدالة تستقبل مرسلات arguments متغيرة

ثم تعمل casting لهذه المتغيرات والموجودة على شكل مصفوفة فى المتغير __VA_ARGS__ والذى تستخدمه اللغة نفسها لهذا الغرض

وتحولها الى مصفوفة من الارقام int[]

ثم تأتى بطولها باستخدام المعامل sizeof

ثم يقسم الناتج على حجم النوع int وفى الغالب هو 4bytes طبعا فى الاجهزة 32bit

فيخرج له طول المصفوفة التى كانت مجهولة

ثم يستخدم هذا الماكرو (الدالة) فى الماكرو الثانى وهو ايضا على شكل دالة مجهولة المرسلات arguments كسابقتها

وهنا يستخدم المصفوفة القياسية standard التى تمثل المرسلات المتغيرة __VA_ARGS__ مرتين

الاولى بارسالها الى الماكرو الاول ليعيد طولها وليكن 10 مثلا اى تحتوى على 10 عناصر elements

والثانية بارسالها كما هى الى الدالة sum الموجودة فى الكود

والتى تستقبل متغيرين الاول عدد المرسلات والثانى مصفوفة مجهولة(عدد مجهول) من المرسلات

وطبعا يستخدم المرسل الاول وهو عدد المرسلات فى الدوران loop

اظن الباقى سهل ولا يحتاج الى توضيح

تحياتى

2


﴿وَمَا كَانَ اللَّهُ لِيُعَذِّبَهُمْ وَأَنْتَ فِيهِمْ ۚ وَمَا كَانَ اللَّهُ مُعَذِّبَهُمْ وَهُمْ يَسْتَغْفِرُونَ﴾

الأنفال ۳۳                                  
 

#6

شكرا أخ فتحي على الرد و إسهابك في الشرح

ولكن لا تزال بعض الأشياء مبهمة بالنسبة لي

لنأخد فقط هذا الجزء من الكود

	void sum(int numargs, ...) {
    	int 	total = 0;
    	va_list ap;

    	printf("sum() called with %d params:", numargs);
    	va_start(ap, numargs);
    	while (numargs--)
    	total += va_arg(ap, int);
    	va_end(ap);

    	printf(" %d\n", total);

    	return;
}

فكما ألاحظ أن هذه الدالة الا منتهية params

مقسمة للأربع ماكرو :

va_list

va_start

va_arg

va_end

ماهي وظيفة كل ماكرو من الأربعة و شكرا مسبقا

i love you mama

#7
AudaNix كتب:

يأخى ما هى الا Function-Like Macros أو Macro Parameter in C

وبما ان الانتخابات انتهت ومرسي الاول ان شاء سنحاول تفسير هذا

والله من وراء القصد

ننتظر شرحك للماكرو بفارغ الصبر

الله يوفقك أخي على كل مجهود تبذله ويجزاك الخير

i love you mama

#8

va_list نوع او type وليس ماكرو

اقتباس

The va_list type is an opaque type that refers to the function's arguments. Declare a local variable of type va_list and supply the variable to the va_start, va_arg, and va_end macros.

va_start ماكرو

اقتباس

The va_start macro initializes ap and prepares to fetch function arguments with va_arg. You must call va_end to clean up and finalize ap. The second argument to va_start is the name of the function's last parameter before the ellipsis. The last named parameter must not have a function, array, or reference type.

va_arg ماكرو

اقتباس

The va_arg macro fetches the next argument, which must be of type T. The ap parameter must have been initialized by calling va_start. The type T must be a type that results from the standard type promotions

va_end ماكرو

اقتباس

The va_end macro finishes fetching arguments. You must call va_end once for each call to va_start. You cannot nest calls to va_start and va_end, but you can call them multiple times sequentially in the same function.

1


﴿وَمَا كَانَ اللَّهُ لِيُعَذِّبَهُمْ وَأَنْتَ فِيهِمْ ۚ وَمَا كَانَ اللَّهُ مُعَذِّبَهُمْ وَهُمْ يَسْتَغْفِرُونَ﴾

الأنفال ۳۳                                  
 

#9

:sad: أريد أن أنوه لموضوع بسيط وهو أن المقالة محذوف ثلاثة أرباعها لسبب أجهله ... :sad: يبدو انني قمت بحذف النصف الثاني أثناء تحريره لذلك أعتذر بشدة

وسأحاول إعادة كتابة المقال ... أرجو ألا تؤاخذوني على هذا الخطأ فقد كان الموضوع يتضمن دوال الماكرو بل إنها كانت الفكرة الرئيسية فيه

ولا حول ولا قوة الا بالله

تم تعديل هذه المشاركة بواسطة مصطفى 36a2 في 28 مايو 2012 في 14:53

1
#10
اقتباس

ولا حول ولا قوة الا بالله

اخى مصطفى لا مشكلة مازال الموضوع مفتوحا ويمكنك الاضافة عليه

بل ارى انه من الافضل ان توزع الدرس على عدة فقرات وكل فقرة فى رد مستقل فى نفس الموضوع طبعا

نحن متابعين للموضوع وبانتظار المزيد :)

بالتوفيق


﴿وَمَا كَانَ اللَّهُ لِيُعَذِّبَهُمْ وَأَنْتَ فِيهِمْ ۚ وَمَا كَانَ اللَّهُ مُعَذِّبَهُمْ وَهُمْ يَسْتَغْفِرُونَ﴾

الأنفال ۳۳                                  
 

#11
اقتباس
نحن متابعين للموضوع وبانتظار المزيد :)
#12

شكراً على تفهمكم جزاكم الله خيراً ...

وهذه تتمة الموضوع مع إعادة لآخر كود :

#include<iostream.h>
#define Write cout
#define number int
#define loop for
#define infinite 999999
#define PI 3.14
#define M Write<<"This Is Amazing Thing!!\n";
#define endline <<endl
#define pulsplus ++
void main()
{
number x=infinite;
float y=PI;
loop(number i=0;i<y;i pulsplus)
M
Write<<"Welocme To Macro++"endline;
}

لاحظ أنه يمكننا كتابة أي شيء نريده في الجزء الأيمن (المعرف بالنسبة للمعالج ) ما عدا هذا الرمز " لوحده حيث أن المعالج يعتبره فاتحة لكتابة نص

أما القسم الأيسر (الذي نريد تعريفه ) فهو يخضع لقوانين التسمية المعروفة ( أي أنه يجب ألا يبدأ برقم وألا يحتوي على رموز غير_) وهكذا ...

ويجب الانتباه إلى ان الوحدة يجب أن تكون في سطر واحد فقط .. وإلا فسيعطينا المترجم خطأ قواعديا معقدا..

والآن إلى الشكل الثاني والأكثر روعة :

أنه يشبه التوابع ولكنه أكثر مرونه بكثير .. ولا ننسى أنه يجب كتابته على سطر واحد ..

ويأخذ الشكل :

#define Macroname(parameters) AnyThimgToDoWithParameters

وفرقه عن التوابع العادية كبير ... فهو يستبدل الوسيط بمكانه تماماً كما لو قمنا بعمل ctrl+H واستبدلنا الوسيط يدوياً ..

كما أنه لا ينظر لنوع الوسيط فهو يستبدله دون أي مشاكل ...

مثال :

#define loop(x) for(int i=0;i<x;i++)

لاحظ أنه أصبح بإمكاننا الآن تغيير شكل أي أمر أو عبارة إلى أمر مختصر ..

فطالما نريد تكرار أمر معين عدداً من المرات فلماذا نكتب حلقة for كل مرة سنكتب الآن loop ونعطيها وسيط عدد مرات التكرار ...

وهذا فرق آخر عن التوابع العادية ... فالتابع كتلة برمجية لا علاقة لها بما قبلها وما بعدها إلا عن طريق الوسطاء والقيم المعادة .. أما هنا فالماكرو هو جزء من بنية البرنامج يستبدله المترجم بما يكافئه ..

وإليكم هذا المثال ..

#define max(x,y) (x>y?x:y)

وعند الاستبدال سيبقى xأو y أيهما أكبر ... ونلاحظ هنا كيف أننا استغنينا عن فكرة القوالب templates فالماكرو يتعامل مع جميع الأنواع دون تفريق ... فالنتيجة نفسها ...

مثال آخر ..

إذا أردنا أن نكتب عبارة معينة .. فإننا نستخدم الكائن cout وإذا أردنا أن نكتب على ملف سنيتخدم كائن من ofstream

إذا أردنا كتابة ماكرو يكتب نفس العبارة على الشاشة وفي الملف .. انظروا إلى المثال التالي :

#define Write(X) {X<<"ITS MACRO !!";}

وعند استدعائه سنكتب Write(cout) من أجل الكتابة على الشاشة وWrite(out) حيث out هو كائن من ofstream

بينما سنضطر إلى إنشاء قالب أو إلى التحميل الزائد للتابع لو لم نستخدم الماكرو ........

تحويل التابع العادي إلى ماكرو ...

إليكم هذا التابع : والذي يقوم بمضاعفة عدد مدخل على شكل مصفوفة مثلاً : مصفوفة عناصرها 1و2و3 تمثل الرقم 123

التابع هو :

*inline void dublicate_Number_On_Right(const int&size,int&digits,char&buffer,char*number,char&hold)
{//الوسطاء هي حجم المصفوفة الأصلية المخزن قيها الرقم ثم عدد المنازل التي سنتعامل معها ثم وسيط مساعد لعملية الضرب ثم نمرر مصفوفة الارقام ثم الوسيط المساعد لعمليةالحمل للمنزلة التالية
	for(int j=size-1;j>size-1-digits;j--)//نجري العملية على المنازل التي تحوي الأرقم فقط أما الجزء الزائد سنتجاهله
	{
		buffer=number[j]*2;//المتحول المساعد سيحتفظ بضعف الرقم
		number[j]=buffer%10+hold;//في المنزلة التي نجري عليها المضاعفة سيصبح الرقم هو آحاد المتحول المساعد ونضيف اليه الحمل من المنزلة السابقة
		hold=buffer/10;//الحمل الآن هو عشرات المتحول المساعد صفر أو واحد
	}
	if(hold==1)//بعد انتهاء عمليةالمضاعفة قد يكون هناك واحد مضاف إلى منزلة كانت خالية
	{
		digits++;//سنزيد عدد المنازل 
		number[j]=1;//سنسند القيمة واحد إلى المنزلة الجديدة
	}
}

وعندما سنحوله إلى ماكرو سيصبح :

#define dublicate_Number_On_Right(S,D,B,N,H)	{for(int j=S-1;j>S-1-D;j--){B=N[j]*2;N[j]=B%10+H;H=B/10;}if(H==1){D++;N[j]=1;}}

لاحظو كيف ان كل ما فعلناه يتلخص بكتابة الدالة على سطر واحد ...

ثم اختصار أسماء المتحولات إلى حرف واحد لكل متحول ...

والآن أصبحت لدينا دالة وقت الوصول إليها معدوم وهي أسرع من ال inline functions وهكبع دون المساس بسرعة التنفيذ

ويمكننا تعميم ما سبق بأنه : نستطيع تحويل أي تابع ليس له قيمة معادة إلى ماكرو وننتظر من ينقض هذه الفكرة :)

في الختام يجب ذكر أن

التعليمة define هي أحدى تعليمات الpreprocessor والتي من ضمنها include المعروفة ...

ما ذُكر في الموضوع السابق غيض من فيض تعليمات الpreprocessor والتي لا يمكن فهم مكاتب اللغة بدونها ....

أرجو أن أكون قد وُفّقت في شرح الفكرة ... وأكرر اعتذاري على الخطأ الغير مقصود

ما كان من خير فمن الله تعالى ..

وما كان من تقصير فمني وأستغفر الله العظيم ...

والسلام عليكم ورحمة الله وبركاته

#13

سأضيف اليوم فكرة صغيرة من ضمن الموضوع وهو العملية ## والعملية #

فكرة الموضوع غريبة بالفعل .. حيث يمكننا كتابة مثلاً

cout<<past(a,b,c)

حيث سيطبع ذلك كلمة abc !!

ويمكن أيضاً بعملية أخرى أن يطبع قيمة متحول اسمه abc !!! حيث أن a , b ,c غير معرفة مسبقا كمتحولات :

العملية هي :

#define past(a1,a2,a3) a1##a2##a3

حيث ان a1 , a2 , a3 هي مجرد أحرف وليست متحولات .. ويقوم هذا الماكرو بإعادة متحول اسمه هو دمج الحروف السابقة

العمليةالثانية :

#define past2(a,s,d)	#a#s#d

وذلك يعيد نصاً هو دمج الحروف السابقة ...

قد تبدو للوهلة الأولى ... مضحكة .. ولكن هذه الأمور ومثيلاتها تفتح الكثير أما من لديه فكرة وينتظر الأداة ...

أترككم مع كود تطبيقي ...

والسلام عليكم ...

#include<iostream>
using namespace std;
#define past(a1,a2,a3) a1##a2##a3 
#define past2(a,s,d)	#a#s#d
void main()
{
	int asd=14;
	int as=0;
	int ds=9;
	int ad=6;
	cout<<past(a,s,d)<<endl;
	cout<<past2(a,s,1)<<endl;
	cout<<past(d,s)<<endl;
	cout<<past(a,d)<<endl;
}

مواضيع مشابهة