قسم لغات البرمجة php java c++ جافا Visual Basic فيجوال بيسك c# Delphi دلفي API PERL HTML و TML JavaScript ASP XML Oracle اوركال MySql sql server Access

   

الصورة الرمزية F.Abdelaziz
F.Abdelaziz
:: استاذ و مشرف قسم الالكترونيات ::
تاريخ التسجيل: May 2007
المشاركات: 6,832
نشاط [ F.Abdelaziz ]
قوة السمعة:
قديم 18-07-2019, 11:00 PM المشاركة 33   
افتراضي



مراجعة المصفوفات Arrays

سوف تتعرف على :
• إعلان declare المصفوفة .
• تهيئة initialize المصفوفة .
• الوصول access إلى عناصر المصفوفة array elements.

في البرمجة ، واحدة من المشاكل التى تنشأ في كثير من الأحيان هي التعامل مع العديد من البيانات من نفس النوع.
بوضع هذا الموقف في اعتبارك ، إفترض أنك تقوم بإجراء استطلاع على 100 شخص وعليك تخزين عمرهم age.. لحل هذه المشكلة في C++ ، يمكنك إنشاء مصفوفة array أعداد صحيحة تحتوي على 100 عنصر elements.
المصفوفة عبارة عن مجموعة collection من البيانات تحتوي (تحتفظ) على عدد ثابت من القيم من نفس النوع . فمثلا:

كود:
int age[100];

هنا ، يمكن أن تحتوي المصفوفة المسماة age على 100 عنصر من نوع العدد الصحيح.
لا يمكن تغيير حجم [100] ونوع int المصفوفات بعد الإعلان عنها.

كيف تعلن عن مصفوفة ؟

كود:
 dataType arrayName[arraySize];

على سبيل المثال :

كود:
float mark[5];


هنا ، أعلنا عن مصفوفة اسمها ، mark ، بنوع float ، وحجمها 5. بمعنى أنها يمكن أن تحتوي على 5 قيم float .

عناصر المصفوفة وكيفية الوصول إليها ؟
يمكنك الوصول إلى عناصر المصفوفة باستخدام الفهارس indices .
افترض أنك أعلنت المصفوفة المسماة mark كما هو موضح أعلاه . العنصر الأول هو mark[0] ، والعنصر الثاني هو mark[1] وهكذا.




بعض الملاحظات الأساسية:
• أول عنصر فى المصفوفات يكون مؤشره (فهرسه) index صفر 0 وليس 1. في هذا المثال ، يعد mark[0] هو العنصر الأول.
• إذا كان حجم المصفوفة هو n ، للوصول إلى العنصر الأخير ، فسيتم استخدام الفهرس (n-1). في هذا المثال ، يعد mark[4] هو العنصر الأخير.
• افترض أن عنوان address البداية للعنصر mark[0] هو 2120d . عندئذ ، سيكون العنوان التالي ، mark[1] ، هو 2124d ، وسيكون عنوان mark[2] هو 2128d وهكذا . ذلك لأن حجم float 4 بايت.

كيفية تهيئة مصفوفة ؟
من الممكن تهيئة المصفوفة أثناء الإعلان. فمثلا،

كود:
int mark[5] = {19, 10, 8, 17, 9};

هناك طريقة أخرى لتهيئة المصفوفة أثناء الإعلان:

كود:
int mark[] = {19, 10, 8, 17, 9};




هنا ،

كود:
 mark[0] is equal to 19
mark[1] is equal to 10
mark[2] is equal to 8
mark[3] is equal to 17
mark[4] is equal to 9


كيفية إدراج وطباعة عناصر مصفوفة ؟


كود:
int mark[5] = {19, 10, 8, 17, 9}
	
	// change 4th element to 9
	mark[3] = 9;
	
	// take input from the user and insert in third element
	cin >> mark[2];
		
	// take input from the user and insert in (i+1)th element
	cin >> mark[i];
	
	// print first element of the array
	cout << mark[0];
	
	// print ith element of the array
	cout >> mark[i-1];


مثال :
برنامج لتخزين وحساب مجموع 5 أرقام أدخلها المستخدم باستخدام المصفوفات.


كود:
#include <iostream>
using namespace std;
	
int main() 
	{
	    int numbers[5], sum = 0;
	    cout << "Enter 5 numbers: ";
	    
	    //  Storing 5 number entered by user in an array
	    //  Finding the sum of numbers entered
	    for (int i = 0; i < 5; ++i) 
	    {
	        cin >> numbers[i];
	        sum += numbers[i];
	    }
	    
	    cout << "Sum = " << sum << endl;  
	    
	    return 0;
	}

الخرج :

كود:
Enter 5 numbers: 3
4
5
4
2
Sum = 18

ملاحظة :
افترض أنك أعلنت مصفوفة من 10 عناصر مثل :

كود:
int testArray[10];

يمكنك استخدام أعضاء المصفوفة من testArray[0] إلى testArray[9] .
إذا حاولت الوصول إلى عناصر المصفوفة خارج حدودها ، دعنا نقول testArray[14] ، قد لا يظهر المترجم أي خطأ. ومع ذلك ، قد يؤدي هذا إلى إخراج غير متوقع (سلوك غير محدد).

تمرين 1 :
برنامج لحساب متوسط أعداد باستخدام المصفوفات .
يأخذ هذا البرنامج n عدد العناصر من المستخدم (حيث يتم تحديد n بواسطة المستخدم) ، ويخزن البيانات في مصفوفة ويحسب متوسط هذه الأعداد .

كود:
 //Calculate Average of Numbers Using Arrays
	#include <iostream>
	using namespace std;
	
	int main()
	{
	    int n, i;
	    float num[100], sum=0.0, average;
	
	    cout << "Enter the numbers of data: ";
	    cin >> n;
	
	    while (n > 100 || n <= 0)
	    {
	        cout << "Error! number should in range of (1 to 100)." << endl;
	        cout << "Enter the number again: ";
	        cin >> n;
	    }
	
	    for(i = 0; i < n; ++i)
	    {
	        cout << i + 1 << ". Enter number: ";
	        cin >> num[i];
	        sum += num[i];
	    }
	
	    average = sum / n;
	    cout << "Average = " << average;
	
	    return 0;
	}


الخرج :

كود:
Enter the numbers of data: 6
1. Enter number: 45.3
2. Enter number: 67.5
3. Enter number: -45.6
4. Enter number: 20.34
5. Enter number: 33
6. Enter number: 45.6
Average = 27.69

يحسب هذا البرنامج المتوسط إذا كان عدد البيانات من 1 إلى 100.
إذا قام المستخدم بإدخال قيمة n أعلى من 100 أو أقل من 1 ، يتم تنفيذ حلقة while ، والتي تطلب من المستخدم إدخال قيمة n حتى تكون بين 1 و 100.
عندما يتم إدخال الأعداد من قبل المستخدم ، يتم حساب sum بالتتابع وتخزينه في المتغير sum.
بعد تخزين جميع الأعداد ، يتم حساب المتوسط وعرضه.


تمرين 2 :
برنامج لإيجاد أكبر عنصر فى المصفوفة :
يأخذ هذا البرنامج n عدد العناصر من المستخدم (حيث يتم تحديد n بواسطة المستخدم) ويخزن البيانات في مصفوفة . ثم ، يعرض هذا البرنامج أكبر عنصر في هذه المصفوفة باستخدام الحلقات.


كود:
// Display Largest Element of an array
	#include <iostream>
	using namespace std;
	
	int main()
	{
	    int i, n;
	    float arr[100];
	
	    cout << "Enter total number of elements(1 to 100): ";
	    cin >> n;
	    cout << endl;
	
	    // Store number entered by the user
	    for(i = 0; i < n; ++i)
	    {
	       cout << "Enter Number " << i + 1 << " : ";
	       cin >> arr[i];
	    }
	
	    // Loop to store largest number to arr[0]
	    for(i = 1;i < n; ++i)
	    {
	       // Change < to > if you want to find the smallest element
	       if(arr[0] < arr[i])
	           arr[0] = arr[i];
	    }
	    cout << "Largest element = " << arr[0];
	
	    return 0;
	}



الخرج :

كود:
Enter total number of elements: 8

Enter Number 1: 23.4
Enter Number 2: -34.5
Enter Number 3: 50
Enter Number 4: 33.5
Enter Number 5: 55.5
Enter Number 6: 43.7
Enter Number 7: 5.7
Enter Number 8: -66.5

Largest element = 55.5


• يأخذ هذا البرنامج عددًا من العناصر من المستخدم ويخزنها في المصفوفة arr[] .
• لإيجاد أكبر عنصر ، يتم تحديد أول عنصرين من المصفوفة ويتم وضع أكبر عنصر من هذين العنصرين في arr[0] . ثم ، يتم التحقق من العنصرين الأول والثالث ويوضع أكبر هذين العنصرين في arr[0] .
• تستمر هذه العملية حتى يتم التحقق من العنصرين الأول والأخير.
• بعد هذه العملية ، سيكون أكبر عنصر في المصفوفة في وضع [0] arr.

مراجعة المصفوفات متعددة الأبعاد :
سنتعرف على المصفوفات متعددة الأبعاد . بشكل أكثر تحديدًا ، كيفية الإعلان عنها declare والوصول إليها access واستخدامها بكفاءة في البرنامج.
يمكنك إنشاء مصفوفة مصفوفة وهو ما يعرف باسم المصفوفة متعدد الأبعاد multi-dimensional array . فمثلا:

كود:
int x[3][4];

هنا ، x عبارة عن مصفوفة ثنائية الأبعاد. يمكن أن تحتفظ بما يصل إلى 12 عنصرا كحد أقصى.
يمكنك التفكير في هذه المصفوفة كجدول يحتوي على 3 صفوف rows ولكل صف 4 أعمدة columns كما هو موضح أدناه.



المصفوفة ثلاثية الأبعاد تعمل أيضا بطريقة مماثلة. فمثلا:

كود:
float x[2][4][3];

يمكن أن تحتوي هذه المصفوفة x على 24 عنصرًا بحد أقصى. يمكنك التفكير في هذا المثال على النحو التالي : يمكن لكل العنصرين 2 أن يضم 4 عناصر ، مما يصنع 8 عناصر ويمكن لكل عنصر من هذه العناصر الـثمانية 8 أن يضم 3 عناصر. وبالتالي ، إجمالي عدد العناصر التي يمكن أن تحتوي عليها هذه المصفوفة هو 24.

تهيئة المصفوفة متعددة الأبعاد :
يمكنك تهيئة المصفوفة متعددة الأبعاد بأكثر من طريقة.


• تهيئة المصفوفة ثنائية الأبعاد :

كود:
 int test[2][3] = {2, 4, 5, 9, 0, 0};

أفضل طريقة لتهيئة هذه المصفوفة مع نفس عناصر المصفوفة أعلاه

كود:
int  test[2][3] = { {2, 4, 5}, {9, 0 0}};

• تهيئة المصفوفة ثلاثية الأبعاد :

كود:
int test[2][3][4] = {3, 4, 2, 3, 0, -3, 9, 11, 23, 12, 23, 
                 2, 13, 4, 56, 3, 5, 9, 3, 5, 5, 1, 4, 9};

أفضل طريقة لتهيئة هذه المصفوفة مع نفس عناصر المصفوفة أعلاه :

كود:
int test[2][3][4] = { 
                     		{ {3, 4, 2, 3}, {0, -3, 9, 11}, {23, 12, 23, 2} },
                     		{ {13, 4, 56, 3}, {5, 9, 3, 5}, {5, 1, 4, 9} }
                 	      };

مثال 1 :
برنامج عرض جميع عناصر مصفوفة مهيئة ثنائية الأبعاد :


كود:
 // Two Dimensional Array
//display all elements of an  nitialized two dimensional array.
	
#include <iostream>
using namespace std;
	
int main()
	{
	    int test[3][2] =
	    {
	        {2, -5},
	        {4, 0},
	        {9, 1}
	    };
	
	    // Accessing two dimensional array using
	    // nested for loops
	    for(int i = 0; i < 3; ++i)
	    {
	        for(int j = 0; j < 2; ++j)
	        {
	            cout<< “test[“ << i << “][“ << j << “] = “ << test[i][j] << endl;
	        }
	    }
	
	    return 0;
	}

الخرج :

كود:
test[0][0] = 2
test[0][1] = -5
test[1][0] = 4
test[1][1] = 0
test[2][0] = 9
test[2][1] = 1

مثال 2 :
برنامج لتخزين درجة حرارة مدينتين مختلفتين لمدة اسبوع وعرضها :

كود:
 // Two Dimensional Array
// store temperature of two different cities for a week and display it.
#include <iostream>
using namespace std;
	
const int CITY = 2;
const int WEEK = 7;
	
int main()
{
   int temperature[CITY][WEEK];
	
  cout << "Enter all temperature for a week of first city and then second city. \n";

  // Inserting the values into the temperature array
  for (int i = 0; i < CITY; ++i)
  {
     for(int j = 0; j < WEEK; ++j)
    {
     cout << "City " << i + 1 << ", Day " << j + 1 << " : ";
     cin >> temperature[i][j];
   }
}
	
cout << "\n\nDisplaying Values:\n";
	
// Accessing the values from the temperature array
for (int i = 0; i < CITY; ++i)
{
  for(int j = 0; j < WEEK; ++j)
  {
  cout << "City " << i + 1 << ", Day " << j + 1 << " = " << temperature[i][j] << endl;
  }
}
	
return 0;
}



الخرج :

كود:
Enter all temperature for a week of first city and then second city. 
City 1, Day 1 : 32
City 1, Day 2 : 33
City 1, Day 3 : 32
City 1, Day 4 : 34
City 1, Day 5 : 35
City 1, Day 6 : 36
City 1, Day 7 : 38
City 2, Day 1 : 23
City 2, Day 2 : 24
City 2, Day 3 : 26
City 2, Day 4 : 22
City 2, Day 5 : 29
City 2, Day 6 : 27
City 2, Day 7 : 23


Displaying Values:
City 1, Day 1 = 32
City 1, Day 2 = 33
City 1, Day 3 = 32
City 1, Day 4 = 34
City 1, Day 5 = 35
City 1, Day 6 = 36
City 1, Day 7 = 38
City 2, Day 1 = 23
City 2, Day 2 = 24
City 2, Day 3 = 26
City 2, Day 4 = 22
City 2, Day 5 = 29
City 2, Day 6 = 27
City 2, Day 7 = 23


مثال 3 :
برنامج لتخزين قيمة مدخلة بمعرفة المستخدم فى مصفوفة ثلاثية الأبعاد وعرضها :



كود:
// Three Dimensional Array
// Store value entered by user in three dimensional array and display it.
	#include <iostream>
	using namespace std;
	
	int main()
	{
	    // This array can store upto 12 elements (2x3x2)
	    int test[2][3][2];
	
	    cout << "Enter 12 values: \n";
	    
	    // Inserting the values into the test array
	    // using 3 nested for loops.
	    for(int i = 0; i < 2; ++i)
	    {
	        for (int j = 0; j < 3; ++j)
	        {
	            for(int k = 0; k < 2; ++k )
	            {
	                cin >> test[i][j][k];
	            }
	        }
	    }
	
	    cout<<"\nDisplaying Value stored:"<<endl;
	
	    // Displaying the values with proper index.
	    for(int i = 0; i < 2; ++i)
	    {
	        for (int j = 0; j < 3; ++j)
	        {
	            for(int k = 0; k < 2; ++k)
	            {
	                cout << "test[" << i << "][" << j << "][" << k << "] = " << test[i][j][k] << endl;
	            }
	        }
	    }
	
	    return 0;
	}

الخرج :

كود:
 Enter 12 values: 
1
2
3
4
5
6
7
8
9
10
11
12

Displaying Value stored:
test[0][0][0] = 1
test[0][0][1] = 2
test[0][1][0] = 3
test[0][1][1] = 4
test[0][2][0] = 5
test[0][2][1] = 6
test[1][0][0] = 7
test[1][0][1] = 8
test[1][1][0] = 9
test[1][1][1] = 10
test[1][2][0] = 11
test[1][2][1] = 12

مع زيادة عدد الأبعاد ، يزداد التعقيد بشكل كبير على الرغم من أن المفهوم مشابه تمامًا.


تمرير مصفوفة إلى دالة Passing Array to a Function
يمكن تمرير المصفوفات إلى دالة كوسيط argument. المثال التالى يعرض تمرير مصفوفة أحادية البعد إلى دالة :
برنامج لعرض درجات 5 طلاب عن طريق تمرير مصفوفة أحادية البعد إلى دالة .


كود:
// Passing One-dimensional Array to a Function
// display marks of 5 students by passing one-dimensional array to a function.
#include <iostream>
using namespace std;
	
void display(int marks[5]);
	
int main()
	{
	    int marks[5] = {88, 76, 90, 61, 69};
	    display(marks);
	    return 0;
	}
	
	void display(int m[5])
	{
	    cout << "Displaying marks: "<< endl;
	
	    for (int i = 0; i < 5; ++i)
	    {
	        cout << "Student "<< i + 1 <<": "<< m[i] << endl;
	    }
	}

الخرج :

كود:
Displaying marks: 
Student 1: 88
Student 2: 76
Student 3: 90
Student 4: 61
Student 5: 69

عندما يتم تمرير مصفوفة كوسيط للدالة (عند الاستدعاء ) ، يتم استخدام اسم المصفوفة فقط كوسيط كما يلى :

كود:
 display(marks);

لاحظ أيضًا الفرق أثناء تمرير المصفوفة كوسيط بدلاً من متغير (عند التعريف) .

كود:
void display(int m[5]);

يمثل الوسيط marks في الكود أعلاه عنوان ذاكرة العنصر الأول من المصفوفة marks[5] .
ويتحول الوسيط الرسمى int m[5] في تعريف الدالة إلى int* m . يشير هذا المؤشر إلى نفس العنوان المشار إليه بواسطة المصفوفة marks .
يعالج C++ تمرير المصفوفة إلى دالة بهذه الطريقة لحفظ الذاكرة والوقت.

تمرير مصفوفة متعددة الأبعاد إلى دالة Passing Multidimensional Array to a Function
يمكن تمرير مصفوفة متعددة الأبعاد بطريقة مماثلة لمصفوفة أحادية الأبعاد. المثال التالى يوضح تمرير مصفوفة ثنائية الأبعاد إلى دالة :

كود:
 // Passing Multidimensional Array to a Function
// display the elements of two dimensional array by passing it to a function.
#include <iostream>
using namespace std;
	
void display(int n[3][2]);
	
int main()
	{
	    int num[3][2] = {
	        {3, 4},
	        {9, 5},
	        {7, 1}
	
	    };
	    display(num);
	
	    return 0;
	}
	
	void display(int n[3][2])
	{
	
	    cout << "Displaying Values: " << endl;
	    for(int i = 0;  i < 3; ++i)
	    {
	        for(int j = 0; j < 2; ++j)
	        {
	            cout << n[i][j] << " ";
	        }
	    }
	}

الخرج :

كود:
 Displaying Values: 
3 4 9 5 7 1

في البرنامج أعلاه ، يتم تمرير المصفوفة متعدد الأبعاد num إلى الدالة display() .
في داخل ، الدالة display() ، المصفوفة num يتم معالجتها باستخدام لحلقة for متداخلة .
يستخدم البرنامج 2حلقة for للتكرار عبر العناصر الموجودة في المصفوفة ثنائية الأبعاد. إذا كانت المصفوفة ثلاثية الأبعاد ، فيجب عليك استخدام 3 حلقات.
أخيرًا ، تتم طباعة جميع العناصر على الشاشة.

ملاحظة: يمكن تمرير مصفوفة متعددة الأبعاد ذات أبعاد تزيد عن 2 بطريقة مماثلة للمصفوفة ثنائية الأبعاد.

اعلانات

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


السلاسل في C++ Strings in C++
نحن نعلم أن السلسلة string عبارة عن مصفوفة array من الأحرف characters . لنلقي نظرة على السلسلة ونعرف المزيد عنها.
هناك نوعان مختلفان من السلاسل في C++ .
• سلسلة على غرار (نمط) C C-style string (موروثة من لغة C )
• سلسلة الفئة std::string (جزء من مكتبة C++ القياسية ، تجعل العمل مع السلاسل فى C++ سهل )
في هذه الفقرة ، سوف نركز على سلسلة النمط C .

سلسلة النمط C C-style String

• إعلان السلسلة :
يمكننا التفكير في السلسلة باعتبارها مصفوفة من الأحرف ، مثل "Sam" عبارة عن سلسلة وهي عبارة عن مصفوفة من الأحرف عناصرها 'S' و 'a' و 'm' و '\0' .

كود:
element	  'S'	'a'	'm'	'\0'
index	   0	1	 2	  3

انظر إلى الحرف في الفهرس index 3 . إنه يمثل حرف null character . قيمة ASCII للحرف '\0' هي 0 وقيمة الحرف ‘0’ هي 48. وهو يمثل حرف إنهاء السلسلة. لذلك ، إذا أعلنا: -

كود:
char name[ ]= "Sam";

فهذا يعنى :

كود:
 ['S','a','m','\0']

ملاحظة :
دائما نضع السلسلة بين أقواس اقتباس مزدوجة “ “
يمكننا أيضا أن نعلن متغير السلسلة باستخدام الحروف على النحو التالي :

كود:
char name[ ]= { 'S', 'a', 'm', '\0'};

• طباعة (إخراج) السلسلة
دعونا نرى مثالين لطباعة سلسلة ، أحدهما بدون والآخر بحلقة for .


كود:
#include <iostream>
using namespace std;

int main()
   {
   char str[ ] = "Hello"; // Declaration
   cout << str << endl;//Print , Output
   return 0;
   }

الخرج :

كود:
Hello

في المثال أعلاه ، قمنا بطباعة السلسلة بالكامل مرة واحدة. الآن ، دعونا نرى نفس المثال لطباعة الأحرف الفردية من السلسلة باستخدام حلقة for .

كود:
#include <iostream>
using namespace std;

int main()
   {
     char str[ ] = "Hello";
     int i;
     for( i=0; i<6; i++)
       {
        cout << str[i];
        }
   return 0;
 }

الخرج :

كود:
Hello

في المثال الأول ، قمنا بطباعة السلسلة بالكامل مرة واحدة. بينما في المثال الثاني ، قمنا بطباعة حرف في كل مرة.

• إدخال السلسلة String Input
الآن دعونا نرى كيفية إدخال سلسلة من المستخدم بمثال.

كود:
#include <iostream>
using namespace std;

int main()
{
	char name[20];       		//declaring string 'name'

	cin >> name; 			//taking string input
	cout << name << endl; 	//printing string
	return 0;
}

الخرج :

كود:
Peter
Peter

العبارة :

كود:
 char name[20];

من خلال كتابة هذه العبارة ، فإننا نعلن عن مصفوفة من الأحرف المسماة 'name' ومنحناها حجم مصفوفة من 20 عنصر لأننا لا نعرف بالضبط طول السلسلة. وبالتالي ، احتلت مساحة في الذاكرة ذات حجم مطلوب من 20 حرفًا. لذلك ، لا يمكن للمصفوفة 'name' تخزين أكثر من 20 حرفًا.
العبارة :

كود:
 cin >> name;

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

ملاحظة :
يأخذ الكود أعلاه كلمة واحدة فقط من المستخدم إلى متغير السلسلة. وينتهي مع أي مسافة بيضاء. حاول أن تكتب شيئًا ما بعد الفراغ وسيأخذ الكلمة الأولى فقط.
على سبيل المثال ، إذا قمنا في المثال أعلاه بإدخال Sam Brad كإسم ، فسيكون الإخراج هو Sam فقط لأن الكود يأخذ في الاعتبار كلمة واحدة فقط وينتهي بعد الكلمة الأولى (بعد مسافة بيضاء).

• إدخال سلسلة من كلمات متعددة
يمكننا أخذ مدخلات لسلسلة تتكون من أكثر من كلمة واحدة باستخدام الدالة cin.getline. لنرى مثالا:

كود:
#include <iostream>
using namespace std;

int main()
{
	char name[20];       			//declaring string 'name'

	cin.getline(name, sizeof(name)); 	//taking string input
	cout << name << endl; 		//printing string
	return 0;
}


الخرج :

كود:
Sam Bard
Sam Bard

فى العبارة :

كود:
cin.getline(name, sizeof(name));

تأخذ الدالة cin.getline وسيطين ، متغير السلسلة name وحجم هذا المتغير sizeof(name) . لقد استخدمنا الدالة sizeof للحصول على حجم متغير السلسلة 'name' .

• تمرير السلاسل إلى الدوال
هذا هو نفس ما نفعل مع المصفوفات الأخرى. الفرق الوحيد أن هنا مصفوفة من الأحرف . لنرى مثال .


كود:
#include <iostream>
using namespace std;

void display( char ch[] )
{
cout << ch;
}

int main(){
	char arr[30];
	cout << "Enter a word" << endl;
	cin >> arr;
	display(arr);
	return 0;
}

الخرج :

كود:
Enter a word
cpp
cpp


دوال السلسلة المعرفة مسبقا Predefined string functions
يمكننا تنفيذ أنواع مختلفة من دوال السلسلة مثل ربط joining سلسلتين أو مقارنة comparing سلسلة بأخرى أو إيجاد طول السلسلة. دعونا نلقي نظرة على قائمة هذه الدوال .



هذه الدوال المحددة مسبقًا جزء من مكتبة cstring. لذلك ، نحتاج إلى تضمين هذه المكتبة في الكود الخاص بنا عن طريق كتابة :

كود:
#include <cstring>


سنرى بعض أمثلة على الدوال strlen و strcpy و strcat و strcmp لأن هذه هي الأكثر شيوعًا.

• الدالة strlen(s1) : لحساب طول السلسلة s1 .
يتم حساب المسافة البيضاء فى طول السلسلة .


كود:
#include <iostream>
#include <cstring>

using namespace std;

int main()
{
	char name[ ]= "Hello";
  	int len1, len2;
  	len1 = strlen(name);
  	len2 = strlen("Hello World");
  	cout << "Length of " << name << " = " << len1 << endl;
  	cout << "Length of " << "Hello World" << " = " << len2 << endl;
	return 0;
}

الخرج :

كود:
 Length of Hello = 5
Length of Hello World = 11

ملاحظة :
الدالة strlen لا تحسب حرف الإنتهاء ‘\0’ عند حساب طول السلسلة .

• الدالة strcpy(s1, s2) تنسخ السلسلة الثانية s2 إلى السلسلة الأولى s1 .


كود:
 #include <iostream>
#include <cstring>

using namespace std;

int main()
{
	char s2[ ]= "Hello";
  	char s1[10];
  	strcpy(s1, s2);
  	cout << "Source string " << s2 << endl;
  	cout << "Target string " << s1 << endl;
	return 0;
}

الخرج :

كود:
 Source string Hello
Target string Hello

• الدالة strcat(s1, s2) تسلسل (تربط ) concatenates(joins) السلسلة الثانية s2 إلى السلسلة الأولى s1

كود:
 #include <iostream>
#include <cstring>

using namespace std;

int main()
{
	char s2[ ]= "World";
  	char s1[20]= "Hello";
  	strcat(s1, s2);
  	cout << "Source string " << s2 << endl;
  	cout << "Target string " << s1 << endl;
	return 0;
}


الخرج :

كود:
Source string World
Target string HelloWorld

لاحظ أنه في المثال أعلاه ،أعطينا حجم المصفوفة إلى السلسلة s1 لأننا نقوم بإضافة أحرف سلسلة أخرى إليها. يجب أن يكون حجم المصفوفة المعطى أكبر من أو يساوي حجم مصفوفة السلسلة بعد التسلسل (الربط) .

• الدالة strcmp(s1, s2) تقارن سلسلتين وإيجاد ما إذا كانت متشابهة أم مختلفة.
فهى تقارن أحرف السلسلتين حرف بحرف حتى يكون هناك عدم تطابق. إذا كانت السلسلتان متطابقتان identical ، فسوف تُرجع 0. وإذا لم تكن كذلك ، فتُرجع الفرق بين قيم ASCII للأزواج الأولى غير المطابقة من الأحرف.


كود:
#include <iostream>
#include <cstring>

using namespace std;

int main()
{
	char s1[ ]= "Hello";
  	char s2[ ]= "World";
  	int i, j;
  	i = strcmp(s1, "Hello");
  	j = strcmp(s1, s2);
  	cout << i << endl;
  	cout << j << endl;
	return 0;
}

الخرج :

كود:
0
-15

مصفوفة الأحرف ثتائية الأبعاد :
كما هو الحال مع المصفوفة ثنائية الأبعاد من الأعداد الصحيحة وأنواع البيانات الأخرى ، لدينا مصفوفة ثنائية الأبعاد من الأحرف أيضًا.
على سبيل المثال ، يمكننا كتابة


كود:
char names[4][10] = 
{ 
                "Andrew",
                "Kary",
                "Brown",
                "Lawren" 
       	};

نظرًا لاستخدام السلسلة على نطاق واسع ، توفر C++ نوع بيانات سلسلة مضمّن سنتعلمه في الفقرة التالية .

لا تستخدم سلاسل النمط C Don’t use C-style strings
من المهم معرفة السلاسل ذات النمط C لأنها تستخدم في الكثير من الأكواد . ومع ذلك ، والآن بعد أن أوضحنا كيفية عملها ، سنوصي بتجنبها تمامًا كلما كان ذلك ممكنًا! ما لم يكن لديك سبب محدد وجذاب لاستخدام سلاسل النمط C ، استخدم الفئة std :: string (المعرفة في الرأس <string>) بدلاً من ذلك. الفئة std::string أسهل وأكثر أمانا وأكثر مرونة.

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

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


السلسلة فى C++ باستخدام الفئة (النوع) std::string
نظرًا لاستخدام السلسلة بشكل مكثف ، يوفر C++ نوع بيانات سلسلة مضمنة built-in . تمامًا مثل أنواع البيانات int أو float أو أنواع البيانات الأخرى ، يمكننا استخدام نوع بيانات السلسلة أيضًا. إنه ببساطة يجعل استخدام السلاسل أسهل.

• مثل cin و cout ، يتم تعريف السلسلة أيضًا في مساحة الاسم std namespace . لاستخدام السلاسل بهذه الطريقة ، نحتاج إلى تضمين ملف الرأس حيث يتم الإعلان عنه في رأس البرنامج . يتم ضمه عن طريق كتابة :

كود:
#include <string>

• يتم إعلان declare المتغيرات من النوع std::string كما يلى :

كود:
 std::string name;

فى حالة استخدام :

كود:
using namespace std;
يكون الإعلان بالصيغة :

كود:
string name;

إى النوع ثم الاسم .

هنا name متغير سلسلة تمامًا مثلما لدينا متغيرات int أو متغيرات float أو متغيرات أنواع بيانات أخرى.

• نقوم بتعيين assign قيمة لمتغير السلسلة تمامًا كما نقوم بتعيين قيمة لمتغير من أي نوع بيانات آخر كما يلي :

كود:
 name = "Hall";

• يمكننا أيضًا تعيين قيمة لمتغير السلسلة في وقت الإعلان عنها كما يلى :

كود:
std::string name("Hall");

متغير السلسلة مثله مثل أي متغير آخر.
الآن دعونا نرى مثالا لطباعة سلسلة.

كود:
#include <iostream>
#include <string>
using namespace std;

int main()
{
	string name;
	name = "Hall";
	cout << name << endl;
	return 0;
}

الخرج :

كود:
Hall

• إدخال سلسلة Input a string
تعرفنا بالفعل على كيفية إدخال سلسلة باستخدام مصفوفة أحرف. الآن ، دعونا ندخل سلسلة باستخدام متغير سلسلة.
نحن ببساطة نستخدم cin لإدخال سلسلة كما اعتدنا على القيام به باستخدام مصفوفة الأحرف.

كود:
 #include <iostream>
#include <string>
using namespace std;

int main()
{
	string name;
	cin >> name;
	cout << name << endl;
	return 0;
}

الخرج :

كود:
Hall
Hall

• إدخال كلمات متعددة :


كود:
 #include <iostream>
#include <string>
using namespace std;

int main()
{
	string name;
	cin >> name;
	cout << name << endl;
	return 0;
}

الخرج :

كود:
Hall Doe
Hall

كما في المثال أعلاه ، تأخذ cin أحرفًا فقط إلى مسافة بيضاء. وبالتالي متغير السلسلة name سوف يخزن فقط القيمة Hall .

• إدخال سلسلة كلمات متعددة باستخدام الدالة std::getline
لإدخال سلسلة متعدد الكلمات ، نستخدم الدالة std :: getline. دعونا نرى مثالا يأخذ مدخلات متعددة الكلمات.


كود:
 #include <iostream>
#include <string>
using namespace std;


int main()
{
	string name;
	getline(cin, name);
	cout << name << endl;
	return 0;
}

الخرج :

كود:
Hall Doe
Hall Doe

العبارة :

كود:
 std::getline(std::cin, name);

هذا هو بناء جملة استخدام الدالة getline() . يتم تعريف الدالة getline() أيضًا في مساحة الاسم std namespace وبالتالي نكتبها std::getline .
تأخذ الدالة getline() بارامترين . أول بارامتر هو std :: cin والثاني هو اسم متغير السلسلة name .

إلحاق سلسلتين Append two strings
يمكننا أن نسلسل concatenate السلاسل (ربط join السلاسل) باستخدام العامل + وإلحاق append سلسلة إلى أخرى باستخدام العامل += .

كود:
#include <iostream>
#include <string>
using namespace std;

int main()
{
	string st1("Hello");
	string st2("World");
	cout << st1 + st2 << endl;
	st1 += "cpp";
	cout << st1 << endl;
	return 0;
}

الخرج :

كود:
HelloWorld
Hellocpp

في المثال أعلاه ، تقوم متغيرات السلسلة s1 و s2 بتخزين القيم "Hello" و "World" على التوالي.
العبارة :

كود:
 cout << st1 + st2 << endl;

مكافئة للعبارة :

كود:
 cout << st1 << st2 << endl;

التى تطبع سلسلة تلو الأخرى.
فى العبارة :

[CODEst1 += "cpp"; ] [/CODE]

أضفنا السلسلة "cpp" إلى قيمة المتغير st1 وبالتالي نجعل قيمته "Hellocpp".

دوال الأعضاء Member Functions
هناك العديد من الدوال الأعضاء في الفئة std :: string (الدوال المعرفة مسبقًا والمعرفة في std :: string) مثل length() ، size() ، resize() ، reserve() . دعونا ننظر في بعض هذه الدوال .

• الدالة length() :
يتم استخدام الدالة length() لإيجاد طول سلسلة. هذه الدالة معرفة مسبقًا والتى تنتمي إلى الفئة std :: string. دعونا نرى مثالا .


كود:
 #include <iostream>
#include <string>
using namespace std;

int main()
{
	string name;
	name = "I have 4 chocolates";
	cout << name.length() << endl;
	return 0;
}

الخرج :

كود:
19

وهكذا فإن الدالة length() تُرجع طول السلسلة 'name' بما في ذلك أحرف المسافة البيضاء.

• الدالة empty :
تقوم الدالة empty()بإرجاع ما إذا كانت السلسلة فارغة empty أم لا. تقوم بإرجاع 1 إذا كان طول السلسلة 0 (فارغة) و 0 إذا لم تكن فارغة .


كود:
#include <iostream>
#include <string>
using namespace std;

int main()
{
	string s1, s2;
	s1 = "";
	s2 = "Brownie";
	cout << s1.empty() << endl;
	cout << s2.empty() << endl;
	return 0;
}

الخرج :

كود:
1
0

نظرا لأن s1 تخزن سلسلة فارغة ، لذلك فإن الدالة s1.empty() ترجع 1 وحيث أن s2 تخزن السلسلة "Brownie" ، فإن s2.empty() ترجع 0 .

• الدالة size() :
تستخدم أيضًا لإرجاع طول السلسلة. وهى ترجع نفس القيمة التى ترجعها length() .


كود:
#include <iostream>
#include <string>
using namespace std;

int main()
{
	string name;
	name = "I have 4 chocolates";
	cout << name.size() << endl;
	return 0;
}

الخرج :

كود:
19

• الدالة resize() :
تقوم دالة تغيير الحجم resize() بتغيير حجم السلسلة إلى الطول المحدد.
افترض أن الطول المحدد هو 5. إذا كان الطول المحدد (على سبيل المثال 5) أصغر من الطول الحالي (افترض 7) ، فلن تحتوي السلسلة إلا على الأحرف الخمسة الأولى. إذا كان الطول المحدد (5) أكبر من الطول الحالي (افترض 3) ، فستحتوي السلسلة على 5 أحرف (3 من السلسلة الأصلية وحرفين محددين آخرين). دعونا نرى مثالا لفهم هذا.

كود:
 #include <iostream>
#include <string>
using namespace std;

int main()
{
	string s = "I love C";
	int size = s.size();             //  size of s
	s.resize( size+2, '+' );         //  resizing s
	cout << s << endl;
	return 0;
}


الخرج :

كود:
I love C++

في هذا المثال ، قمنا أولاً بتخزين حجم (8) وهى القيمة الأصلية للسلسلة في المتغير size.
العبارة :

كود:
s.resize( size+2, '+' );

تقوم بتغيير حجم السلسلة ‘s’ إلى 'size + 2' أي 10 وتعيين الحرف ‘+’ إلى مكاني الأحرف الزائدين في السلسلة التي تم تغيير حجمها.

• العامل operator[ ] :
يقوم بإرجاع الحرف في موضع معين في سلسلة. دعنا نرى مثالا.

كود:
#include <iostream>
#include <string>
using namespace std;

int main()
{
	string s = "I love C";
	cout << "Character at fifth position : " << s[5] << endl;
	return 0;
}



الخرج :

كود:
Character at fifth position : e

هنا ، تمثل s السلسلة بأكملها و تمثل s[i] حرفًا في السلسلة في الموضع ith. لاحظ أن موضع الحرف الأول في السلسلة هو 0.

• الدالة at() :
تقوم أيضًا بإرجاع الحرف في موضع معين في سلسلة.


كود:
#include <iostream>
#include <string>
using namespace std;

int main()
{
	string s = "I love C";
	cout << "Character at fifth position : " << s.at(5) << endl;
	return 0;
}

الخرج :

كود:
Character at fifth position : e


• الدالة compare() :
تقوم الدالة compare() بمقارنة قيمة السلسلة (str1) بسلسلة أخرى (str2).
تقوم بإرجاع 0 إذا كانت كلا من السلاسل متساوية.
تقوم بإرجاع قيمة موجبة إذا كانت str1> str2 أو الحرف الأول غير المتطابق من str1 أكبر من str2.
تقوم بإرجاع قيمة سالبة إذا كانت str1 <str2 أو الحرف الأول غير المتطابق من str1 أقل من str2.

كود:
#include <iostream>
#include <string>
using namespace std;

int main()
{
	string str1 ("have apple");
  	string str2 ("have app");
	cout << str1.compare(str2) << endl;
	return 0;
}

الخرج :

كود:
2

هنا ، طول السلسلة 'str1' هو 2 أكثر من طول السلسلة 'str2'. لذلك ، يكون الناتج هو 2. إذا تم تبادل قيم 'str1' و 'str2' ، فسيكون الناتج -2 .

لنرى مثال آخر :

كود:
 #include <iostream>
#include <string>
using namespace std;

int main()
{
	string str1 ("have gpple");
  	string str2 ("have apple");
	cout << str1.compare(str2) << endl;
	return 0;
}


الخرج :

كود:
 1

هذه المرة ، أول حرف لا مثيل له في 'str1' و 'str2' هما 'g' و 'a' على التوالي. نظرًا لأن قيمة ASCII الخاصة بـ "g" أكبر من القيمة "a" ، فقد حصلنا على ناتج موجب . عند تبادل قيم السلاسل ، سنحصل على إجابة سالبة .

• الدالة find() :
الدالة find() توجد موضع التواجد الأول للحرف أو السلسلة في السلسلة.


كود:
#include <iostream>
#include <string>
using namespace std;

int main()
{
	string str1 ("I love C++");
  	cout << str1.find('l') << endl;
	return 0;
}

الخرج :

كود:
2

في هذا المثال ، أعادت الدالة find() موضع الحرف ‘l’ في السلسلة str1. الآن ، دعونا نرى مثالًا لإيجاد موضع سلسلة في سلسلة أخرى.



كود:
#include <iostream>
#include <string>
using namespace std;

int main()
{
	string str1 ("I love C++");
	string str2 ("C++");
  	cout << str1.find(str2) << endl;
	return 0;
}

الخرج :

كود:
7

• الدالة substr() :
ترجع الدالة substr() سلسلة فرعية substring من سلسلة بتحديد موضعها.


كود:
#include <iostream>
#include <string>
using namespace std;

int main()
{
	string str1 ("I love C++");
	cout << str1.substr(3) << endl;
	return 0;
}

الخرج :

كود:
 ove C++

المثال أعلاه يطبع كل الحروف من الموضع 3 حتى نهاية السلسلة str1.


كود:
 #include <iostream>
#include <string>
using namespace std;

int main()
{
	string str1 ("I love C++");
	cout << str1.substr(3,5) << endl;
	return 0;
}

الخرج :

كود:
 ove C

هنا ، تم طباعة 5 أحرف تبدأ من الموضع 3.


الصورة الرمزية F.Abdelaziz
F.Abdelaziz
:: استاذ و مشرف قسم الالكترونيات ::
تاريخ التسجيل: May 2007
المشاركات: 6,832
نشاط [ F.Abdelaziz ]
قوة السمعة:
قديم 06-08-2019, 03:40 PM المشاركة 36   
افتراضي الدرس السابع المؤشرات Pointers


المؤشرات Pointers
المؤشرات هي غول البرمجة بلغة C++ (و C)، سنحاول في هذا الدرس إزالة الغموض عن المؤشرات وإظهار الاستخدامات العملية لها في برمجة C++ .

ما هي استخدامات المؤشرات ؟
فيما يلي بعض الاستخدامات الشائعة:
• الوصول Accessing إلى عناصر مصفوفة .
• تمرير Passing الوسائط إلى دالة عندما تحتاج الدالة إلى تعديل الوسيط الأصلى .
• تمرير Passing المصفوفات والسلاسل إلى الدوال .
• الحصول على Obtaining الذاكرة من النظام .
• إنشاء Creating هياكل البيانات مثل القوائم المرتبطة linked lists .

العناوين والمؤشرات Addresses and Pointers
الأفكار وراء المؤشرات ليست معقدة. إليك المفهوم الرئيسي الأول : لكل بايت byte في ذاكرة الكمبيوتر عنوان address . العناوين هي أعداد numbers ، تمامًا كما هي الحال بالنسبة للمنازل في الشارع . تبدأ الأرقام من 0 وتصعد ، 1 ، 2 ، 3 ، وهكذا. إذا كان لديك 1 ميغابايت من الذاكرة ، فإن أعلى عنوان هو 1,048,575 . (بالطبع لديك أكثر من ذلك بكثير.)

يشغل (يحتل) برنامجك ، عند تحميله في الذاكرة ، نطاقًا معينًا من هذه العناوين. هذا يعني أن كل متغير وكل دالة في البرنامج الخاص بك تبدأ في عنوان معين. يوضح الشكل كيف يبدو هذا.



الشكل يبين عناوين الذاكرة Memory addresses

• عامل العنوان The Address-of Operator &
يمكنك إيجاد العنوان الذي يشغله متغير باستخدام عامل العنوان & . إليك برنامج قصير ، VARADDR ، يوضح كيفية القيام بذلك :


كود:
// varaddr.cpp
// addresses of variables
#include <iostream>
using namespace std;

int main()
{
int var1 = 11; 		//define and initialize
int var2 = 22; 		//three variables
int var3 = 33;

cout << &var1 << endl 	//print the addresses
        << &var2 << endl 	//of these variables
        << &var3 << endl;
return 0;
}

يعرّف هذا البرنامج البسيط ثلاثة متغيرات من نوع العدد الصحيح int ، ويقوم بتهيئتها إلى القيم 11 و 22 و 33. ثم يقوم بطباعة عناوين هذه المتغيرات.

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

كود:
 0x8f4ffff4 ← address of var1
0x8f4ffff2 ← address of var2
0x8f4ffff0 ← address of var3

تذكر أن عنوان المتغير لا يشبه محتوياته على الإطلاق. محتويات المتغيرات الثلاثة هي 11 و 22 و 33. ويبين الشكل المتغيرات الثلاثة في الذاكرة.



الشكل يبين عناوين ومحتويات المتغيرات Addresses and contents of variables

يفسر interprets عامل الإدراج << العناوين في النظام السداسي عشري ، كما هو مبين بالبادئة 0x قبل كل عدد . هذه هي الطريقة المعتادة لإظهار عناوين الذاكرة. إذا لم تكن على دراية بنظام الأرقام السداسية عشرية ، فلا تقلق. كل ما تحتاج إلى معرفته هو أن كل متغير يبدأ في عنوان فريد. ومع ذلك ، قد تلاحظ في الإخراج أن كل عنوان يختلف عن العنوان التالي بمقدار 2 بايت بالضبط. ذلك لأن الأعداد الصحيحة تشغل 2 بايت من الذاكرة (على نظام 16 بت). إذا استخدمنا متغيرات نوع char ، فستكون لديها عناوين متجاورة ، حيث أن char يشغل بايت واحد ؛ وإذا استخدمنا النوع double ، فستختلف العناوين بمقدار 8 بايت.

تظهر العناوين بترتيب تنازلي لأنه يتم تخزين المتغيرات المحلية local على المكدس stack ، والذي ينمو نزولا في الذاكرة. إذا كنا قد استخدمنا المتغيرات العامة global ، فسيكون لها عناوين تصاعدية ، حيث يتم تخزين المتغيرات العامة على الكومة heap ، التي تنمو للأعلى. مرة أخرى ، لا داعي للقلق بشأن هذه الاعتبارات ، لأن المترجم يتتبع التفاصيل نيابة عنك .

لا تخلط بين عامل العنوان & ، الذي يسبق اسم المتغير في إعلان متغير ، مع عامل المرجع & ، الذي يتبع اسم النوع في النموذج الأولي للدالة أو التعريف. (تم مناقشة المراجع فى درس الدوال )

• متغيرات المؤشر Pointer Variables
العناوين في حد ذاتها محدودة إلى حد ما. من الجيد أن نعرف أنه يمكننا معرفة أين توجد الأشياء في الذاكرة ، كما فعلنا في VARADDR ، لكن طباعة قيم العنوان ليست كل الإفادة . تتطلب إمكانية زيادة قوة البرمجة لدينا فكرة إضافية : "المتغيرات التي تحتوي على قيم العنوان" . لقد رأينا أنواعًا متغيرة تخزن الأحرف والأعداد الصحيحة وأرقام الفاصلة العائمة وما إلى ذلك. يتم تخزين العناوين بالمثل. يُطلق على المتغير الذي يحمل قيمة العنوان اسم متغير المؤشر pointer variable ، أو ببساطة المؤشر pointer.

ما هو نوع بيانات متغيرات المؤشر؟ قد تعتقد أن نوع بيانات المؤشر سيطلق عليه شيء مثل pointer أو ptr. ومع ذلك ، فإن الأمور أكثر تعقيدا قليلا. يعرض البرنامج التالي ، PTRVAR ، بناء جملة متغيرات المؤشر.


كود:
 // ptrvar.cpp
// pointers (address variables)
#include <iostream>
using namespace std;

int main()
{
int var1 = 11; 			//two integer variables
int var2 = 22;

cout << &var1 << endl 		//print addresses of variables
        << &var2 << endl << endl;

int* ptr; 				//pointer to integers
ptr = &var1; 				//pointer points to var1

cout << ptr << endl; 			//print pointer value

ptr = &var2; 				//pointer points to var2
cout << ptr << endl; 			//print pointer value
return 0;
}

يعرف هذا البرنامج متغيرين لعددين صحيحين integer variables ، var1 و var2 ، ويقوم بتهيئتهما للقيمتين 11 و 22. ثم يقوم بطباعة عناوينهما.

بعد ذلك يعرّف البرنامج متغير مؤشر pointer variable في السطر :

كود:
int* ptr;

للمبتدئين هذا بناء جملة غريب إلى حد ما. علامة النجمة (*) تعني المؤشر إلى pointer to . وبالتالي فإن العبارة تعرف المتغير ptr كمؤشر إلى int. هذه طريقة أخرى للقول أن هذا المتغير يمكنه الاحتفاظ بعناوين متغيرات الأعداد الصحيحة.

ما الخطأ في فكرة نوع مؤشر للأغراض العامة يحمل مؤشرات إلى أي نوع بيانات؟ إذا أطلقنا عليه النوع pointer ، فيمكننا كتابة إعلانات مثل

كود:
pointer ptr;

المشكلة هي أن المترجم يحتاج إلى معرفة نوع المتغير الذي يشير إليه المؤشر. (سنرى لماذا عندما نتحدث عن المؤشرات والمصفوفات .) يسمح بناء الجملة المستخدم في C++ بالإعلان عن أي نوع من المؤشرات:

كود:
char* cptr; 		// pointer to char
int* iptr; 		// pointer to int
float* fptr; 		// pointer to float
Distance* distptr; 	// pointer to user-defined Distance class

وهكذا .

• جدال حول بناء الجملة Syntax
تجدر الإشارة إلى أنه من الشائع كتابة تعريفات المؤشر مع وجود علامة النجمة أقرب إلى اسم المتغير من النوع .

كود:
char *charptr;

هذا لا يهم المترجم ، لكن وضع علامة النجمة بجوار النوع يساعد في التأكيد على أن علامة النجمة جزء من نوع المتغير (مؤشر إلى حرف pointer to char ) ، وليست جزءًا من الاسم نفسه.

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

كود:
char* ptr1, * ptr2, * ptr3; 	// three variables of type char*

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

كود:
char *ptr1, *ptr2, *ptr3;	 // three variables of type char*

• يجب أن تحتوي المؤشرات على قيمة
يمكن اعتبار عنوان مثل 0x8f4ffff4 كثابت مؤشر pointer constant. يمكن اعتبار مؤشر مثل ptr كمتغير مؤشر pointer variable . مثلما يمكن تعيين متغير العدد الصحيح var1 القيمة الثابتة 11 ، كذلك يمكن تعيين متغير المؤشر ptr القيمة الثابتة 0x8f4ffff4 .

عند تعريف المتغير لأول مرة ، فإنه لا يحمل أي قيمة (ما لم نقم بتهيئته في نفس الوقت). قد يحتوي على قيمة نفايات ، ولكن هذا ليس له معنى. في حالة المؤشرات ، فإن قيمة بيانات النفايات هي عنوان شيء ما في الذاكرة ، ولكن ربما لا يتعلق بأي شيء نريده. لذلك قبل استخدام المؤشر ، يجب وضع عنوان محدد فيه. في برنامج PTRVAR ، يتم تعيين ptr أولاً بعنوان var1 في السطر

كود:
 ptr = &var1; ← put address of var1 in ptr

بعد ذلك ، يقوم البرنامج بطباعة القيمة الموجودة في ptr ، والتي يجب أن تكون هي نفس العنوان المطبوع &var1 . ثم يتم تعيين نفس المتغير المؤشر ptr بعنوان var2 ، ويتم طباعة هذه القيمة. يوضح الشكل تشغيل برنامج PTRVAR.



الشكل يبين تغيير القيم فى المؤشر ptr
إليك إخراج PTRVAR:

كود:
0x8f51fff4 ←address of var1
0x8f51fff2 ← address of var2
0x8f51fff4 ← ptr set to address of var1
0x8f51fff2 ← ptr set to address of var2

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

• الوصول إلى المتغير ألمشار إليه Accessing the Variable Pointed To
افترض أننا لا نعرف اسم المتغير ولكننا نعرف عنوانه. هل يمكننا الوصول إلى محتويات المتغير؟ (قد يبدو الأمر وكأنه سوء إدارة لفقدان أسماء المتغيرات ، لكننا سنرى قريبًا أن هناك العديد من المتغيرات التي لا نعرف أسماءها.)
يوجد بناء جملة خاص للوصول إلى قيمة المتغير باستخدام عنوانه بدلاً من اسمه.
إليك مثال على ذلك ، برنامج ، PTRACC ، يوضح كيفية تنفيذه:


كود:
// ptracc.cpp
// accessing the variable pointed to
#include <iostream>
using namespace std;

int main()
{
int var1 = 11;			 //two integer variables
int var2 = 22;

int* ptr; 			//pointer to integers

ptr = &var1; 			//pointer points to var1
cout << *ptr << endl; 	//print contents of pointer (11)

ptr = &var2; 			//pointer points to var2
cout << *ptr << endl; 	//print contents of pointer (22)
return 0;
}



يشبه هذا البرنامج برنامج PTRVAR ، باستثناء أنه بدلاً من طباعة قيم العنوان في ptr ، فإننا نقوم بطباعة قيمة العدد الصحيح المخزنة في العنوان المخزن في ptr. إليك الإخراج:


كود:
11
22

التعبير الذي يصل إلى المتغيرات var1 و var2 هو *ptr ، والذي يحدث في كل من جملتي cout.

عند استخدام علامة النجمة (*) أمام اسم متغير ، كما هو الحال في التعبير *ptr ، يطلق عليه عامل dereference (أو أحيانًا عامل indirection ). وهذا يعني "قيمة المتغير المشار إليه بواسطة" . وبالتالي فإن التعبير *ptr يمثل قيمة المتغير الذي يشير إليه ptr. عندما يتم تعيين ptr على عنوان var1 ، يكون التعبير *ptr له القيمة 11 ، لأن var1 هو 11. عندما يتم تغيير ptr إلى عنوان var2 ، يكتسب التعبير *ptr القيمة 22 ، لأن var2 هو 22. اسم آخر بالنسبة لعامل dereference هو عامل contents of ، وهي طريقة أخرى لقول نفس الشيء. يوضح الشكل كيف يبدو هذا.



الشكل يبين الوصول عبر المؤشر Access via pointer

يمكنك استخدام المؤشر ليس فقط لعرض قيمة المتغير ، ولكن أيضًا لتنفيذ أي عملية تقوم بها على المتغير مباشرةً. فيما يلي برنامج ، PTRTO ، يستخدم مؤشرًا لتعيين قيمة لمتغير ، ثم لتعيين هذه القيمة لمتغير آخر:


كود:
// ptrto.cpp
// other access using pointers
#include <iostream>
using namespace std;

int main()
{
int var1, var2; 		//two integer variables
int* ptr; 			//pointer to integers

ptr = &var1;			 //set pointer to address of var1
*ptr = 37; 			//same as var1=37
var2 = *ptr; 			//same as var2=var1

cout << var2 << endl; 	//verify var2 is 37
return 0;
}

تذكر أن علامة النجمة المستخدمة كعامل dereference لها معنى مختلف عن علامة النجمة المستخدمة في الإعلان عن متغيرات المؤشر. يسبق العامل dereference المتغير ويعني "قيمة المتغير المشار إليه بواسطة". علامة النجمة المستخدمة في الإعلان تعني "المؤشر إلى" .


كود:
 int* ptr; 		//declaration: pointer to int
*ptr = 37; 		//indirection: value of variable pointed to by ptr

يسمى استخدام عامل dereference للوصول إلى القيمة المخزنة في عنوان "عنونة غير مباشرة" indirect addressing ، أو في بعض الأحيان "إلغاء المؤشر" dereferencing.
• فيما يلي ملخص لما تعلمناه حتى الآن:

كود:
int v; 		//defines variable v of type int
int* p; 	//defines p as a pointer to int
p = &v; 	//assigns address of variable v to pointer p
v = 3;		 //assigns 3 to v
*p = 3; 	//also assigns 3 to v

توضح العبارتان الأخيرتان الفرق بين العنونة العادية أو المباشرة ، حيث نشير إلى متغير بالاسم ، والمؤشر أو العنونة غير مباشرة ، حيث نشير إلى المتغير نفسه باستخدام عنوانه .
في الأمثلة على البرامج التي أظهرناها حتى الآن في هذا الدرس ، لا توجد ميزة لاستخدام تعبير المؤشر للوصول إلى المتغيرات ، حيث يمكننا الوصول إليها مباشرة. تصبح قيمة المؤشرات واضحة عندما يتعذر عليك الوصول إلى متغير مباشرةً ، كما سنرى لاحقًا.

• مؤشر إلى الفراغ Pointer to void
قبل أن ننتقل لرؤية المؤشرات في العمل ، يجب أن نلاحظ خصوصية أنواع بيانات المؤشر.
عادةً ، يجب أن يكون العنوان address الذي تضعه في المؤشر هو نفس نوع المؤشر. لا يمكنك تعيين عنوان متغير float لمؤشر int ، على سبيل المثال:


كود:
float flovar = 98.6;
int* ptrint = &flovar; 	//ERROR: can’t assign float* to int*


ومع ذلك ، هناك استثناء لهذا. يوجد نوع مؤشر للأغراض العامة يمكن أن يشير إلى أي نوع بيانات. وهذا ما يسمى مؤشر للفراغ (خالى) pointer to void ، ويتم تعريفه مثل هذا:

كود:
void* ptr; //ptr can point to any data type


هذه المؤشرات لها استخدامات متخصصة معينة ، مثل تمرير المؤشرات إلى دوال والتى تعمل بشكل مستقل عن نوع البيانات المشار إليها.

المثال التالي يستخدم مؤشرًا إلى void ويوضح أيضًا أنه إذا كنت لا تستخدم void ، فيجب أن تكون حريصًا على تعيين المؤشرات والعنوان من نفس النوع مثل المؤشر. إليك القائمة PTRVOID:



كود:
// ptrvoid.cpp
// pointers to type void
#include <iostream>
using namespace std;

int main()
{
int intvar; 		//integer variable
float flovar; 		//float variable

int* ptrint; 		//define pointer to int
float* ptrflo; 		//define pointer to float
void* ptrvoid; 	//define pointer to void

ptrint = &intvar; 	//ok, int* to int*
// ptrint = &flovar; 	//error, float* to int*
// ptrflo = &intvar; 	//error, int* to float*
ptrflo = &flovar; //	ok, float* to float*

ptrvoid = &intvar; 	//ok, int* to void*
ptrvoid = &flovar; 	//ok, float* to void*
return 0;
}

يمكنك تعيين عنوان intvar إلى ptrint لأنهما كلاهما int* ، لكن لا يمكنك تعيين عنوان flovar إلى ptrint لأن الأول هو
نوع float* والثاني هو نوع int* . ومع ذلك ، يمكن إعطاء ptrvoid أي قيمة مؤشر ، مثل int* ، لأنها مؤشر إلى void .


الصورة الرمزية F.Abdelaziz
F.Abdelaziz
:: استاذ و مشرف قسم الالكترونيات ::
تاريخ التسجيل: May 2007
المشاركات: 6,832
نشاط [ F.Abdelaziz ]
قوة السمعة:
قديم 06-08-2019, 10:30 PM المشاركة 37   
افتراضي


المؤشرات والمصفوفات Pointers and Arrays
هناك ارتباط وثيق بين المؤشرات والمصفوفات. لقد رأينا كيفية الوصول إلى عناصر المصفوفة. البرنامج التالي ، ARRNOTE ، يوفر مراجعة.


كود:
 // arrnote.cpp
// array accessed with array notation
#include <iostream>
using namespace std;

int main()
{ 						//array
int intarray[5] = { 31, 54, 77, 52, 93 };

for(int j=0; j<5; j++) 			//for each element,
cout << intarray[j] << endl; 	//print value
return 0;
}

تقوم عبارة cout بطباعة كل عنصر في المصفوفة كل فى دوره. على سبيل المثال ، عندما تكون j هي 3 ، يأخذ التعبير intarray[j] قيمة intarray[3] ويصل إلى عنصر المصفوفة الرابع ، العدد الصحيح 52. إليك خرج ARRNOTE:

كود:
31
54
77
52
93

والمثير للدهشة أن عناصر المصفوفة يمكن الوصول إليها باستخدام تدوين (بدلالة) المؤشر وكذلك تدوين المصفوفة .
المثال التالي ، PTRNOTE ، يشبه ARRNOTE فيما عدا أنه يستخدم تدوين المؤشر.


كود:
// ptrnote.cpp
// array accessed with pointer notation
#include <iostream>
using namespace std;

int main()
{ 						//array
int intarray[5] = { 31, 54, 77, 52, 93 };

for(int j=0; j<5; j++) 			//for each element,
cout << *(intarray+j) << endl; 	//print value
return 0;
}


التعبير *(intarray+j) في PTRNOTE له نفس التأثير تمامًا مثل intarray[j] في ARRNOTE ، وخرج البرنامج مماثل. لكن كيف نفسر التعبير *(intarray+j) ؟ افترض أن j هي 3 ، وبالتالي فإن التعبير يكافئ *(intarray+3) . نريد أن يمثل هذا محتويات العنصر الرابع للمصفوفة (52). تذكر أن اسم المصفوفة هو عنوانها. التعبير intarray + j هو بالتالي عنوان مع إضافة شيء إليه. قد تتوقع أن intarray + 3 يؤدي إلى إضافة 3 بايت إلى intarray. لكن هذا لا ينتج النتيجة التي نريدها : intarray هي مصفوفة من الأعداد الصحيحة ، و 3 بايت في هذه المصفوفة هي منتصف العنصر الثاني ، وهي ليست مفيدة للغاية. نريد الحصول على العدد الصحيح الرابع في المصفوفة ، وليس البايت الرابع ، كما هو موضح في الشكل. (هذا الشكل يفترض أعداد صحيحة 2 بايت.)



الشكل يبين العد بالأعداد الصحيحة Counting by integers

برنامج مترجم C++ ذكي بما يكفي لأخذ حجم البيانات في الاعتبار عندما ينفذ الحساب في عناوين البيانات. إنه يعرف أن intarray عبارة عن مصفوفة من النوع int لأنه تم إعلانها بهذه الطريقة. لذلك عندما يرى التعبير intarray + 3 ، فإنه يفسره كعنوان للعدد الصحيح الرابع في intarray ، وليس البايت الرابع.
لكننا نريد قيمة عنصر المصفوفة الرابع هذا ، وليس العنوان. لأخذ القيمة ، نستخدم العامل dereference (*). التعبير الناتج ، عندما تكون j هى 3 ، هو *(intarray+3) ، وهو محتوى عنصر المصفوفة الرابع ، أو 52.
الآن رأينا لماذا يجب أن يتضمن إعلان المؤشر نوع المتغير المشار إليه. يحتاج المترجم إلى معرفة ما إذا كان المؤشر هو مؤشر إلى int أو مؤشر إلى double بحيث يمكن إجراء الحساب الصحيح للوصول إلى عناصر المصفوفة فهو يضاعف قيمة الفهرس بمقدار 2 في حالة النوع int ، ولكن يضاعفه في 8 في حالة double.


ثوابت المؤشر ومتغيرات المؤشر Pointer Constants and Pointer Variables
افترض أنه ، بدلاً من إضافة j إلى intarray للدخول إلى عناوين المصفوفة ، أردت استخدام عامل الزيادة. هل يمكن أن تكتب *(intarray++) ؟
الجواب هو لا ، والسبب هو أنه لا يمكنك زيادة ثابت (أو تغييره بالفعل بأي طريقة). التعبير intarray هو العنوان الذي اختاره النظام لوضع المصفوفة الخاص بك ، وستظل على هذا العنوان حتى ينتهي البرنامج . intarray هو مؤشر ثابت.
لا يمكنك قول intarray++ . (في نظام تعدد المهام ، قد تتغير العناوين المتغيرة أثناء تنفيذ البرنامج. قد يتم تبديل البرنامج النشط إلى القرص ثم إعادة تحميله في موقع ذاكرة مختلف. ومع ذلك ، فإن هذه العملية غير مرئية للبرنامج.)
ولكن بينما لا يمكنك زيادة عنوان ، يمكنك زيادة مؤشر يحمل عنوانًا. المثال التالي ، PTRINC ، يوضح كيف:


كود:
 // ptrinc.cpp
// array accessed with pointer
#include <iostream>
using namespace std;

int main()
{
int intarray[] = { 31, 54, 77, 52, 93 }; 	//array
int* ptrint; 					//pointer to int
ptrint = intarray; 				//points to intarray

for(int j=0; j<5; j++) 			//for each element,
cout << *(ptrint++) << endl; 	//print value
return 0;
}


هنا نحدد مؤشرًا إلى int هو ptrint ونمنحه القيمة intarray ، عنوان المصفوفة . الآن يمكننا الوصول إلى محتويات عناصر المصفوفة بالتعبير

كود:
 *(ptrint++)

يبدأ المتغير ptrint بنفس قيمة العنوان مثل intarray ، مما يتيح الوصول إلى عنصر المصفوفة الأول ، intarray[0] ، الذي يحتوي على القيمة 31 ، كما كان من قبل. ولكن نظرًا لأن ptrint متغير وليس ثابتًا ، فيمكن زيادته. بعد زيادته ، يشير إلى عنصر المصفوفة الثاني ، intarray[1] . يمثل التعبير *(ptrint++) محتويات عنصر المصفوفة الثانى أو 54. تؤدي الحلقة إلى وصول التعبير إلى كل عنصر من عناصر المصفوفة كل بدوره. خرج PTRINC هو نفسه خرج PTRNOTE.


الصورة الرمزية F.Abdelaziz
F.Abdelaziz
:: استاذ و مشرف قسم الالكترونيات ::
تاريخ التسجيل: May 2007
المشاركات: 6,832
نشاط [ F.Abdelaziz ]
قوة السمعة:
قديم 06-08-2019, 10:51 PM المشاركة 38   
افتراضي


المؤشرات والدوال Pointers and Functions
في الدوال ، هناك ثلاث طرق لتمرير الوسائط إلى دالة : بواسطة القيمة ، وبواسطة المرجع ، وبواسطة المؤشر. إذا كانت الدالة تهدف إلى تعديل المتغيرات في برنامج الاستدعاء ، لا يمكن تمرير هذه المتغيرات بالقيمة ، لأن الدالة لا تحصل إلا على نسخة من المتغير. ومع ذلك ، يمكن استخدام وسيط مرجعى أو مؤشر في هذا الموقف.

تمرير متغيرات بسيطة Passing Simple Variables
سنراجع أولاً الطريقة التي يتم بها تمرير الوسائط حسب المرجع ، ثم نقوم بمقارنتها بتمرير وسائط المؤشر . يظهر برنامج PASSREF التمرير بالمرجع .


كود:
// passref.cpp
// arguments passed by reference
#include <iostream>
using namespace std;

int main()
{
void centimize(double&); 			//prototype

double var = 10.0; 				//var has value of 10 inches

cout << “var = “ << var << “ inches” << endl;

centimize(var);				 //change var to centimeters
cout << “var = “ << var << “ centimeters” << endl;
return 0;
}
//--------------------------------------------------------------
void centimize(double& v)
{
v *= 2.54; //v is the same as var
}


هنا نريد تحويل متغير var في main() من بوصة إلى سم. نقوم بتمرير المتغير بواسطة المرجع إلى الدالة centimize() . (تذكر أن العامل & الذى يتبع نوع البيانات double في النموذج الأولي لهذه الدالة يشير إلى أن الوسيط يتم تمريره بالمرجع .) الدالة centimize() تضرب المتغير الأصلي فى 2.54. لاحظ كيف تشير الدالة إلى المتغير. فهى تستخدم ببساطة اسم الوسيط v ؛ v و var هي أسماء مختلفة لنفس الشيء.
بمجرد أن يتم تحويله var إلى سنتيمترات ، يعرض main() النتيجة. إليك إخراج PASSREF:


كود:
 var = 10 inches
var = 25.4 centimeters


يوضح المثال التالي PASSPTR موقفًا مكافئًا عند استخدام المؤشرات:


كود:
// passptr.cpp
// arguments passed by pointer
#include <iostream>
using namespace std;

int main()
{
void centimize(double*); 		//prototype

double var = 10.0; 			//var has value of 10 inches
cout << “var = “ << var << “ inches” << endl;

centimize(&var); 			//change var to centimeters
cout << “var = “ << var << “ centimeters” << endl;
return 0;
}
//--------------------------------------------------------------
void centimize(double* ptrd)
{
*ptrd *= 2.54; //*ptrd is the same as var
}


خرج PASSPTR هو نفسه خرج PASSREF.


يتم إعلان الدالة centimize() على أنها تأخذ وسيط يمثل مؤشرًا إلى double :

كود:
 void centimize(double*) // argument is pointer to double

عندما تقوم main() باستدعاء الدالة ، فإنها تزود عنوان المتغير كوسيط :

كود:
centimize(&var);

تذكر أن هذا ليس هو المتغير نفسه ، حيث إنه يمرر بالمرجع ، ولكن عنوان المتغير.

نظرًا لأن الدالة centimize() تمرر عنوان ، فيجب أن تستخدم عامل dereference ، * ptrd ، للوصول إلى القيمة المخزنة في هذا العنوان:

كود:
*ptrd *= 2.54; // multiply the contents of ptrd by 2.54

بالطبع هذا هو نفسه

كود:
*ptrd = *ptrd * 2.54; // multiply the contents of ptrd by 2.54


حيث تعني علامة النجمة المستقلة عملية الضرب.

نظرًا لأن ptrd يحتوي على عنوان var ، فإن أي شيء يتم إجراؤه على *ptrd يتم فعلًا على var.
يوضح الشكل كيف أن تغيير *ptrd في الدالة يغير var في برنامج الاستدعاء .




الشكل يبين تمرير مؤشر إلى دالة Pointer passed to function


إن تمرير المؤشر كوسيط للدالة يشبه إلى حد ما تمرير المرجع. كلاهما يسمح بتعديل المتغير في برنامج الاستدعاء بواسطة الدالة. ومع ذلك ، فإن الآلية مختلفة. المرجع هو اسم مستعار للمتغير الأصلي ، بينما المؤشر هو عنوان المتغير.



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