دادهها و اعلانها
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