آرایه ها

آردوینو – آرایه ها

آرایه ، یک گروه متوالی از مکان های حافظه است که از یک نوع هستند. برای مراجعه به یک مکان یا عنصر خاص در آرایه ، نام آرایه و شماره موقعیت عنصر خاص را در آرایه مشخص می کنیم.

تصویری که در زیر آورده شده است ، یک آرایه صحیح به نام C را نشان می دهد که شامل 11 عنصر است. شما به هر یک از این عناصر با دادن نام آرایه و به دنبال آن شماره موقعیت عنصر خاص در براکت های مربع ([]) مراجعه می کنید. شماره موقعیت به صورت رسمی بیشتر به عنوان زیروَند یا شماره عنصر (فهرست) نامیده می شود (این عدد تعداد عناصر را از ابتدای آرایه مشخص می کند). عنصر اول دارای زیروند 0 (صفر) است و گاهی اوقات عنصر صفر نامیده می شود.

بنابراین ، آرایه ی C داری عناصر  [C[0 (تلفظ “C زیروند صفر”) ، [C[1] ، C[2 و غیره است. بالاترین زیروند 10 در آرایه ی C است این نشان می دهد که شماره عنصر از تعداد عناصر موجود در آرایه C یک مورد(شماره) کمتر است. قوانین نام آرایه ها مانند قوانین نام سایر متغیر ها  است.

 

elements_of_array

زیروند آرایه باید یک عدد صحیح یا یک عبارت صحیح باشد. اگر یک برنامه از یک عبارت به عنوان زیروند آرایه استفاده کند ، در آن برنامه این عبارت را برای تعیین زیروند تست می کند. به عنوان مثال ، اگر فرض کنیم متغیر a برابر است با 5 و متغیر b برابر 6 باشد ، آن گاه عبارت ، 2 را به عنصر آرایه ی [11]C اضافه می کند.

نام زیروند آرایه ، یک مقدار متغیر است ، می تواند در سمت چپ یک واگذاری (مساوی =) نیز استفاده شود (مثلا a=2) ، همان طور که نام های متغیر غیر آرایه ای می توانند. یعنی نام زیروند آرایه می تواند یک متغیر عددی باشد و در قسمت های مختلف برنامه ، عدد متغیر تغییر کند.

اجازه دهید آرایه ی C ای را که در شکل نشان داده شده است با دقت بیشتری بررسی کنیم. نام کل آرایه ، C است. 11 عنصر آن به [C[0 تا [C[10 گفته می شود. مقدار C[0] ، -45 است. مقدار C[1] ، 6 است. مقدار C[2] ، 0 است. مقدار C [7] ، 62 است و مقدار 78 ، [C[10 است.

برای نمایش مجموع مقادیر موجود در سه عنصر اول آرایه ی C ، می نویسیم :

Serial.print (C[ 0 ] + C[ 1 ] + C[ 2 ] );

برای مثال :  تقسیم مقدار [C[6 به 2 و نتیجه را به متغیر x اختصاص می دهیم :

x = C[ 6 ] / 2;

تعریف کردن آرایه ها :

آرایه ها ، فضای حافظه را اشغال می کنند. برای مشخص کردن نوع عناصر و تعداد عناصر مورد نیاز یک آرایه ، از فرم دستوری زیر استفاده کنید :

type arrayName [ arraySize ] ;

کامپایلر مقدار مناسب حافظه را رزرو می کند. دستور arraySize باید یک عدد صحیح بزرگتر از صفر باشد. به عنوان مثال ، برای این که به کامپایلر بفهمانیم تا  11 عدد صحیح را به عنوان عناصرِ آرایه ی C ذخیره کند ، از تعریف زیر استفاده می کنیم :

int C[ 12 ]; // C is an array of 12 integers

آرایه ها می توانند توسط مقادیرِ هر نوع داده ی غیر مرجعی تعریف شوند. به عنوان مثال ، یک آرایه از نوع رشته ای می تواند برای ذخیره رشته های کاراکتری استفاده شود.

مثال هایی با استفاده از آرایه ها :

در این بخش مثال های زیادی وجود دارد که نشان می دهد چگونه می توان تعریف ، مقدار اولیه و تغییر آرایه ها را بیان کرد.

مثال 1: تعریف کردن یک آرایه و استفاده از یک حلقه برای شروع عناصر آرایه :

این برنامه یک آرایه ی عدد صحیح 10 عنصری به نام n را تعریف می کند. خط a-b از دستور for استفاده می کند تا عناصر آرایه را به صفر برساند. مانند سایر متغیر های خودکار ، آرایه های خودکار به طور ضمنی به مقدار اولیه صفر نمی رسند. اولین عبارت خروجی (خط c) عناوین ستون را نمایش می دهد و خطوط d-e ستون ها را نمایش می دهند. به این ترتیب آرایه با فرمت جدولی نمایش داده می شود.

مثال :

int n[ 10 ] ; // n is an array of 10 integers

void setup () {

}

void loop () {
   for ( int i = 0; i < 10; ++i ) // initialize elements of array n to 0 {
      n[ i ] = 0; // set element at location i to 0
      Serial.print (i) ;
      Serial.print (‘\r’) ;
   }
   for ( int j = 0; j < 10; ++j ) // output each array element's value {
      Serial.print (n[j]) ;
      Serial.print (‘\r’) ;
   } 
}

نتیجه :  با این دستورات ، مقادیر زیر را حاصل می شوند :

 

Element Value

0

1

2

3

4

5

6

7

8

9

0

0

0

0

0

0

0

0

0

0


 

مثال 2 : شروع یک آرایه با تعریف آن و یک لیست اولیه :

عناصر یک آرایه نیز می توانند به وسیله ی نام آرایه با یک علامت مساوی و یک لیست از مقادیر اولیه که با کاما از یکدیگر جدا شده اند ، مقداردهی اولیه شوند. این برنامه با استفاده از یک لیست با مقادیر اولیه برای تعریف و مقداردهی اولیه یک آرایه عدد صحیح با 10 عنصر (خط a) نوشته شده است و این برنامه ، نمایش آرایه را با فرمت جدولی (خطوط b-c) نشان می دهد.

مثال :

// n is an array of 10 integers
int n[ 10 ] = { 32, 27, 64, 18, 95, 14, 90, 70, 60, 37 } ; // a خط

void setup () {

}

void loop () {
   for ( int i = 0; i < 10; ++i )  {  // b خط
      Serial.print (i) ;
      Serial.print (‘\r’) ;
   }
   for ( int j = 0; j < 10; ++j ) // output each array element's value { // c خط
      Serial.print (n[j]) ;
      Serial.print (‘\r’) ;
   } 
}

نتیجه : حاصل برنامه ی بالا نتیجه ی زیر است :

 

Element Value

0

1

2

3

4

5

6

7

8

9

32

27

64

18

95

14

90

70

60

37


 

مثال 3 : جمع بندی عناصر یک آرایه :

اغلب ، عناصر آرایه یک سری مقادیر را نشان می دهند که باید در یک عملیات حساب مثل جمع و تفریق و… استفاده شوند. به عنوان مثال ، اگر عناصر یک آرایه نمرات امتحان را تشکیل دهند ، یک استاد ممکن است بخواهد عناصر آرایه را جمع کند و از آن برای محاسبه میانگین کلاس استفاده کند. این برنامه مقادیر موجود در آرایه ی عددِ صحیحِ 10 عنصری را جمع می کند.

مثال :

const int arraySize = 10; // constant variable indicating size of array
int a[ arraySize ] = { 87, 68, 94, 100, 83, 78, 85, 91, 76, 87 };
int total = 0;

void setup () {

}
void loop () {
   // sum contents of array a
   for ( int i = 0; i < arraySize; ++i )
      total += a[ i ];
   Serial.print (“Total of array elements : ”) ;
   Serial.print(total) ;
}

نتیجه :

Total of array elements: 849

 

آرایه ها برای آردوینو مهم هستند و باید مورد توجه بیشتری قرار بگیرند. مفاهیم مهم زیر در رابطه با آرایه باید مشخص باشد :

  1. انتقال از آرایه ها به توابع
  2. آرایه های چند بعدی

1- انتقال از آرایه ها به توابع :

برای انتقال آرگومان آرایه به یک تابع ، نام آرایه را بدون هیچ گونه براکت مشخص کنید. به عنوان مثال ، اگر آرایه ی  hourlyTemperatures به عنوان تابع تعریف شده باشد ، فراخوانی آرایه ی  hourlyTemperatures و اندازه آن برای تابع modifyArray منتقل می شود.

نکات مهم :

در اینجا لیستی از نکات مهمی که باید هنگام انتقال آرایه ها به توابع بدانید ، ارائه شده است :

  • هنگام انتقال یک آرایه به یک تابع ، معمولا اندازه آرایه نیز منتقل می شود ، بنابراین تابع می تواند تعداد مشخصی از عناصر موجود در آرایه را پردازش کند. در غیر این صورت ، ما نیاز داریم که این دانش را در خود تابع بنامیم یا بدتر از آن ، اندازه آرایه را در یک متغیر جهانی قرار دهیم.
  • ++C آرایه ها را با استفاده از مرجع به توابع منتقل می کند ، یعنی توابع فراخوان داده شده می توانند مقادیر عنصر را در آرایه های اصلی فراخوان دهندگان تغییر دهند.
  • مقدار نام آرایه ، آدرسی در حافظه ی کامپیوترِ اولین عنصر آرایه است. زمانی که آدرس شروع آرایه منتقل شد ، تابع فراخوان داده شده دقیقاً می داند که آرایه در کدام حافظه ذخیره می شود. بنابراین ، وقتی تابع فراخوان داده شده عناصر آرایه را در بدنه ی تابع خود تغییر می دهد در واقع دارد عناصر واقعی آرایه را در مکان های حافظه اصلی خود را تغییر می دهد.
  • اگرچه کل آرایه ها به وسیله مرجع منتقل می شوند ، عناصر آرایه ی فردی دقیقاً به همان اندازه ی متغیرهای ساده منتقل می شوند.
  • برای انتقال عنصر یک آرایه به یک تابع ، از نام زیروند عنصر آرایه به عنوان آرگومان در فراخوانی تابع استفاده کنید.
  • برای دریافت یک تابع از طریق یک فراخوانی ، لیست پارامتر های تابع باید مشخص کند که تابع انتظار دارد یک آرایه را دریافت کند.
  • به عنوان مثال ، هدر تابع برای تابع modifyArray ممکن است به صورت زیر نوشته شود :
void modifyArray( int b[], int arraySize )
  • این عبارت نشان می دهد که modifyArray انتظار دارد ، آدرس یک آرایه اعداد صحیح را در پارامتر b و تعداد عناصر آرایه در پارامتر arraySize را دریافت کند. اندازه آرایه در براکت های آرایه لازم نیست بیاید و اگر بیاید ، کامپایلر آن را نادیده می گیرد. بنابراین ، آرایه ها با هر اندازه ای می توانند به تابع منتقل شوند.
  • ++C با استفاده از مرجع ، آرایه ها را به توابع منتقل می کند. هنگامی که تابع فراخوان داده شده از نام آرایه b استفاده می کند ، به آرایه واقعی موجود در فراخوان دهنده اشاره می کند (یعنی arrayhourlyTemperatures که در ابتدای این بخش مورد بحث قرار گرفته است).

به ظاهر عجیب نمونه اولیه تابع modifyArray توجه کنید.

void modifyArray( int [] , int ) ;

این نمونه اولیه می توانست به روش زیر  ارسال شود.

void modifyArray( int anyArrayName[], int anyVariableName ) ;

با این حال ، کامپایلر های ++C نام های متغیر را در نمونه های اولیه نادیده می گیرند. به یاد داشته باشید ، نمونه اولیه تعداد آرگومان ها و نوع هر آرگومان را به ترتیب نمایش آرگومان به کامپایلر می گوید.

این برنامه در مثال بعدی تفاوت بین عبور از کل آرایه و عبور از یک عنصر آرایه را نشان می دهد.

مثال :

void modifyArray( int [], int ); // appears strange; array and size
void modifyElement( int ); // receive array element value

void setup () {
   Serial.begin (9600);
   const int arraySize = 5; // size of array a
   int a[ arraySize ] = { 0, 1, 2, 3, 4 }; // initialize array a
   Serial.print ( "Effects of passing entire array by reference:" ) ;
   // output original array elements
   for ( int i = 0; i < arraySize ; ++i )
   Serial.print ( a[ i ] ) ;
   Serial.print ("\r" ) ;
   Serial.print ("The values of the modified array are:\n" );
   // output modified array elements
   for ( int j = 0; j < arraySize; ++j )
   Serial.print ( a[j ] ) ;
   Serial.print ("\r" ) ;
   Serial.print ("\r\rEffects of passing array element by value:" );
   Serial.print ( "\ra[3] before modifyElement: " );
   Serial.print ( a[ 3 ] );
   Serial.print ( "\ra[3] after modifyElement: " );
   Serial.print ( a[ 3 ] );
}

void loop () {

}

// in function modifyArray, "b" points to the original array "a" in memory

void modifyArray( int b[], int sizeOfArray ) {
   // multiply each array element by 2
   for ( int k = 0 ; k < sizeOfArray ; ++k )
   b[ k ] *= 2;
} 

// end function modifyArray
// in function modifyElement, "e" is a local copy of
// array element a[ 3 ] passed from main

void modifyElement( int e ) {
   // multiply parameter by 2
   Serial.print ( "Value of element in modifyElement: " );
   Serial.print ( ( e *= 2 ) );
} 

// end function modifyElement

نتیجه :

Effects of passing entire array by reference:01234
The values of the modified array are:01234

Effects of passing array element by value:
a[3] before modifyElement: 3
a[3] after modifyElement: 3
$ is not a hexadecimal digit
f is a hexadecimal digit

2- آرایه های چند بعدی :

آرایه های با دو بُعد (یعنی زیروند ها) اغلب جدول هایی از مقادیر را تشکیل می دهند که شامل اطلاعاتی هستند که در ردیف ها و ستون ها مرتب شده اند.

در زیر ویژگی های کلیدی آرایه های چند بعدی آمده است :

  • برای شناسایی یک عنصر جدول خاص ، باید دو زیروند را مشخص کنیم.
  • طبق قرارداد ، اولین زیروند ، ردیف عناصر و دومین زیروند ، ستون عناصر را مشخص می کند.
  • آرایه هایی که برای شناسایی یک عنصر خاص به دو زیروند نیاز دارند ، آرایه های دو بعدی یا آرایه های 2D نامیده می شوند.
  • آرایه ها با دو یا چند بُعد به عنوان آرایه های چند بُعدی شناخته شده اند و می توانند بیش از دو بعد داشته باشند.

شکل زیر یک آرایه دو بعدی ، a را نشان می دهد. آرایه شامل سه ردیف و چهار ستون است ، بنابراین یک آرایه 3 بر 4 است. به طور کلی ، آرایه ای با ردیف های M و ستون های n ، یک آرایه m-by-n نامیده می شود.

multidimensional_array

هر عنصر در آرایه a با یک عنصر از فرم [a[i][j مشخص می شود. در اینجا ، a آرایه نام دارد ، و i و j زیروند هایی هستند که هر عنصر را به طور جداگانه در a مشخص می کنند. توجه کنید که نام عناصر در ردیف 0 ، تماماً دارای اولین زیروند از 0 هستند. نام عناصر موجود در ستون 3 ، تماماً دارای دومین زیروندِ 3 هستند.

یک آرایه چند بعدی می تواند دقیقاً مثل یک آرایه یک بعدی مقداردهی اولیه و تعریف شود. به عنوان مثال ، یک آرایه دو بعدی b با مقادیر 1 و 2 در ردیف 0 عناصر و مقادیر 3 و 4 در ردیف 1 عناصر می تواند به شرح زیر تعریف و مقداردهی اولیه شود –

int b[ 2 ][ 2 ] = { { 1, 2 }, { 3, 4 } };

مقادیر با ردیف در پرانتز گروه بندی می شوند. بنابراین ، 1 و 2 به ترتیب [b[0][0 و [b[0][1 را مقداردهی می کنند و 3 و 4 به ترتیب [b[1][0 و [b[1][1 را مقداردهی می کنند. اگر برای یک سطر معین تمام مقادیر اولیه وجود نداشته باشند ، عناصر باقیمانده آن ردیف در 0 مقداردهی می شوند. بنابراین ، خط زیر [b[0][0 تا 1 ، [b[0][1 تا 0 ، [b[1][0 تا 3 و [b[1][1 تا 4 را مقداردهی اولیه می کند.

int b[ 2 ][ 2 ] = { { 1 }, { 3, 4 } };

مثال :

در اینجا مثالی از مقداردهی آرایه های دو بعدی در تعاریف می زنیم.

  • خطوط a-c سه آرایه را اعلام می کنند ، هر یک با دو ردیف و سه ستون.
  • عبارت array1 (خط a) شش مقداردهی اولیه را در دو لیست فرعی ارائه می دهد. اولین لیست فرعی ، سطر 0 آرایه را به مقادیر 1 ، 2 و 3 مقداردهی می کند. دومین لیست فرعی ، سطر 1 آرایه را به مقادیر 4 ، 5 و 6 مقداردهی می کند.
  • اگر پرانتز های اطراف هر لیست فرعی از لیست مقداردهی اولیه array1 حذف شود ، کامپایلر عناصر ردیف 0 را مقداردهی می کند و سپس عناصر ردیف 1 را مقداردهی می کند و نتیجه مشابهی را کسب می نماید.
  • عبارت array2 (خط b) تنها پنج مقدار اولیه را ارائه می دهد.
  • مقادیر اولیه به ردیف 0 و سپس ردیف 1 اختصاص داده می شوند. هر عنصری که دارای مقدار اولیه مشخصی نباشد ، به صفر تبدیل می شود ، بنابراین [array2[1][2 به صفر مقداردهی اولیه می شود.
  • عبارت array3 (خط c) سه مقدار اولیه را در دو لیست فرعی ارائه می دهد.
  • لیست فرعی ردیف 0 برای دو عنصر اول ردیف 0 مقادیر 1 و 2 را مقداردهی اولیه می کند. سومین عنصر به طور ضمنی مقدار صفر را به خود می گیرد.
  • لیست فرعی برای ردیف 1 ، عنصر اول را به 4 مقداردهی می کند و به طور ضمنی دو عنصر آخر را به صفر مقدار می دهد.
  • این برنامه تابع printArray را برای خروجی عناصر هر آرایه فراخوانی می کند. توجه کنید که نمونه اولیه تابع (خط k) پارامتر [ستون ها][]const int a   را مشخص می کند.
  • وقتی یک تابع ، آرایه ای یک بعدی را به عنوان آرگومان دریافت می کند ، براکت های آرایه در لیست پارامتر های تابع خالی هستند.
  • اندازه اولین بُعد یک آرایه دو بعدی (یعنی تعداد ردیف ها) لازم نیست ، اما تمام اندازه های بَعدی لازم هستند. کامپایلر از این اندازه ها برای تعیین مکان در حافظه عناصرِ آرایه های چند بعدی استفاده می کند.
  • تمام عناصر آرایه بدون در نظر گرفتن تعداد ابعاد ، به طور متوالی در حافظه ذخیره می شوند. در یک آرایه دو بعدی ، ابتدا ردیف 0 در حافظه و به دنبال آن ردیف 1 ذخیره می شود.

مثال :

void printArray ( const int [][ 3 ] ); // prototype
const int rows = 2;
const int columns = 3;
int array1[ rows ][ columns ] = { { 1, 2, 3 }, { 4, 5, 6 } };
int array2[ rows ][ columns ] = { 1, 2, 3, 4, 5 };
int array3[ rows ][ columns ] = { { 1, 2 }, { 4 } };

void setup () {

}
void loop () {
   Serial.print ("Values in array1 by row are: ") ;
   Serial.print (“\r” ) ;
   printArray(array1) ;
   Serial.print ("Values in array2 by row are: ") ;
   Serial.print (“\r” ) ;
   printArray(array2) ;
   Serial.print ("Values in array3 by row are: ") ;
   Serial.print (“\r” ) ;
   printArray(array3) ;
}

// output array with two rows and three columns

void printArray( const int a[][ columns ] ) {
   // loop through array's rows
   for ( int i = 0; i < rows; ++i ) {
      // loop through columns of current row
      for ( int j = 0; j < columns; ++j )
      Serial.print (a[ i ][ j ] );
      Serial.print (“\r” ) ; // start new line of output
   } 
// end outer for
} 

// end function printArray

نتیجه :

Values in array1 by row are:
1 2 3
4 5 6
Values in array2 by row are:
1 2 3
4 5 0
Values in array3 by row are:
1 2 0
4 0 0

توجه : هر ردیف ، یک آرایه یک بعدی است. برای قرار دادن یک عنصر در یک ردیف خاص ، تابع باید دقیقاً بداند که تعداد عناصر در هر سطر چقدر است تا بتواند هنگام دستیابی به آرایه ، تعداد مناسبی از مکان های حافظه را پرش کند. بنابراین ، هنگام دستیابی به [2][1]a ، تابع می تواند سه عنصر در حافظه را پرش کند تا به ردیف 1 برسد. سپس ، این تابع به عنصر 2 آن ردیف دسترسی پیدا می کند. در بسیاری از تغییرات رایج آرایه از for استفاده می کنند.

به عنوان مثال ، عبارت for برای همه عناصر در ردیف 2 آرایه a ، مقدار صفر را قرار می دهد.

for ( int column = 0; column < 4; ++column )
   a[ 2 ][ column ] = 0;

عبارت for فقط با دومین عبارت متفاوت است (یعنی زیر مجموعه ستون). نتیجه for مثال قبلی برابر است با عبارت های انتسابی زیر :

a[ 2 ][ 0 ] = 0;
a[ 2 ][ 1 ] = 0;
a[ 2 ][ 2 ] = 0;
a[ 2 ][ 3 ] = 0;

جمله زیر Nested for (فور تو در تو) ، تعداد کل عناصر موجود در آرایه a را تعیین می کند :

total = 0;
for ( int row = 0; row < 3; ++row )
for ( int column = 0; column < 4; ++column )
total += a[ row ][ column ];

دستور for جمع عناصر آرایه یک ردیف در هر زمان را به دست می آورد. عبارت بیرونی for با تنظیم ردیف ها (به عنوان مثال ، زیروند ردیف) از 0 شروع می شود. بنابراین ، مقادیر عناصر ردیف 0 ممکن است توسط عبارت داخلی for جمع شوند.

عبارت بیرونی for به ردیف 1 می رود (با افزایش زیروند ردیف) ، به طوری که می تواند عناصر ردیف 1 را آماده ی جمع زدن کند. سپس ، عبارت بیرونی for به ردیف 2 می رسد ، به طوری که می تواند عناصر ردیف 2 را جمع زند. هنگامی که دستور for تو در تو خاتمه یابد ، تعداد کل عناصر آرایه را شامل می شود. بنابراین ما مجموع تمام عناصر را خواهیم داشت.

 

منبع : https://b2n.ir/414212

مطالعه بیشتر