• متغیر در c چیست. انواع داده ها و عملیات در زبان C. اصطلاحات

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

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

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

    نمونه های توضیحات:

    char a,b; /* متغیرهای a و b دارای نوع هستند

    char */intx; /* متغیر x - نوع int

    */ نماد کاراکتر؛ /" متغیرهای sym از نوع char شرح داده شده است.

    */ int count.num; /* تعداد و تعداد از نوع int */

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

    مثال ها: char backch = "\0";

    انواع پایه را در زبان C در نظر بگیرید.

    int - عدد صحیح ("عدد صحیح").مقادیر این نوع اعداد صحیح از محدوده محدودی هستند (معمولا - 32768 تا 32767). محدوده با اندازه سلول برای نوع تعیین می شود و مخصوص ماشین است. علاوه بر این، کلمات کمکی هستند که می توانند با نوع int استفاده شوند: short int ("عدد صحیح کوتاه" - "عدد صحیح کوتاه")، int بدون علامت ("عدد صحیح بدون علامت" - "عدد صحیح بدون علامت")، long int ("عدد صحیح طولانی" ") که دامنه نمایش اعداد را کاهش می دهد یا برعکس، گسترش می دهد.

    کاراکتر- شخصیت ("شخصیت"). مقدار معتبر برای این نوع یک کاراکتر است (با متن اشتباه نشود!). نماد به صورت آپستروف نوشته شده است.

    مثال ها:"x" 2 "؟

    یک کاراکتر یک بایت در حافظه کامپیوتر اشغال می کند. در واقع، یک کاراکتر ذخیره نمی شود، بلکه یک عدد - یک کد کاراکتر (از 0 تا 255). در جداول رمزگذاری ویژه، تمام کاراکترهای معتبر و کدهای مربوط به آنها نشان داده شده است.

    در زبان C، استفاده از نوع char به عنوان یک نوع عددی، یعنی انجام عملیات با کد کاراکتر، در حالی که از مشخص کننده نوع عدد صحیح در براکت ها استفاده می شود - (int) مجاز است.

    شناور - واقعی (نقطه شناور).مقادیر این نوع اعداد هستند، اما برخلاف char و int، لزوما اعداد صحیح نیستند.

    12.87 -316.12 -3.345e5 12.345e-15

    اعداد واقعی با دقت دو برابر.این نوع شبیه به نوع شناور است، اما دامنه مقادیر بسیار بیشتری دارد (به عنوان مثال، برای سیستم برنامه نویسی Borland-C از 1.7E-308 تا 1.7E+308 به جای محدوده از 3.4E-38 تا 3.4E+38 برای نوع شناور). اما افزایش دامنه و دقت نمایش اعداد منجر به کاهش سرعت اجرای برنامه و استفاده بیهوده از حافظه دسترسی تصادفیکامپیوتر.


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

    لازم به ذکر است که آخرین عنصر آرایه کاراکتر \0 است. این کاراکتر تهی است و در C برای علامت گذاری انتهای یک رشته استفاده می شود. کاراکتر تهی رقم 0 نیست. چاپ نمی شود و در جدول کد اسکی عدد 0 را دارد.وجود یک کاراکتر تهی به این معنی است که تعداد سلول های آرایه باید باشد. حداقل یک کاراکتر بیشتر از تعداد کاراکترهایی که باید در حافظه ذخیره شوند.

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

    برنامه 84

    #عبارتند از main()

    scanf("%s",string) ;

    printf("%s",string);

    این مثال آرایه ای از 31 مکان حافظه را توصیف می کند که 30 مورد از آنها می توانند یک عنصر کاراکتر را در خود جای دهند. زمانی که تابع scanf("%s",string); فراخوانی می شود، وارد می شود. هنگام تعیین یک آرایه کاراکتر، "&" وجود ندارد.

    اشاره گرها. اشاره گر - برخی از نمایش نمادین آدرس سلول حافظه اختصاص داده شده برای متغیر.

    به عنوان مثال، &name یک اشاره گر به متغیر نام است.

    در اینجا & عملیات به دست آوردن یک آدرس است. آدرس واقعی یک عدد است و نمایش نمادین آدرس &name یک ثابت اشاره گر است.

    زبان C نیز دارای متغیرهای اشاره گر است. درست مثل ارزش متغیر نوع char یک کاراکتر است و مقدار متغیری از نوع int یک عدد صحیح است، مقدار متغیری از نوع اشاره گر آدرس مقداری است.

    اگر به اشاره گر نام ptr را بدهیم، می توانیم عبارت زیر را بنویسیم:

    ptr = /* آدرس نام را به ptr */ اختصاص می دهد

    ما در این مورد می گوییم که prt یک نام "اشاره گر به" است. تفاوت بین دو نماد ptr و &name این است که prt یک متغیر است در حالی که &name یک ثابت است. در صورت لزوم، می توانید نقطه متغیر ptr را به شی دیگری تبدیل کنید:

    ptr= /* ptr به bah اشاره می کند، نه نام */

    حال مقدار متغیر prt آدرس متغیر bah است. فرض کنید می دانیم که متغیر ptr حاوی ارجاع به متغیر bah است. سپس برای دسترسی به مقدار این متغیر می توانید از عملیات آدرس دهی غیر مستقیم * استفاده کنید:

    val = *ptr; /* مقداری را که توسط ptr به آن اشاره می شود تعیین کنید */ دو عبارت آخر که با هم جمع شده اند معادل عبارت زیر هستند:

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

    مثال:پرستار = 22;

    ptr= /* اشاره گر به پرستار */

    نتیجه تخصیص مقدار 22 به متغیر val است.

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

    مثال ها توضیحات درستاشاره گر: int *pi; char*pc;

    مشخصات نوع نوع متغیری را که اشاره گر به آن اشاره می کند مشخص می کند و نماد * خود متغیر را به عنوان یک اشاره گر تعریف می کند. شرح را تایپ کنید int *pi; می گوید که pi یک اشاره گر است و *pi یک مقدار int است.

    زبان C امکان تعریف نام نوع داده را فراهم می کند. به هر نوع داده ای می توان با استفاده از تعریف typedef یک نام داد و این نام را می توان بعداً هنگام توصیف اشیا استفاده کرد.

    فرمت: typedef<старый тип> <новый тип> مثال: typedef long LARGE; /* نوع بزرگ تعریف شده است که معادل طولانی */

    تعریف typedef هیچ نوع جدیدی را معرفی نمی کند، فقط یک نام جدید برای قبلا اضافه می کند نوع موجود. متغیرهایی که به این روش اعلام شده اند دقیقاً همان ویژگی های متغیرهای اعلام شده به صراحت را دارند. تغییر نام نوع برای معرفی نام‌های نوع معنی‌دار یا مخفف استفاده می‌شود که درک برنامه‌ها را بهبود می‌بخشد و قابلیت حمل برنامه را بهبود می‌بخشد (اسامی از یک نوع داده ممکن است در رایانه‌های مختلف متفاوت باشد).

    عملیات.زبان C با طیف گسترده ای از عملیات (بیش از 40) متمایز می شود. در اینجا ما فقط موارد اصلی را در نظر می گیریم، جدول. 3.3.

    عملیات حسابی. شامل می شوند

    اضافه (+)،

    تفریق (دودویی) (-)،

    ضرب (*)،

    بخش (/)،

    باقیمانده تقسیم عدد صحیح (%)،

    تفریق (یونری) (-) .

    در زبان C این قاعده پذیرفته شده است: اگر تقسیم کننده و تقسیم کننده از نوع int باشند، تقسیم به طور کامل انجام می شود، یعنی قسمت کسری نتیجه کنار گذاشته می شود.

    طبق معمول، در عبارات، عملیات ضرب، تقسیم و باقیمانده قبل از جمع و تفریق انجام می شود. از پرانتز برای تغییر ترتیب اعمال استفاده می شود.

    برنامه 85

    #عبارتند از

    5 = -3 + 4 * 5 - 6; printf("%d\n",s);

    s = -3 + 4% 5 - 6; printf("%d\n",s);

    s = -3 * 4٪ - 6/5; printf("%d\n",s);

    s= (7 + 6)%5/2; printf("%d\n",s);

    نتیجه اجرای برنامه: 11 1 0 1

    جدول 3.3 ارشدیت و ترتیب اجرای عملیات

    مبانی زبان

    کد برنامه و داده هایی که برنامه دستکاری می کند به صورت دنباله ای از بیت ها در حافظه کامپیوتر نوشته می شود. بیتکوچکترین عنصر است حافظه کامپیوتر، می تواند 0 یا 1 را ذخیره کند. روشن سطح فیزیکیمطابقت دارد ولتاژ الکتریکی، که معلوم است یا وجود دارد یا نه. با نگاهی به محتویات حافظه کامپیوتر، چیزی شبیه به زیر را مشاهده خواهیم کرد:
    ...

    درک چنین دنباله ای بسیار دشوار است، اما گاهی اوقات ما نیاز به دستکاری چنین داده های بدون ساختار داریم (معمولاً نیاز به این امر هنگام برنامه نویسی درایورهای دستگاه های سخت افزاری ایجاد می شود). C++ مجموعه ای از عملیات را برای کار با داده های بیتی ارائه می دهد. (ما در این مورد در فصل 4 صحبت خواهیم کرد.)
    به عنوان یک قاعده، ساختاری به دنباله بیت ها تحمیل می شود و بیت ها را به گروه بندی می کند بایت هاو کلمات. یک بایت شامل 8 بیت و یک کلمه شامل 4 بایت یا 32 بیت است. با این حال، تعریف یک کلمه می تواند در سیستم عامل های مختلف متفاوت باشد. اکنون انتقال به سیستم های 64 بیتی آغاز شده است و اخیراً سیستم هایی با کلمات 16 بیتی رایج شده اند. اگرچه اندازه بایت در اکثریت قریب به اتفاق سیستم ها یکسان است، ما همچنان به این مقادیر به عنوان دستگاه خاص اشاره خواهیم کرد.

    حالا می توانیم مثلاً در مورد یک بایت در آدرس 1040 یا یک کلمه در آدرس 1024 صحبت کنیم و بگوییم که بایت آدرس 1032 با بایت آدرس 1040 برابر نیست.
    با این حال، ما نمی دانیم که هر بایت، هر کلمه ماشینی چه چیزی را نشان می دهد. چگونه معنی 8 بیت خاص را بفهمیم؟ برای اینکه معنی این بایت (یا کلمه یا مجموعه دیگری از بیت ها) را بدون ابهام تفسیر کنیم، باید نوع داده های نمایش داده شده توسط این بایت را بدانیم.
    C++ مجموعه ای از انواع داده های داخلی را ارائه می دهد: کاراکتر، عدد صحیح، واقعی، و مجموعه ای از انواع ترکیبی و توسعه یافته: رشته ها، آرایه ها، اعداد مختلط. علاوه بر این، برای اقدامات با این داده ها، وجود دارد مجموعه پایهعملیات: مقایسه، حساب و عملیات دیگر. همچنین انتقال، حلقه، عبارات شرطی وجود دارد. این عناصر زبان C++ مجموعه‌ای از بلوک‌های ساختمانی را تشکیل می‌دهند که می‌توانید از آن‌ها سیستمی با هر پیچیدگی بسازید. اولین گام در تسلط بر C++ یادگیری این عناصر اساسی است که قسمت دوم این کتاب به آن اختصاص دارد.
    فصل 3 یک نمای کلی از انواع داخلی و توسعه یافته و مکانیسم هایی که توسط آنها می توانید انواع جدید ایجاد کنید ارائه می دهد. البته، اساساً این مکانیسم کلاسی است که در بخش 2.3 معرفی شده است. فصل 4 به عبارات، عملگرهای داخلی و اولویت آنها و تبدیل نوع می پردازد. فصل 5 در مورد دستورالعمل های زبان صحبت می کند. در نهایت، فصل 6 کتابخانه استاندارد C++ و انواع کانتینر، بردار و آرایه انجمنی را معرفی می کند.

    3. انواع داده C++

    این فصل یک نمای کلی ارائه می دهد تعبیه شده است، یا ابتدایی، انواع داده های زبان C++. با تعریف شروع می شود به معنای واقعی کلمهمانند 3.14159 یا pi و سپس مفهوم متغیر، یا هدف - شی، که باید یکی از انواع داده ها باشد. بقیه فصل به این موضوع اختصاص دارد توصیف همراه با جزئیاتهر نوع داخلی همچنین انواع داده های مشتق شده را برای رشته ها و آرایه های ارائه شده توسط کتابخانه استاندارد C++ فهرست می کند. اگرچه این انواع ابتدایی نیستند، اما برای نوشتن برنامه های C++ واقعی بسیار مهم هستند و می خواهیم هر چه زودتر خواننده را با آنها آشنا کنیم. ما چنین انواع داده ای را فراخوانی خواهیم کرد گسترشانواع پایه C++.

    3.1. تحت اللفظی

    C++ دارای مجموعه ای از انواع داده های داخلی برای نمایش اعداد صحیح و واقعی، نمادها و همچنین یک نوع داده "آرایه کاراکتر" است که برای ذخیره سازی استفاده می شود. رشته های کاراکتر. نوع char برای ذخیره کاراکترها و اعداد صحیح کوچک استفاده می شود. یک بایت ماشین را اشغال می کند. انواع کوتاه، int و long برای نمایش اعداد صحیح هستند. این انواع فقط در محدوده مقادیری که اعداد می توانند بگیرند متفاوت هستند و اندازه های خاص انواع ذکر شده به اجرا بستگی دارد. معمولا short نصف کلمه ماشینی است، int یک کلمه، long یک یا دو کلمه است. در سیستم های 32 بیتی، int و long معمولاً یک اندازه هستند.

    انواع float، double و long double برای اعداد ممیز شناور هستند و از نظر دقت نمایش (تعداد ارقام قابل توجه) و محدوده متفاوت هستند. به طور معمول، شناور (تک دقت) یک کلمه ماشینی، double (دقت مضاعف) به دو و طولانی دوبل (دقت توسعه یافته) سه نیاز دارد.
    char، short، int و long با هم تشکیل می دهند انواع عدد صحیح، که به نوبه خود می تواند باشد نمادین(امضا) و بدون امضا(بدون امضا). در انواع علامت دار، از سمت چپ ترین بیت برای ذخیره علامت استفاده می شود (0 مثبت، 1 منهای)، و بیت های باقی مانده حاوی مقدار هستند. در انواع بدون علامت، همه بیت ها برای مقدار استفاده می شوند. یک کاراکتر علامت دار 8 بیتی می تواند مقادیری از 128- تا 127 را نشان دهد و یک کاراکتر بدون علامت می تواند مقادیر 0 تا 255 را نشان دهد.

    هنگامی که عددی مانند 1 در برنامه ای رخ می دهد، این عدد فراخوانی می شود تحت اللفظی، یا ثابت تحت اللفظی. یک ثابت، زیرا نمی توانیم مقدار آن را تغییر دهیم، و یک لفظ، زیرا مقدار آن در متن برنامه ظاهر می شود. Literal یک مقدار غیر آدرس‌پذیر است: اگرچه در واقع در حافظه دستگاه ذخیره می‌شود، اما راهی برای دانستن آدرس آن وجود ندارد. هر لفظی نوع خاصی دارد. بنابراین، 0 از نوع int، 3.14159 از نوع double است.

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

    20 // اعشار
    024 // اکتال
    0x14 // هگز

    اگر حرف تحت اللفظی با 0 شروع شود به عنوان اکتال و اگر با 0x یا 0X شروع شود به عنوان هگزادسیمال در نظر گرفته می شود. نماد معمولی به عنوان یک عدد اعشاری در نظر گرفته می شود.
    به‌طور پیش‌فرض، تمام اعداد صحیح به صورت int امضا می‌شوند. شما می توانید با الحاق حرف L به انتهای عدد، صریحاً یک عدد صحیح را از نوع طولانی تعریف کنید (هم از L بزرگ و هم از L کوچک استفاده می شود، اما برای خوانایی، حروف کوچک نباید استفاده شود: به راحتی می توان آن را با حروف کوچک اشتباه گرفت.

    1). U (یا u) در انتها حرف را به صورت int بدون علامت و دو حرف UL یا LU را به صورت طولانی بدون علامت تعریف می کند. مثلا:

    128u 1024UL 1L 8Lu

    لفظ هایی که اعداد حقیقی را نشان می دهند را می توان با نقطه اعشار یا با نماد علمی (نمایی) نوشت. به طور پیش فرض آنها از نوع double هستند. برای مشخص کردن صریح نوع شناور، باید از پسوند F یا f و برای طولانی دوبل - L یا l استفاده کنید، اما فقط در مورد علامت گذاری با نقطه اعشار. مثلا:

    3.14159F 0/1f 12.345L 0.0 3el 1.0E-3E 2.1.0L

    کلمات true و false از نوع bool هستند.
    ثابت های کاراکتر تحت اللفظی قابل نمایش به صورت نویسه های تک نقل قولی نوشته می شوند. مثلا:

    "a" "2" """ "" (فضا)

    کاراکترهای ویژه (tab، carriage return) به صورت توالی فرار نوشته می شوند. دنباله های زیر تعریف شده اند (با یک کاراکتر بک اسلش شروع می شوند):

    خط جدید \n زبانه افقی \t پشتی \b برگه عمودی \v بازگشت کالسکه \r تغذیه برگه \f زنگ \a اسلش معکوس \\ سوال \؟ نقل قول تک \" نقل قول دوگانه \"

    دنباله فرار نمای کلیبه شکل \ooo است، که در آن ooo یک تا سه رقم اکتالی است. این شماره کد کاراکتر است. با استفاده از کد اسکی می توانیم لفظ های زیر را بنویسیم:

    \7 (تماس) \14 (خط جدید) \0 (تهی) \062 ("2")

    یک کاراکتر تحت اللفظی را می توان با L (به عنوان مثال، L"a") پیشوند، که به معنای نوع خاص wchar_t، یک نوع کاراکتر دو بایتی است که برای ذخیره کاراکترهای الفبای ملی استفاده می شود، اگر آنها را نتوان با نوع char معمولی نشان داد. مانند حروف چینی یا ژاپنی.
    String Literal رشته‌ای از کاراکترهای محصور در دو گیومه است. چنین لفظی ممکن است چندین خط را شامل شود، در این صورت یک بک اسلش در انتهای خط قرار می گیرد. کاراکترهای خاص را می توان با توالی فرار خود نشان داد. در اینجا نمونه هایی از لفظ رشته ای آورده شده است:

    "" (رشته خالی) "a" "\nCC\options\tfile."

    در واقع یک رشته لفظی یک آرایه است ثابت های کاراکتر، جایی که طبق قرارداد زبان های C و C++، آخرین عنصر همیشه یک کاراکتر خاص با کد 0 (\0) است.
    حرف "A" یک کاراکتر منفرد A را مشخص می کند و رشته تحت اللفظی "A" آرایه ای از دو عنصر را مشخص می کند: "A" و \0 (یک کاراکتر خالی).
    از آنجایی که یک نوع wchar_t وجود دارد، لفظ هایی از این نوع وجود دارد که مانند کاراکترهای منفرد، با پیشوند L نشان داده می شوند:

    L"یک رشته عریض به معنای واقعی کلمه"

    رشته‌ای از نوع wchar_t آرایه‌ای از کاراکترهای هم‌نوع با پایان‌بندی تهی است.
    اگر دو یا چند حرف رشته (از نوع char یا wchar_t) در یک ردیف در آزمایش برنامه ظاهر شوند، کامپایلر آنها را در یک رشته به هم متصل می کند. مثلا متن زیر

    "دو" "بعضی"

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

    // این ایده خوبی نیست "دو" L" مقداری"

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

    تمرین 3.1

    تفاوت تعاریف لفظ های زیر را توضیح دهید:

    (a) "a"، L"a"، "a"، L"a" (b) 10، 10u، 10L، 10uL، 012، 0*C (c) 3.14، 3.14f، 3.14L

    تمرین 3.2

    اشتباهات در مثال های زیر چیست؟

    (الف) "چه کسی با F\144rgus می رود؟\014" (ب) 3.14e1L (ج) "دو" L" برخی" (د) 1024f (ه) 3.14UL (f) "نظر چند خطی"

    3.2. متغیرها

    تصور کنید که در حال حل مشکل افزایش 2 به توان 10 هستیم.

    #عبارتند از
    int main() (
    // راه حل اول
    کوت<< "2 raised to the power of 10: ";
    کوت<< 2 * 2 * 2 * 2 * 2 * 2 * 2 * 2 * 2 * 2;
    کوت<< endl;
    بازگشت 0;
    }

    مشکل حل شده است، اگرچه ما مجبور بودیم بارها بررسی کنیم که آیا 2 واقعاً 10 بار تکرار شده است یا خیر. ما در نوشتن این دنباله طولانی دوتایی اشتباه نکردیم و برنامه نتیجه صحیح - 1024 را برگرداند.
    اما اکنون از ما خواسته شد که 2 را به توان 17 و سپس به 23 برسانیم. تغییر دادن متن برنامه در هر بار بسیار ناخوشایند است! و بدتر از آن، اشتباه کردن با نوشتن دو عدد اضافی یا رد کردن آن بسیار آسان است... اما اگر نیاز به چاپ جدول توان های دو از 0 تا 15 داشته باشید، چه؟ 16 بار دو خط را که یک شکل مشترک دارند تکرار کنید:

    کوت<< "2 в степени X\t"; cout << 2 * ... * 2;

    که در آن X به ترتیب 1 افزایش می یابد و تعداد لفظ مورد نیاز به جای نقطه جایگزین می شود؟

    بله، کار را انجام دادیم. بعید است مشتری به جزئیات بپردازد، زیرا از نتیجه راضی است. در زندگی واقعی، این رویکرد اغلب کار می کند، علاوه بر این، قابل توجیه است: مشکل تا حد زیادی نه به ظریف ترین روش، بلکه در چارچوب زمانی مورد نظر حل می شود. به دنبال یک گزینه زیباتر و شایسته تر می تواند اتلاف وقت غیرعملی باشد.

    در این صورت «روش brute force» پاسخ صحیح را می دهد، اما چقدر ناخوشایند و خسته کننده است که مشکل را به این شکل حل کنیم! ما دقیقا می دانیم که چه مراحلی را باید برداریم، اما خود مراحل ساده و یکنواخت هستند.

    درگیر مکانیسم های پیچیده تر برای یک کار، به عنوان یک قاعده، زمان مرحله آماده سازی را به میزان قابل توجهی افزایش می دهد. علاوه بر این، هرچه مکانیسم های پیچیده تری استفاده شود، احتمال خطا بیشتر می شود. اما حتی با وجود اشتباهات اجتناب ناپذیر و حرکت های بد، استفاده از "فناوری بالا" می تواند مزایایی در سرعت توسعه به همراه داشته باشد، ناگفته نماند که این فناوری ها قابلیت های ما را به شدت گسترش می دهند. و - چه جالب! خود فرآیند تصمیم گیری می تواند جذاب شود.
    بیایید به مثال خود بازگردیم و سعی کنیم اجرای آن را از نظر فناوری بهبود دهیم. ما می توانیم از یک شی با نام برای ذخیره مقدار توانی که می خواهیم شماره خود را به آن افزایش دهیم استفاده کنیم. همچنین، به جای تکرار دنباله لفظی، می توانیم از عملگر حلقه استفاده کنیم. در اینجا به چه شکل خواهد بود:

    #عبارتند از
    int main()
    {
    // اشیاء از نوع int
    مقدار int = 2;
    بین قدرت = 10;
    کوت<< value << " в степени "
    << pow << ": \t";
    int res = 1;
    // عملگر حلقه:
    // تکرار محاسبه res
    // تا زمانی که cnt از pow بیشتر شود
    برای (int cnt=1; cnt<= pow; ++cnt)
    res = res * value;
    کوت<< res << endl;
    }

    value، pow، res و cnt متغیرهایی هستند که به شما امکان ذخیره، تغییر و بازیابی مقادیر را می دهند. دستور حلقه for بارهای رشته محاسبه نتیجه را تکرار می کند.
    بدون شک ما برنامه بسیار انعطاف پذیرتری ایجاد کرده ایم. با این حال، این هنوز یک تابع نیست. برای به دست آوردن یک تابع واقعی که می تواند در هر برنامه ای برای محاسبه درجه یک عدد استفاده شود، باید قسمت کلی محاسبات را انتخاب کنید و مقادیر خاصی را به عنوان پارامتر تنظیم کنید.

    int pow(int val, int exp) (برای (int res = 1; exp > 0; --exp) res = res * val; return res; )

    اکنون به دست آوردن هر درجه ای از عدد مورد نظر دشوار نخواهد بود. در اینجا نحوه اجرای آخرین کار ما آمده است - چاپ جدولی از دو توان از 0 تا 15:

    #عبارتند از extern int pow(int,int); int main() (int val = 2; int exp = 15;
    کوت<< "Степени 2\n";
    برای (int cnt=0; cnt<= exp; ++cnt)
    کوت<< cnt << ": "
    << pow(val, cnt) << endl;
    بازگشت 0;
    }

    البته، تابع ()pow ما هنوز به اندازه کافی عمومی نیست و به اندازه کافی قابل اعتماد نیست. نمی تواند روی اعداد واقعی کار کند، به اشتباه اعداد را به توان منفی افزایش می دهد - همیشه 1 را برمی گرداند. نتیجه افزایش یک عدد بزرگ به توان بزرگ ممکن است در یک متغیر int قرار نگیرد و سپس مقداری اشتباه تصادفی برگردانده می شود. ببینید نوشتن توابعی که قرار است به طور گسترده مورد استفاده قرار گیرند چقدر می تواند دشوار باشد؟ بسیار دشوارتر از پیاده سازی یک الگوریتم خاص با هدف حل یک مسئله خاص است.

    3.2.1. متغیر چیست

    متغیر، یا یک شی- این یک منطقه نامگذاری شده از حافظه است که ما از برنامه به آن دسترسی داریم. می توانید مقادیر را در آنجا قرار دهید و سپس آنها را بازیابی کنید. هر متغیر C++ نوع خاصی دارد که اندازه و مکان آن ناحیه حافظه، محدوده مقادیری که می‌تواند ذخیره کند و مجموعه عملیات‌های قابل اعمال بر آن متغیر را مشخص می‌کند. در اینجا مثالی از تعریف پنج شی از انواع مختلف آورده شده است:

    int student_count; دو برابر حقوق؛ bool on_loan; strins street_address; جداکننده char;

    یک متغیر، مانند یک literal، نوع خاصی دارد و مقدار آن را در بخشی از حافظه ذخیره می کند. آدرس پذیری- این چیزی است که تحت اللفظی فاقد آن است. دو مقدار مرتبط با یک متغیر وجود دارد:

    • خود مقدار یا مقدار r (از مقدار خوانده شده - مقدار برای خواندن) که در این ناحیه حافظه ذخیره می شود و هم در متغیر و هم در یک کلمه ذاتی است.
    • مقدار آدرس ناحیه حافظه مرتبط با متغیر یا l-value (از مقدار مکان - مقدار مکان) - مکانی که مقدار r در آن ذخیره می شود. فقط به شی تعلق دارد.

    در بیان

    Ch = ch - "0"؛

    متغیر ch هم در سمت چپ و هم در سمت راست نماد تخصیص است. در سمت راست مقدار خوانده شده است (ch و کاراکتر تحت اللفظی "0"): داده های مرتبط با متغیر از ناحیه حافظه مربوطه خوانده می شود. در سمت چپ مقدار مکان است: نتیجه تفریق در ناحیه حافظه مرتبط با متغیر ch قرار می گیرد. به طور کلی، عملوند سمت چپ یک عملگر انتساب باید یک مقدار l باشد. ما نمی توانیم عبارات زیر را بنویسیم:

    // خطاهای کامپایل: مقادیر سمت چپ l-value نیستند // خطا: literal یک l-value نیست 0 = 1. // خطا: عبارت حسابی حقوق و دستمزد l-value نیست + حقوق * 0.10 = new_salary;

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

    // file module0.C // یک رشته نام fileName را تعریف می کند. // ... filename را روی یک مقدار تنظیم کنید
    // فایل module1.C
    // از شی filename استفاده می کند
    // متأسفانه کامپایل نمی شود:
    // نام فایل در module1.C تعریف نشده است
    ifstream input_file(filename);

    C++ مستلزم آن است که یک شی قبل از اولین دسترسی به آن شناخته شود. این به دلیل نیاز به اطمینان از استفاده صحیح از شی با توجه به نوع آن است. در مثال ما، module1.C یک خطای کامپایل ایجاد می کند زیرا fileName در آن تعریف نشده است. برای جلوگیری از این خطا، باید به کامپایلر در مورد متغیر fileName تعریف شده از قبل اطلاع دهیم. این کار با استفاده از عبارت اعلان متغیر انجام می شود:

    // file module1.C // از شی fileName استفاده می کند // fileName اعلام می شود، یعنی برنامه دریافت می کند
    // اطلاعاتی در مورد این شی بدون تعریف ثانویه آن
    نام فایل رشته خارجی; ifstream input_file (نام فایل)

    یک اعلان متغیر به کامپایلر می گوید که یک شی با نام داده شده و از نوع مشخص شده در جایی از برنامه تعریف شده است. زمانی که متغیر تعریف می شود، هیچ حافظه ای به آن اختصاص داده نمی شود. (کلید کلیدی خارجی در بخش 8.2 مورد بحث قرار گرفته است.)
    یک برنامه می تواند شامل هر تعداد اعلان از همان متغیر باشد، اما فقط یک بار می توان آن را تعریف کرد. چنین اعلان‌هایی به راحتی در فایل‌های هدر قرار می‌گیرند، از جمله آنها در ماژول‌هایی که به آنها نیاز دارند. بنابراین می‌توانیم اطلاعات مربوط به اشیاء را در یک مکان ذخیره کنیم و در صورت لزوم امکان اصلاح آن را فراهم کنیم. (در بخش 8.2 در مورد فایل های هدر بیشتر صحبت خواهیم کرد.)

    3.2.2. نام متغیر

    نام متغیر یا مشخص کننده، می تواند شامل حروف لاتین، اعداد و کاراکتر زیرخط باشد. حروف بزرگ و کوچک در نام ها متفاوت است. زبان C++ طول یک شناسه را محدود نمی کند، اما استفاده از نام های بسیار طولانی مانند gosh_this_is_an_impossibly_name_to_type ناخوشایند است.
    برخی از کلمات کلیدواژه های C++ هستند و نمی توان از آنها به عنوان شناسه استفاده کرد. جدول 3.1 فهرست کاملی از آنها را ارائه می دهد.

    جدول 3.1. کلمات کلیدی C++

    asm خودکار بوول زنگ تفريح مورد
    گرفتن کاراکتر کلاس پایان const_cast
    ادامه هید پیش فرض حذف انجام دادن دو برابر
    dynamic_cast دیگر enum صریح صادرات
    خارجی نادرست شناور برای دوست
    رفتن به اگر خطی بین المللی طولانی
    قابل تغییر فضای نام جدید اپراتور خصوصی
    حفاظت شده عمومی ثبت نام reinterpret_cast برگشت
    کوتاه امضاء شده اندازه ایستا static_cast
    ساخت تعویض قالب این پرت كردن
    typedef درست است، واقعی تلاش كردن تایپ شده اسم را تایپ کن
    اتحاد. اتصال اتحاد باطل استفاده كردن مجازی خالی

    برای درک بیشتر متن برنامه خود، توصیه می کنیم از قراردادهای پذیرفته شده عمومی برای نامگذاری اشیا پیروی کنید:

    • نام متغیر معمولا با حروف کوچک نوشته می شود، به عنوان مثال index (برای مقایسه: Index نام نوع است و INDEX یک ثابت است که با استفاده از دستور #define preprocessor تعریف شده است).
    • شناسه باید معنایی داشته باشد که هدف شی را در برنامه توضیح دهد، به عنوان مثال: تولد_تاریخ یا حقوق.

    اگر چنین نامی از چند کلمه تشکیل شده باشد، مانند تاریخ تولد، آنگاه مرسوم است که کلمات را با یک کاراکتر زیرخط (تاریخ_تولد) جدا کنید، یا هر کلمه بعدی را با حروف بزرگ بنویسید (تاریخ_تاریخ). مشاهده شده است که برنامه نویسانی که به رویکرد شی گرا عادت کرده اند، حروف بزرگ کلمات را ترجیح می دهند، در حالی که کسانی_که_نوشته_C زیاد دارند از کاراکتر زیر خط استفاده می کنند. اینکه کدام یک از این دو روش بهتر است یک موضوع سلیقه ای است.

    3.2.3. تعریف شی

    در ساده‌ترین حالت، عملگر تعریف شی شامل موارد زیر است مشخص کننده نوعو نام شیو با نقطه ویرگول به پایان می رسد. مثلا:

    دو برابر حقوق؛ دستمزد دو برابر؛ ماه بین المللی; روز بین المللی; سال بین المللی؛ مسافت طولانی بدون علامت؛

    شما می توانید چندین شی از یک نوع را در یک دستور تعریف کنید. در این مورد، نام آنها با کاما از هم جدا شده است:

    دو برابر حقوق، دستمزد؛ بین ماه، روز، سال؛ مسافت طولانی بدون علامت؛

    صرف تعریف یک متغیر، مقدار اولیه آن را تعیین نمی کند. اگر یک شی به عنوان جهانی تعریف شود، مشخصات C++ تضمین می کند که مقدار اولیه آن به null می شود. اگر متغیر محلی باشد یا به صورت پویا (با استفاده از عملگر جدید) تخصیص داده شود، مقدار اولیه آن تعریف نشده است، یعنی ممکن است حاوی مقداری تصادفی باشد.
    استفاده از متغیرهایی مانند این یک اشتباه بسیار رایج است و همچنین تشخیص آن دشوار است. توصیه می شود به صراحت مقدار اولیه یک شی را مشخص کنید، حداقل در مواردی که مشخص نیست آیا شی می تواند خود را مقداردهی اولیه کند یا خیر. مکانیسم کلاس مفهوم سازنده پیش فرض را معرفی می کند که برای تخصیص مقادیر پیش فرض استفاده می شود. (ما قبلاً در این مورد در بخش 2.3 صحبت کردیم. در بخش های 3.11 و 3.15 کمی بعد در مورد سازنده های پیش فرض صحبت خواهیم کرد، جایی که در مورد کلاس های رشته و پیچیده از کتابخانه استاندارد بحث خواهیم کرد.)

    int main() ( // شی محلی بدون مقدار اولیه int ival;
    // شی از نوع رشته مقداردهی اولیه می شود
    // سازنده پیش فرض
    پروژه رشته;
    // ...
    }

    مقدار اولیه را می توان مستقیماً در عبارت تعریف متغیر مشخص کرد. در C++، دو شکل مقداردهی اولیه متغیر مجاز است - صریح، با استفاده از یک عملگر انتساب:

    Intival = 1024; string project = "Fantasia 2000";

    و ضمنی، با مقدار اولیه مشخص شده در پرانتز:

    Intival(1024); پروژه رشته ("فانتازیا 2000")؛

    هر دو گزینه معادل هستند و مقادیر اولیه را برای متغیر عدد صحیح ival روی 1024 و برای رشته پروژه "Fantasia 2000" تنظیم می کنند.
    هنگام تعریف متغیرها به عنوان یک لیست نیز می توان از مقداردهی اولیه صریح استفاده کرد:

    حقوق دو برابر = 9999.99، دستمزد = حقوق + 0.01; int month = 08; روز=07، سال=1955;

    متغیر به محض تعریف قابل مشاهده (و معتبر در برنامه) می شود، بنابراین می توانیم متغیر دستمزد را با مجموع متغیر حقوق تعریف شده جدید با مقداری ثابت مقداردهی اولیه کنیم. بنابراین تعریف این است:

    // صحیح، اما بی معنی int bizarre = عجیب و غریب;

    از نظر نحوی معتبر است، اگرچه بی معنی است.
    انواع داده های داخلی دارای دستور خاصی برای تنظیم مقدار تهی هستند:

    // ival روی 0 و dval روی 0.0 تنظیم شده است ival = int(); double dval = double();

    در تعریف زیر:

    // int() برای هر یک از 10 عنصر برداری اعمال می شود< int >ivec (10)؛

    هر یک از ده عنصر بردار با int() مقدار دهی اولیه می شود. (ما قبلاً در مورد کلاس برداری در بخش 2.8 صحبت کردیم. برای اطلاعات بیشتر در این مورد به بخش 3.10 و فصل 6 مراجعه کنید.)
    یک متغیر را می توان با بیانی با هر پیچیدگی، از جمله فراخوانی تابع، مقدار دهی اولیه کرد. مثلا:

    #عبارتند از #عبارتند از
    قیمت دو برابر = 109.99، تخفیف = 0.16;
    دو برابر فروش_قیمت (قیمت * تخفیف);
    string pet ("چروک"); extern int get_value(); intval = get_value();
    بدون علامت abs_val = abs(val);

    abs() یک تابع استاندارد است که مقدار مطلق یک پارامتر را برمی گرداند.
    get_value() یک تابع تعریف شده توسط کاربر است که یک مقدار صحیح را برمی گرداند.

    تمرین 3.3

    کدام یک از تعاریف متغیر زیر حاوی خطاهای نحوی است؟

    (a) int car = 1024، auto = 2048; (ب) int ival = ival; (ج) int ival(int()); د) دو برابر حقوق = دستمزد = 9999.99; (ه) cin >> int_value;

    تمرین 3.4

    تفاوت بین l-value و r-value را توضیح دهید. مثال بزن.

    تمرین 3.5

    تفاوت استفاده از متغیرهای name and student را در خط اول و دوم هر مثال بیابید:

    (الف) نام رشته خارجی؛ stringname("ورزش 3.5a"); (ب) بردار خارجی دانش آموزان؛ بردار دانش آموزان؛

    تمرین 3.6

    چه نام اشیایی در C++ مجاز نیست؟ آنها را طوری تغییر دهید که از نظر نحوی صحیح باشند:

    (الف) int double = 3.14159; (ب) بردار< int >_؛ (ج) فضای نام رشته؛ (د) رشته catch-22; (ه) char 1_or_2 = "1"; (f) float Float = 3.14f;

    تمرین 3.7

    تفاوت بین تعاریف متغیر جهانی و محلی زیر چیست؟

    رشته global_class. int global_int; int main() (
    int local_int;
    رشته local_class; //...
    }

    3.3. اشاره گرها

    اشاره گرها و تخصیص حافظه پویا به طور خلاصه در بخش 2.2 معرفی شدند. اشاره گریک شی است که حاوی آدرس یک شی دیگر است و به شما امکان می دهد آن شی را به طور غیر مستقیم دستکاری کنید. به طور معمول، اشاره گرها برای کار با اشیاء ایجاد شده به صورت پویا، برای ساختن ساختارهای داده مرتبط مانند لیست های پیوندی و درختان سلسله مراتبی، و برای ارسال اشیاء بزرگ - آرایه ها و اشیاء کلاس - به عنوان پارامتر به توابع استفاده می شوند.
    هر اشاره گر با نوع خاصی از داده ها مرتبط است و نمایش داخلی آنها به نوع داخلی آن بستگی ندارد: هم اندازه حافظه اشغال شده توسط یک شی از نوع اشاره گر و هم محدوده مقادیر برای آنها یکسان است. تفاوت در نحوه برخورد کامپایلر با شی آدرس دهی شده است. اشاره گرها به انواع مختلف می توانند مقدار یکسانی داشته باشند، اما مکان حافظه که در آن انواع مربوطه قرار دارند می تواند متفاوت باشد:

    • اشاره گر به int حاوی مقدار آدرس 1000 به ناحیه حافظه 1000-1003 هدایت می شود (در یک سیستم 32 بیتی).
    • اشاره گر به دو برابر حاوی مقدار آدرس 1000 به ناحیه حافظه 1000-1007 هدایت می شود (در یک سیستم 32 بیتی).

    در اینجا چند نمونه آورده شده است:

    int *ip1, *ip2; مجتمع *cp; string*psstring; بردار *pvec; دو برابر *dp;

    نشانگر با یک ستاره جلوی نام نشان داده می شود. در تعریف متغیرهای لیست، یک ستاره باید قبل از هر اشاره گر قرار گیرد (به بالا نگاه کنید: ip1 و ip2). در مثال زیر، lp یک اشاره گر به یک شی از نوع long، و lp2 یک شی از نوع long است:

    طولانی *lp، lp2;

    در حالت زیر، fp به عنوان یک شی شناور تفسیر می شود و fp2 یک اشاره گر به آن است:

    Float fp، *fp2;

    عملگر حذف ارجاع (*) را می توان با فاصله از نام و حتی مستقیماً در مجاورت یک کلمه کلیدی نوع جدا کرد. بنابراین، تعاریف زیر از نظر نحوی صحیح و کاملاً معادل هستند:

    //warning: ps2 یک اشاره گر به رشته نیست! رشته* ps، ps2;

    می توان فرض کرد که هر دو ps و ps2 اشاره گر هستند، اگرچه اشاره گر تنها اولین آنهاست.
    اگر مقدار اشاره گر 0 باشد، آنگاه هیچ آدرس شی ای وجود ندارد.
    اجازه دهید یک متغیر از نوع int داده شود:

    Intival = 1024;

    در زیر نمونه هایی از تعریف و استفاده از اشاره گر به int pi و pi2 آورده شده است:

    //pi به آدرس صفر اولیه int *pi = 0;
    // pi2 با آدرس ival مقداردهی اولیه شد
    int *pi2 =
    // صحیح: pi و pi2 حاوی آدرس ival هستند
    pi = pi2;
    // pi2 حاوی آدرس صفر است
    pi2 = 0;

    به یک اشاره گر نمی توان مقداری را که آدرس نیست اختصاص داد:

    // خطا: pi نمی تواند int pi = ival باشد

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

    دو dval; دوبرابر *ps =

    سپس هر دو عبارت انتساب زیر باعث خطای کامپایل می شوند:

    // خطاهای کامپایل // انتساب نوع داده نامعتبر: int*<== double* pi = pd pi = &dval;

    اینطور نیست که متغیر pi نمی تواند آدرس یک شی dval را شامل شود - آدرس اشیاء انواع مختلف طول یکسانی دارند. چنین عملیات اختلاط آدرس عمدا ممنوع است، زیرا تفسیر کامپایلر از اشیا به نوع اشاره گر به آنها بستگی دارد.
    البته، مواقعی وجود دارد که ما به ارزش خود آدرس علاقه مند هستیم، نه شیئی که به آن اشاره می کند (مثلاً می خواهیم این آدرس را با آدرس دیگری مقایسه کنیم). برای حل چنین شرایطی، یک نشانگر خالی ویژه معرفی شده است که می تواند به هر نوع داده ای اشاره کند و عبارات زیر صحیح خواهد بود:

    // صحیح: void* می تواند حاوی // آدرس هایی از هر نوع باشد void *pv = pi; pv=pd;

    نوع شی ای که با void* به آن اشاره می کند ناشناخته است و ما نمی توانیم این شی را دستکاری کنیم. تنها کاری که می‌توانیم با چنین اشاره‌گری انجام دهیم این است که مقدار آن را به یک اشاره‌گر دیگر اختصاص دهیم یا آن را با مقداری آدرس مقایسه کنیم. (در بخش 4.14 بیشتر در مورد نشانگر خالی صحبت خواهیم کرد.)
    برای ارجاع به یک شی که دارای آدرس آن است، باید عملیات عدم ارجاع یا آدرس دهی غیرمستقیم را که با ستاره (*) مشخص می شود، اعمال کنید. داشتن تعاریف متغیر زیر:

    int ival = 1024;, ival2 = 2048; int*pi =

    // انتساب غیرمستقیم ival به ival2 *pi = ival2;
    // استفاده غیر مستقیم از ival به عنوان rvalue و lvalue
    *pi = abs(*pi); // ival = abs(ival);
    *pi = *pi + 1; // ival = ival + 1;

    هنگامی که عملیات آدرس (&) را به یک شی از نوع int اعمال می کنیم، نتیجه نوع int* را دریافت می کنیم.
    int*pi =
    اگر همین عملیات روی یک شی از نوع int* (اشاره‌گر به int) اعمال شود، یک اشاره‌گر به یک اشاره‌گر به int می‌گیریم، یعنی. بینایی**. int** آدرس یک شی است که حاوی آدرس یک شی از نوع int است. با عدم ارجاع ppi، یک شی از نوع int* حاوی آدرس ival دریافت می کنیم. برای به دست آوردن خود شی ival باید دو بار عملیات dereference ppi اعمال شود.

    int **ppi = π int *pi2 = *ppi;
    کوت<< "Значение ival\n" << "явное значение: " << ival << "\n"
    << "косвенная адресация: " << *pi << "\n"
    << "дважды косвенная адресация: " << **ppi << "\n"

    از اشاره گرها می توان در عبارات حسابی استفاده کرد. به مثال زیر توجه کنید، جایی که دو عبارت عملکردهای کاملاً متفاوتی را انجام می دهند:

    int i, j, k; int *pi = // i = i + 2
    *pi = *pi + 2; // آدرس موجود در پی را 2 افزایش دهید
    پی = پی + 2;

    شما می توانید یک مقدار صحیح به یک اشاره گر اضافه کنید، همچنین می توانید از آن کم کنید. افزودن 1 به یک اشاره گر، مقدار آن را با اندازه ناحیه حافظه اختصاص داده شده به یک شی از نوع مربوطه افزایش می دهد. اگر char 1 بایت بگیرد، int 4 و double به 8 ببرد، آنگاه با افزودن 2 به اشاره گرها به char، int و double مقدار آنها به ترتیب 2، 8 و 16 افزایش می یابد، چگونه می توان این را تفسیر کرد؟ اگر اشیایی از همان نوع یکی پس از دیگری در حافظه قرار گیرند، افزایش نشانگر به میزان 1 باعث می شود که به شی بعدی اشاره کند. بنابراین، محاسبات اشاره گر بیشتر در پردازش آرایه استفاده می شود. در هر مورد دیگر آنها به سختی قابل توجیه هستند.
    در اینجا مثالی معمولی از استفاده از حساب آدرس در هنگام تکرار بر روی عناصر یک آرایه با استفاده از یک تکرار کننده به نظر می رسد:

    ابتدا int *iter = int *iter_end =
    در حالی که (iter != iter_end) (
    do_something_with_value(*iter);
    ++ iter;
    }

    تمرین 3.8

    تعاریف متغیر ارائه شده است:

    int ival = 1024، ival2 = 2048; int *pi1 = &ival، *pi2 = &ival2، **pi3 = 0;

    وقتی عملیات انتساب زیر انجام می شود چه اتفاقی می افتد؟ آیا در این مثال ها خطا وجود دارد؟

    (a) ival = *pi3; (e) pi1 = *pi3; (ب) *pi2 = *pi3; (f) ival = *pi1; (ج) ival = pi2; (g) pi1 = ival; (د) pi2 = *pi1; (h) pi3 =

    تمرین 3.9

    دستکاری اشاره گر یکی از مهم ترین جنبه های C و C++ است، اما اشتباه کردن آسان است. مثلا کد

    پی = پی = پی + 1024;

    تقریباً مطمئناً باعث می شود که پی به یک ناحیه تصادفی از حافظه اشاره کند. این اپراتور تخصیص چه کاری انجام می دهد و در چه صورت خطا نمی کند؟

    تمرین 3.10

    این برنامه حاوی خطای مربوط به استفاده نادرست از نشانگرها است:

    int foobar(int *pi) (*pi = 1024; return *pi; )
    int main() (
    int*pi2 = 0;
    int ival = foobar(pi2);
    بازگشت 0;
    }

    خطا چیست؟ چگونه می توانم آن را تعمیر کنم؟

    تمرین 3.11

    خطاهای دو تمرین قبلی به دلیل عدم اعتبارسنجی مقادیر اشاره گر در C++ در زمان اجرا ظاهر می شوند و منجر به عواقب مرگبار می شوند. به نظر شما چرا چنین چکی اجرا نشد؟ آیا می توانید چند دستورالعمل کلی برای ایمن تر کردن کار با اشاره گر ارائه دهید؟

    3.4. انواع رشته

    C++ از دو نوع رشته پشتیبانی می کند، نوع داخلی که از C به ارث رسیده است و کلاس رشته از کتابخانه استاندارد C++. کلاس string ویژگی‌های بسیار بیشتری را ارائه می‌کند و بنابراین استفاده از آن راحت‌تر است، اما در عمل اغلب موقعیت‌هایی پیش می‌آید که باید از یک نوع داخلی استفاده کنید یا درک خوبی از نحوه عملکرد آن داشته باشید. (یک مثال تجزیه گزینه‌های خط فرمان است که به تابع main() منتقل می‌شود. ما این را در فصل 7 پوشش خواهیم داد.)

    3.4.1. نوع رشته داخلی

    همانطور که قبلاً ذکر شد، نوع رشته داخلی به C ++ منتقل شد و از C به ارث رسید. کتابخانه استاندارد C مجموعه ای از توابع را برای دستکاری رشته ها فراهم می کند. مثلا:

    // طول رشته را برمی گرداند int strlen(const char*);
    // دو رشته را با هم مقایسه می کند
    int strcmp(const char*, const char*);
    // یک رشته را در رشته دیگر کپی می کند
    char* strcpy(char*, const char*);

    کتابخانه استاندارد C بخشی از کتابخانه C++ است. برای استفاده از آن، باید یک فایل هدر اضافه کنیم:

    #عبارتند از

    اشاره گر به char که با آن به رشته دسترسی داریم، به آرایه کاراکترهای مربوط به رشته اشاره می کند. حتی زمانی که یک رشته را به صورت تحت اللفظی مانند می نویسیم

    Const char *st = "قیمت یک بطری شراب\n";

    کامپایلر تمام کاراکترهای رشته را در یک آرایه قرار می دهد و سپس آدرس اولین عنصر آرایه را به st اختصاص می دهد. چگونه می توان با استفاده از چنین اشاره گر با رشته ای کار کرد؟
    معمولاً از حساب آدرس برای شمارش کاراکترهای یک رشته استفاده می شود. از آنجایی که رشته همیشه با یک کاراکتر تهی به پایان می رسد، می توانید نشانگر را 1 افزایش دهید تا کاراکتر بعدی صفر شود. مثلا:

    در حالی که (*st++) ( ... )

    st ارجاع داده نمی شود و مقدار حاصل از نظر درستی بررسی می شود. هر مقدار غیر صفر درست در نظر گرفته می شود و بنابراین با رسیدن به یک کاراکتر با کد 0 حلقه به پایان می رسد.عملیات افزایشی ++ 1 را به نشانگر st اضافه می کند و در نتیجه آن را به کاراکتر بعدی ارتقا می دهد.
    در اینجا پیاده سازی تابعی که طول یک رشته را برمی گرداند چگونه ممکن است به نظر برسد. توجه داشته باشید که از آنجایی که یک اشاره گر می تواند حاوی مقدار تهی باشد (به چیزی اشاره نمی کند)، باید قبل از عملیات reference بررسی شود:

    int string_length(const char *st) (int cnt = 0; if (st) while (*st++) ++cnt; return cnt; )

    یک رشته نوع داخلی را می توان در دو حالت خالی در نظر گرفت: اگر نشانگر رشته دارای مقدار تهی باشد (پس اصلاً رشته ای نداریم) یا اگر به آرایه ای متشکل از یک کاراکتر تهی اشاره کند (یعنی یک رشته ای که حاوی یک کاراکتر مهم نیست).

    // pc1 هیچ آرایه کاراکتری را آدرس نمی دهد *pc1 = 0; // آدرس های pc2 کاراکتر null const char *pc2 = "";

    برای یک برنامه نویس تازه کار، استفاده از رشته هایی از نوع داخلی به دلیل سطح بسیار پایین پیاده سازی و ناتوانی در انجام محاسبات آدرس، مملو از خطا است. در زیر برخی از اشتباهات معمول مبتدیان را نشان خواهیم داد. کار ساده است: طول یک رشته را محاسبه کنید. نسخه اول نادرست است. درستش کن

    #عبارتند از const char *st = "قیمت یک بطری شراب\n"; int main() (
    intlen = 0;
    while (st++) ++len; کوت<< len << ": " << st;
    بازگشت 0;
    }

    در این نسخه، اشاره گر st حذف نشده است. بنابراین، این کاراکتری که توسط st به آن اشاره شده است برای برابری 0 نیست، بلکه خود اشاره گر است. از آنجایی که این اشاره گر در ابتدا یک مقدار غیر صفر (آدرس ردیف) داشت، هرگز صفر نمی شود و حلقه به طور نامحدود اجرا می شود.
    در نسخه دوم برنامه این خطا برطرف شده است. برنامه با موفقیت به پایان می رسد، اما نتیجه نادرست است. این بار کجا اشتباه می کنیم؟

    #عبارتند از const char *st = "قیمت یک بطری شراب\n"; int main()
    {
    intlen = 0;
    در حالی که (*st++) ++len; کوت<< len << ": " << st << endl;
    بازگشت 0;
    }

    خطا این است که پس از پایان حلقه، نشانگر st به کاراکتر اصلی literal خطاب نمی‌کند، بلکه کاراکتری را که پس از صفر پایانی این حرف در حافظه قرار دارد، نشان می‌دهد. هر چیزی می تواند در این مکان باشد و خروجی برنامه یک دنباله تصادفی از کاراکترها خواهد بود.
    می توانید سعی کنید این خطا را برطرف کنید:

    St = st - len; کوت<< len << ": " << st;

    اکنون برنامه ما چیزی معنادار را تولید می کند، اما نه به طور کامل. پاسخ به این شکل است:

    18: یک بطری شراب

    فراموش کردیم که در نظر بگیریم که کاراکتر تهی پایانی در طول محاسبه شده لحاظ نشده است. st باید با طول رشته به اضافه 1 جبران شود. در نهایت عبارت صحیح این است:

    St = st - len - 1;

    و در اینجا نتیجه صحیح است:

    18: قیمت یک بطری شراب

    با این حال، نمی توانیم بگوییم که برنامه ما زیبا به نظر می رسد. اپراتور

    St = st - len - 1;

    برای تصحیح اشتباهی که در مرحله اولیه طراحی برنامه ایجاد شده است - به طور مستقیم نشانگر st را افزایش می دهد. این عبارت در منطق برنامه نمی گنجد و درک کد اکنون دشوار است. اصلاحاتی از این نوع اغلب به عنوان وصله نامیده می شود - چیزی که برای ایجاد سوراخ در یک برنامه موجود طراحی شده است. راه حل بسیار بهتر، بازنگری در منطق است. یک گزینه در مورد ما این است که یک اشاره گر دوم را با مقدار st تعریف کنیم:

    Const char *p = st;

    حالا p را می توان در حلقه طول استفاده کرد و مقدار st را بدون تغییر باقی گذاشت:

    در حالی که (*p++)

    3.4.2. کلاس رشته

    همانطور که قبلاً دیدیم، استفاده از نوع رشته داخلی مستعد خطا است و به دلیل این واقعیت که در سطح بسیار پایین اجرا می شود، چندان راحت نیست. بنابراین، بسیار معمول است که کلاس یا کلاس‌های خود را برای نشان دادن یک نوع رشته ایجاد کنید - تقریباً هر شرکت، بخش یا پروژه فردی پیاده‌سازی خاص خود را از یک رشته داشت. چه بگویم، در دو چاپ قبلی این کتاب هم همین کار را کردیم! این امر باعث بروز مشکلاتی در سازگاری و قابلیت حمل برنامه ها شد. اجرای کلاس رشته استاندارد توسط کتابخانه استاندارد C++ به منظور پایان دادن به این اختراع دوچرخه بود.
    بیایید سعی کنیم حداقل مجموعه عملیاتی را که کلاس رشته باید داشته باشد را مشخص کنیم:

    • مقداردهی اولیه با آرایه ای از کاراکترها (رشته ای از نوع داخلی) یا شی دیگری از نوع رشته. نوع توکار قابلیت دوم را ندارد.
    • کپی کردن یک خط به خط دیگر برای یک نوع داخلی، باید از تابع strcpy() استفاده کنید.
    • دسترسی به کاراکترهای جداگانه یک رشته برای خواندن و نوشتن. در آرایه داخلی، این کار با استفاده از عملیات زیرنویس یا آدرس دهی غیر مستقیم انجام می شود.
    • مقایسه دو رشته برای برابری برای یک نوع داخلی، از تابع strcmp() استفاده کنید.
    • الحاق دو رشته، به دست آوردن نتیجه یا به عنوان یک رشته سوم یا به جای یکی از رشته های اصلی. برای یک نوع داخلی، از تابع strcat() استفاده می شود، اما برای دریافت نتیجه در یک خط جدید، باید از توابع strcpy() و strcat() به ترتیب استفاده کنید.
    • محاسبه طول یک رشته با استفاده از تابع strlen() می توانید طول یک رشته نوع داخلی را دریابید.
    • توانایی یافتن خالی بودن یک رشته. برای رشته های داخلی، دو شرط باید برای این منظور بررسی شود: char str = 0; //... اگر (! str || ! *str) بازگشت;

    کلاس string کتابخانه استاندارد C++ همه این عملیات را اجرا می کند (و خیلی بیشتر، همانطور که در فصل 6 خواهیم دید). در این قسمت با نحوه استفاده از عملیات پایه این کلاس آشنا می شویم.
    برای استفاده از اشیاء کلاس رشته، باید فایل هدر مناسب را وارد کنید:

    #عبارتند از

    در اینجا مثالی از یک رشته از بخش قبلی است که با یک شی از نوع رشته نمایش داده شده و با یک رشته کاراکتر مقداردهی اولیه شده است:

    #عبارتند از string st("قیمت یک بطری شراب\n");

    طول رشته توسط تابع عضو size() برگردانده می شود (طول شامل کاراکتر تهی پایانی نمی شود).

    کوت<< "Длина " << st << ": " << st.size() << " символов, включая символ новой строки\n";

    شکل دوم تعریف رشته یک رشته خالی را مشخص می کند:

    رشته st2; // خط خالی

    چگونه بفهمیم یک رشته خالی است؟ البته می توانید طول آن را با 0 مقایسه کنید:

    if (! st.size()) // صحیح: خالی

    با این حال، یک متد ()() ویژه نیز وجود دارد که مقدار true را برای رشته های خالی و false را برای رشته های غیر خالی برمی گرداند:

    if (st.empty()) // صحیح: خالی

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

    رشته st3(st);

    رشته st3 با رشته st مقدار دهی اولیه می شود. چگونه می توانیم مطمئن شویم که این رشته ها مطابقت دارند؟ بیایید از عملگر مقایسه (==):

    اگر (st == st3) // مقداردهی اولیه با موفقیت انجام شد

    چگونه یک خط را به خط دیگر کپی کنیم؟ با عملگر انتساب معمول:

    st2 = st3; // st3 را در st2 کپی کنید

    برای به هم پیوستن رشته ها، از عملگر جمع (+) یا عملگر جمع-انتساب (+=) استفاده کنید. بگذارید دو خط داده شود:

    رشته s1("سلام،"); رشته s2("world\n");

    می‌توانیم یک رشته سوم متشکل از الحاق دو مورد اول بدست آوریم، به این ترتیب:

    رشته s3 = s1 + s2;

    اگر بخواهیم s2 را به انتهای s1 اضافه کنیم باید بنویسیم:

    S1 += s2;

    عملیات جمع می تواند اشیاء کلاس رشته را نه تنها در بین خود، بلکه با رشته هایی از نوع داخلی به هم پیوند دهد. می‌توانید مثال بالا را به گونه‌ای بازنویسی کنید که کاراکترهای خاص و نشانه‌های نقطه‌گذاری با یک نوع داخلی نشان داده شوند و کلمات قابل توجه اشیاء رشته کلاس باشند:

    Const char *pc = ", "; رشته s1 ("سلام"); رشته s2 ("جهان");
    رشته s3 = s1 + pc + s2 + "\n";

    چنین عباراتی کار می کنند زیرا کامپایلر می داند چگونه به طور خودکار اشیاء نوع داخلی را به اشیاء رشته ای تبدیل کند. همچنین می توان به سادگی یک رشته داخلی را به یک شی رشته اختصاص داد:

    رشته s1; const char *pc = "یک آرایه کاراکتر"; s1=pc; // درست

    با این حال، تبدیل معکوس کار نمی کند. تلاش برای انجام اولیه سازی رشته نوع داخلی زیر باعث خطای کامپایل می شود:

    Char*str = s1; // خطای کامپایل

    برای انجام این تبدیل، باید به صراحت تابع عضو را با نام کمی عجیب c_str():

    Char *str = s1.c_str(); // تقریبا درست است

    تابع ()c_str یک اشاره گر را به یک آرایه کاراکتری حاوی رشته شی رشته، همانطور که در نوع رشته داخلی است، برمی گرداند.
    مثال بالا در مورد مقداردهی اولیه یک اشاره گر char *str هنوز کاملاً صحیح نیست. ()c_str یک اشاره گر را به آرایه const برمی گرداند تا از تغییر مستقیم محتویات شیء از طریق این اشاره گر که دارای نوع است جلوگیری کند.

    کاراکتر Const *

    (در قسمت بعدی در مورد کلمه کلیدی const صحبت خواهیم کرد.) مقداردهی اولیه صحیح به صورت زیر است:

    Const char *str = s1.c_str(); // درست

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

    Stringstr("fa.disney.com"); int size = str.size(); برای (int ix = 0; ix< size; ++ix) if (str[ ix ] == ".")
    str[ix] = "_";

    این تمام چیزی است که می‌خواستیم در مورد کلاس رشته بگوییم. در واقع این کلاس ویژگی ها و قابلیت های جالب تری دارد. فرض کنید مثال قبلی با فراخوانی یک تابع ()replacement نیز پیاده سازی شده است:

    Replace(str.begin()، str.end()، "."، "_");

    ()replace یکی از الگوریتم های تعمیم یافته ای است که در بخش 2.8 معرفی کردیم و در فصل 12 به تفصیل توضیح داده خواهد شد. رشته، و عناصر برابر با پارامتر سوم خود را با پارامتر چهارم جایگزین می کند.

    تمرین 3.12

    در عبارات زیر به دنبال خطا باشید:

    (a) char ch = "جاده طولانی و پر پیچ و خم"; (ب) int ival = (c) char *pc = (d) string st(&ch); (ه) pc = 0; (i) pc = "0"؛
    (f) st = pc; (j) st =
    (g) ch = pc; (k) ch = *pc;
    (h) pc = st; (l) *pc = ival;

    تمرین 3.13

    تفاوت رفتار عبارات حلقه زیر را توضیح دهید:

    while (st++) ++cnt;
    در حالی که (*st++)
    ++cnt;

    تمرین 3.14

    دو برنامه معادل معنایی داده شده است. اولی از نوع رشته داخلی استفاده می کند، دومی از کلاس رشته استفاده می کند:

    // ***** پیاده سازی با استفاده از رشته های C ***** #include #عبارتند از
    int main()
    {
    خطاهای int = 0;
    const char *pc = "رشته لفظی بسیار طولانی"; برای (int ix = 0; ix< 1000000; ++ix)
    {
    int len ​​= strlen(pc)؛
    char *pc2 = کاراکتر جدید[ len + 1 ];
    strcpy (pc2، pc)؛
    if (strcmp(pc2, pc))
    ++ خطاها؛ حذف pc2;
    }
    کوت<< "C-строки: "
    << errors << " ошибок.\n";
    ) // ***** پیاده سازی با استفاده از کلاس رشته ***** #include
    #عبارتند از
    int main()
    {
    خطاهای int = 0;
    string str("یک رشته تحت اللفظی بسیار طولانی"); برای (int ix = 0; ix< 1000000; ++ix)
    {
    int len ​​= str.size();
    string str2 = str;
    if (str != str2)
    }
    کوت<< "класс string: "
    << errors << " ошибок.\n;
    }

    این برنامه ها چه کار می کنند؟
    به نظر می رسد که اجرای دوم دو برابر سریعتر از اولین است. آیا انتظار چنین نتیجه ای را داشتید؟ چطور آن را توضیح می دهی؟

    تمرین 3.15

    آیا چیزی وجود دارد که بتوانید در بخش آخر مجموعه عملیات کلاس رشته را بهبود یا اضافه کنید؟ پیشنهادات خود را توضیح دهید

    3.5. مشخص کننده const

    بیایید کد زیر را مثال بزنیم:

    برای(int index = 0; index< 512; ++index) ... ;

    استفاده از 512 literal دو مشکل دارد. اولین مورد سهولت درک متن برنامه است. چرا باید کران بالای متغیر حلقه دقیقاً 512 باشد؟ چه چیزی پشت این ارزش پنهان است؟ او تصادفی به نظر می رسد ...
    مشکل دوم مربوط به سهولت اصلاح و نگهداری کد است. فرض کنید برنامه از 10000 خط تشکیل شده است و 512 به معنای واقعی کلمه در 4٪ آنها رخ می دهد. فرض کنید در 80 درصد موارد باید عدد 512 را به 1024 تغییر دهید.
    هر دوی این مشکلات به طور همزمان حل می شوند: ما باید یک شی با مقدار 512 ایجاد کنیم. با دادن یک نام معنی دار به آن، مانند bufSize، برنامه را بسیار قابل درک تر خواهیم کرد: مشخص است که حلقه دقیقاً چیست. متغیر با.

    فهرست مطالب< bufSize

    در این مورد، تغییر اندازه bufSize نیازی به بررسی 400 خط کد برای تغییر 320 خط از آنها ندارد. چقدر احتمال خطا به قیمت اضافه کردن یک شی کاهش می یابد! اکنون مقدار 512 است محلی شده است.

    bufSize = 512; // اندازه بافر ورودی // ... برای (int index = 0; index< bufSize; ++index)
    // ...

    یک مشکل کوچک باقی می ماند: متغیر bufSize در اینجا یک مقدار l است که می تواند به طور تصادفی در برنامه تغییر کند، که منجر به یک خطای سخت می شود. در اینجا یکی از رایج ترین اشتباهات وجود دارد - استفاده از عملگر انتساب (=) به جای مقایسه (==):

    // تغییر تصادفی مقدار bufSize اگر (bufSize = 1) // ...

    در نتیجه اجرای این کد، مقدار bufSize برابر با 1 می شود که می تواند منجر به رفتار کاملا غیرقابل پیش بینی برنامه شود. تشخیص خطاهای این نوع معمولاً بسیار دشوار است زیرا به سادگی قابل مشاهده نیستند.
    استفاده از مشخص کننده const این مشکل را حل می کند. با اعلام یک شی به عنوان

    Const int bufSize = 512; // اندازه بافر ورودی

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

    // خطا: تلاش برای اختصاص یک مقدار به یک ثابت اگر (bufSize = 0) ...

    از آنجایی که نمی توان به یک ثابت مقداری اختصاص داد، باید در محل تعریف آن مقدار دهی اولیه شود. تعریف یک ثابت بدون مقداردهی اولیه آن نیز باعث خطای کامپایل می شود:

    Const double pi; // خطا: ثابت بدون مقدار اولیه

    حداقل دستمزد دو برابر = 9.60; // درست؟ خطا؟
    دو برابر *ptr =

    آیا کامپایلر باید این تخصیص را مجاز کند؟ از آنجایی که minWage یک ثابت است، نمی توان مقداری به آن اختصاص داد. از سوی دیگر، هیچ چیز ما را از نوشتن منع نمی کند:

    *ptr += 1.40; // شی minWage را تغییر دهید!

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

    Const double *cptr;

    که در آن cptr یک اشاره گر به یک شی از نوع const double است. ظرافت در این واقعیت نهفته است که نشانگر خود یک ثابت نیست، به این معنی که می توانیم مقدار آن را تغییر دهیم. مثلا:

    Const double *pc = 0; const double minWage = 9.60; // صحیح: نمی توان minWage را با کامپیوتر تغییر داد
    pc = دو برابر dval = 3.14; // صحیح: نمی توان minWage را با کامپیوتر تغییر داد
    // اگرچه dval یک ثابت نیست
    pc = // صحیح dval = 3.14159; //درست
    * pc = 3.14159; // خطا

    آدرس یک شیء const فقط به یک اشاره گر به یک ثابت اختصاص داده می شود. با این حال، به چنین اشاره گر می توان آدرس یک متغیر معمولی را نیز اختصاص داد:

    کامپیوتر =

    یک اشاره گر ثابت اجازه نمی دهد که شی مورد نظر توسط آدرس دهی غیرمستقیم اصلاح شود. اگرچه dval در مثال بالا ثابت نیست، کامپایلر اجازه نمی دهد dval از طریق کامپیوتر تغییر کند. (دوباره، زیرا نمی تواند تعیین کند که نشانگر چه آدرسی را در هر نقطه از اجرای برنامه نگه می دارد.)
    در برنامه های واقعی، نشانگرهای ثابت اغلب به عنوان پارامترهای رسمی توابع استفاده می شوند. استفاده از آنها تضمین می کند که شیء ارسال شده به تابع به عنوان آرگومان واقعی توسط آن تابع تغییر نخواهد کرد. مثلا:

    // در برنامه های واقعی، نشانگرهای ثابت اغلب به عنوان پارامترهای رسمی توابع int strcmp (const char *str1, const char *str2) استفاده می شوند.

    (زمانی که در مورد توابع صحبت می کنیم، در فصل 7 بیشتر در مورد نشانگرهای ثابت صحبت خواهیم کرد.)
    همچنین نشانگرهای ثابتی وجود دارد. (به تفاوت بین اشاره گر ثابت و اشاره گر به ثابت توجه کنید!). یک اشاره گر ثابت می تواند هم یک ثابت و هم متغیر را نشان دهد. مثلا:

    Inter errNumb = 0; int *const currErr =

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

    کاری بکنید()؛ اگر (*curErr) (
    errorHandler();
    *curErr = 0; // correct: مقدار errNumb را بازنشانی کنید
    }

    تلاش برای اختصاص یک مقدار به یک اشاره گر ثابت باعث ایجاد یک خطای کامپایل می شود:

    CurErr = // خطا

    یک اشاره گر ثابت به یک ثابت، اتحاد دو حالت در نظر گرفته شده است.

    Const double pi = 3.14159; const double *const pi_ptr = π

    نه مقدار شی مورد اشاره توسط pi_ptr و نه مقدار خود اشاره گر را نمی توان در برنامه تغییر داد.

    تمرین 3.16

    مفهوم پنج تعریف زیر را توضیح دهید. آیا هیچ کدام از آنها اشتباه می کنند؟

    (الف) int i; (د) int *const cpi; (ب) Const int ic; (ه) const int *const cpic; (ج) const int *pic;

    تمرین 3.17

    کدام یک از تعاریف زیر صحیح است؟ چرا؟

    (الف) int i = -1; (ب) const int ic = i; (ج) const int *pic = (d) int *const cpi = (e) const int *const cpic =

    تمرین 3.18

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

    (الف) i = ic; (د) عکس = cpic; (ب) pic = (i) cpic = (ج) cpi = عکس; (f) ic = *cpic;

    3.6. نوع مرجع

    یک نوع مرجع، که گاهی اوقات نام مستعار نیز نامیده می شود، برای دادن یک نام اضافی به یک شی استفاده می شود. یک مرجع به شما امکان می دهد یک شی را به طور غیرمستقیم دستکاری کنید، درست مانند یک اشاره گر. با این حال، این دستکاری غیرمستقیم به نحو خاص مورد نیاز برای اشاره گرها نیاز ندارد. معمولاً از مراجع به عنوان پارامترهای رسمی توابع استفاده می شود. در این بخش، استفاده از اشیاء نوع مرجع به تنهایی را بررسی خواهیم کرد.
    یک نوع مرجع با مشخص کردن عملگر آدرس (&) در مقابل نام متغیر نشان داده می شود. پیوند باید مقداردهی اولیه شود. مثلا:

    Intival = 1024; // صحیح: refVal - ارجاع به ival int &refVal = ival; // خطا: مرجع باید به int مقداردهی شود

    Intival = 1024; // خطا: refVal از نوع int است، نه int* int &refVal = int *pi = // صحیح: ptrVal ارجاع به یک اشاره گر int است *&ptrVal2 = pi;

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

    int min_val = 0; // ival مقدار min_val را می گیرد، // not refVal مقدار را به min_val تغییر می دهد refVal = min_val;

    RefVal += 2; 2 را به ival اضافه می کند، متغیری که refVal به آن ارجاع می دهد. به طور مشابه int ii = refVal; ii را به مقدار فعلی ival تنظیم می کند، int *pi = pi را به آدرس ival مقدار دهی اولیه می کند.

    // دو شی از نوع int تعریف شده اند int ival = 1024, ival2 = 2048; // یک مرجع و یک شی تعریف شده int &rval = ival, rval2 = ival2; // یک شی، یک اشاره گر و یک مرجع تعریف شده است
    int inal3 = 1024، *pi = ival3، &ri = ival3; // دو مرجع تعریف شده int &rval3 = ival3، &rval4 = ival2;

    یک مرجع ثابت را می توان با یک شی از نوع دیگر (البته اگر امکان تبدیل یک نوع به نوع دیگر وجود داشته باشد) و همچنین یک مقدار غیر آدرس مانند یک ثابت تحت اللفظی مقداردهی اولیه کرد. مثلا:

    دو dval = 3.14159; // فقط برای مراجع const درست است
    const int &ir = 1024;
    const int &ir2 = dval;
    const double &dr = dval + 1.0;

    اگر مشخص‌کننده const را مشخص نکنیم، هر سه تعریف مرجع باعث خطای کامپایل می‌شوند. با این حال، دلیل اینکه کامپایلر از چنین تعاریفی چشم پوشی نمی کند، مشخص نیست. بیایید سعی کنیم آن را بفهمیم.
    برای لفظ ها، این کم و بیش روشن است: ما نباید قادر باشیم به طور غیرمستقیم مقدار یک لفظ را با استفاده از اشاره گرها یا مراجع تغییر دهیم. در مورد اشیاء از نوع دیگر، کامپایلر شی اصلی را به یک مورد کمکی تبدیل می کند. مثلاً اگر بنویسیم:

    دو dval = 1024; const int &ri = dval;

    سپس کامپایلر آن را به چیزی شبیه به این تبدیل می کند:

    int temp = dval; const int &ri = temp;

    اگر بتوانیم یک مقدار جدید به مرجع ri اختصاص دهیم، در واقع نه dval، بلکه temp را تغییر می دهیم. مقدار dval یکسان باقی می ماند که برای برنامه نویس کاملاً واضح نیست. بنابراین، کامپایلر چنین اقداماتی را ممنوع می کند و تنها راه برای مقداردهی اولیه یک مرجع با یک شی از نوع متفاوت، اعلام آن به عنوان const است.
    در اینجا مثال دیگری از پیوندی است که درک آن برای اولین بار دشوار است. ما می خواهیم یک مرجع به آدرس یک شی const تعریف کنیم، اما اولین گزینه ما باعث ایجاد یک خطای کامپایل می شود:

    Const int ival = 1024; // خطا: مرجع ثابت مورد نیاز است
    int *&pi_ref =

    تلاش برای رفع مشکل با افزودن مشخص کننده const نیز با شکست مواجه می شود:

    Const int ival = 1024; // همچنان خطای const int *&pi_ref =

    دلیل ش چیه؟ با خواندن دقیق تعریف، خواهیم دید که pi_ref اشاره ای به یک اشاره گر ثابت به یک شی از نوع int است. و ما به یک اشاره گر non-const برای یک شی const نیاز داریم، بنابراین ورودی زیر صحیح خواهد بود:

    Const int ival = 1024; // درست
    int *const &piref=

    دو تفاوت اصلی بین یک مرجع و یک اشاره گر وجود دارد. ابتدا، مرجع باید در محل تعریف آن مقداردهی اولیه شود. ثانیاً، هر تغییری در مرجع، آن را تغییر نمی دهد، بلکه شیئی را که به آن اشاره می کند، تغییر می دهد. بیایید به نمونه هایی نگاه کنیم. اگر بنویسیم:

    int*pi = 0;

    ما نشانگر pi را صفر می کنیم، به این معنی که pi به هیچ شیئی اشاره نمی کند. در عین حال رکورد

    const int &ri = 0;
    یعنی چیزی شبیه این:
    int temp = 0;
    const int &ri = temp;

    در مورد عملیات تخصیص، در مثال زیر:

    int ival = 1024، ival2 = 2048; int *pi = &ival، *pi2 = pi = pi2;

    متغیر ival که توسط pi به آن اشاره شده است بدون تغییر باقی می ماند و pi مقدار آدرس ival2 را دریافت می کند. هر دو pi و pi2 همچنان به یک شی ival2 اشاره می کنند.
    اگر با لینک ها کار کنیم:

    Int &ri = ival، &ri2 = ival2; ri = ri2;

    // مثال استفاده از مراجع // مقدار در پارامتر next_value برگردانده می شود
    bool get_next_value(int &next_value); // بارگذاری بیش از حد عملگر ماتریس+(const Matrix&, const Matrix&);

    اینتیوال در حالی که (get_next_value(ival)) ...

    int &next_value = ival;

    (استفاده از مراجع به عنوان پارامترهای رسمی برای توابع با جزئیات بیشتر در فصل 7 مورد بحث قرار گرفته است.)

    تمرین 3.19

    آیا در این تعاریف خطایی وجود دارد؟ توضیح. چگونه آنها را تعمیر می کنید؟

    (الف) int ival = 1.01; (ب) int &rval1 = 1.01; (ج) int &rval2 = ival; (د) int &rval3 = (e) int *pi = (f) int &rval4 = pi; (g) int &rval5 = pi*; (h) int &*prval1 = pi; (i) const int &ival2 = 1; (j) const int &*prval2 =

    تمرین 3.20

    آیا هر یک از تکالیف زیر (با استفاده از تعاریف تمرین قبلی) اشتباه است؟

    (الف) rval1 = 3.14159; (ب) prval1 = prval2; (ج) prval2 = rval1; (د) *prval2 = ival2;

    تمرین 3.21

    در دستورالعمل های زیر به دنبال خطا باشید:

    (الف) int ival = 0; const int *pi = 0; const int &ri = 0; (ب) پی =
    ری =
    پی =

    3.7. bool را تایپ کنید

    یک شی از نوع bool می تواند یکی از دو مقدار درست و غلط را بگیرد. مثلا:

    // مقداردهی اولیه رشته string search_word = get_word(); // مقداردهی اولیه متغیر پیدا شده
    bool یافت = نادرست; رشته next_word; در حالی که (cin >> next_word)
    if (next_word == search_word)
    یافت = درست
    // ... // کوتاه نویسی: اگر (یافت == درست)
    اگر (پیدا شد)
    کوت<< "ok, мы нашли слово\n";
    دیگر cout<< "нет, наше слово не встретилось.\n";

    اگرچه bool یکی از انواع اعداد صحیح است، اما نمی توان آن را به عنوان علامت، بدون علامت، کوتاه یا طولانی اعلام کرد، بنابراین تعریف فوق اشتباه است:

    // خطا short bool found = false;

    اشیاء از نوع bool به طور ضمنی به نوع int تبدیل می شوند. مقدار true می شود 1 و false 0 می شود. به عنوان مثال:

    bool یافت = نادرست; int origin_count = 0; در حالی که (/* زمزمه */)
    {
    یافت = جستجو_برای(/* چیزی */); // مقدار یافت شده به 0 یا 1 تبدیل می شود
    وقوع_شمار += یافت شد; )

    به همین ترتیب، مقادیر انواع اعداد صحیح و اشاره گرها را می توان به مقادیر bool تبدیل کرد. در این مورد، 0 به عنوان نادرست، و هر چیز دیگر به عنوان درست تفسیر می شود:

    // تعداد رخدادهای extern int find(const string&); bool یافت = نادرست; if (found = find("sebbud")) // correct: found == true // یک اشاره گر به عنصر برمی گرداند
    extern int* find(int value); if (found = find(1024)) // صحیح: found == true

    3.8. شمارش ها

    اغلب لازم است متغیری تعریف شود که مقادیری را از یک مجموعه خاص بگیرد. فرض کنید یک فایل در هر یک از سه حالت باز می شود: برای خواندن، برای نوشتن، برای پیوست.
    البته برای نشان دادن این حالت ها می توان سه ثابت تعریف کرد:

    ورودی Const int = 1; خروجی const int = 2; const int append = 3;

    و از این ثابت ها استفاده کنید:

    bool open_file (رشته file_name، int open_mode); //...
    open_file("Phoenix_and_the_Crane"، ضمیمه)؛

    این راه حل قابل قبول است، اما کاملاً قابل قبول نیست، زیرا نمی توانیم تضمین کنیم که آرگومان ارسال شده به تابع open_file() فقط 1، 2 یا 3 باشد.
    استفاده از نوع برشماری این مشکل را حل می کند. وقتی می نویسیم:

    Enum open_modes (ورودی = 1، خروجی، پیوست)؛

    ما یک نوع جدید open_modes تعریف می کنیم. مقادیر معتبر برای یک شی از این نوع به 1، 2 و 3 محدود می شود و هر یک از مقادیر مشخص شده دارای یک نام یادگاری است. ما می توانیم از نام این نوع جدید برای تعریف یک شی از آن نوع و نوع پارامترهای رسمی تابع استفاده کنیم:

    Open_file باطل (رشته file_name، open_modes om);

    ورودی، خروجی و الحاقی هستند عناصر شمارش. مجموعه عناصر شمارش مجموعه مقادیر معتبر را برای یک شی از یک نوع معین مشخص می کند. متغیری از نوع open_modes (در مثال ما) با یکی از این مقادیر مقداردهی اولیه می شود، همچنین می توان هر یک از آنها را به آن اختصاص داد. مثلا:

    Open_file("ققنوس و جرثقیل"، پیوست);

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

    // خطا: 1 یک عضو enum نیست open_modes open_file("Jonah", 1);

    راهی برای تعریف متغیری از نوع open_modes وجود دارد، مقدار یکی از عناصر شمارش را به آن اختصاص می دهیم و آن را به عنوان پارامتر به تابع ارسال می کنیم:

    open_modes om=input; // ... om = ضمیمه; open_file("TailTell"، om);

    با این حال، نمی توان نام چنین عناصری را به دست آورد. اگر یک دستور خروجی بنویسیم:

    کوت<< input << " " << om << endl;

    ما هنوز دریافت می کنیم:

    این مشکل با تعریف یک آرایه رشته ای حل می شود که در آن عنصر با شاخصی برابر با مقدار عنصر enum نام خود را در خود دارد. با چنین آرایه ای می توانیم بنویسیم:

    کوت<< open_modes_table[ input ] << " " << open_modes_table[ om ] << endl Будет выведено: input append

    همچنین، نمی‌توانید روی همه مقادیر enum تکرار کنید:

    // برای (open_modes iter = input; iter != append; ++inter) پشتیبانی نمی شود // ...

    از کلمه کلیدی enum برای تعریف شمارش استفاده می شود و نام عناصر در پرانتزهای مجعد مشخص می شود که با کاما از هم جدا می شوند. به طور پیش فرض، اولی 0، بعدی 1 و غیره است. اپراتور انتساب می تواند این قانون را تغییر دهد. در این حالت، هر عنصر بعدی بدون مقدار مشخصی 1 بیشتر از عنصر قبل از خود در لیست خواهد بود. در مثال ما به صراحت مقدار 1 را برای ورودی مشخص کرده ایم و خروجی و ضمیمه 2 و 3 خواهد بود. در اینجا مثال دیگری وجود دارد:

    // shape == 0, sphere == 1, cylinder == 2, polygon == 3 enum Forms( share, spre, cylinder, polygon );

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

    // point2d == 2، point2w == 3، point3d == 3، point3w == 4 تعداد امتیاز (point2d=2، point2w، point3d=3، point3w=4);

    شیئی که نوع آن enum است را می توان تعریف کرد، در عبارات استفاده کرد و به عنوان آرگومان به تابع ارسال کرد. چنین شیئی فقط با مقدار یکی از عناصر شمارش مقداردهی اولیه می شود و فقط آن مقدار به طور صریح یا به عنوان مقدار شی دیگری از همان نوع به آن اختصاص داده می شود. حتی مقادیر صحیح مربوط به عناصر شمارش معتبر را نمی توان به آن اختصاص داد:

    Void mumble() ( Points pt3d = point3d; // correct: pt2d == 3 // خطا: pt3w برای تایپ int Points pt3w = 3 مقداردهی اولیه می شود؛ // خطا: چند ضلعی در Points enum نیست pt3w = چند ضلعی؛ // صحیح است : هر دو شی از نوع Points pt3w = pt3d؛ )

    با این حال، در عبارات حسابی، یک enum را می توان به طور خودکار به نوع int تبدیل کرد. مثلا:

    const int array_size = 1024; // صحیح: pt2w به int تبدیل می شود
    int chunk_size = array_size * pt2w;

    3.9. "آرایه" را تایپ کنید

    ما قبلاً در بخش 2.1 آرایه ها را لمس کردیم. آرایه مجموعه ای از عناصر از همان نوع است که با شاخص - شماره ترتیبی عنصر در آرایه - به آنها دسترسی پیدا می شود. مثلا:

    اینتیوال

    ival را به عنوان متغیری از نوع int و عبارت تعریف می کند

    int ia[ 10];

    آرایه ای از ده شی int را مشخص می کند. برای هر یک از این اشیاء، یا عناصر آرایه، با استفاده از عملیات گرفتن شاخص قابل دسترسی است:

    ایوال = ia[ 2 ];

    مقدار عنصر آرایه ia با اندیس 2 را به متغیر ival نسبت می دهد. به طور مشابه

    Ia[ 7 ] = ival;

    عنصر را در شاخص 7 برابر ival قرار می دهد.

    تعریف آرایه شامل یک نوع مشخص کننده، نام آرایه و اندازه است. اندازه تعداد عناصر آرایه (حداقل 1) را مشخص می کند و در پرانتز مربع قرار می گیرد. اندازه یک آرایه باید از قبل در زمان کامپایل شناخته شده باشد، و بنابراین باید یک عبارت ثابت باشد، اگرچه لزومی ندارد که به معنای واقعی کلمه باشد. در اینجا نمونه هایی از تعاریف درست و نادرست آرایه آورده شده است:

    extern int get_size(); // ثابت buf_size و max_files
    const int buf_size = 512، max_files = 20;
    intstaff_size = 27; // صحیح: char ثابت input_buffer[ buf_size ]; // صحیح: عبارت ثابت: 20 - 3 char *fileTable[ max_files-3 ]; // خطا: حقوق دو برابر ثابت نیست[ staff_size ]; // خطا: یک عبارت ثابت نیست int test_scores[ get_size() ];

    اشیاء buf_size و max_files ثابت هستند، بنابراین تعاریف آرایه input_buffer و fileTable صحیح هستند. اما staff_size یک متغیر است (البته به مقدار ثابت 27 مقداردهی اولیه شده است)، بنابراین حقوق غیرقانونی است. (کامپایلر قادر به یافتن مقدار متغیر staff_size در زمان تعریف آرایه حقوق نیست.)
    عبارت max_files-3 را می توان در زمان کامپایل ارزیابی کرد، بنابراین تعریف آرایه fileTable از نظر نحوی صحیح است.
    شماره گذاری عناصر از 0 شروع می شود، بنابراین برای یک آرایه 10 عنصری، محدوده شاخص صحیح 1 - 10 نیست، بلکه 0 - 9 است. در اینجا مثالی از تکرار روی همه عناصر آرایه آمده است:

    int main() (const int array_size = 10; int ia[ array_size ]; for (int ix = 0; ix< array_size; ++ ix)
    ia[ ix ] = ix;
    }

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

    const int array_size = 3; int ia[ array_size ] = ( 0, 1, 2 );

    اگر به صراحت لیستی از مقادیر را مشخص کنیم، نمی توانیم اندازه آرایه را مشخص کنیم: کامپایلر تعداد عناصر را خودش محاسبه می کند:

    // آرایه با اندازه 3 int ia = ( 0, 1, 2 );

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

    // ia ==> ( 0, 1, 2, 0, 0 ) const int array_size = 5; int ia[ array_size ] = ( 0, 1, 2 );

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

    Const char cal = ("C"، "+"، "+"); const char cal2 = "C++";

    بعد آرایه ca1 3 است، بعد آرایه ca2 4 است (کاراکتر تهی پایانی در حرف های رشته ای در نظر گرفته می شود). تعریف زیر باعث خطای کامپایل می شود:

    // error: string "Daniel" از 7 عنصر تشکیل شده است const char ch3[ 6 ] = "Daniel";

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

    const int array_size = 3; int ix, jx, kx; // صحیح: آرایه ای از اشاره گرها از نوع int* int *iar = ( &ix, &jx, &kx ); // خطا: آرایه های مرجع نامعتبر هستند int &iar = ( ix, jx, kx ); int main()
    {
    int ia3( array_size ]; // صحیح است
    // خطا: آرایه های داخلی قابل کپی نیستند
    ia3 = ia;
    بازگشت 0;
    }

    برای کپی کردن یک آرایه به آرایه دیگر، باید این کار را برای هر عنصر جداگانه انجام دهید:

    const int array_size = 7; int ia1 = ( 0، 1، 2، 3، 4، 5، 6); int main() (
    int ia3[اندازه_آرایه]; برای (int ix = 0; ix< array_size; ++ix)
    ia2[ ix ] = ia1[ ix ]; بازگشت 0;
    }

    یک شاخص آرایه می تواند هر عبارتی باشد که یک نتیجه از نوع عدد صحیح به دست می دهد. مثلا:

    int someVal, get_index(); ia2[ get_index() ] = someVal;

    تاکید می کنیم که زبان C++ نه در زمان کامپایل و نه در زمان اجرا کنترلی بر شاخص های آرایه ارائه نمی دهد. خود برنامه نویس باید اطمینان حاصل کند که ایندکس از مرزهای آرایه فراتر نمی رود. خطاهای ایندکس بسیار رایج هستند. متأسفانه، یافتن نمونه‌هایی از برنامه‌هایی که کامپایل می‌شوند و حتی اجرا می‌شوند، اما با این وجود حاوی خطاهای مهلکی هستند که دیر یا زود منجر به خرابی می‌شوند، چندان دشوار نیست.

    تمرین 3.22

    کدام یک از تعاریف آرایه زیر حاوی خطا است؟ توضیح.

    (الف) int ia[buf_size]؛ (د) int ia[ 2 * 7 - 14 ] (ب) int ia[ get_size() ]; (ه) char st[ 11 ] = "بنیادی"; (ج) int ia[ 4 * 7 - 14 ];

    تمرین 3.23

    قطعه کد زیر باید هر عنصر آرایه را با مقدار شاخص مقداردهی اولیه کند. اشتباهاتی که مرتکب شده اید را پیدا کنید:

    int main() (const int array_size = 10; int ia[ array_size ]; for (int ix = 1; ix<= array_size; ++ix)
    ia[ia] = ix; //...
    }

    3.9.1. آرایه های چند بعدی

    در C++ امکان استفاده از آرایه های چند بعدی وجود دارد که باید با مرز سمت راست هر بعد در براکت های مجزا اعلان شوند. در اینجا تعریف آرایه دو بعدی آمده است:

    Int ia[ 4 ][ 3 ];

    مقدار اول (4) تعداد سطرها و دومی (3) تعداد ستون ها را مشخص می کند. شی ia به عنوان آرایه ای از چهار رشته با سه عنصر تعریف می شود. آرایه های چند بعدی نیز می توانند مقداردهی اولیه شوند:

    Int ia[ 4 ][ 3 ] = ( ( 0, 1, 2 ), ( 3, 4, 5 ), ( 6, 7, 8 ), ( 9, 10, 11 ) );

    بریس های مجعد داخلی که لیست مقادیر را به خطوط تقسیم می کنند اختیاری هستند و معمولاً برای خوانایی کد استفاده می شوند. مقداردهی اولیه زیر دقیقاً مشابه مثال قبلی است، اگرچه کمتر واضح است:

    int ia = (0,1,2,3,4,5,6,7,8,9,10,11)؛

    تعریف زیر فقط اولین عناصر هر ردیف را مقداردهی اولیه می کند. عناصر باقیمانده صفر خواهند بود:

    Int ia[ 4 ][ 3 ] = ( (0)، (3)، (6)، (9));

    اگر بریس های مجعد داخلی را حذف کنید، نتیجه کاملاً متفاوت است. هر سه عنصر ردیف اول و عنصر اول دوم مقدار مشخص شده را دریافت می کنند و بقیه به طور ضمنی به 0 مقداردهی اولیه می شوند.

    Int ia[ 4 ][ 3 ] = ( 0, 3, 6, 9 );

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

    int main() (const int rowSize = 4; const int colSize = 3; int ia[ rowSize ][ colSize ]; for (int = 0; i< rowSize; ++i)
    برای (int j = 0; j< colSize; ++j)
    ia[ i ][ j ] = i + j j;
    }

    طرح

    Ia [ 1، 2 ]

    از نظر نحو C++ معتبر است، اما به معنای آن چیزی نیست که یک برنامه نویس بی تجربه انتظار دارد. این به هیچ وجه اعلان یک آرایه دو بعدی 1 در 2 نیست. یک مجموع در پرانتز لیستی از عبارات جدا شده با کاما است که به مقدار نهایی 2 منجر می شود (عملگر "کاما" را در بخش 4.2 ببینید. ). بنابراین، اعلام ia معادل ia است. این یک فرصت دیگر برای اشتباه کردن است.

    3.9.2. رابطه بین آرایه ها و اشاره گرها

    اگر تعریف آرایه ای داشته باشیم:

    Int ia = ( 0، 1، 1، 2، 3، 5، 8، 13، 21);

    پس یک علامت ساده از نام آن در برنامه به چه معناست؟

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

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

    // هر دو عبارت اولین عنصر *ia; ia;

    برای گرفتن آدرس عنصر دوم آرایه، می نویسیم:

    همانطور که قبلا ذکر کردیم، بیان

    همچنین آدرس عنصر آرایه دوم را می دهد. بر این اساس، ارزش آن با دو روش زیر به ما داده می شود:

    *(ia+1)؛ ia;

    به تفاوت عبارات توجه کنید:

    *ia+1 و *(ia+1)؛

    عملیات ارجاع بالاتری دارد یک اولویتاز عملیات جمع (برای اطلاعات بیشتر در مورد اولویت عملگر به بخش 4.13 مراجعه کنید). بنابراین عبارت اول ابتدا متغیر ia را تغییر می دهد و اولین عنصر آرایه را می گیرد و سپس 1 را به آن اضافه می کند و عبارت دوم مقدار عنصر دوم را ارائه می دهد.

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

    #عبارتند از int main() ( int ia = ( 0, 1, 1, 2, 3, 5, 8, 13, 21 )؛ int *pbegin = ia؛ int *pend = ia + 9؛ while (pbegin != pend) ( کوت<< *pbegin <<; ++pbegin; } }

    اشاره گر pbegin با آدرس اولین عنصر آرایه مقدار دهی اولیه می شود. هر تکرار از طریق حلقه این اشاره گر را 1 افزایش می دهد، به این معنی که به عنصر بعدی منتقل می شود. چگونه بدانیم کجا اقامت کنیم؟ در مثال ما یک نشانگر pend دوم تعریف کرده ایم و آن را با آدرسی که آخرین عنصر آرایه ia را دنبال می کند مقداردهی اولیه می کنیم. به محض اینکه مقدار pbegin برابر با pend شد، می دانیم که آرایه تمام شده است. بیایید این برنامه را بازنویسی کنیم تا ابتدا و انتهای آرایه به عنوان پارامتر به یک تابع عمومی که می تواند آرایه ای با هر اندازه ای را چاپ کند، ارسال شود:

    #عبارتند از void ia_print(int *pbegin, int *pend) (
    در حالی که (pbegin != pend) (
    کوت<< *pbegin << " ";
    ++pbegin;
    }
    ) int main()
    {
    int ia = ( 0، 1، 1، 2، 3، 5، 8، 13، 21);
    ia_print(ia, ia + 9);
    }

    تابع ما جهانی تر شده است، با این حال، فقط می تواند با آرایه های int کار کند. راهی برای حذف این محدودیت نیز وجود دارد: تبدیل تابع داده شده به یک الگو (الگوها به طور خلاصه در بخش 2.5 معرفی شدند):

    #عبارتند از قالب void print(elemType *pbegin, elemType *pend) ( while (pbegin != pend) (cout<< *pbegin << " "; ++pbegin; } }

    اکنون می‌توانیم تابع print() خود را برای چاپ آرایه‌هایی از هر نوع فراخوانی کنیم:

    int main() ( int ia = ( 0, 1, 1, 2, 3, 5, 8, 13, 21 )؛ double da = ( 3.14, 6.28, 12.56, 25.12 )؛ string sa = ( "piglet", " چشم، "pooh"); print(ia، ia+9);
    print(da,da+4);
    چاپ (sa, sa+3);
    }

    ما نوشتیم تعمیم یافته استتابع. کتابخانه استاندارد مجموعه ای از الگوریتم های عمومی را ارائه می دهد (که قبلاً در بخش 3.4 به این موضوع اشاره کردیم) که به این روش پیاده سازی شده اند. پارامترهای چنین توابعی نشانگرهایی به ابتدا و انتهای آرایه هستند که با آنها اقدامات خاصی را انجام می دهند. به عنوان مثال، در اینجا نحوه فراخوانی به الگوریتم مرتب سازی تعمیم یافته است:

    #عبارتند از int main() ( int ia = ( 107, 28, 3, 47, 104, 76 ); string sa = ( "piglet", "eeyore", "pooh"); sort(ia, ia+6);
    مرتب سازی (sa, sa+3);
    };

    (ما الگوریتم های تعمیم یافته را به تفصیل در فصل 12 مورد بحث قرار خواهیم داد؛ نمونه هایی از استفاده از آنها در ضمیمه آورده شده است.)
    کتابخانه استاندارد C++ شامل مجموعه ای از کلاس هاست که استفاده از کانتینرها و اشاره گرها را در بر می گیرد. (ما در این مورد در بخش 2.8 بحث کردیم.) در بخش بعدی، بردار استاندارد نوع ظرف را بررسی خواهیم کرد که یک پیاده سازی شی گرا از یک آرایه است.

    3.10. کلاس برداری

    استفاده از کلاس برداری (به بخش 2.8 مراجعه کنید) جایگزینی برای استفاده از آرایه های داخلی است. این کلاس ویژگی های بسیار بیشتری را ارائه می دهد، بنابراین استفاده از آن ترجیح داده می شود. با این حال، شرایطی وجود دارد که نمی توان آرایه هایی از نوع داخلی را کنار گذاشت. یکی از این موقعیت ها پردازش پارامترهای خط فرمان است که به برنامه منتقل می شود که در بخش 7.8 به آن خواهیم پرداخت. کلاس برداری، مانند کلاس رشته، بخشی از کتابخانه استاندارد C++ است.
    برای استفاده از وکتور، باید یک فایل هدر اضافه کنید:

    #عبارتند از

    دو رویکرد کاملاً متفاوت برای استفاده از بردار وجود دارد، بیایید آنها را اصطلاح آرایه و اصطلاح STL بنامیم. در حالت اول، یک شی از کلاس بردار به همان روشی که یک آرایه از نوع داخلی استفاده می شود. بردار یک بعد معین تعریف می شود:

    بردار< int >ivec (10)؛

    که همان تعریف آرایه ای از نوع داخلی است:

    int ia[ 10];

    برای دسترسی به عناصر منفرد بردار، از عملیات گرفتن شاخص استفاده می شود:

    void simp1e_examp1e() (const int e1em_size = 10؛ برداری< int >ivec(e1em_size); int ia[ e1em_size ]; برای (int ix = 0; ix< e1em_size; ++ix)
    ia[ ix ] = ivec[ ix ]; //...
    }

    می‌توانیم اندازه یک بردار را با استفاده از تابع size() دریابیم و با استفاده از تابع ()() خالی بودن بردار را بررسی کنیم. مثلا:

    چاپ_بردار خالی (بردار ivec) (اگر (ivec.empty()) بازگشت؛ برای (int ix=0; ix< ivec.size(); ++ix)
    کوت<< ivec[ ix ] << " ";
    }

    عناصر برداری با مقادیر پیش فرض مقداردهی اولیه می شوند. برای انواع عددی و اشاره گرها، این مقدار 0 است. اگر اشیاء کلاس به عنوان عناصر عمل کنند، آغازگر آنها توسط سازنده پیش فرض مشخص می شود (به بخش 2.3 مراجعه کنید). با این حال، آغازگر را نیز می توان به صراحت با استفاده از فرم مشخص کرد:

    بردار< int >ivec (10, -1)؛

    هر ده عنصر بردار برابر با 1- خواهد بود.
    یک آرایه از نوع داخلی را می توان به طور صریح با یک لیست مقداردهی اولیه کرد:

    int ia[ 6 ] = (-2، -1، 0، 1، 2، 1024);

    برای یک شی از کلاس برداری، عمل مشابه امکان پذیر نیست. با این حال، چنین شیئی را می توان با آرایه ای از نوع داخلی مقداردهی کرد:

    // 6 عنصر ia در بردار ivec کپی می شود< int >ivec(ia، ia+6)؛

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

    // 3 عنصر کپی می شود: بردار ia، ia، ia< int >ivec(&ia[2]، &ia[5]);

    تفاوت دیگر بین یک بردار و یک آرایه از نوع داخلی، توانایی مقداردهی اولیه یک شی از نوع برداری با دیگری و استفاده از عملگر انتساب برای کپی کردن اشیا است. مثلا:

    بردار< string >svec; void init_and_assign() (// یک بردار توسط بردار دیگر مقداردهی اولیه می شود< string >user_names(svec); // ... // یک بردار در دیگری کپی می شود
    svec = user_names;
    }

    صحبت از اصطلاح STL، منظور ما یک رویکرد بسیار متفاوت برای استفاده از یک برداری است. به جای اینکه فوراً اندازه مناسب را تنظیم کنیم، یک بردار خالی تعریف می کنیم:

    بردار< string >متن;

    سپس با استفاده از توابع مختلف به آن عناصر اضافه می کنیم. برای مثال، تابع push_back() یک عنصر را در انتهای یک بردار درج می کند. در اینجا یک قطعه کد است که دنباله ای از خطوط را از ورودی استاندارد می خواند و آنها را به یک بردار اضافه می کند:

    کلمه رشته while (cin >> word) ( text.push_back(word); // ... )

    اگرچه می توانیم از عملیات take index برای تکرار بر روی عناصر یک بردار استفاده کنیم:

    کوت<< "считаны слова: \n"; for (int ix =0; ix < text.size(); ++ix) cout << text[ ix ] << " "; cout << endl;

    معمول‌تر در این اصطلاح استفاده از تکرارکننده‌ها است:

    کوت<< "считаны слова: \n"; for (vector::iterator it = text.begin(); it != text.end(); ++آن) cout<< *it << " "; cout << endl;

    تکرارکننده یک کلاس کتابخانه استاندارد است که در واقع نشانگر عنصری از یک آرایه است.
    اصطلاح

    تکرار کننده را حذف می کند و خود عنصر برداری را می دهد. دستورالعمل

    اشاره گر را به عنصر بعدی می برد. نیازی به ترکیب این دو رویکرد نیست. هنگام تعریف بردار خالی، از اصطلاح STL پیروی کنید:

    بردار ivec;

    اشتباه است اگر بنویسیم:

    ما هنوز یک عنصر از بردار ivec نداریم. تعداد عناصر با استفاده از تابع size() مشخص می شود.

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

    بردار ia (10);

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

    Const int size = 7; int ia [ اندازه ] = ( 0، 1، 1، 2، 3، 5، 8); بردار< int >ivec (اندازه)؛ برای (int ix = 0; ix< size; ++ix) ivec.push_back(ia[ ix ]);

    این به معنای مقداردهی اولیه بردار ivec با مقادیر عناصر ia بود که به جای آن یک بردار ivec با اندازه 14 دریافت کردیم.
    با پیروی از اصطلاح STL، نه تنها می توانید عناصر یک بردار را اضافه کنید، بلکه حذف کنید. (ما همه اینها را به تفصیل و با مثال هایی در فصل 6 پوشش خواهیم داد.)

    تمرین 3.24

    آیا در تعاریف زیر اشتباهاتی وجود دارد؟
    int ia[ 7] = ( 0، 1، 1، 2، 3، 5، 8);

    (الف) بردار< vector< int >>ivec;
    (ب) بردار< int >ivec = (0، 1، 1، 2، 3، 5، 8)؛
    ج) بردار< int >ivec (ia، ia+7)؛
    (د) بردار< string >svec = ivec;
    (ه) بردار< string >svec(10، string("null"));

    تمرین 3.25

    تابع زیر را اجرا کنید:
    bool is_equal(const int*ia, int ia_size,
    بردار const &ivec)؛
    تابع is_equal() دو ظرف را عنصر به عنصر مقایسه می کند. در مورد اندازه های مختلف ظروف، "دم" طولانی تر در نظر گرفته نمی شود. واضح است که اگر همه عناصر مقایسه شده برابر باشند، تابع true و اگر حداقل یکی متفاوت باشد، false را برمی گرداند. از یک تکرار کننده برای تکرار روی عناصر استفاده کنید. یک تابع main() بنویسید که is_equal() را فراخوانی کند.

    3.11. کلاس پیچیده

    کلاس اعداد مختلط کلاس دیگری از کتابخانه استاندارد است. طبق معمول، برای استفاده از آن، باید فایل هدر را وارد کنید:

    #عبارتند از

    یک عدد مختلط دو بخش دارد - واقعی و خیالی. قسمت خیالی جذر یک عدد منفی است. یک عدد مختلط معمولاً به صورت نوشته می شود

    که در آن 2 قسمت واقعی و 3i قسمت خیالی است. در اینجا نمونه هایی از تعاریف پیچیده اشیاء آورده شده است:

    // عدد خیالی خالص: 0 + 7-i مختلط< double >پوره (0, 7); // قسمت خیالی 0: 3 + مجتمع Oi است< float >rea1_num (3); // هر دو بخش واقعی و خیالی 0: 0 + 0-i مختلط هستند< long double >صفر؛ // مقدار دهی اولیه یک عدد مختلط توسط مختلط دیگر< double >purei2 (purei);

    از آنجایی که پیچیده، مانند برداری، یک الگو است، می‌توانیم آن را با float، double و long double مانند مثال‌های بالا نمونه‌سازی کنیم. همچنین می توانید آرایه ای از عناصر پیچیده را تعریف کنید:

    مجتمع< double >مزدوج[ 2 ] = (مختلط< double >(2، 3)، مجتمع< double >(2, -3) };

    مجتمع< double >*ptr = پیچیده< double >&ref = *ptr;

    3.12. دستورالعمل typedef

    دستورالعمل typedef به شما امکان می دهد مترادف یک نوع داده داخلی یا تعریف شده توسط کاربر را مشخص کنید. مثلا:

    دستمزد دو برابر Typedef; وکتور typedef vec_int; typedef vec_int test_scores; typedef bool in_attendance; typedef int *Pint;

    نام های تعریف شده با دستورالعمل typedef را می توان به همان روشی که تعیین کننده نوع استفاده کرد:

    // دو ساعتی، هفتگی؛ دستمزد ساعتی، هفتگی؛ // بردار vecl(10);
    vec_int vecl(10); // بردار test0 (c1ass_size); const int c1ass_size = 34; test_scores test0(c1ass_size); // بردار< bool >حضور؛ بردار< in_attendance >حضور (c1ass_size)؛ // int *table[ 10]; جدول پینت [ 10 ];

    این دستورالعمل با کلمه کلیدی typedef و به دنبال آن یک نوع مشخص کننده شروع می شود و با یک شناسه به پایان می رسد که نام مستعار برای نوع مشخص شده می شود.
    نام های تعریف شده با دستورالعمل typedef برای چه مواردی استفاده می شود؟ با استفاده از نام های یادگاری برای انواع داده، می توانید درک برنامه خود را آسان تر کنید. همچنین مرسوم است که از چنین نام هایی برای انواع پیچیده پیچیده استفاده شود که در غیر این صورت درک آنها دشوار است (به مثال در بخش 3.14 مراجعه کنید)، برای اعلام اشاره گر به توابع و توابع عضو کلاس (به بخش 13.6 مراجعه کنید).
    در زیر نمونه سوالی است که تقریباً همه به آن پاسخ اشتباه می دهند. این خطا به دلیل درک نادرست دستورالعمل typedef به عنوان یک جایگزین ساده متنی کلان ایجاد می شود. تعریف ارائه شده است:

    Typedef char *cstring;

    نوع متغیر cstr در اعلان زیر چیست:

    extern const cstring cstr;

    پاسخی که واضح به نظر می رسد این است:

    Const char *cstr

    با این حال، این درست نیست. مشخص کننده const به cstr اشاره دارد، بنابراین پاسخ صحیح یک اشاره گر const به char است:

    Char *const cstr;

    3.13. مشخص کننده فرار

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

    فرار int disp1ay_register; وظیفه فرار *curr_task; volatile int ixa[ max_size ]; فرار صفحه نمایش bitmap_buf;

    display_register یک شی ناپایدار از نوع int است. curr_task یک اشاره گر به یک شیء Task ناپایدار است. ixa یک آرایه ناپایدار از اعداد صحیح است و هر عنصر از چنین آرایه ای ناپایدار در نظر گرفته می شود. bitmap_buf یک شی فرار از کلاس Screen است که هر یک از اعضای داده آن نیز فرار در نظر گرفته می شود.
    تنها هدف از استفاده از مشخص کننده فرار این است که به کامپایلر بگوییم که نمی تواند تعیین کند چه کسی و چگونه می تواند مقدار یک شی معین را تغییر دهد. بنابراین، کامپایلر نباید کدی را که از این شیء استفاده می کند، بهینه کند.

    3.14. کلاس جفت

    کلاس جفت کتابخانه استاندارد C++ به ما امکان می دهد در صورت وجود رابطه معنایی بین آنها، یک جفت مقادیر را با یک شی تعریف کنیم. این مقادیر می توانند انواع مشابه یا متفاوت باشند. برای استفاده از این کلاس، باید یک فایل هدر اضافه کنید:

    #عبارتند از

    به عنوان مثال، دستورالعمل

    جفت کردن< string, string >نویسنده ("جیمز"، "جویس")؛

    یک شی نویسنده از نوع جفت ایجاد می کند که از دو مقدار رشته تشکیل شده است.
    بخش‌های مجزای یک جفت را می‌توان با استفاده از اعضای اول و دوم بازیابی کرد:

    رشته firstbook; اگر (Joyce.first == "James" &&
    Joyce.second == "جویس")
    firstBook = "Stephen Hero";

    اگر نیاز به تعریف چندین شی از یک نوع از این کلاس دارید، استفاده از دستورالعمل typedef راحت است:

    جفت Typedef< string, string >نویسندگان؛ نویسندگان proust("marcel", "proust"); نویسندگان جویس ("جیمز"، "جویس"); نویسندگان musil("robert", "musi1");

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

    ورودی کلاس؛ extern EntrySlot* 1ook_up(string); جفت typedef< string, EntrySlot* >SymbolEntry; SymbolEntry current_entry("نویسنده"، 1ook_up("نویسنده"));
    // ... if (EntrySlot *it = 1ook_up("ویرایشگر")) (
    current_entry.first = "ویرایشگر";
    current_entry.second = آن;
    }

    (ما به بحث کلاس جفت در فصل 6 در مورد انواع کانتینر و الگوریتم های عمومی در فصل 12 باز خواهیم گشت.)

    3.15. انواع کلاس ها

    مکانیسم کلاس به شما اجازه می دهد تا انواع داده های جدید ایجاد کنید. انواع رشته، برداری، مختلط و جفتی را که در بالا توضیح داده شد معرفی کرد. در فصل 2، ما در مورد مفاهیم و مکانیسم هایی صحبت کردیم که از رویکرد شی گرا و شی گرا پشتیبانی می کنند و از پیاده سازی کلاس Array به عنوان مثال استفاده می کنیم. در اینجا، بر اساس رویکرد شی، یک کلاس String ساده ایجاد می کنیم، که اجرای آن به درک، به ویژه بارگذاری اپراتور کمک می کند - ما در مورد آن در بخش 2.3 صحبت کردیم. (در فصل های 13، 14 و 15 دروس به تفصیل بحث شده است). توضیح مختصری در مورد کلاس داده ایم تا مثال های جالب تری ارائه دهیم. خواننده‌ای که تازه شروع به یادگیری C++ کرده است، می‌تواند این بخش را نادیده بگیرد و منتظر توضیح سیستماتیک‌تر کلاس‌ها در فصل‌های بعدی باشد.)
    کلاس String ما باید از مقداردهی اولیه با یک شی کلاس String، یک string literal و یک نوع رشته داخلی و همچنین عملیات تخصیص مقادیر این نوع به آن پشتیبانی کند. برای این کار از سازنده کلاس و یک عملگر انتساب اضافه بار استفاده می کنیم. دسترسی به کاراکترهای منفرد از یک رشته به عنوان یک عملگر دریافت شاخص اضافه بار اجرا می شود. علاوه بر این، ما به تابع size() برای دریافت اطلاعات در مورد طول رشته نیاز داریم. عملیات مقایسه اشیاء از نوع String و یک شی String با رشته ای از نوع داخلی. و همچنین عملیات I/O شی ما. در نهایت، ما توانایی دسترسی به نمایش داخلی رشته خود را به عنوان یک رشته نوع داخلی اجرا می کنیم.
    تعریف کلاس با کلمه کلیدی کلاس و به دنبال آن یک شناسه، نام کلاس یا نوع شروع می شود. به طور کلی، یک کلاس از بخش هایی تشکیل شده است که قبل از کلمات عمومی (باز) و خصوصی (بسته) قرار می گیرند. یک بخش باز معمولاً شامل مجموعه ای از عملیات پشتیبانی شده توسط کلاس است که متدها یا توابع عضو کلاس نامیده می شوند. این توابع عضو، رابط عمومی کلاس را تعریف می کنند، به عبارت دیگر، مجموعه اقداماتی را که می توان روی اشیاء آن کلاس انجام داد. یک بخش خصوصی معمولاً شامل اعضای داده ای است که یک پیاده سازی داخلی را ارائه می دهند. در مورد ما، اعضای داخلی شامل _string - یک اشاره گر به char، و همچنین _size از نوع int هستند. _size اطلاعات مربوط به طول رشته را ذخیره می کند و _string آرایه ای از کاراکترها است که به صورت پویا تخصیص داده شده است. در اینجا تعریف کلاس به نظر می رسد:

    #عبارتند از کلاس رشته; istream& operator>>(istream&, String&);
    جریان و اپراتور<<(ostream&, const String&); class String {
    عمومی:
    // مجموعه ای از سازنده ها
    // برای مقدار دهی اولیه خودکار
    // Stringstrl; // رشته ()
    // String str2("literal"); // رشته (const char*);
    // رشته str3(str2); // رشته (const String&); string();
    رشته (const char*);
    رشته (const String&); // ویرانگر
    ~string(); // عملگرهای انتساب
    // strl = str2
    // str3 = "a string literal" String& operator=(const String&);
    String& operator=(const char*); // عملگرهای برابری
    // strl == str2;
    // str3 == "یک رشته تحت اللفظی"; عملگر bool==(const String&);
    bool operator==(const char*); // بارگذاری بیش از حد اپراتور دسترسی بر اساس شاخص
    // strl[ 0 ] = str2[ 0 ]; char& operator(int); // دسترسی به اعضای کلاس
    int size() ( return _size; )
    char* c_str() ( return _string; ) private:
    int_size;
    char*_string;
    }

    کلاس String سه سازنده دارد. همانطور که در بخش 2.3 بحث شد، مکانیسم اضافه بار اجازه می دهد تا چندین پیاده سازی از توابع با نام یکسان تعریف شوند، تا زمانی که همه آنها در تعداد و/یا نوع پارامترهایشان متفاوت باشند. اولین سازنده

    این سازنده پیش فرض است زیرا به مقدار اولیه صریح نیاز ندارد. وقتی می نویسیم:

    برای str1، چنین سازنده ای نامیده می شود.
    دو سازنده باقیمانده هر کدام یک پارامتر دارند. بله، برای

    رشته str2 ("رشته کاراکتر");

    سازنده نامیده می شود

    رشته (const char*);

    رشته str3(str2);

    سازنده

    رشته (const String&);

    نوع سازنده فراخوانی شده با نوع آرگومان واقعی تعیین می شود. آخرین سازنده، String(const String&)، سازنده کپی نامیده می شود زیرا یک شی را با یک کپی از یک شی دیگر مقدار دهی اولیه می کند.
    اگر بنویسید:

    stringstr4(1024);

    این باعث یک خطای کامپایل می شود زیرا سازنده ای با پارامتر نوع int وجود ندارد.
    یک اعلامیه اپراتور بارگذاری شده دارای فرمت زیر است:

    عملگر return_type op(parameter_list);

    جایی که عملگر یک کلمه کلیدی است و op یکی از عملگرهای از پیش تعریف شده است: +، =، ==، و غیره. (برای تعریف دقیق نحو، به فصل 15 مراجعه کنید.) در اینجا اعلان عملگر زیر بارگذاری شده است:

    Char& operator(int);

    این عملگر دارای یک پارامتر واحد از نوع int است و یک مرجع به char برمی گرداند. اگر لیست پارامترهای نمونه های جداگانه متفاوت باشد، یک اپراتور بارگذاری شده می تواند خود بارگذاری شود. برای کلاس String خود، دو عملگر تخصیص و برابری متفاوت ایجاد خواهیم کرد.
    برای فراخوانی یک تابع عضو، از نقطه (.) یا فلش (->) اپراتورهای دسترسی عضو استفاده کنید. فرض کنید اعلان شی از نوع String داریم:

    شی رشته ("Danny");
    String *ptr = new String("Anna");
    Stringarray;
    در اینجا فراخوانی تابع size() برای این اشیاء به نظر می رسد:
    بردار اندازه (3)؛

    // دسترسی اعضا برای اشیاء (.); // اشیاء 5 اندازه هستند[ 0 ] = object.size(); // دسترسی اعضا برای نشانگرها (->)
    // ptr دارای اندازه 4 است
    sizes[ 1 ] = ptr->size(); // دسترسی اعضا (.)
    // آرایه اندازه 0 دارد
    sizes[ 2 ] = array.size();

    به ترتیب 5، 4 و 0 را برمی گرداند.
    عملگرهای بارگذاری شده مانند عملگرهای معمولی به یک شی اعمال می شوند:

    نام رشته ("Yadie"); Stringname2 ("Yodie"); // عملگر bool==(const String&)
    اگر (نام == نام 2)
    برگشت؛
    دیگر
    // String& operator=(const String&)
    name=name2;

    یک اعلان تابع عضو باید در داخل تعریف کلاس باشد و یک تعریف تابع می تواند داخل یا خارج از تعریف کلاس باشد. (هر دو size() و c_str() در داخل یک کلاس تعریف شده اند.) اگر تابعی خارج از یک کلاس تعریف شده باشد، باید مشخص کنیم که به کدام کلاس تعلق دارد. در این مورد، تعریف تابع در یک فایل منبع، مثلا String.C، و تعریف خود کلاس در یک فایل هدر (در مثال ما String.h) قرار می‌گیرد، که باید در فایل منبع گنجانده شود:

    // محتویات فایل منبع: String.C // شامل تعریف کلاس String
    #include "String.h" // شامل تعریف تابع strcmp() است
    #عبارتند از
    bool // نوع برگشتی
    رشته:: // کلاسی که تابع به آن تعلق دارد
    عملگر== // نام تابع: عملگر برابری
    (const String &rhs) // لیست پارامترها
    {
    if (_size != rhs._size)
    بازگشت نادرست؛
    strcmp(_stringq، rhs._string) را برگردانید؟
    غلط درست؛
    }

    به یاد بیاورید که strcmp() یک تابع کتابخانه استاندارد C است که دو رشته نوع داخلی را با هم مقایسه می کند و اگر رشته ها مساوی باشند 0 و در غیر این صورت غیر صفر است. عملگر شرطی (?:) مقدار قبل از علامت سوال را آزمایش می کند. اگر درست باشد، مقدار عبارت سمت چپ کولون برگردانده می شود، در غیر این صورت در سمت راست دو نقطه است. در مثال ما، اگر strcmp() یک مقدار غیر تهی را برگرداند، مقدار عبارت false است و اگر null باشد true است. (اپراتور شرطی در بخش 4.7 پوشش داده شده است.)
    عملیات مقایسه اغلب استفاده می شود، تابعی که آن را پیاده سازی می کند کوچک است، بنابراین مفید است که این تابع را داخلی (داخلی) اعلام کنید. کامپایلر متن تابع را به جای فراخوانی جایگزین می کند، بنابراین زمانی برای چنین فراخوانی صرف نمی شود. (توابع داخلی در بخش 7.6 مورد بحث قرار می گیرند.) یک تابع عضو که در یک کلاس تعریف شده است به طور پیش فرض داخلی است. اگر خارج از کلاس تعریف شده باشد، برای اعلام آن به صورت inline، باید از کلمه کلیدی inline استفاده کنید:

    Inline bool String::operator==(const String &rhs) (// همینطور)

    تعریف یک تابع درون خطی باید در یک فایل هدر حاوی تعریف کلاس باشد. با تعریف مجدد عملگر == به عنوان یک عملگر داخلی، باید خود متن تابع را از فایل String.C به فایل String.h منتقل کنیم.
    در زیر اجرای عملیات مقایسه یک شی String با یک رشته نوع داخلی است:

    bool درون خطی String::operator==(const char *s) ( return strcmp(_string, s) ? false: true; )

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

    #عبارتند از // رشته درون خطی سازنده پیش فرض::String()
    {
    _size=0;
    _string=0;
    ) Inline String::String(const char *str) ( if (! str) ( _size = 0; _string = 0; ) other ( _size = str1en(str); strcpy(_string, str);) // سازنده کپی
    رشته درون خطی:: رشته (const String &rhs)
    {
    اندازه = rhs._size;
    if(!rhs._string)
    _string=0;
    دیگر(
    _string = کاراکتر جدید[_size + 1];
    } }

    از آنجایی که ما به صورت پویا حافظه را با عملگر جدید تخصیص دادیم، باید آن را با یک فراخوانی آزاد کنیم تا زمانی که دیگر به شی String نیاز نداریم، آن را حذف کنیم. یکی دیگر از عملکردهای عضو ویژه به این منظور خدمت می کند، تخریبگر، که به طور خودکار برای یک شیء زمانی که آن شیء وجود ندارد فراخوانی می شود. (به فصل 7 در مورد طول عمر شی مراجعه کنید.) نام یک تخریبگر از کاراکتر tilde (~) و نام کلاس تشکیل شده است. در اینجا تعریف تخریبگر کلاس String آورده شده است. اینجاست که ما عملیات حذف را برای آزاد کردن حافظه اختصاص داده شده در سازنده فراخوانی می کنیم:

    رشته درون خطی: :~String() (حذف _string;)

    هر دو اپراتور تخصیص بارگذاری شده از کلمه کلیدی ویژه این استفاده می کنند.
    وقتی می نویسیم:

    نام رشته ("اورویل")، name2 ("ویلبر");
    name = "Orville Wright";
    این یک اشاره گر است که به شی name1 در بدنه تابع عملیات تخصیص آدرس می دهد.
    این همیشه به شی کلاسی که تابع از طریق آن فراخوانی می شود اشاره می کند. اگر
    ptr->size();
    obj[1024];

    سپس در داخل size() مقدار این آدرس ذخیره شده در ptr خواهد بود. در داخل عملیات get index، این شامل آدرس obj است. با عدم ارجاع به این (با استفاده از *this)، خود شی را دریافت می کنیم. (این اشاره گر به تفصیل در بخش 13.4 توضیح داده شده است.)

    رشته و رشته درون خطی::operator=(const char *s) (اگر (! s) ( _size = 0؛ حذف _string؛ _string = 0; ) دیگری ( _size = str1en(s؛ حذف _string؛ _string = کاراکتر جدید[ _size + 1 ]; strcpy(_string, s); ) *this;)

    هنگام اجرای عملیات تخصیص، اغلب یک اشتباه مرتکب می شود: آنها فراموش می کنند بررسی کنند که آیا شیء کپی شده همان چیزی نیست که در آن کپی می شود یا خیر. ما این بررسی را با استفاده از همین نشانگر انجام خواهیم داد:

    رشته و رشته درون خطی::operator=(const String &rhs) ( // در عبارت // namel = *pointer_to_string // this is name1, // rhs - *pointer_to_string. if (this != &rhs) (

    در اینجا متن کامل عملیات اختصاص دادن یک شی از همان نوع به یک شی String است:

    رشته و رشته درون خطی::operator=(const String &rhs) ( if (this != &rhs) (حذف _string؛ _size = rhs._size؛ if (! rhs._string)
    _string=0;
    دیگر(
    _string = کاراکتر جدید[_size + 1];
    strcpy(_string, rhs._string);
    }
    }
    بازگشت *این;
    }

    عملیات زیرنویس تقریباً مشابه پیاده سازی آرایه ای است که در بخش 2.3 ایجاد کردیم:

    #عبارتند از کاراکتر درون خطی&
    رشته::عملگر (عنصر int)
    {
    عنوان (elem >= 0 && elem< _size);
    بازگشت _string[elem];
    }

    عملگرهای ورودی و خروجی به عنوان توابع مجزا و نه اعضای کلاس پیاده سازی می شوند. (در بخش 15.2 به دلایل این موضوع خواهیم پرداخت. بخش های 20.4 و 20.5 بارگذاری بیش از حد عملگرهای ورودی و خروجی کتابخانه iostream را مورد بحث قرار می دهند.) اپراتور ورودی ما نمی تواند بیش از 4095 کاراکتر را بخواند. setw() یک دستکاری کننده از پیش تعریف شده است، تعداد مشخص شده کاراکترها را منهای 1 از جریان ورودی می خواند، بنابراین اطمینان می دهد که بافر داخلی inBuf سرریز نمی شود. (فصل 20 به طور مفصل درباره دستکاری‌کننده setw() بحث می‌کند.) برای استفاده از دستکاری‌کننده‌ها، باید فایل هدر مناسب را وارد کنید:

    #عبارتند از istream& عملگر داخلی>>(istream &io، String &s) (// محدودیت مصنوعی: 4096 کاراکتر const int 1imit_string_size = 4096; char inBuf[ limit_string_size ]; // setw() بخشی از کتابخانه iostream است // بلوک خوانده شده را محدود می کند اندازه به 1imit_string_size -l io >> setw(1imit_string_size) >> inBuf; s = mBuf; // String::operator=(const char*); return io; )

    دستور خروجی نیاز به دسترسی به نمایش داخلی رشته دارد. از آنجایی که اپراتور<< не является функцией-членом, он не имеет доступа к закрытому члену данных _string. Ситуацию можно разрешить двумя способами: объявить operator<< дружественным классу String, используя ключевое слово friend (дружественные отношения рассматриваются в разделе 15.2), или реализовать встраиваемую (inline) функцию для доступа к этому члену. В нашем случае уже есть такая функция: c_str() обеспечивает доступ к внутреннему представлению строки. Воспользуемся ею при реализации операции вывода:

    جریان داخلی و اپراتور<<(ostream& os, const String &s) { return os << s.c_str(); }

    در زیر یک نمونه برنامه است که از کلاس String استفاده می کند. این برنامه کلمات را از جریان ورودی می گیرد و تعداد کل آنها و همچنین تعداد کلمات "the" و "it" را می شمارد و حروف صدادار مواجه شده را ثبت می کند.

    #عبارتند از #include "String.h" int main() ( int aCnt = 0، eCnt = 0، iCnt = 0، oCnt = 0، uCnt = 0، theCnt = 0، itCnt = 0، wdCnt = 0، notVowel = 0؛ / / کلمات "The" و "It"
    // با عملگر==(const char*) بررسی می کنیم
    رشته but, the("the"), it("it"); // عملگر >> (ostream&، String&)
    در حالی که (cin >> buf) (
    ++wdCnt; // اپراتور<<(ostream&, const String&)
    کوت<< buf << " "; if (wdCnt % 12 == 0)
    کوت<< endl; // String::operator==(const String&) and
    // رشته::operator==(const char*);
    if (buf == the | | buf == "The")
    ++theCnt;
    دیگر
    اگر (buf == آن || buf == "این")
    ++itCnt; // رشته::s-ize() را فراخوانی می کند
    برای (int ix = 0; ix< buf.sizeO; ++ix)
    {
    // رشته:: عملگر(int) را فراخوانی می کند
    سوئیچ (buf[ix])
    {
    case "a": case "A": ++aCnt; زنگ تفريح؛
    مورد "e": مورد "E": ++eCnt; زنگ تفريح؛
    case "i": مورد "I": ++iCnt; زنگ تفريح؛
    case "o": case "0": ++oCnt; زنگ تفريح؛
    case "u": case "U": ++uCnt; زنگ تفريح؛
    پیش فرض: ++notVowe1; زنگ تفريح؛
    }
    }
    ) // اپراتور<<(ostream&, const String&)
    کوت<< "\n\n"
    << "Слов: " << wdCnt << "\n\n"
    << "the/The: " << theCnt << "\n"
    << "it/It: " << itCnt << "\n\n"
    << "согласных: " < << "a: " << aCnt << "\n"
    << "e: " << eCnt << "\n"
    << "i: " << ICnt << "\n"
    << "o: " << oCnt << "\n"
    << "u: " << uCnt << endl;
    }

    بیایید برنامه را آزمایش کنیم: یک پاراگراف از یک داستان کودکانه نوشته یکی از نویسندگان این کتاب را به آن پیشنهاد می کنیم (در فصل 6 با این داستان آشنا خواهیم شد). این هم خروجی برنامه:

    آلیس اِما موهای بلند و قرمزی دارد. باباش می گوید وقتی باد میان موهایش می زند، تقریباً زنده به نظر می رسد، مثل یک پرنده آتشین در حال پرواز. او به او می گوید که یک پرنده آتشین زیبا، جادویی اما رام نشده است. او به او می گوید: "بابا، شوش، چنین چیزی وجود ندارد." با خجالت می پرسد: یعنی بابا آنجا هست؟ کلمات: 65
    the/The: 2
    آن/آن: 1
    حروف صامت: 190
    a:22
    e:30
    من:24
    تقریبا 10
    u:7

    تمرین 3.26

    در پیاده سازی سازنده ها و عملگرهای انتساب ما تکرارهای زیادی وجود دارد. همانطور که در بخش 2.3 انجام دادیم، کد تکراری را به یک تابع عضو خصوصی جداگانه منتقل کنید. مطمئن شوید که گزینه جدید کار می کند.

    تمرین 3.27

    برنامه آزمون را تغییر دهید تا صامت های b، d، f، s، t را نیز بشمارید.

    تمرین 3.28

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

    Class String ( public: // ... int count(char ch) const; // ... );

    تمرین 3.29

    عملگر الحاق رشته (+) را طوری پیاده کنید که دو رشته را به هم متصل کرده و نتیجه را در یک شی String جدید برگرداند. در اینجا اعلان تابع است:

    Class String ( public: // ... String operator+(const String &rhs) const; // ... );

    یک نوع داده در برنامه نویسی مجموعه ای از دو مجموعه است: مجموعه ای از مقادیر و مجموعه ای از عملیات که می توان برای آنها اعمال کرد. به عنوان مثال، نوع داده اعداد صحیح غیر منفی، که از مجموعه محدودی از اعداد طبیعی تشکیل شده است، می تواند برای عملیات جمع (+)، ضرب (*)، تقسیم اعداد صحیح (/)، باقیمانده (%) اعمال شود. و تفریق (-).

    یک زبان برنامه نویسی معمولاً دارای مجموعه ای از انواع داده های اولیه است - انواعی که توسط زبان برنامه نویسی به عنوان یک واحد داخلی پایه ارائه می شود. در C++، خالق زبان این گونه انواع را انواع بنیادی می نامد. انواع اساسی در C++ عبارتند از:

    • بولی (بول)؛
    • شخصیت (به عنوان مثال، کاراکتر)؛
    • عدد صحیح (به عنوان مثال int)؛
    • نقطه شناور (به عنوان مثال شناور).
    • enums (تعریف شده توسط برنامه نویس)؛
    • خالی .

    در بالای موارد فوق، انواع زیر ساخته شده است:

    • اشاره گر (به عنوان مثال، int*)؛
    • آرایه ها (به عنوان مثال char)؛
    • مرجع (به عنوان مثال double&);
    • ساختارهای دیگر

    بیایید به مفهوم یک کلمه (به عنوان مثال، 1، 2.4F، 25e-4، 'a'، و غیره) برویم: literal ورودی در کد منبع یک برنامه است که یک مقدار ثابت را نشان می دهد. به عبارت دیگر، Literal صرفاً نمایشی از یک شی (مقدار) از نوعی در کد برنامه است. C++ توانایی نوشتن مقادیر صحیح، مقادیر ممیز شناور، کاراکتر، بولی، مقادیر رشته را دارد.

    Literal نوع صحیح را می توان به صورت زیر نوشت:

    • سیستم شماره 10. برای مثال 1205 ;
    • سیستم شماره هشتم با فرمت 0 + عدد. به عنوان مثال، 0142 ;
    • سیستم شماره شانزدهم با فرمت 0x + عدد. به عنوان مثال، 0x2F.

    24، 030، 0x18 همه ورودی های یکسان در سیستم های اعداد مختلف هستند.
    برای نوشتن اعداد ممیز شناور، از علامت نقطه استفاده می شود: 0.1، 0.5، 4. - یا در
    نماد نمایی - 25e-100. در چنین رکوردی نباید هیچ فاصله ای وجود داشته باشد.

    نامی که می توانیم مقادیر نوشته شده توسط literals را با آن مرتبط کنیم متغیر نامیده می شود. متغیر یک مکان نامگذاری شده یا آدرس پذیر دیگری در حافظه است که آدرس آن می تواند برای دسترسی به داده ها استفاده شود. این داده ها در حین اجرای برنامه به روش خاصی در حافظه نوشته، بازنویسی و پاک می شوند. متغیر به شما امکان می دهد در هر زمان به داده ها دسترسی داشته باشید و در صورت لزوم آنها را تغییر دهید. داده هایی که می توانند از نام متغیر بازیابی شوند، مقدار متغیر نامیده می شوند.
    برای استفاده از یک متغیر در برنامه باید آن را اعلان کرد و در صورت لزوم می توان آن را تعریف کرد (= مقداردهی اولیه). یک اعلان متغیر در متن برنامه لزوماً شامل 2 بخش است: نوع پایه و اعلام کننده. مشخص کننده و اولیه بخش های اختیاری هستند:

    const int مثال = 3; // اینجا const - specifier // int - نوع پایه // مثال - نام متغیر // = 3 - مقداردهی اولیه.

    نام متغیر دنباله ای از کاراکترها از حروف الفبای لاتین (کوچک و بزرگ)، اعداد و/یا زیرخط است، اما کاراکتر اول نمی تواند رقمی باشد. نام متغیر باید به گونه ای انتخاب شود که همیشه به راحتی بتوان حدس زد که چه چیزی را ذخیره می کند، به عنوان مثال "monthPayment". به صورت انتزاعی و عملی، از نماد CamelCase برای قوانین نوشتن متغیرها استفاده می کنیم. نام یک متغیر نمی تواند با کلمات رزرو شده در زبان مطابقت داشته باشد، نمونه هایی از این کلمات: if، while، function، goto، switch و غیره.

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

    • * - اشاره گر؛ قبل از نام
    • *const - نشانگر ثابت؛ قبل از نام
    • & - ارتباط دادن؛ قبل از نام
    • - آرایه؛ بعد از نام؛
    • () - تابع؛ بعد از نام

    یک مقدار دهی اولیه به شما امکان می دهد تا مقدار متغیر را بلافاصله پس از اعلان تعریف کنید. مقداردهی اولیه با لفظ (=) برابر شروع می شود و سپس فرآیند تنظیم مقدار متغیر انجام می شود. به طور کلی، علامت مساوی در C++ یک عملیات انتساب را نشان می دهد. می توان از آن برای تنظیم و تغییر مقدار یک متغیر استفاده کرد. ممکن است برای انواع مختلف متفاوت باشد.

    مشخص کننده ویژگی های اضافی غیر از نوع را مشخص می کند. مشخص کننده const داده شده در مثال به شما امکان می دهد تغییرات بعدی در مقدار متغیر را ممنوع کنید. این گونه متغیرهای تغییرناپذیر ثابت یا ثابت نامیده می شوند.

    اعلان یک ثابت بدون مقداردهی اولیه به دلایل منطقی کار نخواهد کرد:

    Const int EMPTY_CONST; // خطا، متغیر ثابت اولیه نشده const int EXAMPLE = 2; // ثابت با مقدار 2 EXAMPLE = 3; // خطا، تلاش برای اختصاص یک مقدار به یک متغیر ثابت

    برای نامگذاری ثابت ها، مرسوم است که فقط از حروف بزرگ استفاده کنید و کلمات را با یک کاراکتر زیر خط جدا کنید.

    انواع داده های پایه در C++

    در تشریح هر نوع، خواننده باید به خاطر داشته باشد که نوع داده را تعریف کند.

    1. نوع عدد صحیح (char، short (int)، int، long (int، long long)

    از نام به راحتی می توان فهمید که مجموعه مقادیر از اعداد صحیح تشکیل شده است. همچنین مجموعه مقادیر هر یک از انواع فهرست شده می تواند دارای علامت (امضا) یا بدون علامت (بدون علامت) باشد. تعداد عناصر موجود در یک مجموعه به اندازه حافظه مورد استفاده برای ذخیره مقدار آن نوع بستگی دارد. به عنوان مثال، برای متغیری از نوع char، 1 بایت حافظه اختصاص داده می شود، بنابراین کل عناصر به صورت زیر خواهد بود:

    • 2 8N = 2 8 * 1 = 256، که در آن N مقدار حافظه در بایت برای ذخیره مقدار است.

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

    • - برای کاراکتر بدون امضا
    • [-128..127] - برای کاراکتر امضا شده

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

    مقادیر طولانی بدون علامت; // یک نوع بدون علامت عدد صحیح (طولانی) را تعریف می کند.

    انواع ذکر شده فقط در میزان حافظه مورد نیاز برای ذخیره سازی متفاوت است. از آنجایی که زبان C++ یک استاندارد زبان نسبتاً مخصوص ماشین است، فقط شرایط زیر را تضمین می کند:

    • 1 = اندازه کاراکتر ≤ اندازه کوتاه ≤ اندازه int ≤ اندازه بلند.

    معمولاً اندازه انواع به شرح زیر است: char - 1، short - 2، int - 4، long - 8، long long - 8 بایت.

    می توانید عملیات حسابی را با مقادیر نوع صحیح انجام دهید: +, -, *, /, %; عملیات مقایسه: ==، !=،<=, <, >، >=; عملیات بیت: &، |، xor،<<, >>.
    درک اکثر عملیات مانند جمع، ضرب، تفریق و مقایسه آسان است. گاهی اوقات، پس از انجام عملیات حسابی، ممکن است نتیجه خارج از محدوده مقادیر باشد. در این صورت برنامه خطا می دهد.
    تقسیم عدد صحیح (/) قسمت صحیح تقسیم یک عدد صحیح بر دیگری را پیدا می کند. مثلا:

    • 6 / 4 = 1;
    • 2 / 5 = 0;
    • 8 / 2 = 4.

    نماد درصد (%) عملیات تعیین باقیمانده تقسیم دو عدد صحیح را نشان می دهد:

    • 6 % 4 = 2;
    • 10 % 3 = 1.

    درک عملیات به صورت بیتی دشوارتر است: & (AND)، | (OR)، xor (انحصاری OR)،<< (побитовый сдвиг влево), >> (تغییر بیتی به راست).

    عملیات بیت AND، OR و XOR عملیات منطقی مربوطه را برای هر بیت از اطلاعات اعمال می کنند:

    • 1 10 = 01 2
    • 3 10 = 11 2
    • 1 10 & 3 10 = 01 2 & 11 2 = 01 2
    • 1 10 | 3 10 = 01 2 | 11 2 = 11 2
    • 1 10 xor 3 10 = 01 2 xor 11 2 = 10 2

    در پردازش تصویر، از 3 کانال برای رنگ استفاده می شود: قرمز، آبی و سبز - به علاوه شفافیت، که در متغیری از نوع int ذخیره می شود، زیرا هر کانال دارای محدوده ای از مقادیر از 0 تا 255 است. در هگزادسیمال، یک مقدار به صورت زیر نوشته می شود: 0x180013FF; سپس مقدار 18 16 مربوط به کانال قرمز، 00 16 - آبی، 13 16 - سبز، FF - کانال آلفا (شفافیت) است. برای انتخاب یک کانال خاص از چنین عدد صحیحی، از به اصطلاح استفاده کنید. ماسک، که در آن موقعیت های مورد علاقه ما F 16 یا 1 2 هستند. یعنی برای برجسته کردن مقدار کانال آبی، باید از ماسک استفاده کنید. به صورت بیتی و:

    int blue_channel = 0x180013FF & 0x00FF0000;

    پس از آن، مقدار دریافتی با تعداد بیت مورد نیاز به سمت راست منتقل می شود.

    یک شیفت بیتی هر تعداد بیت از یک عدد را که در سمت راست عملیات مشخص شده است به چپ یا راست تغییر می دهد. برای مثال عدد 39 برای نوع char به صورت باینری به صورت زیر نوشته می شود: 00100111. سپس:

    Char binaryExample = 39; // 00100111 نتیجه کاراکتر = باینری مثال<< 2; // сдвигаем 2 бита влево, результат: 10011100

    اگر متغیر از نوع بدون علامت باشد، نتیجه عدد 156 خواهد بود و برای متغیر علامت دار برابر با 100 است. توجه داشته باشید که برای انواع عدد صحیح علامت دار، واحد در مرتبه بالای نمایش بیت نشانه منفی بودن عدد است. در این مورد، یک مقدار به شکل باینری که از همه آنها تشکیل شده است با -1 مطابقت دارد. اگر 1 فقط در مهمترین رقم باشد و ارقام باقیمانده صفر باشند، چنین عددی حداقل مقدار را برای نوع خاصی از مقدار دارد: برای کاراکتر 128- است.

    2. نوع نقطه شناور (شناور، دوگانه (شناور))

    مجموعه مقادیر ممیز شناور زیر مجموعه ای از اعداد واقعی، اما هر عدد واقعی را نمی توان به صورت باینری نشان داد، که گاهی اوقات منجر به اشتباهات احمقانه می شود:

    مقدار شناور = 0.2; مقدار == ​​0.2; // خطا، مقدار در اینجا برابر با 0.2 نخواهد بود.

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

    مقدار - 0.2< 1e-6; // ok, подбирать интервал тоже нужно осторожно

    علاوه بر عملیات مقایسه، نوع ممیز شناور از 4 عملیات حسابی پشتیبانی می کند که کاملاً با عملیات ریاضی با اعداد واقعی مطابقت دارد.

    3. نوع بولی (منطقی) (bool)

    فقط از دو مقدار تشکیل شده است: true (true) و false (false). برای کار با متغیرهایی از این نوع، از عملیات منطقی استفاده می شود: ! (نه)، == (برابری)، != (نابرابری)، && (و منطقی)، || (OR منطقی). نتیجه هر عملیات را می توان در جدول حقیقت مربوطه یافت. مثلا:

    X Y XOR0 0 0 0 1 1 1 0 1 1 1 0

    4. نوع کاراکتر (char، wchar_t)

    نوع کاراکتر نه تنها یک نوع عدد صحیح است (معمولاً به چنین نوع بایتی گفته می شود)، بلکه یک نوع کاراکتر است که شماره کاراکتر جدول را به عنوان یک کاراکتر ASCII ذخیره می کند. به عنوان مثال، کد 0x41 با کاراکتر "A" و 0x71 - "t" مطابقت دارد.

    گاهی اوقات لازم است از کاراکترهایی استفاده کنید که در جدول ASCII ثابت نیستند و بنابراین برای ذخیره شدن به بیش از 1 بایت نیاز دارند. یک شخصیت گسترده (wchar_t) برای آنها وجود دارد.

    5.1. آرایه ها

    آرایه ها به شما امکان می دهند مجموعه ای متوالی از عناصر از یک نوع را ذخیره کنید. یک آرایه در یک بلوک پیوسته در حافظه ذخیره می شود، بنابراین نمی توانید یک آرایه را بدون تعیین اندازه آن اعلام کنید. برای اعلام یک آرایه، بعد از نام متغیر، براکت () را بنویسید که اندازه آن را نشان می دهد. مثلا:

    int myArray; // آرایه ای از 5 عنصر از نوع عدد صحیح

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

    شانس int = (1، 3، 7، 9، 11); // آرایه با 5 مقدار مقداردهی اولیه می شود

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

    شانس // دسترسی به اولین عنصر آرایه. شانس 1 را برمی گرداند. // دسترسی به عنصر سوم. مقدار 7 شانس = 13 را برمی گرداند. // یک مقدار جدید به عنصر پنجم شانس آرایه اختصاص دهید. // خطای دسترسی

    5.3. رشته های

    برای نوشتن یک رشته، برنامه نویسان از این ایده استفاده می کنند که یک رشته یک سری (آرایه) متوالی از کاراکترها است. برای شناسایی انتهای یک خط، استفاده کنید شخصیت خاصانتهای خط: '\0'. این کاراکترهای خاص که از یک بک اسلش و یک کاراکتر شناسایی تشکیل شده اند، کاراکترهای کنترل یا فرار نامیده می شوند. هنوز وجود دارد، برای مثال، '\n' - ابتدای یک خط جدید، '\t' - جدول بندی. برای ضبط یک بک اسلش در یک خط، از escaping استفاده می شود - یک اسلش دیگر قبل از خود علامت قرار می گیرد: '\'. از Escape برای نوشتن گیومه نیز استفاده می شود.

    بیایید یک متغیر رشته ایجاد کنیم:

    Char textExample = ('T', 'e', ​​'s', 't', '\0'); // رشته "Test" نوشته شده است

    یک نماد ساده برای مقداردهی اولیه رشته وجود دارد:

    Char textExample = "تست"; // آخرین کاراکتر نوشته نشده است، اما اندازه آن هنوز 5 است

    بدون پرداختن به جزئیات، در اینجا یک نوع داده مفید دیگر وجود دارد - رشته. رشته های
    از این نوع، برای مثال، می توانید اضافه کنید:

    رشته سلام = "سلام، "; stringname = "Max!"; رشته hello_name = سلام + نام; // رشته "سلام، مکس!"

    6. پیوند

    Int a = 2; // متغیر "a" به مقدار 2 اشاره می کند int &b = a; // متغیر "b" به همان مکان "a" اشاره می کند b = 4; // با تغییر مقدار b، برنامه نویس مقدار a را تغییر می دهد. اکنون a = 4 int & c = 4; // خطا، شما نمی توانید این کار را انجام دهید، زیرا نمی توان به مرجع یک مقدار اختصاص داد

    7. اشاره گر

    برای مقابله با این نوع داده ها، لازم به یادآوری است که مجموعه مقادیر این نوع، آدرس سلول های حافظه است که داده ها از آنجا شروع می شوند. اشاره گر همچنین از عملیات جمع (+)، تفریق (-) و عدم ارجاع (*) پشتیبانی می کند.

    آدرس‌های 0x0 به این معنی است که نشانگر خالی است، یعنی. به هیچ داده ای اشاره نمی کند. این آدرس به معنای واقعی کلمه خود را دارد - NULL:

    Int *nullPtr = NULL; // نشانگر تهی

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

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

    Int valueInMemory = 2; // یک متغیر از نوع عدد صحیح تنظیم کنید int *somePtr = // آدرس متغیر را کپی کنید، در اینجا & - آدرس متغیر somePtr را برمی گرداند. // آدرس سلول حافظه، به عنوان مثال، 0x2F *somePtr; مقدار // در 4 سلول ذخیره می شود: 0x2F، 0x30، 0x31 و 0x32

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

    خود اشاره گر مانند مقادیر متغیرهای انواع دیگر در حافظه ذخیره می شود و 4 بایت طول می کشد، بنابراین می توانید یک اشاره گر به یک اشاره گر ایجاد کنید.

    8. نقل و انتقالات

    Enumerations تنها نوع پایه ای است که توسط برنامه نویس تعریف شده است. به طور کلی، شمارش مجموعه‌ای مرتب از ثابت‌های عدد صحیح نام‌گذاری‌شده است که نام شمارش نوع پایه آن است.

    Enumcolor (قرمز، آبی، سبز)؛

    به طور پیش فرض، قرمز = 0، آبی = 1، سبز = 2. بنابراین، مقادیر را می توان با یکدیگر مقایسه کرد، به عنوان مثال. قرمز< BLUE < GREEN. Программист при объявлении перечисления может самостоятельно задать значения каждой из констант:

    دسترسی به فهرست (READ=1، WRITE=2، EXEC=4)؛

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

    8 10 = 00001000 2

    نتیجه جمع کردن این اعداد با هم همیشه به وضوح نشان می دهد که کدام اعداد اضافه شده اند:

    37 10 = 00100101 2 = 00000001 2 + 00000100 2 + 00100000 2 = 1 10 + 4 10 + 32 10

    خالی

    از نظر نحوی، نوع void یکی از انواع اساسی است، اما تنها می تواند به عنوان بخشی از انواع پیچیده تر استفاده شود، زیرا اشیاء از نوع void وجود ندارند. به طور معمول، این نوع برای اطلاع از اینکه یک تابع مقدار بازگشتی ندارد یا به عنوان نوع پایه اشاره گر به اشیا با انواع تعریف نشده استفاده می شود:

    شیء خالی؛ // خطا، هیچ شیئی از نوع void void // خطا وجود ندارد، هیچ ارجاعی به void void *ptr وجود ندارد. // خوب، یک اشاره گر را به یک نوع ناشناخته ذخیره کنید

    اغلب ما به طور خاص از void استفاده می کنیم تا نشان دهیم یک تابع هیچ مقداری را بر نمی گرداند. یک اشاره گر نوع خالی زمانی مدیریت می شود که برنامه نویس مسئولیت کامل یکپارچگی حافظه و ریختن نوع صحیح را بر عهده بگیرد.

    قالب

    اغلب لازم است که مقدار یک متغیر از یک نوع را به دیگری منتقل کنیم. در موردی که مجموعه مقادیر نوع اصلی زیرمجموعه ای از نوع بزرگتر باشد (مثلا int زیرمجموعه long و long دو برابر است)، کامپایلر می تواند به طور ضمنی ( به طور ضمنی) نوع مقدار را تغییر دهید.

    int عدد صحیح = 2; floating floating = عدد صحیح; // شناور = 2.0

    تبدیل نوع با از دست دادن اطلاعات انجام می شود، بنابراین فقط قسمت صحیح عدد ممیز شناور باقی می ماند، قسمت کسری از بین می رود.

    امکان تبدیل نوع صریح (به صراحت) وجود دارد، برای این کار، در سمت چپ متغیر یا مقداری از نوع اصلی، در داخل پرانتز، نوع ریخته گری را بنویسید:

    مقدار int = (int) 2.5;

    عملیات واحد و باینری

    به عملیاتی که قبلا انجام دادیم باینری می گویند: در سمت چپ و سمت راست نماد عملیات مقادیر یا متغیرهایی وجود دارد، به عنوان مثال، 2 + 3. علاوه بر عملیات باینری، زبان های برنامه نویسی از عملیات یونری نیز استفاده می کنند. که برای متغیرها اعمال می شود. آنها می توانند در سمت چپ یا راست یک متغیر باشند، چندین عملیات از این قبیل قبلاً با آن مواجه شده اند - عملیات ارجاع (*) و گرفتن آدرس یک متغیر (&) یکسان هستند. عملگرهای "++" و "-" مقدار یک متغیر عدد صحیح را به ترتیب 1 افزایش و کاهش می دهند و می توانند در سمت چپ یا راست متغیر نوشته شوند.

    C++ همچنین از نماد مختصر برای عملیات باینری استفاده می کند، زمانی که سمت چپ و راست عبارت حاوی متغیر یکسانی باشد، یعنی. برخی از عملیات با مقدار متغیر انجام می شود و نتیجه عملیات در همان متغیر ذخیره می شود:

    A += 2; // مشابه a = a + 2; b /= 5; // مانند b = b / 5; c &= 3; // مانند c = c & 3;

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

    با تایپ پویا، یک متغیر در زمان مقداردهی اولیه با یک نوع مرتبط است. به نظر می رسد که یک متغیر در قسمت های مختلف کد می تواند انواع مختلفی داشته باشد. تایپ پویا توسط Java Script، Python، Ruby، PHP پشتیبانی می شود.

    تایپ استاتیک برعکس تایپ پویا است. هنگامی که یک متغیر اعلام می شود، نوعی را دریافت می کند که در آینده تغییر نمی کند. زبان های C و C++ فقط همین هستند. این روش برای نوشتن کدهای پیچیده راحت تر است و بسیاری از خطاها در مرحله کامپایل حذف می شوند.

    زبان ها به طور غیررسمی به دو دسته قوی تایپ شده و تایپ ضعیف تقسیم می شوند. تایپ قوی به این معنی است که اگر نوع مورد انتظار و واقعی با هم مطابقت نداشته باشند، کامپایلر خطا می دهد.

    x = 1 + "2"؛ //error - نمی توانید یک علامت کاراکتر به عدد اضافه کنید

    نمونه ای از تایپ ضعیف

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

    کلاس های حافظه

    متغیرها صرف نظر از نوعشان، دامنه و طول عمر خود را دارند.

    کلاس های حافظه:

    • خودکار؛
    • استاتیک؛
    • خارجی؛
    • ثبت نام.

    تمام متغیرهای C به طور پیش فرض محلی هستند. آنها را فقط می توان در داخل یک تابع یا بلوک استفاده کرد. وقتی تابع به پایان می رسد، مقدار آنها از بین می رود.

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

    متغیر خارجی جهانی است. در هر قسمت از کد و حتی در فایل دیگری موجود است.

    مشخص‌کننده‌های نوع داده در C ممکن است در موارد زیر حذف شوند:

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

    برای تعیین انواع ساده، مشخص کننده های int، char، float یا double مشخص می شوند. اصلاح کننده های بدون علامت (بدون علامت)، امضا شده (امضا)، کوتاه، طولانی، طولانی می توانند جایگزین متغیرها شوند.

    به طور پیش فرض، همه اعداد امضا شده اند، بنابراین آنها فقط می توانند در محدوده اعداد مثبت باشند. برای تعریف متغیری از نوع char به صورت signed، signed char را بنویسید. طولانی، طولانی طولانی و کوتاه نشان می دهد که چقدر فضای حافظه برای ذخیره سازی اختصاص داده شده است. بزرگ ترین بلند بلند، کوچکترین کوتاه است.

    Char کوچکترین نوع داده در C است. فقط 1 بایت حافظه به مقادیر ذخیره شده اختصاص داده شده است. به یک متغیر از نوع کاراکتر معمولاً کاراکترهایی اختصاص می‌یابد، و معمولاً به ارقام اختصاص می‌یابد. مقادیر کاراکترها در گیومه قرار می گیرند.

    نوع int اعداد صحیح را ذخیره می کند، اندازه آن تعریف نشده است - بسته به معماری رایانه، حداکثر 4 بایت حافظه را می گیرد.

    تبدیل صریح یک متغیر بدون علامت به صورت زیر ارائه می شود:

    حالت ضمنی به این صورت است:

    اعداد را با یک نقطه شناور و دوبار تعریف کنید. شناورها به صورت -2.3 یا 3.34 نمایش داده می شوند. Double برای دقت بیشتر استفاده می شود - ارقام بیشتری بعد از جداکننده اعشاری مشخص می شوند. این نوع فضای حافظه بیشتری نسبت به شناور اشغال می کند.

    Void یک مقدار خالی دارد. توابعی را تعریف می کند که چیزی را بر نمی گرداند. این مشخص کننده یک مقدار خالی را در آرگومان های متد مشخص می کند. اشاره گرهایی که می توانند هر نوع داده ای را دریافت کنند نیز به عنوان باطل تعریف می شوند.

    نوع بولی

    در تست های وضعیت و حلقه ها استفاده می شود. فقط دو معنی دارد:

    • درست است، واقعی؛
    • دروغ.

    مقادیر بولی را می توان به مقدار int تبدیل کرد. True معادل یک و false برابر با صفر است. تبدیل نوع فقط بین bool و int ارائه می شود، در غیر این صورت کامپایلر خطا ایجاد می کند.

    if (x) (//خطا: "نمی توان به طور ضمنی نوع 'int' را به 'bool' تبدیل کرد""

    if (x != 0) // راه C#

    رشته ها و آرایه ها

    آرایه ها انواع داده های پیچیده در C هستند. JS مانند جاوا اسکریپت یا روبی با رشته ها کار نمی کند. در C، تمام رشته ها آرایه هایی از عناصر ارزش کاراکتر هستند. رشته ها با بایت تهی به پایان می رسند.

    نوع داده توصیفی از محدوده مقادیری است که یک متغیر از نوع مشخص شده می تواند بگیرد. هر نوع داده با موارد زیر مشخص می شود:
    1. تعداد بایت های اشغال شده (اندازه)
    2. محدوده مقادیری که یک متغیر از این نوع می تواند بگیرد.

    تمام انواع داده ها را می توان به انواع زیر تقسیم کرد:
    1. انواع ساده (اسکالری) و پیچیده (بردار).
    2. پایه (سیستم) و کاربر (تعریف شده توسط کاربر).
    در زبان C، سیستم نوع پایه از چهار نوع داده تشکیل شده است:
    1. نمادین،
    2. عدد صحیح،
    3. دقت تک واقعی،
    4. دقت دو برابر واقعی.

    شرح تفصیلی انواع داده در زبان C

    نوع نوع کاراکتر نوع عدد صحیح تک نوع واقعی دقیق دقت دو برابر نوع واقعی
    شرح کاراکتر بین المللی شناور دو برابر
    اندازه 1 بایت (8 بیت) 4 بایت (32 بیت) 4 بایت (32 بیت)
    23 بیت - مانتیس;
    8 بیت - سفارش؛
    1 بیت - علامت.
    8 بایت (64 بیت)
    52 بیت - مانتیس;
    11 بیت - سفارش؛
    1 بیت - علامت.
    محدوده ارزش -128 ... 127 2147483648 ... 2147483647 ±3.4E±38
    دقت تا 7 رقم اعشار
    308±1.7E
    دقت تا 17 رقم اعشار

    زبان C دو نوع تغییر دهنده نوع داده را ارائه می دهد:
    1. اصلاح کننده علامت: امضا شده و بدون امضا.
    2. اصلاح کننده اندازه: کوتاه و بلند.
    اصلاح کننده های نوع با جزئیات بیشتر در جدول توضیح داده شده اند:

    اعداد مختلط در SI

    اعداد مختلط در استاندارد C99 معرفی شده اند.
    float _Complex
    دوتایی _کمپلکس
    بلند دوبل _Complex
    این همه شادی در کتابخانه است مجتمع.h :)

    حداقل و حداکثر مقادیر همه انواع داده های پایه زبان SI در کتابخانه ها توضیح داده شده است: limits.h - شامل محدوده مقادیر صحیح، float.h - شامل محدوده مقادیر واقعی است.

    نوع داده بولی در SI

    استاندارد C89:

    نوع بولی - بین المللی
    0 - نادرست (نادرست)؛
    نه 0 - درست (درست). یعنی به این ترتیب، نوع منطقی ایجاد نمی شود، بلکه به جای آن از int استفاده می شود.
    استاندارد C99:
    نوع بولی - _بول
    کلمات کلیدی: bool true false
    و این شادی در کتابخانه است stdbool.h

    اپراتورهای اعلامیه

    متغیر یک ناحیه حافظه نامگذاری شده از یک رایانه است که برای ذخیره مقادیر از یک نوع خاص طراحی شده است، با یک روش دسترسی دلخواه: خواندن و نوشتن. نام متغیر یک شناسه زبان C قانونی است که قبلاً برای اشاره به سایر متغیرها، انواع، اعضای enum یا نام توابع استفاده نشده بود. عملگر اعلان متغیر دارای نحو زیر است: نوع name1[,name2[,...]]; مثال ها:
    int a, b, c;
    دو برابر x، y;
    چارچ;
    برخی از قوانین ناگفته وجود دارد، به عنوان مثال اجرا که شکل خوبی است، اما انجام این کار ضروری نیست:
    1. هر اعلان متغیرهای یک نوع جدید از یک خط جدید شروع می شود.
    2. از نام متغیر باید مشخص شود که چرا وجود دارد و چه چیزی در آن ذخیره می شود (البته گاهی اوقات به دلیل چنین نام های آموزنده ای، سرعت نوشتن کد کاهش می یابد، زیرا برخی افراد گم می شوند و متغیرها را صدا می کنند. جملات کامل)؛
    3. بنابراین یک قانون وجود دارد: نام متغیر نباید خیلی طولانی باشد.
    4. پس از اعلام یک متغیر، بسیار مطلوب است که در نظرات مشخص کنید که چرا آن است.
    5. لازم است نام متغیرها را با فاصله جدا کنید.
    عملگر اعلان متغیر با مقداردهی اولیه دارای نحو زیر است: نوع name1[=value1][, name2[=value2][,...]]; مثال ها:
    int a=26, b=032, c=0x1A;
    دو برابر x=2.5e2,y=0x1.ffe-3;
    charch='Z';

    ثابت های SI

    سه نوع ثابت در زبان C وجود دارد:
    1. عدد صحیح،
    2. واقعی،
    3. نمادین.
    ثابت های عدد صحیح
    1. ثابت اعشاری با یک عدد اعشاری به شکل معمول نشان داده می شود.
    2. یک ثابت هشتی با عددی نشان داده می شود که با رقم صفر شروع می شود و شامل ارقام 0...7 است.
    3. یک ثابت هگزادسیمال با یک عدد صحیح با پیشوند 0x یا 0X نشان داده می شود که شامل اعداد 0...9 و حروف الفبای لاتین a...f، A...F است.
    ثابت های واقعی به صورت اعشاری یا هگزادسیمال نوشته می شوند. موقعیت کاما با یک نقطه نشان داده می شود، توان بعد از حرف لاتین e (یا E) نشان داده می شود. قبل از ثابت های کاراکتر یک کاراکتر \ وجود دارد، این به اصطلاح "فرار" است. در زبان C کاراکترهای خاصی وجود دارد:
    "\" - نقل قول تک،
    "\" - دو نقل قول،
    "\\" - اسلش معکوس،
    '\؟' - علامت سوال،
    "\a" - سیگنال صوتی،
    "\b" - حذف یک کاراکتر،
    "\f" - پیمایش صفحه،
    "\n" - فید خط،
    "\r" - بازگشت کالسکه به ابتدای خط،
    "\t" - زبانه افقی،
    '\v' - برگه عمودی.

    در C نیز می توانید متغیرهایی ایجاد کنید که مقدار ثابتی دارند (مقدار آنها قابل تغییر نیست). اعلان چنین "متغیرهایی" دارای نحو زیر است: نوع const name1=value1[,name2=value2[,...]]; مثال ها:
    const بدون علامت int x=80, y=25;
    const double pi=3.1415;

    اپراتور برای ایجاد انواع داده در SI

    عملگر typedef برای ایجاد انواع داده های سفارشی استفاده می شود، نحو استفاده از آن عبارت است از: typedef old_type_name new_type_name; مثال:
    typedef intword بدون علامت;
    در SI، طبق استاندارد، تعریف نوع را می توان تقریباً در هر نقطه از برنامه انجام داد (یعنی هیچ بلوک کاملاً تعریف شده ای برای تعریف انواع داده وجود ندارد). تابعی برای تعیین اندازه یک نوع یا یک متغیر از هر نوع: sizeof ، تعداد بایت های اشغال شده در حافظه را برمی گرداند. مثال:
    sizeof(int) //بازگشت 4
    sizeof(char) //نتیجه 1
    sizeof(double) // 8 را برمی گرداند