|
| 1 | + |
| 2 | +<div dir="rtl"> |
| 3 | + |
| 4 | +# آموزش Alignment و Padding در C: از پایه تا پیشرفته با سناریوهای کاربردی |
| 5 | + |
| 6 | +در زبان C، وقتی از `struct` استفاده میکنیم، موضوعاتی مثل Alignment (تراز کردن) و Padding (پُر کردن) اهمیت زیادی پیدا میکند. این مفاهیم تعیین میکنند که دادهها چطور در حافظه ذخیره میشوند و چطور به آنها دسترسی پیدا میکنیم. در این مقاله، با استفاده از سناریوهای کاربردی، مفاهیم Alignment و Padding را به شکل کامل بررسی میکنیم. همچنین با استفاده از شکلهایی که با کاراکترهای متنی ساخته میشوند، سعی میکنیم این مفاهیم را به سادهترین شکل ممکن توضیح دهیم. |
| 7 | + |
| 8 | +## 1. مفهوم Alignment |
| 9 | + |
| 10 | +در زبان C، دادهها باید به شکلی در حافظه ذخیره شوند که دسترسی به آنها سریع و بهینه باشد. Alignment به این معناست که هر داده باید در آدرس حافظهای ذخیره شود که با اندازه خودش هماهنگ باشد. مثلاً، یک داده `int` که معمولاً 4 بایت است، بهتر است در آدرسی ذخیره شود که مضربی از 4 باشد. این کار باعث میشود پردازنده بتواند داده را به شکل بهینهتری بخواند. |
| 11 | + |
| 12 | +### 1.1 علت وجود Alignment |
| 13 | + |
| 14 | +علت اصلی وجود Alignment به معماری پردازنده و سرعت دسترسی به دادهها برمیگردد. پردازندهها معمولاً دادهها را در واحدهای مشخص (مثل 4 بایت یا 8 بایت) میخوانند. اگر دادهها در آدرسی ذخیره شوند که با این واحدها هماهنگ نباشد (مثلاً یک داده 4 بایتی در آدرسی که مضربی از 4 نیست)، پردازنده باید داده را به صورت چند تکه بخواند و دوباره آن را سرهم کند که این کار باعث کاهش سرعت میشود. |
| 15 | + |
| 16 | +### سناریو 1: درک Alignment با یک مثال ساده |
| 17 | + |
| 18 | +فرض کن یک `struct` داری که شامل دو متغیر است: یکی `char` و یکی `int`. در حالت عادی، انتظار داری که `struct` به اندازه جمع اندازههای این دو متغیر باشد، ولی به خاطر Alignment این اتفاق نمیافتد. |
| 19 | + |
| 20 | +<div dir="ltr"> |
| 21 | + |
| 22 | +```c |
| 23 | +#include <stdio.h> |
| 24 | + |
| 25 | +typedef struct { |
| 26 | + char c; |
| 27 | + int i; |
| 28 | +} MyStruct; |
| 29 | + |
| 30 | +int main() { |
| 31 | + printf("Size of MyStruct: %lu\n", sizeof(MyStruct)); |
| 32 | + return 0; |
| 33 | +} |
| 34 | +``` |
| 35 | +</div> |
| 36 | + |
| 37 | + |
| 38 | +#### توضیح |
| 39 | + |
| 40 | +در این مثال، `char` یک بایت و `int` چهار بایت فضا میگیرد. ولی وقتی سایز `struct` را چک میکنی، میبینی که اندازه آن به جای 5 بایت، 8 بایت است. چرا؟ به خاطر Alignment. |
| 41 | + |
| 42 | +#### تصویرسازی Alignment |
| 43 | + |
| 44 | +حالا ببینیم این `struct` چطور در حافظه ذخیره میشود: |
| 45 | +<div dir="ltr"> |
| 46 | + |
| 47 | +``` |
| 48 | +Memory Layout: | c (1 byte) | Padding (3 bytes) | i (4 bytes) | |
| 49 | +``` |
| 50 | +</div> |
| 51 | + |
| 52 | +اینجا به این دلیل که `int` باید در آدرسی ذخیره شود که مضربی از 4 باشد، سه بایت بعد از `char` برای پُر کردن اضافه میشوند. |
| 53 | + |
| 54 | +## 2. مفهوم Padding |
| 55 | + |
| 56 | +Padding به معنی اضافه کردن بایتهای خالی (ناخواسته) به `struct` است تا دادهها به درستی تراز شوند. این بایتهای خالی به طور خودکار توسط کامپایلر اضافه میشوند تا مطمئن شود که هر فیلد در یک آدرس بهینه در حافظه قرار دارد. |
| 57 | + |
| 58 | +### سناریو 2: درک Padding با تغییر ترتیب متغیرها |
| 59 | + |
| 60 | +حالا بیایم ترتیب متغیرهای `struct` را تغییر دهیم و ببینیم چه اتفاقی میافتد. اگر `int` را اول و `char` را دوم قرار دهیم، Padding چطور تغییر میکند؟ |
| 61 | + |
| 62 | +<div dir="ltr"> |
| 63 | + |
| 64 | +```c |
| 65 | +#include <stdio.h> |
| 66 | + |
| 67 | +typedef struct { |
| 68 | + int i; |
| 69 | + char c; |
| 70 | +} MyStruct; |
| 71 | + |
| 72 | +int main() { |
| 73 | + printf("Size of MyStruct: %lu\n", sizeof(MyStruct)); |
| 74 | + return 0; |
| 75 | +} |
| 76 | +``` |
| 77 | +</div> |
| 78 | + |
| 79 | + |
| 80 | +#### توضیح |
| 81 | + |
| 82 | +این بار، `int` اول قرار دارد و `char` بعد از آن میآید. چون `int` به درستی تراز شده، هیچ Padding قبل از آن اضافه نمیشود. اما بعد از `char`، 3 بایت Padding اضافه میشود تا ساختار به درستی تراز شود. |
| 83 | + |
| 84 | +#### تصویرسازی Padding |
| 85 | +<div dir="ltr"> |
| 86 | + |
| 87 | +``` |
| 88 | +Memory Layout: | i (4 bytes) | c (1 byte) | Padding (3 bytes) | |
| 89 | +``` |
| 90 | +</div> |
| 91 | + |
| 92 | +## 3. بهینهسازی Alignment و Padding |
| 93 | + |
| 94 | +گاهی اوقات میتوان با تغییر ترتیب فیلدها در `struct`، فضای حافظه را بهینهتر کرد و از Padding غیرضروری جلوگیری کرد. |
| 95 | + |
| 96 | +### سناریو 3: بهینهسازی فضای حافظه با تغییر ترتیب فیلدها |
| 97 | + |
| 98 | +فرض کن یک `struct` داری که شامل یک `char`، یک `int` و یک `double` است. اگر این فیلدها را به ترتیب زیر بچینی، چه اتفاقی میافتد؟ |
| 99 | + |
| 100 | +<div dir="ltr"> |
| 101 | + |
| 102 | +```c |
| 103 | +#include <stdio.h> |
| 104 | + |
| 105 | +typedef struct { |
| 106 | + char c; |
| 107 | + int i; |
| 108 | + double d; |
| 109 | +} MyStruct; |
| 110 | + |
| 111 | +int main() { |
| 112 | + printf("Size of MyStruct: %lu\n", sizeof(MyStruct)); |
| 113 | + return 0; |
| 114 | +} |
| 115 | +``` |
| 116 | +</div> |
| 117 | + |
| 118 | + |
| 119 | +#### توضیح |
| 120 | + |
| 121 | +در این حالت، `char` و `int` هر دو نیاز به Padding دارند. اما اگر ترتیب فیلدها را تغییر دهیم تا ابتدا بزرگترین فیلد (`double`) قرار بگیرد، میتوانیم از Padding جلوگیری کنیم. |
| 122 | + |
| 123 | +#### بهینهسازی |
| 124 | + |
| 125 | +حالا ببینیم چطور با تغییر ترتیب فیلدها میتوانیم فضای حافظه را بهینه کنیم: |
| 126 | + |
| 127 | +<div dir="ltr"> |
| 128 | + |
| 129 | +```c |
| 130 | +#include <stdio.h> |
| 131 | + |
| 132 | +typedef struct { |
| 133 | + double d; |
| 134 | + int i; |
| 135 | + char c; |
| 136 | +} MyStruct; |
| 137 | + |
| 138 | +int main() { |
| 139 | + printf("Size of MyStruct: %lu\n", sizeof(MyStruct)); |
| 140 | + return 0; |
| 141 | +} |
| 142 | +``` |
| 143 | +</div> |
| 144 | + |
| 145 | + |
| 146 | +#### تصویرسازی بهینهسازی |
| 147 | +<div dir="ltr"> |
| 148 | + |
| 149 | +``` |
| 150 | +Memory Layout (بهینهسازی شده): | d (8 bytes) | i (4 bytes) | c (1 byte) | Padding (3 bytes) | |
| 151 | +
|
| 152 | +``` |
| 153 | +</div> |
| 154 | + |
| 155 | +در این حالت، فضای Padding کمتر و حافظه بهینهتر شده است. |
| 156 | + |
| 157 | + |
| 158 | +## 4. استفاده از #pragma و __attribute__ برای کنترل Alignment و Padding |
| 159 | + |
| 160 | +در بعضی مواقع، نیاز داریم که کنترل بیشتری روی Alignment و Padding داشته باشیم. در چنین شرایطی میتوانیم از `#pragma pack` یا `__attribute__ ((packed))` استفاده کنیم. |
| 161 | + |
| 162 | +### 4.1 استفاده از `#pragma pack` |
| 163 | + |
| 164 | +`#pragma pack` به ما اجازه میدهد که اندازه Alignment را تنظیم کنیم و از Padding اضافی جلوگیری کنیم. به عنوان مثال، اگر بخواهیم هیچ Padding بین فیلدهای یک `struct` وجود نداشته باشد، میتوانیم از `#pragma pack(1)` استفاده کنیم: |
| 165 | + |
| 166 | +<div dir="ltr"> |
| 167 | + |
| 168 | +```c |
| 169 | +#include <stdio.h> |
| 170 | + |
| 171 | +#pragma pack(1) |
| 172 | +typedef struct { |
| 173 | + char c; |
| 174 | + int i; |
| 175 | + double d; |
| 176 | +} MyStruct; |
| 177 | + |
| 178 | +int main() { |
| 179 | + printf("Size of MyStruct: %lu\n", sizeof(MyStruct)); |
| 180 | + return 0; |
| 181 | +} |
| 182 | +``` |
| 183 | +</div> |
| 184 | + |
| 185 | + |
| 186 | +#### توضیح |
| 187 | + |
| 188 | +با استفاده از `#pragma pack(1)`، کامپایلر را مجبور میکنیم که هیچ Padding بین فیلدها اضافه نکند. این باعث میشود که سایز `struct` دقیقاً برابر با جمع اندازههای فیلدها باشد. |
| 189 | + |
| 190 | +### 4.2 استفاده از `__attribute__ ((packed))` |
| 191 | + |
| 192 | +`__attribute__ ((packed))` نیز به ما اجازه میدهد که یک `struct` را بدون Padding تعریف کنیم: |
| 193 | + |
| 194 | +<div dir="ltr"> |
| 195 | + |
| 196 | +```c |
| 197 | +#include <stdio.h> |
| 198 | + |
| 199 | +typedef struct { |
| 200 | + char c; |
| 201 | + int i; |
| 202 | + double d; |
| 203 | +} __attribute__ ((packed)) MyStruct; |
| 204 | + |
| 205 | +int main() { |
| 206 | + printf("Size of MyStruct: %lu\n", sizeof(MyStruct)); |
| 207 | + return 0; |
| 208 | +} |
| 209 | +``` |
| 210 | +</div> |
| 211 | + |
| 212 | + |
| 213 | +#### توضیح |
| 214 | + |
| 215 | +در این مثال، با استفاده از `__attribute__ ((packed))`، از اضافه شدن Padding به `struct` جلوگیری میکنیم. |
| 216 | + |
| 217 | +## 5. ترکیب Alignment و Padding با Union |
| 218 | + |
| 219 | +گاهی اوقات از `union` استفاده میکنیم تا دادههای مختلف را در یک فضای حافظه نگه داریم. وقتی `union` با `struct` ترکیب شود، Alignment و Padding همچنان اهمیت دارند. |
| 220 | + |
| 221 | +### 5.1 سناریو: درک ترکیب Alignment و Padding با Union |
| 222 | + |
| 223 | +فرض کن یک `union` داری که شامل یک `int` و یک `char` است. اگر این `union` را داخل یک `struct` قرار دهیم که خودش هم شامل یک `double` است، چه اتفاقی میافتد؟ |
| 224 | + |
| 225 | +<div dir="ltr"> |
| 226 | + |
| 227 | +```c |
| 228 | +#include <stdio.h> |
| 229 | + |
| 230 | +typedef union { |
| 231 | + int i; |
| 232 | + char c; |
| 233 | +} MyUnion; |
| 234 | + |
| 235 | +typedef struct { |
| 236 | + double d; |
| 237 | + MyUnion u; |
| 238 | +} MyStruct; |
| 239 | + |
| 240 | +int main() { |
| 241 | + printf("Size of MyStruct: %lu\n", sizeof(MyStruct)); |
| 242 | + return 0; |
| 243 | +} |
| 244 | +``` |
| 245 | +</div> |
| 246 | + |
| 247 | + |
| 248 | +#### توضیح |
| 249 | + |
| 250 | +در این حالت، `double` به درستی تراز شده است. اما چون `union` شامل `int` است، `union` هم باید به درستی تراز شود. این به معنی اضافه شدن Padding قبل از `union` است. |
| 251 | + |
| 252 | +#### تصویرسازی ترکیب |
| 253 | +<div dir="ltr"> |
| 254 | + |
| 255 | +``` |
| 256 | +Memory Layout: | d (8 bytes) | Padding (4 bytes) | u (4 bytes) | |
| 257 | +``` |
| 258 | +</div> |
| 259 | + |
| 260 | +## 6. ترکیب Alignment و Padding با آرایهها |
| 261 | + |
| 262 | +وقتی `struct` ها را به صورت آرایه تعریف میکنیم، Padding و Alignment همچنان اهمیت دارند. |
| 263 | + |
| 264 | +### 6.1 سناریو: درک ترکیب Alignment و Padding با آرایهها |
| 265 | + |
| 266 | +فرض کن یک `struct` داری که شامل یک `char` و یک `int` است. حالا این `struct` را به صورت آرایه تعریف میکنیم: |
| 267 | + |
| 268 | +<div dir="ltr"> |
| 269 | + |
| 270 | +```c |
| 271 | +#include <stdio.h> |
| 272 | + |
| 273 | +typedef struct { |
| 274 | + char c; |
| 275 | + int i; |
| 276 | +} MyStruct; |
| 277 | + |
| 278 | +int main() { |
| 279 | + MyStruct arr[3]; |
| 280 | + printf("Size of MyStruct: %lu\n", sizeof(MyStruct)); |
| 281 | + printf("Size of array: %lu\n", sizeof(arr)); |
| 282 | + return 0; |
| 283 | +} |
| 284 | +``` |
| 285 | +</div> |
| 286 | + |
| 287 | + |
| 288 | +#### توضیح |
| 289 | + |
| 290 | +هر عنصر از آرایه باید به درستی تراز شود، بنابراین Padding بعد از هر عنصر اضافه میشود تا عنصر بعدی نیز به درستی تراز شود. |
| 291 | + |
| 292 | +#### تصویرسازی ترکیب |
| 293 | +<div dir="ltr"> |
| 294 | + |
| 295 | +``` |
| 296 | +Memory Layout: | c (1 byte) | Padding (3 bytes) | i (4 bytes) | <- Element 1 | c (1 byte) | Padding (3 bytes) | i (4 bytes) | <- Element 2 | c (1 byte) | Padding (3 bytes) | i (4 bytes) | <- Element 3 |
| 297 | +``` |
| 298 | +</div> |
| 299 | + |
| 300 | +## 7. جمعبندی |
| 301 | + |
| 302 | +در این مقاله، با مفاهیم Alignment و Padding آشنا شدیم و دیدیم که چطور میتوانیم با تغییر ترتیب فیلدها در `struct` و ترکیب آن با `union` و `array`، فضای حافظه را بهینهتر کنیم. همچنین با استفاده از `#pragma pack` و `__attribute__ ((packed))` میتوانیم کنترل بیشتری بر روی Padding داشته باشیم. استفاده از این تکنیکها میتواند به بهینهتر شدن برنامهها و کاهش مصرف حافظه منجر شود. |
| 303 | + |
| 304 | +## تمرینها |
| 305 | + |
| 306 | + |
| 307 | +| شماره | سوال | بارم | |
| 308 | +|-------|------|------| |
| 309 | +| 1 | یک `struct` بساز که شامل یک `short`، یک `char` و یک `int` باشد. سایز این `struct` را محاسبه کن و دلیل وجود Padding را توضیح بده. | 3 | |
| 310 | +| 2 | ساختار زیر را بهینه کن تا کمترین فضای حافظه را مصرف کند: `struct { char c; double d; int i; }`. | 4 | |
| 311 | +| 3 | یک `union` بساز که شامل یک `int`، یک `float` و یک `char` باشد. این `union` را داخل یک `struct` قرار بده و بررسی کن که چطور Alignment و Padding روی اندازه کلی `struct` تأثیر میگذارند. | 5 | |
| 312 | +| 4 | برنامهای بنویس که سایز `struct` و `union` را در شرایط مختلف (با و بدون Padding) مقایسه کند. | 4 | |
| 313 | +| 5 | از `#pragma pack` و `__attribute__ ((packed))` برای حذف Padding استفاده کن و بررسی کن که چطور این کار روی عملکرد برنامه تأثیر میگذارد. | 5 | |
| 314 | + |
| 315 | +</div> |
0 commit comments