سنتطرق في هذه الفقرة إلى :
- عنونة المتغيرات
- المؤشرات
- المؤشرات والجداول
- جدول المؤشرات
- الحجز الحركي للذاكرة
1. عنونة المتغيرات
توجد عدة أنواع من أنظمة العنونة، وأهمها العنونة المباشرة والعنونة غير المباشرة.
1.1 العنونة المباشرة
عندما نُعَرف متغيرا فإن المترجم يقوم بحجز مكان له في الذاكرة، وللتعامل مع هذا المتغير الموجود حقيقة في الذاكرة نستعمل بطبيعة اسمه الذي عرفناه في البداية، وهذا ما يسمى بالعنونة المباشرة، وبالتالي نكتب :
العنونة المباشرة : الدخول إلى محتوى المتغير انطلاقا من اسمه.
مثال في الحياة اليومية : "دخول البيوت من أبوابها" ، ولا يقوم بها إلا ذَوُو المروءة والأخلاق.
مثال :
![]() |
1.2 العنونة غير المباشرة
في هذه الحالة يتم الدخول إلى محتوى متغير ما عن طريق متغير آخر نسميه مؤشرا يشير أو يحمل عنوان هذا المتغير.
مثال في الحياة اليومية : "دخول البيوت من نوافذها" ، ولا يقوم بها إلا اللصوص.
مثال : ليكن:
-
A متغيرا يحتوي على القيمة 10
-
P مؤشرا يحتوي على عنوان المتغير A .
يمكن تمثيل A و P في الذاكرة على الشكل التالي :
![]() |
2. المؤشرات
المؤشر: بكل بساطة هو متغير يحتوي على عنوان لمتغير آخر.
ليكن P يحمل عنوان المتغير A.
ونقول إذن : P يشير إلى المتغير A.
2.1 العوامل المتحكمة بالمؤشرات
عند التعامل مع المؤشرات نحتاج إلى العوامل التالية :
-
& : عامل العنوان ===> ويستعمل للحصول على عنوان متغير.
-
* : عامل المحتوى ===> ويستعمل للدخول إلى محتوى متغير.
كما نلاحظ بأن العامل & تستعمله الدالة ()scanf للحصول على عنوان متغير.
مثال :
int N; |
2.2 كيف يمكن معرفة عنوان متغير ما في الذاكرة
نعتبر التعريف التالي : int A=10;
محتوى المتغير A هو 10 ، لكن ما هو عنوانه في الذاكرة ؟
الحل 1 : استعمال العامل &
لمعرفة محتوى A نستعمل A بذاته
ولمعرفة عنوانه نستعمل A& ، ونكتب إذن :
int A=10; |
ملاحظة : X% ===> تعني ظهور النتيجة بالنظام الستعشري، ويمكنك استعمال d% عوضها للإظهار بالنظام العشري.
وخلاصة الأمر أن :
A : تعبر عن محتوى A
A& : تعبر عن عنوان A
الحل 2 :استعمال المؤشر
أولا نقوم بتعريف مؤشر من نفس نوع المتغير A، ولأجل ذلك نكتب :
int *P; |
ليكن في العلم ( تذكر هذا جيدا):
- أن P هي بمثابة A& وكلاهما يعبر عن عنوان A
- وأن P* هي بمثابة A وكلاهما يعبر عن محتوى A
وبالتالي نكتب أيضا :
int *P; |
2.3 كيف يمكن تغيير محتوى متغير ما في الذاكرة
نعتبر التعريف التالي : int A=10;
محتوى المتغير A هو 10 ، لكن كيف يمكن تغيير محتواه في الذاكرة ؟
الحل 1 : استعمال المتغير A ذاته
بما أن A ذاته يمثل محتوى A فيمكننا أن نكتب إذا :
int A=10; /* A= 10 */ |
الحل 2 :استعمال المؤشر
أولا نقوم بتعريف مؤشر من نفس نوع المتغير A، ولأجل ذلك نكتب :
int *P; |
ليكن في العلم (تذكر هذا جيدا):
- أن P هي بمثابة A& وكلاهما يعبر عن عنوان A
- وأن P* هي بمثابة A وكلاهما يعبر عن محتوى A
وبالتالي نكتب أيضا :
int *P; printf("A = %d\n",*P); |
خلاصة (تذكر هذا جيدا) : إن أي تغيير يطرأ على P* يطرأ أيضا على A لكن بشرط أن يكون P يحمل عنوان A. |
2.4 أمثلة أخرى
مثال 1 : نعتبر التطبيق التالي (يمكننا التحكم في متغير انطلاقا من مؤشرين أو أكثر)
ملاحظة : الكتابتين التاليتين متكافئتين.
الكتابة الأولى |
الكتابة الثانية |
int *P,*T,A=10; P = &A; /* *P=10 */ T = &A; /* *T=10 */ printf("A = %d\n", A); printf("A = %d\n",*P); printf("A = %d\n",*T); *P=20+*T ; /* A=*T =*P =30 */ printf("A = %d\n", A); printf("A = %d\n",*P); printf("A = %d\n",*T); scanf("%d",&A); /*إدخال قيمة جديدة*/ printf("A = %d\n", A); printf("A = %d\n",*P); printf("A = %d\n",*T); |
int *P,*T,A=10; P = &A; /* *P=10 */ T = P; /* *T=10 */ printf("A = %d\n", A); printf("A = %d\n",*P); printf("A = %d\n",*T); *T=20+A ; /* A=*T =*P =30 */ printf("A = %d\n", A); printf("A = %d\n",*P); printf("A = %d\n",*T); scanf("%d",T); /*إدخال قيمة جديدة*/ printf("A = %d\n", A); printf("A = %d\n",*P); printf("A = %d\n",*T); |
مثال 2 : نعتبر التطبيق التالي
ملاحظة : الكتابتين التاليتين متكافئتين.
الكتابة الأولى |
الكتابة الثانية |
||
|
|
2.4 العمليات الحسابية باستعمال المؤشر
مثال : الكتابتين التالين متكافئتين
الكتابة الأولى |
الكتابة الثانية |
||
|
|
التمرين 9.1
نعتبر الشفرة التالية:
main()
{
int A = 1;
int B = 2;
int C = 3;
int *P1, *P2;
P1=&A;
P2=&C;
*P1=(*P2)++;
P1=P2;
P2=&B;
*P1-=*P2;
++*P2;
*P1*=*P2;
A=++*P2**P1;
P1=&A;
*P2=*P1/=*P2;
return 0;
}
أكمل الجدول التالي من خلال الشفرة أعلاه :
A | B | C | P1 | P2 | |
الحالة البدئية | 1 | 2 | 3 | / | / |
P1=&A | 1 | 2 | 3 | &A | / |
P2=&C | |||||
*P1=(*P2)++ | |||||
P1=P2 | |||||
P2=&B | |||||
*P1-=*P2 | |||||
++*P2 | |||||
*P1*=*P2 | |||||
A=++*P2**P1 | |||||
P1=&A | |||||
*P2=*P1/=*P2 |
3. المؤشرات والجداول
في C هناك علاقة وطيدة بين المؤشرات والجداول، حيث أن جميع العمليات التي تجرى على الجداول هي نفسها التي تجرى على المؤشرات.
3.1. عنونة عناصر الجدول
نعتبر التعريف التالي : int TAB[10];
ليكن في العلم أن عنوان الجدول في الذاكرة هو عنوان أول عنصر فيه، ويمكننا القول بأن :
(تذكر هذا جيدا) &TAB[0] تكافئ TAB
لنعتبر التعريف التالي :
int *P,TAB[10]={1,2,3,4,5,6,7,8,9,10}; |
للتعبير عن المحتوى | للتعبير عن العنوان | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
حسنا، كيف نتحكم في الجدول باستعمال المؤشر ؟
من أجل هذا نعتبر المثال التالي : (الكتابتين التاليتين متكافئتين)
الكتابة الأولى |
الكتابة الثانية |
||
|
|
خلاصة :
إذا كان TAB جدولا فإن:
- TAB تعبر عن عنوان العنصر [0]TAB
- TAB + i تعبر عن عنوان العنصر TAB[i]
- (TAB + i)* تعبر عن محتوى العنصر TAB[i]
وكذلك :
- [0]TAB& تعبر عن عنوان العنصر [0]TAB
- &TAB[i] تعبر عن عنوان العنصر TAB[i]
- TAB[i] تعبر عن محتوى العنصر TAB[i]
إذا كان مؤشرا P يشير للجدول TAB فإن:
- P تعبر عن عنوان العنصر [0]TAB
- P+ i تعبر عن عنوان العنصر TAB[i]
- (P + i)* تعبر عن محتوى العنصر TAB[i]
- P[i] تعبر عن محتوى العنصر TAB[i]
التمرين 9.2
أنجز شفرة تمكن من قراءة جدولين A و B وكذلك بعديهما N و M من خلال لوحة المفاتيح ، قم بعد ذلك بإضافة عناصر B بآخر A . استعمل المؤشر في كل حالة ممكنة.
3.1. العمليات الحسابية
3.1.1 إعطاء محتوى مؤشر لمؤشر آخر
مثال :
float *P1,*P2; int *P3; P1=P2; /*هذه الكتابة صحيحة لأنهمـــــا من نفس النوع*/ P1=P3; /*هذه الكتابة خاطئة لأنهما ليس من نفس النوع*/ |
3.1.2 الزيادة والنقصان عند المؤشرات
إذا كان P مؤشرا يشير إلى العنصر فإن : TAB[i]
P++; | P يشير إلى A[i+1] |
P+=n; | P يشير إلى A[i+n] |
P--; | P يشير إلى A[i-1] |
P-=n; | P يشير إلى A[i-n] |
مجال الزيادة والنقصان :لا يجب أن تتعدى أقصى رتبة للجدول أو أقل رتبة التي هي 0.
مثال :
int A[10]; | |
int *P; | |
/* آخر عنصر -> مقبول */ | P = A+9; |
/* آخر عنصر + 1 -> مقبول */ | P = A+10; |
/* أول عنصر + 2 -> غير مقبول */ | P = A+11; |
/* أول عنصر - 1 -> غير مقبول */ | P = A-1; |
3.1.3 عملية الطرح بين مؤشرين
إذا كان P1 و P2 مؤشرين على نفس الجدول فإن نتيجة العملية P2-P1 تكون :
- سالبة |
إذا كان P1 قبل P2 |
- صفرا |
إذا كان P1 = P2 |
- موجبة |
إذا كان P2 قبل P1 |
- غير محددة |
إذا كان P1 و P2 مؤشرين على جدولين مختلفين |
كما أنه بالإمكان المقارنة بين مؤشرين بالعوامل الطبيعية للمقارنة.
التمرين 9.3
لماذا عمل مطوري C على جعل مؤشرات أول عنصر في الجدول ولا تتواجد في آخر الجدول؟ أعط مثالا.
التمرين 9.4
ليكن P مؤشر 'يشير' إلى الجدول A :
int A[] = {12, 23, 34, 45, 56, 67, 78, 89, 90};
int *P;
P = A;
ما هي قيم أو عناوين الإنشاءات التالية:
-
*P+2
-
*(P+2)
-
&P+1
-
&A[4]-3
-
A+3
-
&A[7]-P
-
P+(*P-10
-
*(P+*(P+8)-A[7])
التمرين 9.5
أنجز شفرة تمكن من قراءة عدد صحيح X وجدول A من نوع int من لوحة المفاتيح ، قم بعد ذلك بإزالة كل مثيلات X في الجدول A .الشفرة تستعمل مؤشرين P1 و P2 للمرور في الجدول.
التمرين 9.6
أنجز شفرة تمكن من تبديل عناصر جدول A من نوع int في الاتجاه المعاكس. الشفرة تستعمل مؤشرين P1 و P2 ومتغير AIDE للمساعدة في التبديل.
معلوم أن المتسلسلات عبارة عن جداول ذات النوع char، لهذا فإن تعامل المؤشرات مع المتسلسلات شبيه بتعاملها مع الجداول.
لمناقشة المتسلسلات من خلال المؤشرات نعتبر الأمثلة التالية :
3.2.1 مثال أول "استعمال الجدول"
char ch[10]="salam"; |
هذه المتسلسلة تمثل في الذاكرة بهذا الشكل :
'\0' | 'm' | 'a' | 'l' | 'a' | 's' | |||||
العناوين | 003A | 0039 | 0038 | 0037 | 0036 | 0035 | 0034 | 0033 | 0032 | 0031 |
كما نلاحظ أن هذه المتسلسلة تحجز 10 خانات أي 10 أثمان في الذاكرة، رغم أنه لدينا فقط 6 خانات مستعملة للكلمة "salam"،
وإذا أردنا كتابة الجملة "salam 3alaikom" في المتسلسلة ch فإنها لن تستطيع استيعابها لأنها متكونة من 15 حرفا،وهذا يعتبر من مساوئ استعمال الجداول لتمثيل الجمل.
ملاحظة : يمكننا أن نستعمل مؤشرا من نفس نوع المتسلسلة ch يعني النوع char ونتحكم فيها من خلال هذا المؤشر كما تطرقنا في الصفحة السابقة.
3.2.2 مثال ثان "استعمال المؤشر"
char *ch="salam"; |
تكافئ |
char *ch; |
هذه المتسلسلة تمثل في الذاكرة بهذا الشكل :
'\0' | 'm' | 'a' | 'l' | 'a' | 's' | |
العناوين | 0036 | 0035 | 0034 | 0033 | 0032 | 0031 |
كما نلاحظ أن هذه المتسلسلة تحجز 6 خانات أي 6 أثمان في الذاكرة فقط وذلك حسب طول المتسلسلة.
وإذا أردنا كتابة الجملة "sisio 3alaikom" في المتسلسلة ch فإننا نكتب بكل بساطة : ch="sisio 3alaikom";
وبالتالي تصبح المتسلسلة تحجز 15 خانة أي 15 ثمنا في الذاكرة، أما كلمة "salam" فقد تم حذفها.
3.2.3 مثال ثالث "الفرق بين استعمال المؤشر واستعمال الجدول"
المثال الأول : استعمال الجداول
char A[45] = "Petite chaîne"; |
خلاصة : في هذا المثال
- لا يجب أن نعطي B لـ A مباشرة، وإذا أردنا ذلك سنستعمل الدالة ()strcpy
- لا يجب أن نعطي جملة لـ C مباشرة، وإذا أردنا ذلك سنستعمل الدالة ()strcpy
المثال الثاني : استعمال المؤشرات
char *A = "Petite chaîne"; في هذه الحالة أصبح A و B مؤشرين على نفس المتسلسلة. |
3.2.4 مثال رابع "مناقشة مثال الدالة strcpy"
تقوم الدالة بنسخ متسلسلةCH2 في أخرى CH1 ، حيث CH1 و CH2 عبارة عن مؤشرين تستعملهما الدالة strcpy للدخول إليها.
أول تقريب للدالة strcpy
void strcpy(char *CH1, char *CH2) { int I; I=0; while ((CH1[I]=CH2[I]) != '\0') I++; } |
ثاني تقريب للدالة strcpy
void strcpy(char *CH1, char *CH2) { int I; I=0; while ((CH1[I]=CH2[I]) != '\0') I++; } |
ثالث تقريب للدالة strcpy
void strcpy(char *CH1, char *CH2) { while ((*CH1=*CH2) != '\0') { CH1++; CH2++; } } |
رابع تقريب للدالة strcpy
void strcpy(char *CH1, char *CH2) { while (*CH1++ = *CH2++); } |
التمرين 9.7
أنجز شفرة تمكن من قراءة جدولين A و B وكذلك بعديهما N و M من خلال لوحة المفاتيح ، قم بعد ذلك بإضافة عناصر B بآخر A . استعمل مؤشرين PB و PA لأجل التحويل وإظهار الجدول الناتج A على الشاشة.
التمرين 9.8
أنجز بطريقتين مختلفتين شفرة تمكن من التحقق بدون استعمال دوال <string.h> ، إذا كانت الجملة أو الكلمة CH المطلوب إدخالها من لوحة المفاتيح معكوسة.
أ) استعمل فقط الجدول.
ب) استعمل المؤشرات مكان الرموز الرقمية.
تذكير : الكلمة أو الجملة المعكوسة : عندما يكون نصفها الأخير مشابه لنصفها الأول لكن في الاتجاه المعاكس.
أمثلة
|
التمرين 9.9
أنجز شفرة تمكن من قراءة متسلسلة حرفية CH وحدد طولها بمساعدة مؤشرP ،الشفرة لا تستعمل أبدا المتغيرات الرقمية.أظهر النتائج على الشاشة.
التمرين 9.10
أنجز شفرة تمكن من قراءة جملة CH وحدد عدد الكلمات الموجودة فيها.استعمل مؤشرP ، ومتغير منطقي، والدالة isspace ومتغير رقمي N الذي يحتوي على عدد الكلمات. أظهر النتائج على الشاشة.
التمرين 9.11
أنجز شفرة تمكن من قراءة متسلسلة حرفية CH المطلوب إدخالها من لوحة المفاتيح، أحسب لكل حرف من الحروف الأعجمية بدون اعتبار هل هي كبيرة أم صغيرة عدد المرات التي تكرر فيها داخل CH . استعمل جدول ABC ذي البعد 26 لحفظ نتيجة كل حرف ، ومؤشر PCH للمرور في CH ومؤشر PABC للمرور في ABC .أظهرفقط عدد التكرار لكل حرف على الشاشة.
مثال :
Write a line of text (max. 100 characters) :
Jeanne
The sentence "Jeanne" has :
1 times of the alphabet 'A'
2 times of the alphabet 'E'
1 times of the alphabet 'J'
3 times of the alphabet 'N'
التمرين 9.12
أنجز شفرة تمكن من قراءة حرف C و جملة CH وحدد عدد المرات التي تكرر فيها C داخل CH ، بعد ذلك احذف جميع الحروف المماثلة لـ C في CH .استعمل مؤشر والدالة strcpy للمساعدة. أظهر النتائج على الشاشة.
التمرين 9.13
أنجز شفرة تمكن من قراءة جملتين CH1 و CH2 ،أزل جميع حروفالجملة CH1 من المتسلسلة الحرفية CH2.استعمل مؤشرين P1 و P2 ومتغير منطقي TROUVE والدالة strcpy للمساعدة. أظهر النتائج على الشاشة.
أمثلة:
|
التمرين 9.14
أنجز شفرة تمكن من قراءة جملتين CH1 و CH2 ،أزل الجملة CH1 من المتسلسلة الحرفية CH2 في المرة الأولى فقط.استعمل فقط المؤشر ومتغير منطقي TROUVE والدالة strcpy للمساعدة. أظهر النتائج على الشاشة.
أمثلة:
|
3.3. المؤشرات والمصفوفات
لنأخذ مثالا : نعتبر M مصفوفة
int M[4][10] = {{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9}, |
M : هو اسم المصفوفة، وهو أيضا عنوان أول عنصر فيه حيث يشير (مفاجأة) إلى السطر الأول أي إلى الجدول M[0] الذي يحتوي على العناصر التالية : {0,1,2,3,4,5,6,7,8,9}.
M+1 : هو عنوان ثاني عنصر في المصفوفة حيث يشير إلى السطر الثاني أي إلى الجدول M[1] الذي يحتوي على العناصر التالية : {10,11,12,13,14,15,16,17,18,19}.
وهكذا ...
شرح وتفصيل:
في الحقيقة أو في الواقع : إن المصفوفات التي نقول أنها جداول ذات بعدين (هذا في التمثيل الرياضي فقط)، ليست إلا جداول أحادية البعد في الذاكرة حيث أن كل عنصر فيها يمثل مجموعة من العناصر نسميها سطرا أو جدولا. حيث أن أول عنصر في المصفوفة M هو المتجهة {0,1,2,3,4,5,6,7,8,9}, وثاني عنصر هو المتجهة {10,11,12,13,14,15,16,17,18,19} وهكذا...
وعموما نقول أن (M+i) عنوان العنصر أو الجدول M[I]
مسألة : كيف يمكن أن نَدْخُلَ أو أن نستعمل عنصر المصفوفة M (أي M[0][0], M[0][1], ... , M[3][9] ) بمساعدة المؤشر؟
مناقشة الحل :
هناك حل، وهو تحويل قيم المصفوفة M (التي هي في الأصل مؤشرا على جدول ذي النوع int ) إلى مؤشر على int. ومن أجل هذا نكتب :
int M[4][10] = {{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9}, |
العملية P = M; هي عبارة عن تحويل تلقائي للعنوان &M[0] إلى العنوان &M[0][0] (لاحظ أن العنوان المحول بقي هو نفسه لكن طبيعة المؤشر هي التي تبدلت.)
في الحقيقة هذا الحل ليس حلا كافيا مائة في المائة.
الحل النهائي :
في حالة الجداول ذات بعدين يجب أن يكون هناك تحويلا إجباري :
int M[4][10] = {{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9}, |
في هذه الحالة أصبح لدينا مؤشرا على كل عناصر المصفوفة، وبالتالي يمكن أن نستعمل المصفوفة M كجدول أحادي البعد من خلال P. أي أن P أصبح جدولا أحادي البعد رتبته هي 10×4.
مثال : لنحسب مجموع عناصر المصفوفة M باستعمال المؤشر P.
int M[4][10] = {{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9}, |
مثال آخر : نعتبر مصفوفة A ذات 3 أسطر و 4 أعمدة، ونريد استعمال سطرين وعمودين فقط :
int A[3][4]; |
في الذاكرة، هذه العناصر تُحجز كما يلي :
![]() |
وتذكر جيدا أن عنوان العنصر A[I][J] يحسب بهذه العلاقة : A + I*4 + J .
التمرين 9.15
أنجز شفرة تمكن من قراءة مصفوفة A ذات البعدين N و M من لوحة المفاتيح. استعمل المؤشر في كل مرة ممكنة وأظهر النتائج على الشاشة كما يلي :
أ) المصفوفة A
ب) مقلوب A
ج) ناقش A كأنها جدول أحادي البعد.
التمرين 9.16
أنجز شفرة تمكن من قراءة مصفوفة A ذات البعدين N و M والمصفوفة B ذات البعدين M و P من لوحة المفاتيح.
أنجز عملية ضرب A في B والنتيجة تحتفظ في المصفوفة C . استعمل المؤشر في كل مرة ممكنة وأظهر النتائج على الشاشة.
التمرين 9.17
أنجز شفرة تمكن من قراءة 5 كلمات (50 حرفا كأقصى حد) حيث يتم تحفيظهم في جدول المتسلسلات الحرفية TABCH . اقلب ترتب حروف الكلمات الخمس بمساعدة مؤشرين P1 وP2 .أظهر النتيجة.
ملاحظة : إذا كان مفهوم المؤشر مازال جديدا لديك لحد الآن، فمن المفضل أن لا تزعج نفسك بالمقطعين 4 و5 لهذه الفقرة ، وتابع القراءة للفقرتين الآتيتين، وما إن تمكنت من خلالهما من فهم عمل المؤشرات فارجع إلى هنا لتكمل ما تم نقصانه.
4. جدول المؤشرات
4.1 التعريف
إذا أردنا استعمال مؤشرات كثيرة، في هذه الحالة من المفضل أن نعرف جدولا للمؤشرات.
أمثلة للتعريف :
char *JOUR[7]; |
ملاحظة : تستعمل غالبا جداول المؤشرات لاقتصاد حجم الذاكرة عندما نستعمل جدول المتسلسلات الحرفية.
4.2 أين تتجلى أهمية جدول المؤشرات
نعتبر المثال التالي:"استعمال جدول المتسلسلات الحرفية"
char JOUR[7][9]= {"lundi", "mardi", "mercredi","jeudi", "vendredi", "samedi","dimanche"}; |
الحجز في الذاكرة :
من خلال هذا المثال نلاحظ أن الجدول JOUR يحجز 7*9*1 = 63 ثمن في الذاكرة.
لإظهار الحروف الأولى للأيام فقط نكتب :
for(I=0; I<7; I++) |
ولإظهار جميع الأيام على الشاشة نكتب :
for(I=0; I<7; I++) |
نعتبر المثال التالي:"استعمال جدول المؤشرات"
char *JOUR[] = {"dimanche", "lundi", "mardi", |
في هذه الحالة عرفنا جدولا ذو 7 مؤشرات على char. وكل مؤشر يحمل عنوان متسلسلة واحدة من بين 7 متسلسلات.
![]() |
الحجز في الذاكرة :
من خلال هذا المثال نلاحظ أن الجدول JOUR يحجز 52 ثمنا في الذاكرة.
لإظهار الحروف الأولى للأيام فقط نكتب :
for(I=0; I<7; I++) |
ولإظهار جميع الأيام على الشاشة نكتب :
for(I=0; I<7; I++) |
العبارة JOUR[I]+J ترمز إلى الحرف ذي الموقع J في الجملة I . وبالتالي يمكننا إظهار الحرف الثالث لكل يوم في الأسبوع :
int I; |
خلاصة :
-
الحجز في الذاكرة باستعمال جدول المؤشرات اقتصادي من استعمال جدول المتسلسلات (52 < 63)
-
كما أن معالجة المتسلسلات باستعمال جدول المؤشرات شبيه تقريبا باستعمال جدول المتسلسلات (كإظهار الحروف الأولى ..)
-
نعتبر D جدولا للمؤشرات، نكتب إذا :int *D[];
D[i] |
يرمز إلى عنوان أول العنصر i |
D[i]+j |
يرمز إلى عنوان العنصر j في العنصر i |
*(D[i]+j) |
يرمز إلى محتوى العنصر j في العنصر i |
التمرين 9.18
نعتبر التعريفات أو المعطيات التالية :
char *NOM1[] = {"Mohammed", "Mustafa", "Omar","Othman", "Zineb" };
char NOM2[][16] = {"Mohammed", "Mustafa", "Omar", "Othman", "Zineb" };
أ) أنجز خطاطة يدويا تبين في كيفية تحفيظ المتغيرين NOM1 و NOM2 .
ب) نفترض أنك تريد إنجاز خوارزم أو شفرة لترتيب الكلمات قاموسيا بالنسبة للمتغيرين NOM1 و NOM2 ، علما أنك استعملت نفس طريقة الترتيب لكل منهما ، من هي في رأيك الشفرة الأسرع : الشفرة المخصصة لـ NOM1 أم الشفرة المخصصة لـ NOM2؟
التمرين 9.19
أنجز شفرة تمكن من قراءة اليوم والشهر والسنة من لوحة المفاتيح ، وأظهر التاريخ بالفرنسية والألمانية. استعمل جدولين للمؤشرات MFRAN و MDEUT يحتويان في البداية على أسماء الشهور بالنسبة للغتين. أول جزء في كل جدول يحتوي على رسالة أو جملة تعبر عن الخطأ ليتم إظهارها في حالة إن كان هناك خطأ.
أمثلة :
Introduisez la date: 1 4 1993
Luxembourg, le 1er avril 1993
Luxemburg, den 1. April 1993
Introduisez la date: 2 4 1993
Luxembourg, le 2 avril 1993
Luxemburg, den 2. April 1993
5.الحجز الحركي للذاكرة
لقد رأينا فقط كيف نتعامل مع المؤشرات حيث نقتصد أثناء التعريف أو التهيئة في حجم الذاكرة، لكن ماذا لو أردنا حجز أثمان أخرى أو تحريرها، في هذه الحالة نتكلم عن الحجز الحركي للذاكرة.
5.1 التعريف الطبيعي للمعطيات
وهو أن كل متغير يحتاج إلى عدد محدد من الأثمان في الذاكرة، ويتم تحديد ذلك أثناء التعريف.
مثال:
float A, B, C; /* حجز 12 ثمنا */ |
المؤشر :
في هذه الحالة عدد الأثمان المحجوزة للمؤشر تتعلق بنوع الحاسوب وكذلك بنوع الذاكرة المختارة، حيث أن كل هذا يكون معروفا لذا المترجم C.
ولنفترض أن المؤشر يحتاج لـ p ثمن في الذاكرة (في DOS : تكون p=2 أو p=4)
مثال:
double *G; /*حجز p ثمن */ |
أمثلة أخرى :
char *J = "Bonjour !"; /* حجز p+10 ثمن */ |
5.2 الحجز الحركي
مشكل:
أحيانا نتعامل مع معطيات لا نستطيع أن نتعرف على طولها أو عدد الأثمان التي ستستعملها. من أجل هذا يجب علينا أن نعطي لها في البداية حجما قصويا لتستعمله. أي أننا نحتاج إلى وسيلة تقوم بإدارة الذاكرة أثنا تنفيذ المشروع.
مثال :
نريد قراءة 10 جملة من لوحة المفاتيح ونقوم بحفظها في جدول المؤشرات من نوع char. نكتب إذا :
char *TEXTE[10];
نحتاج إذا لـ 10*p من أجل هذه العشر المؤشرات. وهذا العدد معروف في البداية والأثمان تحجز في هذه الحالة تلقائيا. لكن المشكل هو أننا لا نعرف عدد الأثمان التي يجب حجزها في الذاكرة أثناء التنفيذ للجمل المطلوب إدخالها من طرف المستعمل...
الحجز الحركي :
الحجز في الذاكرة لهذه الجمل يمكن أن يكون فقط أثناء التنفيذ . نتكلم إذن عن الحجز الحركي للذاكرة.
5.3 الدالة malloc والعامل sizeof
تنتمي الدالة malloc إلى المكتبة <stdlib> وهي تساعدنا في حجز الأثمان للمعطيات حركيا أثناء تنفيذ المشروع.
مثال : malloc(N) ==> يتم حجز N ثمن مباشرة.
مثال : لنفترض أننا نريد حجز جزء من الذاكرة لنص يتكون من 4000 حرف. نقوم في هذه الحالة بتعريف مؤشر T من نوع char.ونكتب إذن :
char *T; |
هنا يتم حجز 4000 ثمن، لكن إن لم يوجد مكان في الذاكرة ، تكون نتيجة T هي 0.
كما أننا حجزنا 4000 ثمن لـ 4000 حرف لأن النوع char يحجز لكل حرف ثمنا واحدا فقط.
لكن إن لم نكن نعرف حجم نوع الذي نريد استعماله، ماذا سنفعل في هذه الحالة ؟
هنا يبرز الدور الكبير الذي تلعبه الدالة sizeof التي تقوم بمعرفة حجم كل نوع.
sizeof(متغير) ==> تكون نتيجتها هو مقدار المتغير.
sizeof(ثابتة) ==> تكون نتيجتها هو مقدار الثابتة.
sizeof(نوع) ==> تكون نتيجتها هو مقدار النوع.
مثال :
short A[10]; |
تكون النتيجة :
|
مثال آخر : نريد أن نحجز في الذاكرة لـ X قيمة من نوع int ، العدد X يتم قراءته من لوحة المفاتيح :
int X; |
5.4 الدالة exit
تستعمل هذه الدالة للخروج من المشروع أثناء التنفيذ لعدة أسباب منها عدم وجود مكان في الذاكرة أثناء الحجز.
تنتمي هذه الدالة إلى المكتبة <stdlib>.
مثال : المشروع التالي يمكن من قراءة 10 جمل من لوحة المفاتيح، حيث يبحث في الذاكرة على الأمكنة الفارغة والقادرة على استيعاب هذه الجمل. في حالة عدم وجود مكان للحجز فيه يظهر المشروع رسالة تعبر عن وجود خطا معين ثم يتم الخروج من التنفيذ مباشرة.
include <stdio.h> for (I=0; I<10; I++) |
التمرين 9.20
أنجز شفرة تمكن من قراءة 10 جمل (200 حرف كأقصى حد) حيث يتم تحفيظهم حركيا في الذاكرة باستعمال جدول المؤشرات . اقلب ترتيب حروف الجمل العشر بتغيير المؤشرات .أظهر النتيجة على الشاشة.
5.5 الدالة free
بعد الحجز الذي قمنا به نحتاج بطبيعة الحال إلى تحرير الأماكن أو الأثمان التي استعملناها، في هذه الحالة يبرز دور الدالة free التي تقوم بهذا العمل،
ملاحظة : تنتمي الدالة free إلى المكتبة <stdlib>
مثال :
char *p; p=malloc(20*sizeof(char)); .... free(p) |
التمرين 9.21
أنجز شفرة تمكن من قراءة 10 جمل (200 حرف كأقصى حد) حيث يتم تحفيظهم في جدول المؤشرات MOT. امسح الجمل العشر لكن حسب ترتبها قاموسيا لتحرر مكانها في الذاكرة .أظهر النتيجة في كل عملية مسح حيث يتم انتظار المستعمل على أن يضغط على المفتاح 'ENTER'.
التمرين 9.22
أنجز شفرة تمكن من قراءة 10 كلمات (50 حرفا كأقصى حد) حيث يتم تحفيظهم في جدول المؤشرات MOT. انسخ أو انقل الكلمات العشر حسب ترتبها قاموسيا لتضعها في جملة واحدة PHRASE . احجز مكانا يكفي لـ PHRASE في الذاكرة قبل عملية نسخ أو نقل الكلمات. حرر مكان كل كلمة بعد نقلها أو نسخها في PHRASE .استعمل دوال <string.h>.
التمرين 9.23
أنجز شفرة تمكن من قراءة 10 كلمات (50 حرفا كأقصى حد) حيث يتم تحفيظهم في جدول المؤشرات MOT. احجز مكانا يكفي لكل كلمة حركيا في الذاكرة. رتب الكلمات قاموسيا بالطريقة التي تناسبك، لكن باستعمال المؤشر.
تأليف
المؤلف الأصلي: فرديريك فابر (Frédéric FABER)
البريد الإلكتروني: عنوان البريد الإلكتروني هذا محمي من روبوتات السبام. يجب عليك تفعيل الجافاسكربت لرؤيته.
الموقع الإلكتروني: http://www.ltam.lu/cours-c
ترجمة بتصرف: محمد عبد الرحمان