پرش به مطلب اصلی

داده‌ها و اعلان‌ها


PAWN یک زبان بدون نوع (typeless) است. تمام عناصر داده از نوع cell هستند و یک cell می‌تواند یک عدد صحیح را در خود نگه دارد. اندازه یک cell (بر حسب بایت) وابسته به سیستم است — معمولاً یک cell برابر با ۳۲ بیت است.

کلمه‌کلیدی new یک متغیر جدید را اعلام می‌کند. برای اعلان‌های ویژه، کلمه‌کلیدی new با static، public یا stock جایگزین می‌شود (به بخش‌های پایین رجوع کنید). یک اعلان متغیر ساده متغیری ایجاد می‌کند که یک «cell» از حافظه داده را اشغال می‌کند. مگر اینکه به‌طور صریح مقداردهی اولیه شود، مقدار متغیر جدید برابر با صفر است.

اعلان یک متغیر می‌تواند در یکی از مکان‌های زیر رخ دهد:

  • در هر موقعیتی که یک دستور معتبر باشد — متغیرهای محلی؛

  • در هر موقعیتی که اعلام تابع (اعلان توابع native) یا پیاده‌سازی تابع معتبر باشد — متغیرهای سراسری؛

  • در اولین عبارت یک دستور for — که آن نیز یک اعلان محلی است.

اعلان‌های محلی

اعلان محلی در داخل یک دستور ترکیبی (compound statement) ظاهر می‌شود. یک متغیر محلی تنها از داخل همان بلوک ترکیبی و بلوک‌های تو در تو قابل دسترسی است. اعلان در عبارت اول یک حلقه for نیز یک اعلان محلی محسوب می‌شود.

اعلان‌های سراسری

اعلان سراسری بیرون از هر تابع ظاهر می‌شود و یک متغیر سراسری برای تمامی توابع قابل دسترسی است. اشیای داده سراسری تنها می‌توانند با عبارات ثابت مقداردهی اولیه شوند.


حلقه "for": صفحه 113

دستور ترکیبی: صفحه 112


• اعلان متغیرهای state

یک متغیر state یک متغیر سراسری است که در انتهای نام آن یک مشخص‌کننده‌ی state الصاق شده است. دامنه (scope) و طول عمر (lifetime) این متغیر به stateهایی محدود است که در مشخص‌کننده ذکر شده‌اند.

متغیرهای state قابل مقداردهی اولیه نیستند. برخلاف متغیرهای معمولی (که بعد از اعلان صفر هستند مگر اینکه صریحاً مقداردهی شوند)، متغیرهای state بعد از اعلان و پس از اولین ورود به یکی از stateهای مشخص‌شده مقدار نامشخصی خواهند داشت. معمولاً از تابع(های) ورود به state برای مقداردهی اولیه‌ی صحیح متغیر state استفاده می‌شود.

• اعلان‌های محلی static

یک متغیر محلی هنگام خروج اجرای برنامه از بلوک ترکیبی‌ای که در آن ساخته شده نابود می‌شود. متغیرهای محلی در یک تابع تنها در طول زمان اجرای آن تابع وجود دارند. هر بار اجرای تابع، متغیرهای محلی جدید ایجاد و مقداردهی می‌شوند. وقتی یک متغیر محلی با کلمه‌کلیدی static به‌جای new اعلام شود، آن متغیر پس از پایان اجرای تابع نیز وجود خواهد داشت. این بدان معنی است که متغیرهای محلی static فضای ذخیره‌سازی خصوصی و دائمی فراهم می‌کنند که تنها از همان تابع (یا بلوک ترکیبی) قابل دسترسی است. مانند متغیرهای سراسری، متغیرهای محلی static تنها می‌توانند با عبارات ثابت مقداردهی شوند.

• اعلان‌های سراسری static

یک متغیر سراسری static همان رفتار متغیر سراسری عادی را دارد، با این تفاوت که حوزه‌ی (scope) آن محدود به فایلی است که اعلان در آن قرار گرفته است. برای اعلام یک متغیر سراسری به‌صورت static، کلمه‌کلیدی new را با static جایگزین کنید.

• اعلان‌های stock

یک متغیر سراسری ممکن است به صورت stock اعلام شود. اعلان stock اعلامی است که مفسر ممکن است در صورتی که مشخص شود متغیر در برنامه استفاده نشده است، آن را حذف یا نادیده بگیرد.

متغیرهای stock در ترکیب با توابع stock مفید هستند. یک متغیر public نیز می‌تواند به صورت stock اعلام شود — اعلام متغیرهای عمومی به‌صورت public stock به شما امکان می‌دهد تا تمام متغیرهایی را که یک برنامه میزبان در یک فایل include فراهم کرده است اعلام کنید، و تنها آن متغیرهایی که اسکریپت واقعاً استفاده می‌کند در فایل P-code باقی بمانند.

• اعلان‌های public

متغیرهای سراسری «ساده» (بدون آرایه) ممکن است به دو روش به صورت public اعلام شوند:

  • متغیر را با کلمه‌کلیدی public به‌جای new اعلام کنید؛

  • یا نام متغیر را با نماد @ شروع کنید.

متغیرهای public مانند متغیرهای سراسری رفتار می‌کنند، با این تفاوت که برنامه میزبان نیز می‌تواند متغیرهای public را بخواند یا بنویسد. یک متغیر سراسری معمولی تنها از طریق توابع درون اسکریپت شما قابل دسترسی است — برنامه میزبان از وجود آن‌ها اطلاع ندارد. از این رو، ممکن است برنامه میزبان نیاز داشته باشد که شما متغیری را با نام مشخص به‌صورت public اعلام کنید — برای اهداف خاص مانند نگهداری آخرین شماره خطا یا وضعیت کلی برنامه.


ثابت‌های نمادین: صفحه 101

توابع stock: صفحه 84


• متغیرهای ثابت (Constant variables)

گاهی اوقات مفید است که بتوان متغیری ایجاد کرد که یک‌بار مقداردهی شده و قابل تغییر نباشد. چنین متغیری مانند یک ثابت نمادین رفتار می‌کند، اما همچنان یک متغیر است.

برای اعلام یک متغیر ثابت، کلمه‌کلیدی const را بین کلمه‌کلیدی آغاز اعلان (new، static، public یا stock) و نام متغیر قرار دهید.

نمونه‌ها:

new const address[4] = { 192, 0, 168, 66 }

public const status /* initialized to zero */

سه وضعیت معمول که ممکن است از یک متغیر ثابت استفاده شود عبارت‌اند از:

  • برای ساخت یک «آرایه» ثابت؛ ثابت‌های نمادین را نمی‌توان اندیس‌گذاری (index) کرد.

  • برای یک متغیر public که باید فقط توسط برنامه میزبان مقداردهی شود و تنها توسط برنامه میزبان قابل تغییر باشد. به بخش اعلان‌های public مراجعه کنید.

  • یک حالت ویژه برای علامت‌گذاری آرگومان‌های آرایه به‌عنوان const در توابع وجود دارد. آرگومان‌های آرایه همیشه به‌صورت ارجاع (by reference) پاس داده می‌شوند؛ اعلان آن‌ها به‌صورت const از تغییر ناخواسته جلوگیری می‌کند. برای نمونه‌ای از آرگومان‌های تابع که به‌صورت const اعلام شده‌اند، به صفحه 72 مراجعه کنید.


همچنین ببینید: "آرایه‌های چندبعدی"، صفحه 66


• آرایه‌ها (تک‌بعدی)

سینتکس name[constant] نام را به‌عنوان یک آرایه با constant عنصر از نوع cell اعلام می‌کند، که هر عنصر یک cell است. name یک شناسه (identifier) است که شما انتخاب می‌کنید و constant باید یک مقدار مثبت و غیرصفر باشد؛ constant می‌تواند غایب باشد. اگر بین کروشه‌ها هیچ مقداری نباشد، تعداد عناصر برابر با تعداد مقداردهی‌های اولیه (initializers) گذاشته می‌شود — نمونه را در پایین ببینید.

برد گسترهٔ اندیس آرایه از صفر آغاز می‌شود، به این معنی که اولین عنصر در name[0] قرار دارد و آخرین عنصر در name[constant-1] است.

• مقداردهی اولیه (Initialization)

اشیای داده می‌توانند هنگام اعلان مقداردهی اولیه شوند. مقداردهی اولیه یک شیء داده سراسری باید یک مقدار ثابت (constant) باشد. آرایه‌ها، چه سراسری و چه محلی، نیز باید با مقادیر ثابت مقداردهی شوند.

داده‌های مقداردهی‌نشده به‌طور پیش‌فرض صفر خواهند بود. نمونه‌ها:

Listing: good declaration

new i = 1
new j /* j is zero */
new k = ’a’ /* k has character code for letter ’a’ */

new a[] = {1,4,9,16,25} /* a has 5 elements */
new s1[20] = {’a’,’b’} /* the other 18 elements are 0 */

new s2[] = "Hello world..." /* a unpacked string */

نمونه‌های اعلان نامعتبر:

Listing: bad declarations

new c[3] = 4                            /* an array cannot be set to a value */
new i = "Good-bye" /* only an array can hold a string */
new q[] /* unknown size of array */
new p[2] = { i + j, k - 3 } /* array initiallers must be constants */

ثابت‌ها: صفحه 98


• مقداردهی پیشرونده (Progressive initiallers) برای آرایه‌ها

عملگر اِلیپس (ellipsis) ادامهٔ تقدم مقادیر اولیه را برای یک آرایه بر اساس دو عنصر آخر مقداردهی‌شده فراهم می‌کند. عملگر اِلیپس (سه نقطه، یا ...) آرایه را تا اندازهٔ اعلام‌شده مقداردهی می‌کند.

نمونه‌ها:

Listing: array initializers

new a[10] = { 1, ... }                  /* sets all ten elements to 1 */
new b[10] = { 1, 2, ... } /* sets: 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 */
new c[8] = { 1, 2, 40, 50, ... } /* sets: 1, 2, 40, 50, 60, 70, 80, 90 */
new d[10] = { 10, 9, ... } /* sets: 10, 9, 8, 7, 6, 5, 4, 3, 2, 1 */

• مقداردهی آرایه و شمارش (enumerations)

اندازهٔ یک آرایه ممکن است با یک ثابت که نمایندهٔ یک شمارش (enumeration) است تعیین شود: نمونه‌ای از این مورد برنامهٔ نمونهٔ «priority queue» در صفحهٔ 21 است. زمانی که فیلدهای جداگانهٔ شمارش دارای اندازه باشند، عناصر متناظر آرایه گاهی اوقات به‌عنوان زیرآرایه (sub-arrays) تفسیر می‌شوند. برای نمونه‌ای از این رفتار به برنامهٔ ماشین‌حساب RPN در صفحهٔ 30 مراجعه کنید.

سینتکس زیر-آرایه همچنین در مقداردهی اولیهٔ یک آرایهٔ «شماره‌گذاری‌شده» (enumerated) اعمال می‌شود. با بازگشت به مثال «priority queue»، برای مقداردهی اولیهٔ آرایه‌ای به‌نام message با مقادیر ثابت، سینتکس به شکل زیر است:

Listing: array initializers

enum message /* declaration copied from "QUEUE.P" */
{
text[40 char],
priority
}

new msg[message] = { !"new message", 1 }

مقداردهی اولیه شامل یک رشته (آرایهٔ حرفی literal) و یک مقدار عددی است؛ این‌ها به ترتیب در فیلدهای text و priority قرار می‌گیرند.

• آرایه‌های چندبعدی (Multi-dimensional arrays)

آرایه‌های چندبعدی آرایه‌هایی هستند که ارجاع به زیرآرایه‌ها را در بر دارند.∗ یعنی، یک آرایهٔ دوبعدی در واقع یک «آرایه از آرایه‌های تک‌بعدی» است. در زیر چند مثال از اعلان آرایه‌های دوبعدی آورده شده است.

Listing: Two-dimensional arrays

new a[4][3]
new b[3][2] = { { 1, 2 }, { 3, 4 }, { 5, 6 } }
new c[3][3] = { { 1 }, { 2, ...}, { 3, 4, ... } }
new d[2][5] = { !"agreement", !"dispute" }
new e[2][] = { "OK", "Cancel" }
new f[][] = { "OK", "Cancel" }

∗ پیاده‌سازی فعلی کامپایلر Pawn تنها از آرایه‌هایی با حداکثر دو بعد پشتیبانی می‌کند.

همان‌طور که دو اعلان آخر (متغیرهای e و f) نشان می‌دهند، بعد نهایی یک آرایه ممکن است طول نامشخص داشته باشد، در این صورت طول هر زیرآرایه از روی مقداردهی اولیهٔ مربوط تعیین می‌شود. هر زیرآرایه ممکن است اندازهٔ متفاوتی داشته باشد؛ در این مثال خاص، e[1][5] حروف "l" از کلمهٔ "Cancel" را در خود دارد، اما e[0][5] نامعتبر است چون طول زیرآرایهٔ e[0] تنها سه سل است (حاوی حروف "O"، "K" و یک خاتم صفر).

تفاوت میان اعلان‌های آرایهٔ e و f این است که در f اجازه داده‌ایم کامپایلر تعداد مقداردهی‌کننده‌ها برای بعد اصلی را بشمارد — sizeof f برابر با 2 است، مانند sizeof e (به بخش بعدی دربارهٔ عملگر sizeof مراجعه کنید).

• آرایه‌ها و عملگر sizeof

عملگر sizeof اندازهٔ یک متغیر را بر حسب «عناصر» برمی‌گرداند. برای یک متغیر ساده (غیرترکیبی)، نتیجهٔ sizeof همیشه 1 است، چون یک عنصر برای یک متغیر ساده یک cell محسوب می‌شود.

یک آرایهٔ یک‌بعدی تعدادی cell دارد و عملگر sizeof آن تعداد را برمی‌گرداند. قطعه کد زیر بنابراین مقدار «5» را روی نمایشگر چاپ خواهد کرد، چون آرایهٔ msg چهار کاراکتر دارد (هرکدام در یک سل) به‌علاوه یک خاتم صفر:

Listing: sizeof operator

new msg[] = "Help"
printf("%d", sizeof msg);

در آرایه‌های چندبعدی، عملگر sizeof می‌تواند تعداد عناصر هر بعد را بازگرداند. برای بعد آخر (بعد خرد یا minor)، یک عنصر مجدداً یک سل خواهد بود، اما برای بعد(های) اصلی، یک عنصر یک زیرآرایه است. در قطعهٔ کد زیر توجه کنید که سینتکس sizeof matrix به بعد اصلیِ آرایهٔ دوبعدی اشاره دارد و سینتکس sizeof matrix[] به بعد خردِ آرایه اشاره دارد. مقادیری که این نمونه چاپ می‌کند به ترتیب ۳ و ۲ هستند (برای بعد اصلی و خرد):

Listing: sizeof operator and multidimensional arrays

new matrix[3][2] = { { 1, 2 }, { 3, 4 }, { 5, 6 } }
printf("%d %d", sizeof matrix, sizeof matrix[]);

به‌کارگیری عملگر sizeof روی آرایه‌های چندبعدی خصوصاً زمانی که به‌عنوان مقدار پیش‌فرض برای آرگومان‌های تابع استفاده می‌شود بسیار مفید است.


Default function arguments and sizeof: 77


• نام‌های tag (Tag names)

یک tag برچسبی است که هدف یا معنی یک متغیر، یک ثابت یا نتیجهٔ یک تابع را مشخص می‌کند. تگ‌ها اختیاری هستند و هدفِ آن‌ها تقویت بررسی خطا در زمان کامپایل بر روی عملوندها در عبارات، پارامترهای تابع و اندیس‌های آرایه است.

یک تگ از یک نام سمبل دنبال‌شده توسط دونقطه تشکیل شده‌است؛ این سینتکس مشابهِ یک label است. یک تگ قبل از نام نماد (متغیر، ثابت یا تابع) قرار می‌گیرد.

در یک انتساب، تنها سمت راستِ علامت = ممکن است تگ‌دار باشد. نمونه‌های معتبر تعریف متغیرها و ثوابت تگ‌دار در زیر آمده‌اند:

Listing: tag names

new bool:flag = true            /* "flag" can only hold "true" or "false" */

const error:success = 0
const error:fatal= 1
const error:nonfatal = 2

error:errno = fatal

دنبالهٔ ثوابت success، fatal و nonfatal را می‌توان راحت‌تر با استفاده از دستور enum تعریف کرد، همان‌طور که در زیر نشان داده شده است. دستور enumeration زیر چهار ثابت success، fatal، nonfatal و error را ایجاد می‌کند که همهٔ آن‌ها تگ error را دارند.

Listing: enumerations

enum error {
success,
fatal,
nonfatal,
}

یک کاربرد معمولِ enum‌های تگ‌دار، همراه کردن آن‌ها با آرایه‌ها است. اگر هر فیلد آرایه هدف مشخص و متفاوتی داشته باشد، می‌توانید از یک enum تگ‌دار برای اعلام اندازهٔ آرایه و افزودن بررسی تگ به استفاده از آرایه در همان گام استفاده کنید:

Listing: enumerations and arrays

enum rectangle
{
left,
top,
right,
bottom
}

new my_rect[rectangle] /* array is declared as having 4 cells */

my_rect[left] = 10
my_rect[top] = 5
my_rect[right] = 30
my_rect[bottom] = 12

for (new i = 0; rectangle:i < rectangle; ++i)
my_rect[rectangle:i] *= 2

بعد از اعلان my_rect بالا، می‌توانید فیلد دوم my_rect را با my_rect[top] دسترسی دهید، اما نوشتن my_rect[1] باعث تولید پیغام تشخیصیِ پارسر (هشدار یا خطا) خواهد شد. یک override تگ (یا tag cast) مقدار یک تابع، ثابت یا متغیر را به تگ دلخواه تنظیم می‌کند. حلقهٔ for در دو خط آخر مثال قبلی این موضوع را نشان می‌دهد: متغیر حلقه i یک سل ساده و بدون تگ است و باید قبل از استفاده به‌عنوان اندیس در my_rect به تگ rectangle تبدیل (cast) شود. توجه کنید که سازهٔ enum هر دو را — یک ثابت و یک تگ با نام rectangle — ایجاد کرده است.

تگ‌هایی که تاکنون معرفی شده‌اند با حرف کوچک آغاز شده‌اند؛ این‌ها تگ‌های «ضعیف» (weak) نامیده می‌شوند. تگ‌هایی که با حرف بزرگ شروع می‌شوند، تگ‌های «قوی» (strong) هستند. تفاوت بین تگ‌های ضعیف و قوی این است که تگ‌های ضعیف ممکن است در برخی شرایط توسط پارسر Pawn به‌طور ضمنی حذف شوند — به‌طوری که یک عبارت با تگ ضعیف به یک عبارت بدون تگ تبدیل شود. مکانیزم بررسی تگ موارد زیر را اعتبارسنجی می‌کند:

  • زمانی که عبارات در دو طرف یک عملگر دودویی تگ‌های متفاوتی دارند، یا وقتی که یکی از عبارات تگ‌دار و دیگری بدون تگ است، کامپایلر یک تشخیص «تفاوت تگ» (tag mismatch) صادر می‌کند. در این وضعیت تفاوتی بین تگ ضعیف و قوی وجود ندارد.

  • یک حالت ویژه برای عملگر انتساب وجود دارد: اگر متغیر در سمت چپ عملگر انتساب تگ داشته باشد و عبارت سمت راست یا تگ متفاوتی داشته باشد یا بدون تگ باشد، کامپایلر تشخیص صادر می‌کند. با این حال، اگر مقدارِ سمت چپ (lvalue) بدون تگ باشد، یک عبارت با تگ ضعیف در انتساب پذیرفته می‌شود. به عبارت دیگر، یک تگ ضعیف هنگام انتساب به یک lvalue بدون تگ حذف می‌شود.

  • ارسال آرگومان به توابع از قاعدهٔ انتساب پیروی می‌کند. کامپایلر زمانی تشخیص صادر می‌کند که پارامتر رسمی (formal parameter) در تعریف تابع تگ داشته باشد و پارامتر واقعی (actual parameter) در فراخوانی تابع یا بدون تگ باشد یا تگ متفاوتی داشته باشد. اما اگر پارامتر رسمی بدون تگ باشد، پارامتری با هر تگ ضعیف را نیز می‌پذیرد.

  • یک آرایه می‌تواند برای هر بعد تگی مشخص کند، مثلاً مثال my_rect بالا را ببینید. بررسی تگ اندیس‌های آرایه از قاعدهٔ بررسی تگِ عملگر دودویی پیروی می‌کند: در این مورد تفاوتی بین تگ ضعیف و قوی وجود ندارد.


Label syntax: 112

"enum" statement: 101

"lvalue": the variable on the left side in an assignment, see page 104


برگشت به فهرست مطالب