قسم الميكروكنترولر والروبوت ودوائر الاتصال بالحاسب الالي قسم المتحكمات الـ microcontroller و المعالجات microprocessor و التحكم الرقمي بالكمبيوتر CNC والانظمة الآلية والروبوت Robots

   

الصورة الرمزية F.Abdelaziz
F.Abdelaziz
:: استاذ و مشرف قسم الالكترونيات ::
تاريخ التسجيل: May 2007
المشاركات: 6,832
نشاط [ F.Abdelaziz ]
قوة السمعة:
قديم 21-02-2013, 08:25 PM المشاركة 41   
افتراضي



1-8 الدوال Functions :
الدالة هى "تغليف" لمجموعة (بلوك) عبارات والتى يمكن أن تستخدم أكثر من مرة فى البرنامج . بعض اللغات تشير إلى الدوال بمسمى "البرامج الفرعية" subroutines أو "الإجراءات" procedures . عامة يتم كتابة الدوال لتجزئة عناصر عمليات البرنامج إلى أجزاء رئيسية . هذا يسمح للمبرمج بتصحيح كل عنصر ثم استخدام هذا العنصر مرة تلو الأخرى .
أحد المميزات الأساسية لاستخدام الدوال هى تطوير (إنشاء) "مكتبة" library . بمجرد إنشاء الدوال التى تنفذ مهمة معينة , يمكن حفظها واستخدامها فيما بعد لتطبيق آخر , أو حتى بمبرمج آخر . هذا يوفر الوقت ويحفظ الاستقرار , لأن الدوال التى تم إنشاؤها قد استخدمت وتم إعادة إستخدامها , واختبارها , وإعادة اختبارها .
الدالة قد تقوم بأداء مهمة معزولة (مستقلة) لا تتطلب أى "بارامترات" parameters . كما أن الدالة قد تتقبل بارامترات ليكون لديها دليل عند إجراء المهمة المصممة لها . الدالة قد لا تتقبل فقط ولكن علاوة على ذلك فإنها قد "تعيد أو ترجع" return قيمة . حتى إذا كانت الدالة تتقبل بارامترات متعددة , إلا أنها يمكنها فقط إعادة قيمة واحدة . فيما يلى بعض الأمثلة :
كود:
sleep( ) ; // this function performs a 1 second delay , // with no parameters, in or out
printf("this is a parameter %d" , x ) ; // printf will print its parameters
c = getchar( ) ; // getchar will return a value from the // standard input

الشكل القياسى للدالة كما يلى :

كود:
type function_name( type param1 , type param2 , …) { statement1 ; statement2 ; ………….. statementx ; }
الصيغة : "النوع" type , يتبعه "الاسم" name , يتبعه " الأقواس" ( ) . الأقواس , أو معامل الدالة , تشير للمترجم إلى "الاسم" name المطلوب تنفيذه كدالة .
نوع الدالة أو بارامتراتها يمكن أن تكون أى نوع متغير صحيح مثل int , char , float . النوع يمكن أيضا أن يكون "فارغ" void. النوع "الفارغ" void نوع صحيح ويستخدم للدلالة عدم وجود بارامتر أو عائد . النوع الافتراضى للدالة هو "العدد الصحيح" int .
لذلك فإن إعلان الدالة قد يكون بالشكل التالى :
كود:
unsigned char getchar(void) { while ( ( PIR & 0x20) == 0 ) ; // wait for character to arrive return RCREG ; // return its unsigned char value to the caller }
فى هذا المثال , الدالة getchar( ) لا تتطلب أى بارامتر وتعيد قيمة unsigned char عند تمام تنفيذها . الدالة getchar( ) هى واحدة من دوال المكتبة الكثيرة والمتوفرة بمترجم لغة السى والمتاحة لاستخدام المبرمج والتى سوف نناقشها تفصيليا وبعمق فيما بعد .
1-8-1 النموذج الأولى وتنظيم الدالة :

اعلانات

الصورة الرمزية F.Abdelaziz
F.Abdelaziz
:: استاذ و مشرف قسم الالكترونيات ::
تاريخ التسجيل: May 2007
المشاركات: 6,832
نشاط [ F.Abdelaziz ]
قوة السمعة:
قديم 21-02-2013, 08:27 PM المشاركة 42   
افتراضي



1-8-1 النموذج الأولى وتنظيم الدالة :
مثل استخدام المتغيرات والثوابت , يجب إعلان نوع الدالة وأنواع بارامتراتها قبل أن تستدعى . يمكن تنفيذ ذلك بطريقتين : الأولى هى ترتيب (تنظيم) إعلانات الدوال , والأخرى استخدام النماذج الأولية للدوال .
تنظيم الدوال فكرة دائما تكون جيدة . فهى تسمح للمترجم بأخذ جميع المعلومات حول الدالة فى مكانها قبل أن تستخدم . وهذا يعنى أيضا أن جميع البرامج سوف يكون لها الشكل التالى :

كود:
// declarations of global variables would be firs int var1 , var2 , var3 ; // declarations of functions would come next int function1( int x , int y) } } void function2(void) { } // main would be built last void main(void) { var3 = function1(var1 , var2); }
كل ذلك أنيق ومنظم لكن أحيانا يكون مستحيل . هناك الكثير من الحالات حيث تستخدم دوال دوال أخرى للقيام بمهمة وببساطة لا يمكنك إعلانها بهذه الطريقة حيث يكون الجميع مرتب من أعلى لأسفل .
تستخدم النماذج الأولية prototypes للدوال فى السماح للمترجم بمعرفة متطلبات ونوع الدالة قبل إعلانها الفعلى , لتقليل الحاجة للترتيب من أعلى إلى أسفل .
يمكن تنظيم المثال السابق بشكل مختلف كما يلى :
كود:
int var1 , var2 , var3 ; // declaration of global variable void main(void) // main { // these functions are not yet known to the compiler var3 = function1(var1,var2); function2( ) ; } // declarations of functions here now generate a // "Function Redefined Error" int function1( int x , int y ) { } void function2 ( void) { }


هذا التنظيم سوف يؤدى إلى رسالة خطأ من المترجم . المترجم ببساطة لا يمتلك معلومات كافية حول الدوال والتى تم استدعاؤها بالدالة الرئيسية main( ) , أو حتى صيغتها , وسوف يكون غير قادر على توليد الكود المناسب . يمكن إضافة النماذج الأولية إل قمة الكود كما يلى :

كود:
// the prototype of function tells compiler what to expect int function1(int , int ) ; int function2(void) ; int var1 , var2 , var3 ; // declaration of global variables void main(void) //main { var3 = function(var1 , var2 ); function2( ) ; } // the declaration of the functions here is // perfectly OK , because //the format of the functions is presented in the prototypes int function1(int x , y ) { } void function2(void) { }
الآن لدى المترجم جميع المعلومات المطلوبة حول كل دالة عندما يعالج اسم الدالة . طالما أن الإعلان الفعلى للدالة يطابق النموذج الأولى , فإن كل شىء يكون حسن .
مترجم لغة السى يوفر كثير من مكتبات الدوال . هذه الدوال تكون متاحة للبرنامج عن طريق العبارة
( #include <filename.h> ) , حيث filename.h سوف يكون هو الملف الذى يحتوى على النماذج الأولية للدوال لمكتبة الدوال . فى الأمثلة السابقة استخدمنا الدالة printf( ) . الدالة printf( ) نفسها يتم إعلانها فى مكان آخر ولكن النموذج الأولى يوجد فى ملف الرأس stdio.h . هذا يسمح للمبرمج ببساطة تضمين ملف رأس عند قمة البرنامج ويبدأ كتابة الكود .

اعلانات اضافية ( قم بتسجيل الدخول لاخفائها )

الصورة الرمزية F.Abdelaziz
F.Abdelaziz
:: استاذ و مشرف قسم الالكترونيات ::
تاريخ التسجيل: May 2007
المشاركات: 6,832
نشاط [ F.Abdelaziz ]
قوة السمعة:
قديم 21-02-2013, 08:31 PM المشاركة 43   
افتراضي



1-8-2 الدالة التى تعيد return قيم :
فى كثير من الحالات يتم تصميم الدالة لإنجاز مهمة والعودة بقيمة أو بحالة من المهمة التى تم أنجازها . تستخدم كلمة التحكم return للدلالة على نقطة خروج بالدالة أو , فى حالة دالة من النوع الغير الفارغ , لاختيار القيمة التى يتم إرجاعها للداعى .
فى الدالة من النوع الفارغ :


كود:
int z ; // global variable z void sum(int x , int y) { z = z + y ; // z is modified by the function , sum( ) }
العودة return تكون مفهومة ضمنيا وتكون عند نهاية الدالة .
إذا تم وضع return فى دالة فارغة كمل يلى :

كود:
void sum(int x , int y) { return ; // this would return nothing .. z = x + y ; // and this would be skipped }

عبارة return سوف ترسل تنفيذ البرنامج عائدا إلى الداعى , ولن يتم تنفيذ العبارات التى تلى return .
فى الدوال من النوع الغير فارغ , كلمة التحكم return سوف ترسل أيضا التنفيذ عائدا إلى الداعى . بالإضافة إلى ذلك , فإن return سوف تضع قيمة التعبير , الذى على يمين return , فى المكدس stack فى شكل نوع محدد بإعلان الدالة . الدالة التالية من نوع float . لذلك سوف يتم وضع float بالمكدس عندما يتم تنفيذ return :
كود:
float cube(float v ) { return (v * v * v); // this returns a type float value }

القدرة على إرجاع قيمة يسمح باستخدام دالة كجزء من تعبير , مثل التخصيص . على سبيل المثال , البرنامج التالى :
كود:
void main(void) { float a ; b = 3.14159 ; // put PI into b a = cube(b) ; // pass b to cubed function , and // assign its return value to a printf("a = %f , b = %f \n" , a , b ) ; // print the result while(1) // done ; }

سوف يخصص للمتغير "a" قيمة الدالة cube(b) . وسوف تظهر النتيجة التالية :
[CODE]
a = 31.006198 , b = 3-14159
[/CODE
1-9 المؤشرات Pointers والمصفوفات Arrays :


banna1980
:: مهندس متميز ::
تاريخ التسجيل: Apr 2010
الدولة: مصر - السويس
المشاركات: 403
نشاط [ banna1980 ]
قوة السمعة:
قديم 21-02-2013, 10:25 PM المشاركة 44   
افتراضي


اعتذر اخي الكريم لمقاطعتي لموضوعك الجميل

ارجوا من حضرتك ان تتفضل علي برايك في هذه (( دائرة انذار الحريق))

http://www.qariya.com/vb/showthread.php?t=124289

واصل الفكرة منكم من منتديات اخرى
http://www.dbaasco.com/vb/showthread.php?t=7412
http://www.eeecb.com/vb/showthread.php?t=740

جعله الله في ميزان حسناتكم
وجزاك الله خيرا


الصورة الرمزية F.Abdelaziz
F.Abdelaziz
:: استاذ و مشرف قسم الالكترونيات ::
تاريخ التسجيل: May 2007
المشاركات: 6,832
نشاط [ F.Abdelaziz ]
قوة السمعة:
قديم 22-02-2013, 02:42 AM المشاركة 45   
افتراضي


اعتذر اخي الكريم لمقاطعتي لموضوعك الجميل

ارجوا من حضرتك ان تتفضل علي برايك في هذه (( دائرة انذار الحريق))

http://www.qariya.com/vb/showthread.php?t=124289

واصل الفكرة منكم من منتديات اخرى
http://www.dbaasco.com/vb/showthread.php?t=7412
http://www.eeecb.com/vb/showthread.php?t=740

جعله الله في ميزان حسناتكم
وجزاك الله خيرا
أخى الكريم

شكرا جزيلا لك

أعتذر لك لأننى مشغول فى الوقت الراهن بترجمة وإعداد هذا الكتاب

مع تمنياتى بدوام التوفيق


الصورة الرمزية F.Abdelaziz
F.Abdelaziz
:: استاذ و مشرف قسم الالكترونيات ::
تاريخ التسجيل: May 2007
المشاركات: 6,832
نشاط [ F.Abdelaziz ]
قوة السمعة:
قديم 22-02-2013, 02:44 AM المشاركة 46   
افتراضي


1-9 المؤشرات Pointers والمصفوفات Arrays :
تستخدم المؤشرات والمصفوفات بشكل كبير فى لغة السى لأنها تسمح للبرامج بإجراء عمليات أكثر عمومية وكفاءة . العمليات التى تتطلب تراكم بيانات قد تستخدم هذه الطرق للوصول بسهولة والتعامل مع البيانات بدون نقل البيانات حول الذاكرة . وهى تسمح أيضا بتجميع المتغيرات المرتبطة مثل حوافز buffer الاتصال والسلاسل النصية strings .
1-9-1 المؤشرات Pointers :
المؤشرات هى المتغيرات التى تحتوى على عنوان أو موقع المتغير , أو الثابت , أو الدالة أو بيانات المقصود . بتم إعلان المتغير على أنه مؤشر بعامل "المراوغة" ( * ) كما يلى :

كود:
char *p ; // p is a pointer to character int *fp ; // fp is a pointer to an integer
البيانات نوع المؤشر تحتل مساحة بالذاكرة كبيرة بما يكفى لحفظ عنوان الآلة للمتغير . على سبيل المثال , فى الميكروكونترولر النموذجى يتم وصف عنوان موقع الذاكرة بستة عشر بت 16 bits . لذلك فى الميكروكونترولر النموذجى , المؤشر إلى حرف سوف يكون قيمتة بستة عشر بت , حتى لو كان الحرف نفسه بقيمة ثمانى بتات 8-bits فقط .
بمجرد أن يتم إعلان المؤشر , فأنت الآن تتعامل مع "عنوان" المتغير الذى يشير إلية , وليس قيمة المتغير نفسه . يجب أن تفكر بمصطلحات المواقع ومحتويات المواقع . يستخدم عامل العنوان ( & ) للوصول إلى عنوان المتغير . هذا العنوان قد يخصص إلى مؤشر أويكون قيمة المؤشر . يستخدم العامل ( * ) للوصول إلى البيانات الموضوعة فى العنوان المذكور بالمؤشر . على سبيل المثال :

كود:
char *p ; // p is a pointer to a character char a , b ; // a and b are characters p = &a ; // p is now pointing to a
فى هذا المثال , "p" يخصص كعنوان للمتغير "a" , لذلك فإن "p" "يشير"إلى "a" .
للحصول على المتغير الذى يشار إليه بالمؤشر "p" , يستخدم العامل ( * ) . عند التنفيذ , يتسبب عامل الإشارة ( * ) بأن تستخدم قيمة "p" , وهى عنوان, فى البحث عن موقع بالذاكرة . عندئذ فأن القيمة يتم قراءتها من أو كتابة بهذا الموقع تبعا للتعبير المذكور بعامل الإشارة . لذلك , ففى الكود التالى , "*p" سوف تتسبب فى أن القيمة الموضوعة بالعنوان الممثل فى "p" يتم قراءتها وتخصيصها للمتغير "b" :


كود:
b = *p ; // b equals the contents pointed by p
لذلك فإن تجميع كود المثالين السابقين سوف يؤديان لنفس النتيجة لأن :

كود:
b = a ;
يمكن أن يظهر عامل الإشارة أيضا على يمين التخصيص .


فى المثال التالى :

كود:
char *p ; // p is a point to a character char a , b; // a and b are characters
p = &a ;
*p = b ; // the location pointed by p , is // assigned the value of b
موقع الذاكرة , عند العنوان المخزن فى p يخصص له قيمة b . وهذ سوف ينتج نفس النتيجة لأن :

كود:
a = b ;
كلما قرأت هذه العوامل , حاول أن تقرؤها كما يلى : " b تعين القيمة المشار إليها بواسطة p " و " p تعين عنوان a " . هذا يساعد على تجنب معظم الأخطاء الشائعة مع المؤشرات :
b = p ; // b will be assigned a value of p , an address , // not what p point to
p = a ; // p will be assigned the value of a , not its address .
استخدام المؤشرات يعتبر إسلوب رائع للوصول إلى محيطات النظام , مثل منفذ الدخل / الخرج . فإذا كان لدينا منفذ 8 بت لخرج متوازى موضوع بالذاكرة بالموقع 0x1010 , فيمكن الوصول لهذا المنفذ من خلال المرواغة كما يلى :

كود:
unsigned char *my_port; // declare my_port as pointer my_port = 0x1010 ; //assign my_port with address value *my_port = 0xaa ; // now assign my_port's address a value
فى هذا الكود , الموقع المشار إليه بواسطة my_port سوف يخصص له القيمة 0xaa . يمكن القول أن أى قيمة تخصص إلى *my_port سوف تكتب على عنوان الذاكرة 0x1010 .
عن طريق بناء لغة السى , من الممكن أن يكون لدينا مؤشرات تشير إلى مؤشرات . فى الحقيقة , لا يوجد حدود لعمق هذا النوع من المراوغة indirection , فيما عدا الالتباس .

1-9-2 المصفوفات Arrays


banna1980
:: مهندس متميز ::
تاريخ التسجيل: Apr 2010
الدولة: مصر - السويس
المشاركات: 403
نشاط [ banna1980 ]
قوة السمعة:
قديم 22-02-2013, 07:19 AM المشاركة 47   
افتراضي


أخى الكريم

شكرا جزيلا لك

أعتذر لك لأننى مشغول فى الوقت الراهن بترجمة وإعداد هذا الكتاب

مع تمنياتى بدوام التوفيق
اعانكم الله ووفقكم وسددكم وجعله الله في ميزان حسناتكم


الصورة الرمزية F.Abdelaziz
F.Abdelaziz
:: استاذ و مشرف قسم الالكترونيات ::
تاريخ التسجيل: May 2007
المشاركات: 6,832
نشاط [ F.Abdelaziz ]
قوة السمعة:
قديم 22-02-2013, 11:23 PM المشاركة 48   
افتراضي


1-9-2 المصفوفات Arrays
المصفوفة هى نظام آخر للمراوغة indirection .
المصفوفة : هى مجموعة بيانات من النوع المعلن , مرتبة بالترتيب .
يتم إعلان المصفوفة مثل أى متغير أو ثابت آخر , فيما عدا العدد المطلوب لعناصر المصفوفة , كما يلى :

كود:
int digits[10] ; // this declares an array of 10 integer char str[20] ; // this declares an array of 20 characters
يتم الإشارة لعنصر المصفوفة , لكى يتم التعامل معه , عن طريق دليل الترتيب (الرقم المسلسل )index . دليل الترتيب يمكن أن يكون فى المدى من الصفر "0" إلى طول المصفوفة المعلنة ناقص واحد كما يلى :

كود:
str[0] , str[1] , str[2] , ………………str[19]


إعلان المصفوفة يمكن أن يتضمن التهيئة . فى مصفوفة المتغيرات قيم التهيئة سوف توضع فى حيز ذاكرة البرنامج ويتم نسخها إلى المصفوفة الفعلية قبل تنفيذ الدالة الرئيسية main( ) . مصفوفة الثوابت تختلف فى أن القيم سوف توضع بذاكرة البرنامج , لتوفير ذاكرة المعطيات RAM , التى تكون عادة صغيرة الحجم .
شكل تهيئة المصفوفة كما يلى :

كود:
int array[5] = {12 , 15 , 27 , 56 , 94 } ;


فى هذه الحالة , العنصر الأول للمصفوفة هو array[0] = 12 والعنصر الثانى array[1] = 15 والعنصر الثالث array[2] = 27 والعنصر الرابع array[3] = 56 والعنصر الخامس array[4] = 94 .
لغة السى ليس لديها احتياط لاختبار حدود المصفوفة . إذا تم تخصيص قيمة لرتبة عنصر والتى تتعدى حدود المصفوفة , فيمكن للذاكرة أن تتغير بطريقة غير متوقعة مما يؤدى إلى نتائج غير مرغوب فيها . على سبيل المثال :

كود:
char digits[10] = {0,1,2,3,4,5,6,7,8,9}; // an array of characters num = digits[12] ; // this reads outside the array .


يتم تخزين المصفوفات فى مواقع متتابعة بالذاكرة . قراءة العنصر digits[5] فى المثال أعلاه سوف تتسبب فى ذهاب المعالج إلى موقع أول رقم مسلسل index للمصفوفة ثم قراءة البيانات الموجودة هناك .
الفرق الأساسى فى استخدام المصفوفة واستتخدام المؤشر هو أنه فى حالة المصفوفة يوجد مساحة ذاكرة فعلية مخصصة من أجل البيانات . مع المؤشر , يتم فقط تخصيص عنوان مرجعى , ويترك للمبرمج إعلان وتعريف مساحة الذاكرة الفعلية ( المتغيرات) التى يتم الوصول إليها .
أكثر أنواع المصفوفات شيوعا هى مصفوفة الحروف . عادة يشار إليها باعتبارها سلسلة نصية string أو سلسلة حروف . متغير السلسلة النصية يعرف كمصفوفة حروف , فى حين أن ثابت السلسلة النصية يتم إعلانه عن طريق وضع سطر نصى بين قوسين "….". فى حالة ثابت السلسلة النصية , سوف يقوم المترجم بإضافة "صفر" عند نهاية السلسلة النصية .لذلك عندما تقوم بإعلان سلسلة نصية لحروف , ثابتة أو متغيرة , يجب أن يكون حجم المصفوفة المعلنة أكبر بواحد لما تحتاجه للسماح بصفر النهاية , كما فى المثال التالى :

كود:
char str[12] ; // variable string printf("Hello World") ; // constant string in program memory const cstr[16] = "Constant String" ; // constant string in program memory

فى المثال السابق يتم تحديد cstr لتحتوى على 16 قيمة لأن السلسلة النصية نفسها تحتوى على 15 حرف ويجب إضافة مكان للسماح لصفر النهاية .
اسم المصفوفة متبوعا برقم مسلسل index يكون مرجع لعنصر منفرد للمصفوفة من أى نوع . من الممكن أيضا الإشارة للعنصر الأول فى أى مصفوفة باسمها فقط . عندما لا يتم تحديد للرقم المسلسل , فإن اسم المصفوفة يعالج كعنوان للعنصر الأول بالمصفوفة .
مثال :
إذا أعطيت الإعلانات التالية :

كود:
char stng[20] ; char *p ;

فإن التخصيص :
كود:
p = stng ; // p is pointing to stng[0]
هو مثل :
كود:
p = &stng[0] ; // p is pointing to stng[0]

سلسلة الحروف النصية غالبا تحتاج إلى المعالجة على أساس " حرف تلو الحرف " . إرسال رسالة إلى جهاز تسلسلى أو إلى وحدة العرض LCD هى أمثلة لهذه المطالب .
المثال التالى يبين كيف أن ترتيب indexing المصفوفة و دالة المؤشر متبادلتين تقريبا . هذا المثال يستخدم دالة المكتبة putchar( ) لإرسال حرف واحد فى كل مرة إلى جهاز الخرج القياسى , مثل المنفذ التسلسلى الأكثر شيوعا :


كود:
#include <16F877.h> //register definition file for #fuses HS,NOWDT //a Microchip PIC16F877 #use delay(clock=10000000 //setup the RS232 port for standard output #use rs232(baud=9600,parity=N,xmit=PIN_C6,rcv=PIN_C7,stream=RS232,bits=8) #include <stdio> const char s[15] = {"This is a test") ; char i ; char *p ; void main(void) { for(i=0;i<15;i++) // print each character of the array putchar(s[i]) ; // by using I as index p = s ; // point to string as whole for(i = 0 ; i<15 ; i++ ) // print each character of array putchar(*p++); // and move to the next element // by incrementing the pointer p while(1) ; }

الجزء الأول فى هذا البرنامج يستحدم حلقة for لإخراج كل حرف من المصفوفة منفردا (على حدة) . يستخدم عداد حلقة for كرقم مسلسل index لتكرار كل حرف بالمصفوفة بحيث يمكن تمريرها إلى الدالة
putchar( ) . الجزء الثانى يستخدم مؤشر للوصول إلى كل عنصر من المصفوفة . السطر "p = s " يحدد المؤشر لعنوان أول حرف بالمصفوفة . بعد ذلك تستخدم حلقة for المؤشر ( تزايد لاحق فى كل مرة ) لتكرار عناصر المصفوفة وتمريرها إلى الدالة putchar( ) .

1-9-3 المصفوفات متعددة الأبعاد Multidimensional Arrays :



الساعة معتمدة بتوقيت جرينتش +3 الساعة الآن: 05:07 PM
موقع القرية الالكترونية غير مسؤول عن أي اتفاق تجاري أو تعاوني بين الأعضاء
فعلى كل شخص تحمل مسئولية نفسه إتجاه مايقوم به من بيع وشراء وإتفاق وأعطاء معلومات موقعه
التعليقات المنشورة لا تعبر عن رأي موقع القرية الالكترونية ولايتحمل الموقع أي مسؤولية قانونية حيال ذلك (ويتحمل كاتبها مسؤولية النشر)