ما هي أنواع البيانات الموجودة في لغة C. المتغيرات في لغة C

04.04.2019
نوع البيانات هو وصف لنطاق القيم التي يمكن أن يأخذها متغير من نوع محدد. ويتميز كل نوع من البيانات بما يلي:
1. عدد البايتات المشغولة (الحجم)
2. مدى القيم التي يمكن أن يأخذها متغير من هذا النوع.

يمكن تقسيم جميع أنواع البيانات إلى الأنواع التالية:
1. الأنواع البسيطة (العددية) والمعقدة (المتجهة)؛
2. الأساسي (النظام) والمستخدم (المحدد من قبل المستخدم).
في لغة SI النظام أنواع أساسيةتشكل أربعة أنواع من البيانات:
1. رمزي،
2. عدد صحيح،
3. دقة واحدة حقيقية،
4. دقة مزدوجة حقيقية.

وصف تفصيلي لأنواع البيانات في لغة SI

يكتب نوع الحرف نوع عدد صحيح نوع حقيقي ذو دقة واحدة دقة مزدوجة النوع الحقيقي
وصف شار كثافة العمليات يطفو مزدوج
مقاس 1 بايت (8 بت) 4 بايت (32 بت) 4 بايت (32 بت)
23 بت – الجزء العشري؛
8 بتات – الترتيب؛
1 بت - التوقيع.
8 بايت (64 بت)
52 بت – الجزء العشري؛
11 بت – الترتيب؛
1 بت - التوقيع.
مدى من القيم -128 ... 127 2147483648 ... 2147483647 ±3.4E±38
دقة تصل إلى 7 منازل عشرية
±1.7E±308
دقة تصل إلى 17 منزلة عشرية

توفر لغة SI نوعين من معدّلات نوع البيانات:
1. معدّلات التوقيع: موقعة وغير موقعة.
2. معدلات الحجم: قصيرة وطويلة.
يتم وصف معدّلات النوع بمزيد من التفاصيل في الجدول:

الأعداد المركبة في SI

تم تقديم الأعداد المركبة في معيار C99.
float_Complex
double_Complex
مجمع مزدوج طويل
كل هذه السعادة في المكتبة معقدة.ح :)

الحد الأدنى و القيم القصوىجميع أنواع البيانات الأساسية للغة SI موصوفة في المكتبات: Limits.h - تحتوي على نطاقات من القيم الصحيحة، وfloat.h - تحتوي على نطاقات من القيم الحقيقية.

نوع البيانات المنطقية في SI

معيار C89:

النوع المنطقي – كثافة العمليات
0 - خطأ؛
ليس 0 - صحيح. وهذا هو، على هذا النحو نوع منطقيلم يتم إنشاؤه، ولكن يتم استخدام int بدلاً من ذلك.
معيار C99:
النوع المنطقي - _بول
الكلمات الرئيسية: منطقي صحيح كاذب
وهذه هي السعادة في المكتبة stdbool.h

مشغلي الإعلان

متغير - منطقة الذاكرة المسماة حاسوب، مصمم لتخزين قيم من نوع معين، بطريقة الوصول العشوائي: القراءة والكتابة. اسم المتغير هو معرف SI قانوني لم يتم استخدامه مسبقًا لتعيين متغيرات أو أنواع أو أعضاء تعداد أو أسماء وظائف أخرى. يحتوي عامل تعريف المتغير على الصيغة التالية: اكتب name1[,name2[,...]]; أمثلة:
كثافة العمليات أ، ب، ج؛
مزدوج س، ذ؛
شار الفصل؛
هناك بعض القواعد غير المعلنة، أي أن التنفيذ جيد الشكل، لكن ليس من الضروري القيام بذلك:
1. يبدأ كل إعلان لمتغيرات من نوع جديد بسطر جديد؛
2. يجب أن يكون واضحًا من اسم المتغير سبب وجوده وما سيتم تخزينه فيه (على الرغم من أنه في بعض الأحيان تنخفض سرعة كتابة التعليمات البرمجية بسبب هذه الأسماء المفيدة، لأن بعض الأشخاص يشعرون بالارتباك ويطلقون على المتغيرات جملًا كاملة)؛
3. لذلك تظهر القاعدة: يجب ألا يكون اسم المتغير طويلاً جدًا؛
4. بعد الإعلان عن المتغير، من المستحسن جدًا الإشارة في التعليقات إلى الغرض منه؛
5. من الضروري الفصل بين أسماء المتغيرات بمسافات.
يحتوي عامل تعريف المتغير مع التهيئة على الصيغة التالية: اكتب name1[=value1][, name2[=value2][,...]]; أمثلة:
كثافة العمليات أ = 26، ب = 032، ج = 0x1A؛
مزدوج x=2.5e2,y=0x1.ffe-3;
شار ch='Z';

الثوابت في SI

هناك ثلاثة أنواع من الثوابت في لغة SI:
1. الأعداد الصحيحة،
2. حقيقي،
3. رمزي.
ثوابت عددية
1. يشار إلى الثابت العشري عدد عشريبالشكل المعتاد.
2. تتم الإشارة إلى الثابت الثماني برقم يبدأ بالرقم صفر ويحتوي على الأرقام 0...7.
3. تتم الإشارة إلى الثابت الست عشري بواسطة عدد صحيح مع البادئة 0x أو 0X، يحتوي على الأرقام 0...9 وحروف الأبجدية اللاتينية a...f، A...F.
الثوابت الحقيقية تكتب بالنظام العشري أو نظام سداسي عشريحساب التفاضل والتكامل. يُشار إلى موضع الفاصلة بنقطة، ويُشار إلى الأس بعدها حرف لاتينيه (أو ه). ثوابت الأحرف مسبوقة بحرف \، وهذا ما يسمى "الهروب". هناك أحرف خاصة في SI:
'\'' - اقتباس واحد،
'\"' - اقتباس مزدوج،
'\\' - شرطة مائلة عكسية،
'\؟' - علامة استفهام،
"\a" - إشارة صوتية،
'\b' - حذف الحرف،
'\f' - تمرير الصفحة،
'\n' - تغذية السطر،
'\r' - حرف العودة إلى بداية السطر،
"\t" - علامة تبويب أفقية،
'\v' - علامة تبويب عمودية.

في SI، يمكنك أيضًا إنشاء متغيرات لها قيمة ثابتة (لا يمكن تغيير قيمتها). يحتوي الإعلان عن مثل هذه "المتغيرات" على الصيغة التالية: const type name1=value1[,name2=value2[,...]]; أمثلة:
const unsigned int x=80, y=25;
ثابت مزدوج بي = 3.1415؛

عامل إنشاء أنواع البيانات في SI

يتم استخدام عامل التشغيل typedef للإنشاء أنواع مخصصةالبيانات، بناء الجملة لاستخدامها: typedef old_type_name new_type_name؛ مثال:
typedef كلمة int غير موقعة؛
في SI، وفقًا للمعيار، يمكن إجراء تعريف النوع في أي مكان تقريبًا في البرنامج (أي لا توجد كتلة محددة بدقة لتحديد أنواع البيانات). وظيفة تحديد حجم النوع، أو متغير من أي نوع: sizeof فإنه يقوم بإرجاع عدد البايتات المحتلة في الذاكرة. مثال:
حجم (كثافة العمليات) // العودة 4
حجم (شار) // النتيجة 1
حجم (مزدوج) // إرجاع 8

يتم استخدام المتغيرات لتخزين البيانات المختلفة في لغات البرمجة. المتغير عبارة عن منطقة من الذاكرة لها اسم، ويُطلق عليها أيضًا اسم المعرف.

إعطاء اسم المتغيريقوم المبرمج في نفس الوقت بتسمية منطقة الذاكرة حيث سيتم كتابة قيم المتغير للتخزين بنفس الاسم.

إنه أسلوب جيد لتسمية المتغيرات بشكل مفيد. يُسمح باستخدام الأحرف الصغيرة و الحروف الكبيرةوالأرقام والشرطة السفلية التي تعتبر حرفًا في لغة C. يجب أن يكون الحرف الأول حرفًا، ويجب ألا تكون هناك مسافات في اسم المتغير. في الإصدارات الحديثةليس لدى المترجمين عملياً أي حدود لطول الاسم. لا يمكن أن يتطابق اسم المتغير مع الكلمات الأساسية المحجوزة. رأس المال و أحرف صغيرةأسماء المتغيرات تختلف، المتغيرات أو أ- متغيرات مختلفة.

الكلمات الرئيسية المحجوزةتلقائي مزدوج int struct Break وإلا طويل التبديل تسجيل tupedef char extern return void case float unsigned default forsign union do if sizeof volatile continue enum short while
في لغة C، يجب الإعلان عن كافة المتغيرات. وهذا يعني أنه، أولاً، في بداية كل برنامج أو وظيفة، يجب عليك تقديم قائمة بجميع المتغيرات المستخدمة، وثانيًا، الإشارة إلى نوع كل منها.

عندما أعلن مترجم متغيريعين له مكانا في الذاكرة اعتمادا على نوعه. بالوسائل القياسيةيعمل AVR دول مجلس التعاون الخليجي مع أنواع البيانات شار(نوع الحرف) و كثافة العمليات(نوع عدد صحيح).

أنواع متغيرة

اكتب شار

شار- هو النوع الأكثر اقتصادا. يمكن أن يكون نوع char موقعًا أو غير موقع. يُشار إليه وفقًا لذلك بـ " حرف موقّع"(النوع الموقع) و" حرف غير موقعة" (نوع غير موقع). يمكن للنوع الموقع تخزين القيم في النطاق من -128 إلى +127. غير موقع - من 0 إلى 255. تحت المتغير اكتب شاريتم تخصيص 1 بايت من الذاكرة (8 بت).

الكلمات الرئيسية (المعدلات) وقعتو غير موقعةالإشارة إلى كيفية تفسير البت الصفري للمتغير المعلن، أي إذا تم تحديد الكلمة الأساسية غير الموقعة، فسيتم تفسير البت الصفري كجزء من رقم، وإلا فسيتم تفسير البت الصفري على أنه موقع.

اكتب كثافة العمليات

قيمة عدد صحيح كثافة العملياتربما قصير(قصير) أو طويل(طويل).

الكلمة الرئيسية (المعدل) قصيريأتي بعد الكلمات الدالة وقعتأو غير موقعة. وهكذا يتم تمييز الأنواع التالية: موقعة قصيرة int، int قصيرة غير موقعة، موقعة طويلة int، غير موقعة طويلة int.

نوع المتغير وقعت قصيرة كثافة العمليات(عدد صحيح قصير مُوقع) يمكن أن يأخذ القيم من -32768 إلى +32767، كثافة العمليات قصيرة غير موقعة(عدد صحيح قصير غير موقّع) - من 0 إلى 65535. يتم تخصيص بايتين من الذاكرة (16 بت) بالضبط لكل منهما.

عند الإعلان عن متغير النوع وقعت قصيرة كثافة العملياتالكلمات الدالة وقعتو قصيريمكن حذفه، ويمكن الإعلان عن هذا النوع المتغير ببساطة كثافة العمليات. من الممكن أيضًا الإعلان عن هذا النوع بكلمة رئيسية واحدة قصير.

عامل كثافة العمليات قصيرة غير موقعةيمكن الإعلان عنها كثافة العمليات غير الموقعةأو قصيرة غير موقعة.

لكل حجم وقعت طويلة كثافة العملياتأو كثافة العمليات طويلة غير موقعةيتم تخصيص 4 بايت من الذاكرة (32 بت). يمكن أن تكون قيم المتغيرات من هذا النوع في النطاقات من -2147483648 إلى 2147483647 ومن 0 إلى 4294967295، على التوالي.

هناك أيضا اكتب المتغيرات كثافة العمليات طويلة طويلة، والتي تم تخصيص 8 بايت من الذاكرة لها (64 بت). ويمكن أيضًا أن تكون موقعة أو غير موقعة. بالنسبة للنوع الموقع، يتراوح نطاق القيم من -9223372036854775808 إلى 9223372036854775807، بالنسبة للنوع غير الموقع - من 0 إلى 18446744073709551615. يمكن الإعلان عن النوع الموقع ببساطة من خلال كلمتين رئيسيتين طويل طويل.

يكتب يتراوح النطاق السداسي مقاس
حرف غير موقعة 0 ... 255 0x00...0xFF 8 بت
حرف موقّع
أو ببساطة
شار
-128 ... 127 -0x80...0x7F 8 بت
كثافة العمليات قصيرة غير موقعة
أو ببساطة
كثافة العمليات غير الموقعةأو قصيرة غير موقعة
0 ... 65535 0x0000...0xFFFF 16 بت
وقعت قصيرة كثافة العملياتأو وقعت كثافة العمليات
أو ببساطة
قصيرأو كثافة العمليات
-32768 ... 32767 0x8000 ... 0x7FFF 16 بت
كثافة العمليات طويلة غير موقعة
أو ببساطة
غير موقعة طويلة
0 ... 4294967295 0x00000000...0xFFFFFFFF 32 بت
وقعت طويلة
أو ببساطة
طويل
-2147483648 ... 2147483647 0x80000000 ... 0x7FFFFFFFF 32 بت
غير موقعة طويلة طويلة 0 ... 18446744073709551615 0x0000000000000000...0xFFFFFFFFFFFFFFFF 64 بت
وقعت طويلة طويلة
أو ببساطة
طويل طويل
-9223372036854775808 ... 9223372036854775807 0x8000000000000000...0x7FFFFFFFFFFFFFF 64 بت

يتم الإعلان عن المتغيرات في بيان الإعلان. يتكون بيان الإعلان من مواصفات النوع وقائمة مفصولة بفواصل بأسماء المتغيرات. يجب أن تكون هناك فاصلة منقوطة في النهاية.

إعلان المتغير له التنسيق التالي:

[المعدلات] معرف type_specifier [، المعرف] ...

الصفات التعريفية- الكلمات الدالة وقعت, غير موقعة, قصير, طويل.
محدد النوع- الكلمة الرئيسية شارأو كثافة العمليات، الذي يحدد نوع المتغير المعلن.
المعرف- اسم المتغير.

مثال: شار س؛ كثافة العمليات أ، ب، ج؛ غير موقعة طويلة طويلة ذ؛
بهذه الطريقة سيتم الإعلان عن المتغيرات س, أ, ب, ج, ذ. إلى متغير سسيكون من الممكن كتابة القيم من -128 إلى 127. في المتغيرات أ, ب, ج- من -32768 إلى +32767. إلى متغير ذ- من 0 إلى 18446744073709551615.

تهيئة قيمة المتغير عند الإعلان

عند الإعلان عن المتغير، يمكن تهيئة المتغير، أي تخصيصه له القيمة البدائية. يمكنك القيام بذلك على النحو التالي. كثافة العمليات س = 100؛ وهكذا في المتغير سعند الإعلان عنه، سيتم تدوين الرقم 100 على الفور.

من الأفضل تجنب خلط المتغيرات المهيأة في بيان تعريف واحد، أي أنه من الأفضل الإعلان عن المتغيرات المهيأة في أسطر منفصلة.

الثوابت

يمكن تعريف المتغير من أي نوع بأنه غير قابل للتعديل. يتم تحقيق ذلك عن طريق إضافة الكلمة الأساسية مقدار ثابتإلى محدد النوع. المتغيرات مع النوع مقدار ثابتهي بيانات للقراءة فقط، مما يعني أنه لا يمكن تعيين قيمة جديدة للمتغير. إذا بعد الكلمة مقدار ثابتإذا لم يكن هناك محدد نوع، فسيتم التعامل مع الثوابت كقيم موقعة ويتم تعيين نوع لها كثافة العملياتأو كثافة العمليات طويلةحسب قيمة الثابت: إذا كان الثابت أقل من 32768، فسيتم تخصيص النوع له كثافة العمليات، خلاف ذلك كثافة العمليات طويلة.

مثال: const long int k = 25; ثابت م = -50؛ // const الضمني int m=-50ثابت ن = 100000؛ // ثابت طويل ضمني int n=100000

تكليف

يتم استخدام علامة "=" للمهمة في C. يتم تقييم التعبير الموجود على يمين علامة الإسناد، ويتم تعيين القيمة الناتجة للمتغير الموجود على يسار علامة الإسناد. في هذه الحالة، يتم مسح القيمة السابقة المخزنة في المتغير واستبدالها بقيمة جديدة.

لا ينبغي فهم عامل التشغيل "=" على أنه مساواة.
على سبيل المثال، التعبير أ = 5؛ يجب قراءتها كـ "تعيين المتغير أ إلى 5".

أمثلة: س = 5 + 3؛ // أضف القيمتين 5 و 3، // قم بتعيين النتيجة للمتغير x (اكتب للمتغير x)ب = أ + 4؛ // إضافة 4 إلى القيمة المخزنة في المتغير أ، // إسناد النتيجة الناتجة إلى المتغير ب (الكتابة إلى المتغير ب)ب = ب + 2؛ // أضف 2 إلى القيمة المخزنة في المتغير b، // قم بتعيين النتيجة الناتجة إلى المتغير b (اكتب إلى المتغير b)
على الجانب الأيمن، يمكن استخدام قيمة المتغير عدة مرات: c = b * b + 3 * b؛

مثال: س = 3؛ // سيتم تعيين القيمة 3 للمتغير xص = س + 5؛ // سيتم إضافة الرقم 5 إلى القيمة المخزنة في المتغير x، // سيتم كتابة النتيجة الناتجة إلى المتغير yض = س * ص؛ // سيتم ضرب قيم المتغير x و y، // سيتم كتابة النتيجة للمتغير zض = ض - 1؛ // سيتم طرح 1 من القيمة المخزنة في المتغير z // سيتم كتابة النتيجة إلى المتغير z
وهكذا في المتغير ضسيتم تخزين الرقم 23

يستثني مشغل بسيطتعيينات "="، يوجد في لغة C العديد من عوامل التعيين المدمجة: "+="، "-="، "*=
أمثلة: x += y; // نفس x = x + y; - أضف x و y // واكتب النتيجة للمتغير xس -= ص; // نفس x = x - y; - اطرح القيمة y من x // واكتب النتيجة للمتغير xس *= ذ; // نفس x = x * y; - اضرب x في y // واكتب النتيجة للمتغير xس /= ص; // نفس x = x / y; - قسمة x على y // واكتب النتيجة للمتغير xس %= ص; // نفس x = x % y; // احسب العدد الصحيح المتبقي عند قسمة x على y // واكتب النتيجة للمتغير x

الزيادة والنقصان

إذا كنت بحاجة إلى تغيير قيمة المتغير إلى 1، فاستخدم زيادة راتبأو إنقاص.

زيادة راتب- عملية زيادة القيمة المخزنة في المتغير بمقدار 1.

مثال: س++؛ // سيتم زيادة قيمة المتغير x بمقدار 1$WinAVR = ($_GET["avr"]); إذا($WinAVR) تشمل($WinAVR);?>
إنقاص- عملية تخفيض القيمة المخزنة في المتغير بمقدار 1.

مثال: x--; // سيتم تخفيض قيمة المتغير x بمقدار 1
الزيادة والنقصان هي عوامل التعيين. عند استخدام التناقص والزيادة مع عامل التعيين "="، استخدم تدوين اللاحقة (x++) أو البادئة (++x). يتم تنفيذ إدخال البادئة أولاً.

أمثلة: ص = س++؛
لنفترض ذلك في المتغير ستم تخزين القيمة 5 ثم في ذسيتم كتابة القيمة 5، وبعدها قيمة المتغير سسيتم زيادتها بمقدار 1. وهكذا، في ذسيكون 5، وفي س- 6.y = --x;
إذا كان في سإذا تم تخزين القيمة 5، فسيتم إجراء التخفيض أولاً سإلى 4 وبعد ذلك سيتم تعيين هذه القيمة للمتغير ذ. هكذا، سو ذسيتم تعيين القيمة 4.

يرجى تعليق AdBlock على هذا الموقع.

لتخزين أي بيانات في برنامجك، تحتاج إلى متغيرات. أولا وقبل كل شيء، عليك أن تتعلم كيفية إنشاء هذه المتغيرات في البرنامج. بمعنى آخر، تذكر تشبيهنا بالصناديق، من أجل وضع شيء ما في صندوق، هذا الصندوق بالذات، سيكون من الجيد الحصول عليه أولاً في مكان ما.

في كتب البرمجة تسمى عملية إنشاء المتغير إعلان متغير. سيكون من الجيد معرفة هذه العبارة لفهم الأدبيات المهنية وخطاب المبرمجين الآخرين. ولكن الأهم من ذلك هو فهم ما هو مخفي وراء هذه العبارة.

كيفية الإعلان عن متغير؟

من أجل الإعلان عن متغير، تحتاج إلى تحديد نوعه وكتابة اسمه. حسنًا، لا تنس أن تضع "؛". يظهر الهيكل العام لإعلان المتغير في الشكل التالي.

رسم بياني 1. بناء الجملة العامإعلانات متغيرة."

في المثال الموجود في الشكل، قمنا بإنشاء متغير يسمى num، والذي يمكنه تخزين الأعداد الصحيحة. يشير نوع البيانات int إلى أننا سنستخدم المتغير لتخزين الأعداد الصحيحة.

بضعة أمثلة أخرى:

القائمة 1. الإعلان عن المتغيرات

إنت ض ؛ // المتغير z من النوع الصحيح char w; // متغير ث من نوع الحرف

هناك قاعدة واحدة لأسماء المتغيرات التي ستحتاج إلى تذكرها.

يمكن أن يكون اسم المتغير أي تسلسل من الأحرف اللاتينية والأرقام والشرطة السفلية "_" التي تبدأ بحرف.

في الواقع، لاسم المتغير هناك قيود إضافيةلكننا لن نخوض في مثل هذه التفاصيل في الوقت الحالي. دعونا نلقي نظرة أفضل على أمثلة الأسماء الصحيحة وغير الصحيحة.

تصحيح أسماء المتغيرات

Peremennaya، العلم، f3، var4، KolichestvoBukv، fd4s، FLaG، key_number

أسماء المتغيرات غير صحيحة

2num - يبدأ برقم
num flat - يحتوي على مسافة في الاسم
nomer-telefona – يحتوي على واصلة

و واحدة اخرى نقطة مهمة. في لغة البرمجة C، حالة الأحرف مهمة جدًا. على سبيل المثال، المتغيرات المسماة flag وFLAG وFlAg وfLAg كلها متغيرات مختلفة. بالإضافة إلى ذلك، هناك عدد من الكلمات التي لا يمكن استخدامها لتسمية المتغيرات. على سبيل المثال، int، void، return وغيرها. هذه كلمات رئيسية خاصة مخصصة لاحتياجات اللغة نفسها ولا يمكن استخدامها في أي مكان آخر.

بالمناسبة، في إعلان واحد يمكنك إنشاء عدة متغيرات من نفس النوع في وقت واحد.

القائمة 2. الإعلان عن متغيرات متعددة

إنت أ، ج؛ // قم بتعريف المتغيرين a وc من النوع الصحيح double x, y, z; // أعلن عن ثلاثة متغيرات حقيقية في وقت واحد

كل شيء بسيط ومنطقي. يشير أولاً إلى نوع المتغيرات، ثم أسمائها، مفصولة بفاصلة.

متغير في ذاكرة الكمبيوتر.

بضع كلمات حول الشكل الذي يبدو عليه إعلان المتغير من وجهة نظر الكمبيوتر.

يمكننا أن نعتبر أنه عندما نعلن، فإننا نطلب من الكمبيوتر تخصيص مكان في الذاكرة للمتغير وربط هذا المكان باسم محدد. يعتمد مقدار المساحة التي سيتم تخصيصها في الذاكرة لتخزين المتغير على نوع ذلك المتغير. دعونا نوضح هذه الفكرة من خلال الشكل التالي.

القائمة 3. الإعلان عن متغيرين

كثافة العمليات ث؛ // أعلن عن متغير عدد صحيح w double z؛ // أعلن عن متغير حقيقي z

تين. 3. المتغيرات في ذاكرة الكمبيوتر

يصور هذا الشكل بشكل تقليدي ذاكرة الكمبيوتر كمجموعة من الخلايا، يمكن لكل منها تخزين شيء ما. في هذه الحالة، يشغل المتغير الحقيقي خليتين، والمتغير الصحيح يشغل خلية واحدة فقط. هذه النسبة (اثنان إلى واحد) مشروطة. في الواقع، في جهاز الكمبيوتر الخاص بك المتغير النوع الحقيقييمكن أن يشغل، على سبيل المثال، مساحة ذاكرة أكبر بأربع مرات من المتغير الصحيح.

يمارس

حل المشكلات المقترحة: لسهولة العمل، قم بالتبديل فورًا إلى وضع ملء الشاشة

المهام البحثية للهاكرز

  1. قم بتعريف متغير في برنامجك باستخدام اسم خاطئومحاولة تجميع البرنامج. انظر ما هو الخطأ الذي يعطيه المترجم.
  2. ابحث عن قائمة بجميع الكلمات الرئيسية للغة C. يمكنك البحث في معيار اللغة (تلميح: "الكلمات الرئيسية")، أو على الإنترنت. لا تحتاج إلى حفظها، لكن الأمر يستحق النظر إليها مرة واحدة.

أساسيات اللغة

يتم تسجيل رمز البرنامج والبيانات التي يعالجها البرنامج في ذاكرة الكمبيوتر كسلسلة من البتات. قليل- هو أصغر عنصر ذاكرة الكمبيوتر، قادر على تخزين إما 0 أو 1. تشغيل المستوى الجسدييتوافق الجهد الكهربائيوهو كما هو معروف إما موجود أو غير موجود. وبالنظر إلى محتويات ذاكرة الكمبيوتر سنرى ما يلي:
...

من الصعب للغاية فهم مثل هذا التسلسل، ولكن في بعض الأحيان نحتاج إلى معالجة مثل هذه البيانات غير المنظمة (عادةً ما تنشأ الحاجة إلى ذلك عند برمجة برامج تشغيل الأجهزة). يوفر C++ مجموعة من العمليات للعمل مع بيانات البت. (سنتحدث عن هذا في الفصل الرابع).
عادةً، يتم فرض بعض البنية على سلسلة من البتات، ويتم تجميع البتات فيها بايتو كلمات. البايت يحتوي على 8 بت، والكلمة تحتوي على 4 بايت، أو 32 بت. ومع ذلك، قد يختلف تعريف الكلمة باختلاف أنظمة التشغيل. لقد بدأ الآن الانتقال إلى أنظمة 64 بت، وفي الآونة الأخيرة انتشرت الأنظمة التي تحتوي على كلمات 16 بت على نطاق واسع. على الرغم من أن الغالبية العظمى من الأنظمة لها نفس حجم البايت، إلا أننا سنظل نسمي هذه الكميات خاصة بالجهاز.

الآن يمكننا أن نتحدث، على سبيل المثال، عن بايت بالعنوان 1040 أو كلمة بالعنوان 1024 ونقول أن البايت بالعنوان 1032 ليس كذلك يساوي بايتبالعنوان 1040.
ومع ذلك، فإننا لا نعرف ما الذي يمثله أي بايت، أو أي كلمة آلية. كيف نفهم معنى 8 بت معينة؟ من أجل تفسير معنى هذه البايت (أو الكلمة، أو مجموعة أخرى من البتات) بشكل فريد، يجب أن نعرف نوع البيانات التي يمثلها البايت.
توفر لغة C++ مجموعة من أنواع البيانات المضمنة: الأحرف، والأعداد الصحيحة، والحقيقية - ومجموعة من الأنواع المركبة والممتدة: السلاسل، والمصفوفات، والأرقام المركبة. بالإضافة إلى ذلك، هناك إجراءات مع هذه البيانات المجموعة الأساسيةالعمليات: المقارنة والعمليات الحسابية وغيرها. هناك أيضًا عوامل انتقالية، وحلقات، وعوامل شرطية. تشكل هذه العناصر من لغة C++ مجموعة من الطوب التي يمكنك من خلالها بناء نظام بأي تعقيد. الخطوة الأولى في إتقان لغة C++ هي دراسة ما يلي العناصر الأساسيةوهو ما خصص له الجزء الثاني من هذا الكتاب.
يقدم الفصل 3 نظرة عامة على الأنواع المضمنة والممتدة، والآليات التي يمكنك من خلالها إنشاء أنواع جديدة. وهذا بالطبع هو آلية الفصل التي تم تقديمها في القسم 2.3. يغطي الفصل الرابع التعبيرات والعمليات المضمنة وأولوياتها وتحويلات الكتابة. الفصل الخامس يتحدث عن تعليمات اللغة. أخيرًا، يقدم الفصل السادس مكتبة C++ القياسية وأنواع الحاويات والمصفوفات الترابطية.

3. أنواع البيانات C++

يقدم هذا الفصل نظرة عامة مدمج، أو ابتدائيأنواع البيانات في لغة C++. يبدأ بالتعريف حرفية، مثل 3.14159 أو pi، ثم يتم تقديم المفهوم عامل، أو هدفوالتي يجب أن تنتمي إلى أحد أنواع البيانات. ويخصص بقية الفصل ل وصف تفصيليكل نوع مدمج. بالإضافة إلى ذلك، يتم توفير أنواع البيانات المشتقة للسلاسل والمصفوفات التي توفرها مكتبة C++ القياسية. على الرغم من أن هذه الأنواع ليست أولية، إلا أنها مهمة جدًا لكتابة برامج C++ حقيقية، ونريد تعريف القارئ بها في أقرب وقت ممكن. سوف نسمي أنواع البيانات هذه توسعأنواع C++ الأساسية.

3.1. حرفية

يحتوي C++ على مجموعة من أنواع البيانات المضمنة لتمثيل الأعداد الصحيحة والأرقام الحقيقية والأحرف ونوع البيانات " مصفوفة الأحرف"، والذي يعمل على التخزين سلاسل الأحرف. يتم استخدام نوع char لتخزين الأحرف الفردية والأعداد الصحيحة الصغيرة. أنها تشغل بايت واحد للجهاز. تم تصميم الأنواع short وint وlong لتمثيل الأعداد الصحيحة. تختلف هذه الأنواع فقط في نطاق القيم التي يمكن أن تأخذها الأرقام، وتعتمد الأحجام المحددة للأنواع المدرجة على التنفيذ. عادة ما يستغرق القصير نصف كلمة آلية، ويستغرق int كلمة واحدة، ويستغرق الطول كلمة أو كلمتين. في أنظمة 32 بت، عادةً ما يكون حجم int وlong بنفس الحجم.

الأنواع العائمة والمزدوجة والمزدوجة الطويلة مخصصة لأرقام الفاصلة العائمة وتختلف في دقة تمثيلها (عدد الأرقام المهمة) ونطاقها. عادةً، يأخذ التعويم (الدقة المفردة) كلمة آلية واحدة، والمزدوج (الدقة المزدوجة) يأخذ كلمتين، والمزدوج الطويل (الدقة الموسعة) يأخذ ثلاثة.
شار، قصير، إنت وطويل معًا يشكلون أنواع كاملة، والذي بدوره يمكن أن يكون مبدع(توقيع) و غير موقعة(غير موقعة). في الأنواع الموقعة، يقوم البت الموجود في أقصى اليسار بتخزين الإشارة (0 زائد، 1 ناقص)، وتحتوي البتات المتبقية على القيمة. في الأنواع غير الموقعة، يتم استخدام كافة البتات للقيمة. يمكن أن يمثل نوع char ذو 8 بتات قيمًا من -128 إلى 127، ويمكن أن يمثل char غير الموقعة قيمًا من 0 إلى 255.

عند مواجهة رقم معين، على سبيل المثال 1، في برنامج ما، يتم استدعاء هذا الرقم حرفي، أو ثابت حرفي. ثابت لأننا لا نستطيع تغيير قيمته، وحرفي لأن قيمته تظهر في نص البرنامج. القيمة الحرفية هي قيمة غير قابلة للعنونة: على الرغم من أنها مخزنة فعليًا في ذاكرة الجهاز، إلا أنه لا توجد طريقة لمعرفة عنوانها. كل حرفي لديه نوع معين. لذلك، 0 لديه نوع كثافة العمليات, 3.14159 - نوع مزدوج.

يمكن كتابة القيم الحرفية من النوع الصحيح بالنظام العشري والثماني والست عشري. إليك الشكل الذي يبدو عليه الرقم 20 ممثلًا بالأعداد العشرية والثمانية والست عشرية:

20 // عشري
024 // ثماني
0x14 // سداسي عشري

إذا بدأ الرقم الحرفي بـ 0، فسيتم التعامل معه على أنه رقم ثماني، وإذا كان يبدأ بـ 0x أو 0X، فسيتم التعامل معه على أنه سداسي عشري. التسجيل المعتاديعامل كرقم عشري.
افتراضيًا، تكون جميع القيم الحرفية ذات الأعداد الصحيحة من النوع المُوقع int. يمكنك تعريف حرفي كامل بشكل صريح على أنه من النوع الطويل عن طريق إلحاق الحرف L بنهاية الرقم (يتم استخدام كل من الأحرف الكبيرة L والأحرف الصغيرة l، ولكن لسهولة القراءة، يجب عدم استخدام الأحرف الصغيرة: يمكن الخلط بسهولة مع

1). يحدد الحرف U (أو u) في النهاية الحرفي كحرف int غير موقع، والحرفين - UL أو LU - كنوع طويل غير موقع. على سبيل المثال:

128u 1024UL 1L 8Lu

تمثل الحروف أرقام حقيقية، يمكن كتابتها إما بفاصلة عشرية أو بالتدوين العلمي (الأسي). بشكل افتراضي فهي من النوع المزدوج. ل إشارة صريحةبالنسبة للنوع العائم، تحتاج إلى استخدام اللاحقة F أو f، وللنوع المزدوج الطويل - L أو l، ولكن فقط إذا كانت مكتوبة بفاصلة عشرية. على سبيل المثال:

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

الكلمات صحيح وخطأ هي حروف منطقية.
تتم كتابة ثوابت الأحرف الحرفية القابلة للتمثيل كأحرف ضمن علامات اقتباس مفردة. على سبيل المثال:

"أ" "2" "،" "(مسافة)

تتم كتابة الأحرف الخاصة (علامة التبويب، حرف الإرجاع) كتسلسلات هروب. يتم تعريف التسلسلات التالية (تبدأ بحرف شرطة مائلة عكسية):

سطر جديد \n علامة تبويب أفقية \t مسافة للخلف \b علامة تبويب رأسية \v رجوع إلى السطر \r تغذية الورق \f استدعاء \a شرطة مائلة عكسية \\ سؤال \؟ اقتباس مفرد \"اقتباس مزدوج \"

تسلسل الهروب منظر عامله النموذج \ooo، حيث ooo عبارة عن رقم ثماني إلى ثلاثة أرقام. هذا الرقم هو رمز الحرف. باستخدام كود ASCII، يمكننا كتابة القيم الحرفية التالية:

\7 (جرس) \14 (سطر جديد) \0 (خالي) \062 ("2")

يمكن أن تبدأ الحرف الحرفي بـ L (على سبيل المثال، L"a")، مما يعني أن النوع الخاص wchar_t هو نوع حرف ثنائي البايت يُستخدم لتخزين الأحرف الأبجديات الوطنية، إذا كان لا يمكن تمثيلها بنوع حرف عادي، مثل الحروف الصينية أو اليابانية.
السلسلة الحرفية هي سلسلة من الأحرف المحاطة بها التنصيص. يمكن لمثل هذا الحرف أن يمتد لعدة أسطر؛ وفي هذه الحالة، يتم وضع شرطة مائلة عكسية في نهاية السطر. يمكن تمثيل الأحرف الخاصة بتسلسلات الهروب الخاصة بها. فيما يلي أمثلة على سلسلة حرفية:

"" (سلسلة فارغة) "a" "\nCC\toptions\tfile.\n" "تشير سلسلة حرفية متعددة الأسطر إلى \ استمرارها بخط مائل عكسي"

في الواقع، السلسلة الحرفية هي مصفوفة من ثوابت الأحرف، حيث، وفقًا لاتفاقية لغات C وC++، يكون العنصر الأخير دائمًا عبارة عن حرف خاص بالكود 0 (\0).
يحدد الحرف "A" حرفًا واحدًا A، وتحدد السلسلة الحرفية "A" مصفوفة من عنصرين: "A" و\0 (الحرف الفارغ).
نظرًا لوجود النوع wchar_t، هناك أيضًا حروف من هذا النوع، يُشار إليها، كما في حالة الأحرف الفردية، بالبادئة L:

L"سلسلة حرفية واسعة"

السلسلة الحرفية من النوع wchar_t عبارة عن مصفوفة منتهية بقيمة خالية من الأحرف من نفس النوع.
إذا ظهرت سلسلتان حرفيتان أو أكثر (مثل char أو wchar_t) في صف واحد في اختبار البرنامج، فسيقوم المترجم بتسلسلها في سلسلة واحدة. على سبيل المثال النص التالي

"ثنائي"

سيتم إنشاء مجموعة من ثمانية أحرف - ثنائية وأخيرة حرف فارغ. نتيجة سلسلة سلاسل من أنواع مختلفة غير محددة. إذا كتبت:

// هذه ليست فكرة جيدة "اثنين" L"بعض"

على أحد أجهزة الكمبيوتر، ستكون النتيجة عبارة عن سلسلة ذات معنى، ولكن على جهاز آخر قد تكون شيئًا مختلفًا تمامًا. البرامج التي تستخدم ميزات التنفيذ لمترجم معين أو نظام التشغيل، لا تطاق. نحن لا نشجع بشدة على استخدام مثل هذه الهياكل.

التمرين 3.1

وضح الفرق في تعريفات الحروف التالية:

(أ) "أ"، L "أ"، "أ"، L "أ" (ب) 10، 10u، 10L، 10uL، 012، 0*C (ج) 3.14، 3.14f، 3.14L

التمرين 3.2

ما هي الأخطاء التي تم ارتكابها في الأمثلة أدناه؟

(أ) "من يذهب مع F\144rgus؟\014" (ب) 3.14e1L (ج) "اثنين" L"بعض" (د) 1024f (هـ) 3.14UL (و) "تعليق متعدد الأسطر"

3.2. المتغيرات

لنتخيل أننا نحل مشكلة رفع 2 إلى قوة 10. نكتب:

#يشمل
انت مين() (
// الحل الأول
cout<< "2 raised to the power of 10: ";
cout<< 2 * 2 * 2 * 2 * 2 * 2 * 2 * 2 * 2 * 2;
cout<< endl;
العودة 0؛
}

تم حل المشكلة، على الرغم من أنه كان علينا التحقق مرارًا وتكرارًا مما إذا كان الرقم 2 قد تم تكراره بالفعل 10 مرات، إلا أننا لم نرتكب خطأً في كتابة هذا التسلسل الطويل من الثنائيات، وأنتج البرنامج النتيجة الصحيحة - 1024.
ولكن الآن طُلب منا رفع 2 إلى القوة 17، ثم إلى القوة 23، ومن غير الملائم للغاية تعديل نص البرنامج في كل مرة! والأسوأ من ذلك أنه من السهل جدًا ارتكاب خطأ بكتابة رقم اثنين إضافيين أو حذفه... ولكن ماذا لو كنت بحاجة إلى طباعة جدول قوى العدد اثنين من 0 إلى 15؟ كرر سطرين لهما الصيغة العامة 16 مرة:

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

حيث يتم زيادة X بمقدار 1 على التوالي، ويتم استبدال العدد الحرفي المطلوب بالعلامة العشرية؟

نعم، تعاملنا مع المهمة. من غير المرجح أن يتعمق العميل في التفاصيل، راضيا عن النتيجة التي تم الحصول عليها. في الحياة الواقعية، غالبًا ما ينجح هذا النهج، علاوة على ذلك، فهو له ما يبرره: يتم حل المشكلة بطريقة بعيدة عن أن تكون أنيقة، ولكن في الإطار الزمني المطلوب. إن البحث عن خيار أكثر جمالاً وكفاءة قد يكون مضيعة للوقت بشكل غير عملي.

في هذه الحالة، تعطي "طريقة القوة الغاشمة" الإجابة الصحيحة، ولكن كم هو مزعج وممل حل المشكلة بهذه الطريقة! نحن نعرف بالضبط ما هي الخطوات التي يجب اتخاذها، ولكن هذه الخطوات في حد ذاتها بسيطة ورتيبة.

يؤدي إشراك آليات أكثر تعقيدًا لنفس المهمة، كقاعدة عامة، إلى زيادة وقت المرحلة التحضيرية بشكل كبير. بالإضافة إلى ذلك، كلما تم استخدام آليات أكثر تعقيدا، كلما زاد احتمال حدوث أخطاء. ولكن حتى على الرغم من الأخطاء الحتمية والتحركات الخاطئة، فإن استخدام "التقنيات العالية" يمكن أن يحقق فوائد في سرعة التطوير، ناهيك عن حقيقة أن هذه التقنيات تعمل على توسيع قدراتنا بشكل كبير. و- ما هو المثير للاهتمام! – يمكن أن تصبح عملية اتخاذ القرار نفسها جذابة.
دعنا نعود إلى مثالنا ونحاول "تحسين" تنفيذه من الناحية التكنولوجية. يمكننا استخدام كائن مسمى لتخزين قيمة الأس الذي نريد رفع الرقم إليه. بالإضافة إلى ذلك، بدلاً من التسلسل المتكرر للقيم الحرفية، يمكن استخدام مشغل الحلقة. وهذا ما سيبدو عليه:

#يشمل
انت مين()
{
// كائنات من النوع int
قيمة كثافة العمليات = 2؛
كثافة العمليات الأسرى = 10؛
cout<< value << " в степени "
<< pow << ": \t";
الدقة الدولية = 1؛
// مشغل الحلقة:
// كرر حساب الدقة
// حتى يصبح CNT أكبر من الأسرى
ل(int cnt=1;cnt<= pow; ++cnt)
الدقة = الدقة * القيمة؛
cout<< res << endl;
}

value وpow وres وcnt هي متغيرات تسمح لك بتخزين القيم وتعديلها واسترجاعها. عبارة for Loop تكرر مرات السطر الناتج.
ليس هناك شك في أننا أنشأنا برنامجًا أكثر مرونة. ومع ذلك، هذه لا تزال ليست وظيفة. للحصول على دالة حقيقية يمكن استخدامها في أي برنامج لحساب قوة الرقم، تحتاج إلى تحديد الجزء العام من الحسابات، وتعيين قيم محددة مع المعلمات.

Int pow(int val, int exp) ( for (int res = 1; exp > 0; --exp) res = res * val; return res; )

الآن لن يكون من الصعب الحصول على أي قوة بالرقم المطلوب. وإليك كيفية تنفيذ مهمتنا الأخيرة - اطبع جدول قوى العدد اثنين من 0 إلى 15:

#يشمل extern int الأسرى(int,int); int main() ( int val = 2; int exp = 15;
cout<< "Степени 2\n";
ل(int cnt=0;cnt<= exp; ++cnt)
cout<< cnt << ": "
<< pow(val, cnt) << endl;
العودة 0؛
}

بالطبع، لا تزال الدالة pow() لدينا غير عامة بدرجة كافية وليست قوية بدرجة كافية. لا يمكن أن تعمل مع أرقام حقيقية، فهي ترفع الأرقام بشكل غير صحيح إلى قوة سالبة - فهي تُرجع دائمًا 1. قد لا تتناسب نتيجة رفع رقم كبير إلى قوة أعلى مع متغير int، ومن ثم سيتم إرجاع بعض القيمة العشوائية غير الصحيحة. هل ترى مدى صعوبة كتابة وظائف مصممة للاستخدام على نطاق واسع؟ أصعب بكثير من تنفيذ خوارزمية محددة تهدف إلى حل مشكلة معينة.

3.2.1. ما هو المتغير

عامل، أو شيء- هذه منطقة محددة من الذاكرة يمكننا الوصول إليها من البرنامج؛ يمكنك وضع القيم هناك ومن ثم استرجاعها. كل متغير C++ له نوع محدد، يحدد حجم وموقع موقع الذاكرة ذلك، ونطاق القيم التي يمكنه تخزينها، ومجموعة العمليات التي يمكن تطبيقها على ذلك المتغير. هنا مثال على تعريف خمسة كائنات أنواع مختلفة:

عدد الطلاب الدولي؛ راتب مزدوج منطقي on_loan؛ سلاسل عنوان الشارع؛ محدد الحرف؛

المتغير، مثل المتغير الحرفي، له نوع محدد ويخزن قيمته في منطقة معينة من الذاكرة. قابلية العنونة- وهذا ما يفتقر إليه الحرفي. هناك كميتين مرتبطتين بالمتغير:

  • القيمة الفعلية، أو قيمة r (من قيمة القراءة - قيمة القراءة)، المخزنة في منطقة الذاكرة هذه والمتأصلة في كل من المتغير والحرفي؛
  • قيمة عنوان منطقة الذاكرة المرتبطة بالمتغير، أو قيمة l (من قيمة الموقع) - المكان الذي يتم فيه تخزين قيمة r؛ متأصلة فقط في الكائن.

في التعبير

الفصل = الفصل - "0"؛

يقع المتغير ch على يسار ويمين رمز المهمة. على اليمين توجد القيمة المطلوب قراءتها (ch والحرف الحرفي "0"): تتم قراءة البيانات المرتبطة بالمتغير من منطقة الذاكرة المقابلة. على اليسار قيمة الموقع: يتم وضع نتيجة الطرح في منطقة الذاكرة المرتبطة بالمتغير ch. بشكل عام، يجب أن يكون المعامل الأيسر لعملية الإسناد قيمة l. لا يمكننا كتابة التعبيرات التالية:

// أخطاء التجميع: القيم الموجودة على اليسار ليست قيم l // خطأ: القيمة الحرفية ليست قيمة l 0 = 1; // خطأ: التعبير الحسابي ليس راتبًا ذو قيمة l + راتب * 0.10 = new_salary;

بيان تعريف المتغير يخصص الذاكرة له. نظرًا لأن الكائن له موقع ذاكرة واحد فقط مرتبط به، فإن مثل هذا البيان يمكن أن يظهر مرة واحدة فقط في البرنامج. إذا كان من الضروري استخدام متغير محدد في ملف مصدر واحد في ملف مصدر آخر، فستنشأ مشاكل. على سبيل المثال:

// file Module0.C // يحدد اسم الملف للكائن، وسلسلة اسم الملف؛ // ... قم بتعيين قيمة لاسم الملف
// ملف الوحدة النمطية1.C
// يستخدم كائن اسم الملف
// للأسف، لا يتم تجميعها:
// لم يتم تعريف اسم الملف في الوحدة النمطية 1.C
ifstream input_file(fileName);

يتطلب C++ أن يكون الكائن معروفًا قبل الوصول إليه لأول مرة. يعد ذلك ضروريًا للتأكد من استخدام الكائن بشكل صحيح وفقًا لنوعه. في مثالنا، سوف يتسبب Module1.C في حدوث خطأ في الترجمة نظرًا لعدم تعريف متغير fileName فيه. لتجنب هذا الخطأ، يجب أن نخبر المترجم عن متغير fileName المحدد بالفعل. يتم ذلك باستخدام عبارة إعلان متغير:

// file Module1.C // يستخدم كائن اسم الملف // تم الإعلان عن اسم الملف، أي أن البرنامج يستقبل
// معلومات حول هذا الكائن بدون تعريفه الثانوي
اسم ملف السلسلة الخارجية؛ ifstream input_file (اسم الملف)

يخبر إعلان المتغير المترجم أن كائنًا يحمل اسمًا معينًا، من نوع معين، تم تعريفه في مكان ما في البرنامج. لا يتم تخصيص الذاكرة للمتغير عند الإعلان عنه. (يتم تناول الكلمة الأساسية الخارجية في القسم 8.2.)
يمكن أن يحتوي البرنامج على أي عدد يريده من الإعلانات لنفس المتغير، ولكن يمكن تعريفه مرة واحدة فقط. من الملائم وضع مثل هذه الإعلانات في ملفات الرأس، بما في ذلك في تلك الوحدات التي تتطلب ذلك. بهذه الطريقة يمكننا تخزين المعلومات حول الكائنات في مكان واحد وتسهيل تعديلها إذا لزم الأمر. (سنتحدث أكثر عن ملفات الرأس في القسم 8.2.)

3.2.2. اسم المتغير

اسم متغير أو معرف، يمكن أن يتكون من أحرف لاتينية وأرقام وشرطة سفلية. تختلف الأحرف الكبيرة والصغيرة في الأسماء. لا تحدد لغة C++ طول المعرف، ولكن استخدام أسماء طويلة جدًا مثل gosh_this_is_an_impossible_name_to_type أمر غير مريح.
بعض الكلمات هي كلمات أساسية في لغة C++ ولا يمكن استخدامها كمعرفات؛ ويقدم الجدول 3.1 قائمة كاملة بها.

الجدول 3.1. الكلمات الرئيسية لـ C++

asm آلي منطقي استراحة قضية
يمسك شار فصل مقدار ثابت const_cast
يكمل تقصير يمسح يفعل مزدوج
Dynamic_cast آخر التعداد صريحة يصدّر
خارجي خطأ شنيع يطفو ل صديق
اذهب إلى لو في النسق كثافة العمليات طويل
متقلب مساحة الاسم جديد المشغل أو العامل خاص
محمي عام يسجل reinterpret_cast يعود
قصير وقعت حجم ثابتة static_cast
البنية يُحوّل نموذج هذا يرمي
typedef حقيقي يحاول typeid أكتب اسم
اتحاد اتحاد باطل استخدام افتراضي فارغ

لجعل برنامجك أكثر قابلية للفهم، نوصي باتباع اصطلاحات تسمية الكائنات المقبولة عمومًا:

  • عادةً ما يُكتب اسم المتغير بأحرف صغيرة، على سبيل المثال الفهرس (للمقارنة: الفهرس هو اسم النوع، وINDEX هو ثابت محدد باستخدام توجيه المعالج المسبق #define)؛
  • يجب أن يكون للمعرف معنى ما، يوضح الغرض من الكائن في البرنامج، على سبيل المثال: تاريخ الميلاد أو الراتب؛

إذا كان هذا الاسم يتكون من عدة كلمات، مثل تاريخ الميلاد، فمن المعتاد إما فصل الكلمات بشرطة سفلية (تاريخ_الميلاد) أو كتابة كل كلمة لاحقة بحرف كبير (تاريخ الميلاد). وقد لوحظ أن المبرمجين الذين اعتادوا على الأسلوب الكائني يفضلون تمييز الكلمات بالأحرف الكبيرة، بينما أولئك الذين كتبوا الكثير في C يستخدمون الشرطة السفلية. أي من الطريقتين أفضل هي مسألة ذوق.

3.2.3. تعريف الكائن

في أبسط حالاته، يتكون عامل تعريف الكائن من محدد النوعو اسم الكائنوينتهي بفاصلة منقوطة. على سبيل المثال:

راتب مضاعف أجر مضاعف؛ شهر كثافة العمليات؛ يوم كثافة العمليات؛ سنة كثافة العمليات؛ مسافة طويلة غير موقعة؛

يمكنك تعريف كائنات متعددة من نفس النوع في عبارة واحدة. وفي هذه الحالة، يتم إدراج أسمائهم مفصولة بفواصل:

راتب مزدوج، أجر؛ إنت الشهر، اليوم، السنة؛ مسافة طويلة غير موقعة؛

إن مجرد تعريف المتغير لا يعطيه قيمة أولية. إذا تم تعريف كائن على أنه عالمي، فإن مواصفات C++ تضمن أنه سيتم تهيئته إلى الصفر. إذا كان المتغير محليًا أو مخصصًا ديناميكيًا (باستخدام عامل التشغيل الجديد)، فإن قيمته الأولية تكون غير محددة، أي أنه قد يحتوي على بعض القيم العشوائية.
يعد استخدام مثل هذه المتغيرات خطأً شائعًا جدًا، ومن الصعب أيضًا اكتشافه. يوصى بتحديد القيمة الأولية للكائن بشكل صريح، على الأقل في الحالات التي لا يُعرف فيها ما إذا كان الكائن يمكنه تهيئة نفسه أم لا. تقدم آلية الفصل مفهوم المُنشئ الافتراضي، والذي يُستخدم لتعيين القيم الافتراضية. (تحدثنا عن هذا بالفعل في القسم 2.3. وسنتحدث عن المنشئات الافتراضية بعد قليل، في القسمين 3.11 و3.15، حيث سننظر إلى السلاسل والفئات المعقدة من المكتبة القياسية.)

Int main() ( // كائن محلي غير مهيأ int ival;
// تتم تهيئة كائن من النوع السلسلة
// المنشئ الافتراضي
مشروع سلسلة؛
// ...
}

يمكن تحديد القيمة الأولية مباشرة في بيان تعريف المتغير. في لغة C++، يُسمح بنوعين من تهيئة المتغير - صريح، باستخدام عامل التعيين:

إنت إيفال = 1024؛ مشروع السلسلة = "فانتازيا 2000";

وضمنيًا، مع تحديد القيمة الأولية بين قوسين:

إنت إيفال(1024); مشروع سلسلة ("فانتازيا 2000")؛

كلا الخيارين متكافئان ويتم ضبط القيم الأولية للمتغير الصحيح ival على 1024 ولسلسلة المشروع على "Fantasia 2000".
يمكن أيضًا استخدام التهيئة الصريحة عند تعريف المتغيرات في القائمة:

الراتب المزدوج = 9999.99، الأجر = الراتب + 0.01؛ كثافة العمليات الشهر = 08؛ اليوم = 07، السنة = 1955؛

يصبح المتغير مرئيا (وصالحا في البرنامج) مباشرة بعد تعريفه، حتى نتمكن من تهيئة متغير الأجر بمجموع متغير الراتب المحدد حديثا مع بعض الثوابت. وبالتالي فإن التعريف هو:

// صحيح، لكن لا معنى له int bizarre = bizarre;

صحيح من الناحية النحوية، على الرغم من أنه لا معنى له.
تحتوي أنواع البيانات المضمنة على صيغة خاصة لتحديد قيمة فارغة:

// ival يحصل على القيمة 0 وdval يحصل على 0.0 int ival = int(); مزدوج dval = مزدوج ()؛

في التعريف التالي:

يتم تطبيق // int() على كل عنصر من العناصر المتجهة العشرة< int >إيفيك (10) ؛

تتم تهيئة كل عنصر من العناصر العشرة للمتجه باستخدام int(). (لقد تحدثنا بالفعل عن فئة المتجهات في القسم 2.8. لمزيد من المعلومات حول هذا، راجع القسم 3.10 والفصل 6.)
يمكن تهيئة المتغير بتعبير بأي تعقيد، بما في ذلك استدعاءات الوظائف. على سبيل المثال:

#يشمل #يشمل
السعر المزدوج = 109.99، الخصم = 0.16؛
سعر البيع المزدوج(السعر * الخصم);
سلسلة الحيوانات الأليفة("التجاعيد"); extern int get_value(); int val = get_value();
abs_val غير الموقعة = abs(val);

abs() هي دالة قياسية تُرجع القيمة المطلقة للمعلمة.
get_value() هي بعض الوظائف المعرفة من قبل المستخدم والتي تقوم بإرجاع قيمة عددية.

تمرين 3.3

أي من تعريفات المتغيرات التالية تحتوي على أخطاء في بناء الجملة؟

(أ) إنت كار = 1024، أوتو = 2048؛ (ب) int ival = ival؛ (ج) int ival(int()); (د) الراتب المزدوج = الأجر = 9999.99؛ (هـ) سين >> int input_value؛

تمرين 3.4

اشرح الفرق بين قيمة l وقيمة r. أعط أمثلة.

التمرين 3.5

أوجد الاختلافات في استخدام الاسم ومتغيرات الطالب في السطر الأول والثاني من كل مثال:

(أ) اسم السلسلة الخارجية؛ اسم السلسلة ("التمرين 3.5 أ")؛ (ب) ناقلات خارجية طلاب؛ المتجه طلاب؛

تمرين 3.6

ما هي أسماء الكائنات غير الصالحة في C++؟ قم بتغييرها بحيث تكون صحيحة من الناحية النحوية:

(أ) كثافة العمليات مزدوجة = 3.14159؛ (ب) ناقلات< int >_؛ (ج) مساحة اسم السلسلة؛ (د) سلسلة الصيد 22؛ (هـ) شار 1_or_2 = "1"؛ (و) تعويم تعويم = 3.14f؛

التمرين 3.7

ما الفرق بين تعريفات المتغيرات العالمية والمحلية التالية؟

سلسلة global_class؛ int global_int; انت مين() (
int local_int;
سلسلة local_class؛ // ...
}

3.3. معالم

تم تقديم المؤشرات وتخصيص الذاكرة الديناميكية لفترة وجيزة في القسم 2.2. المؤشرهو كائن يحتوي على عنوان كائن آخر ويسمح بالتلاعب غير المباشر بهذا الكائن. عادةً، يتم استخدام المؤشرات لمعالجة الكائنات التي تم إنشاؤها ديناميكيًا، ولإنشاء هياكل البيانات ذات الصلة مثل القوائم المرتبطة والأشجار الهرمية، وتمرير الكائنات الكبيرة - المصفوفات وكائنات الفئة - كمعلمات للوظائف.
يرتبط كل مؤشر بنوع بيانات معين، ولا يعتمد تمثيلها الداخلي على النوع الداخلي: حجم الذاكرة التي يشغلها كائن من نوع المؤشر ونطاق القيم متماثلان. الفرق هو كيفية تعامل المترجم مع الكائن الموجه. يمكن أن يكون للمؤشرات إلى أنواع مختلفة نفس القيمة، لكن منطقة الذاكرة التي توجد بها الأنواع المقابلة يمكن أن تكون مختلفة:

  • يتم توجيه مؤشر إلى int يحتوي على قيمة العنوان 1000 إلى منطقة الذاكرة 1000-1003 (على نظام 32 بت)؛
  • يتم توجيه مؤشر مزدوج يحتوي على قيمة العنوان 1000 إلى منطقة الذاكرة 1000-1007 (على نظام 32 بت).

وهنا بعض الأمثلة:

كثافة العمليات *ip1, *ip2; معقد *CP; سلسلة *pstring; المتجه * بولي كلوريد الفينيل. مزدوج * موانئ دبي؛

يُشار إلى الفهرس بعلامة النجمة قبل الاسم. عند تحديد المتغيرات بقائمة، يجب أن تسبق علامة النجمة كل مؤشر (انظر أعلاه: ip1 وip2). في المثال أدناه، lp هو مؤشر لكائن طويل، وlp2 هو كائن طويل:

طويلة *LP، LP2؛

في الحالة التالية، يتم تفسير fp ككائن عائم، ويكون fp2 بمثابة مؤشر إليه:

تعويم فب، *FP2؛

يمكن فصل عامل إلغاء الإشارة (*) بمسافات من الاسم وحتى بجوار الكلمة الأساسية للنوع مباشرةً. ولذلك فإن التعريفات المذكورة أعلاه صحيحة نحويا ومتكافئة تماما:

// انتبه: ps2 ليس مؤشرًا لسلسلة! سلسلة * ملاحظة، PS2؛

يمكن افتراض أن كلاً من ps وps2 عبارة عن مؤشرات، على الرغم من أن المؤشر هو أولهما فقط.
إذا كانت قيمة المؤشر 0، فهذا يعني أنه لا يحتوي على أي عنوان كائن.
دع متغيرًا من النوع int يتم تحديده:

إنت إيفال = 1024؛

فيما يلي أمثلة على تعريف واستخدام المؤشرات إلى int pi وpi2:

// تمت تهيئة pi إلى عنوان صفر int *pi = 0;
// تتم تهيئة pi2 بعنوان ival
كثافة العمليات * pi2 =
// صحيح: يحتوي pi وpi2 على العنوان ival
بي = بي2؛
// pi2 يحتوي على العنوان صفر
بي2 = 0;

لا يمكن تعيين قيمة للمؤشر ليست عنوانًا:

// خطأ: لا يمكن لـ pi قبول القيمة int pi = ival

وبنفس الطريقة، لا يمكنك تعيين قيمة لمؤشر من نوع ما يكون عنوان كائن من نوع آخر. إذا تم تعريف المتغيرات التالية:

مزدوج دفال. مزدوج * ملاحظة =

فإن كلا تعبيري المهمة أدناه سوف يتسببان في حدوث خطأ في الترجمة:

// أخطاء التجميع // تعيين نوع بيانات غير صالح: int*<== double* pi = pd pi = &dval;

لا يعني ذلك أن المتغير pi لا يمكن أن يحتوي على عناوين كائن dval - فعناوين الكائنات من أنواع مختلفة لها نفس الطول. عمليات خلط العناوين هذه محظورة عمدًا لأن تفسير المترجم للكائنات يعتمد على نوع المؤشر إليها.
بالطبع، هناك حالات عندما نكون مهتمين بقيمة العنوان نفسه، وليس بالكائن الذي يشير إليه (دعنا نقول أننا نريد مقارنة هذا العنوان مع عنوان آخر). لحل مثل هذه المواقف، تم تقديم مؤشر فارغ خاص، والذي يمكن أن يشير إلى أي نوع بيانات، وستكون التعبيرات التالية صحيحة:

// صحيح: يمكن أن يحتوي void* على عناوين من أي نوع void *pv = pi; الكهروضوئية = pd;

نوع الكائن المشار إليه بالفراغ* غير معروف ولا يمكننا التعامل مع هذا الكائن. كل ما يمكننا فعله بمثل هذا المؤشر هو تعيين قيمته لمؤشر آخر أو مقارنته ببعض قيم العنوان. (سنتحدث أكثر عن مؤشر الفراغ في القسم 4.14.)
من أجل الوصول إلى كائن معين بعنوانه، تحتاج إلى استخدام عملية إلغاء المرجعية، أو العنونة غير المباشرة، المشار إليها بعلامة النجمة (*). وجود التعريفات المتغيرة التالية:

إنت ival = 1024;, ival2 = 2048; كثافة العمليات * بي =

// إسناد غير مباشر للمتغير ival إلى القيمة ival2 *pi = ival2;
// الاستخدام غير المباشر للمتغير ival مثل rvalue وlvalue
*بي = القيمة المطلقة(*بي); // ival = abs(ival);
*بي = *بي + 1; // ival = ival + 1;

عندما نطبق عامل العنوان (&) على كائن من النوع int، نحصل على نتيجة من النوع int*
كثافة العمليات * بي =
إذا طبقنا نفس العملية على كائن من النوع int* (مؤشر إلى int)، فسنحصل على مؤشر إلى مؤشر int، أي. كثافة العمليات **. int** هو عنوان كائن يحتوي على عنوان كائن من النوع int. من خلال إلغاء مرجعية نقطة في البوصة، نحصل على كائن من النوع int* يحتوي على عنوان ival. للحصول على الكائن ival نفسه، يجب تطبيق عملية عدم المرجعية على نقطة في البوصة مرتين.

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

يمكن استخدام المؤشرات في التعبيرات الحسابية. لاحظ المثال التالي، حيث يقوم تعبيران بأشياء مختلفة تمامًا:

إنت ط، ي، ك؛ int *pi = // i = i + 2
*بي = *بي + 2; // قم بزيادة العنوان الموجود في pi بمقدار 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

يتم إعطاء تعريفات المتغيرات:

إنت ival = 1024، ival2 = 2048؛ int *pi1 = &ival, *pi2 = &ival2, **pi3 = 0;

ماذا يحدث عند تنفيذ عمليات التعيين التالية؟ هل هناك أي أخطاء في هذه الأمثلة؟

(أ) ival = *pi3؛ (هـ) pi1 = *pi3؛ (ب) *pi2 = *pi3؛ (و) ival = *pi1; (ج) ival = pi2؛ (ز) pi1 = ival؛ (د) pi2 = *pi1؛ (ح)pi3 =

التمرين 3.9

يعد العمل باستخدام المؤشرات أحد أهم جوانب C وC++، ولكن من السهل ارتكاب الأخطاء. على سبيل المثال، رمز

بي = باي = باي + 1024؛

من المؤكد تقريبًا أنه سيتسبب في قيام pi بالإشارة إلى موقع ذاكرة عشوائي. ماذا يفعل مشغل المهمة هذا ومتى لن يسبب خطأ؟

التمرين 3.10

يحتوي هذا البرنامج على خطأ يتعلق بالاستخدام غير الصحيح للمؤشرات:

int foobar(int *pi) ( *pi = 1024; return *pi; )
انت مين() (
كثافة العمليات *pi2 = 0;
int ival = foobar(pi2);
العودة 0؛
}

ما هو الخطأ؟ كيف يمكنني إصلاح ذلك؟

التمرين 3.11

تظهر الأخطاء في التمرينين السابقين نفسها وتؤدي إلى عواقب وخيمة بسبب افتقار C++ إلى التحقق من صحة قيم المؤشر أثناء تنفيذ البرنامج. لماذا تعتقد أن هذا الاختيار لم يتم تنفيذه؟ هل يمكنك تقديم بعض الإرشادات العامة لجعل العمل باستخدام المؤشرات أكثر أمانًا؟

3.4. أنواع السلسلة

تدعم لغة C++ نوعين من السلاسل - النوع المدمج الموروث من لغة C، وفئة السلسلة من مكتبة C++ القياسية. توفر فئة السلسلة إمكانيات أكثر بكثير، وبالتالي فهي أكثر ملاءمة للاستخدام، ومع ذلك، في الممارسة العملية، غالبًا ما تكون هناك مواقف عندما يكون من الضروري استخدام نوع مدمج أو الحصول على فهم جيد لكيفية عمله. (أحد الأمثلة هو تحليل معلمات سطر الأوامر التي تم تمريرها إلى الدالة main(). سنغطي هذا في الفصل السابع.)

3.4.1. نوع السلسلة المضمنة

كما ذكرنا سابقًا، يأتي نوع السلسلة المضمنة من C++، الموروثة من C. يتم تخزين سلسلة الأحرف في الذاكرة كمصفوفة ويمكن الوصول إليها باستخدام مؤشر char*. توفر مكتبة C القياسية مجموعة من الوظائف لمعالجة السلاسل. على سبيل المثال:

// يُرجع طول السلسلة int strlen(const char*);
// يقارن سلسلتين
int strcmp(const char*, const char*);
// نسخ سطر إلى آخر
char* strcpy(char*, const char*);

تعد مكتبة C القياسية جزءًا من مكتبة C++. لاستخدامه يجب علينا تضمين ملف الرأس:

#يشمل

يشير مؤشر char، الذي نستخدمه للوصول إلى سلسلة، إلى مجموعة من الأحرف المقابلة للسلسلة. حتى عندما نكتب سلسلة حرفية مثل

Const char *st = "سعر زجاجة النبيذ\n";

يضع المترجم جميع أحرف السلسلة في مصفوفة ثم يعين عنوان العنصر الأول في المصفوفة. كيف يمكنك العمل مع سلسلة باستخدام مثل هذا المؤشر؟
عادةً، يتم استخدام حساب العنوان للتكرار على الأحرف الموجودة في السلسلة. نظرًا لأن السلسلة تنتهي دائمًا بحرف فارغ، فيمكنك زيادة المؤشر بمقدار 1 حتى يصبح الحرف التالي خاليًا. على سبيل المثال:

بينما (*st++) (...)

تتم إلغاء الإشارة إلى st ويتم التحقق من القيمة الناتجة لمعرفة ما إذا كانت صحيحة. تعتبر أي قيمة غير الصفر صحيحة، وبالتالي تنتهي الحلقة عند الوصول إلى حرف ذو رمز 0. تضيف عملية الزيادة ++ 1 إلى المؤشر وبالتالي تنقله إلى الحرف التالي.
هذا هو الشكل الذي قد يبدو عليه تنفيذ دالة تُرجع طول السلسلة. لاحظ أنه بما أن المؤشر يمكن أن يحتوي على قيمة فارغة (لا يشير إلى أي شيء)، فيجب التحقق منه قبل عملية عدم المرجعية:

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

يمكن اعتبار السلسلة المضمنة فارغة في حالتين: إذا كان المؤشر إلى السلسلة يحتوي على قيمة فارغة (وفي هذه الحالة ليس لدينا سلسلة على الإطلاق) أو إذا كان يشير إلى مصفوفة تتكون من حرف فارغ واحد (أي ، إلى سلسلة لا تحتوي على أي أحرف مهمة).

// pc1 لا يعالج أي مصفوفة أحرف char *pc1 = 0; // pc2 يعالج الحرف الفارغ const char *pc2 = "";

بالنسبة للمبرمج المبتدئ، فإن استخدام سلاسل الكتابة المضمنة محفوف بالأخطاء بسبب المستوى المنخفض جدًا للتنفيذ وعدم القدرة على الاستغناء عن حساب العنوان. أدناه سوف نعرض بعض الأخطاء الشائعة التي يرتكبها المبتدئين. المهمة بسيطة: احسب طول السلسلة. الإصدار الأول غير صحيح. أصلحها.

#يشمل const char *st = "سعر زجاجة النبيذ\n"; انت مين() (
إنت لين = 0;
بينما (st++) ++len; cout<< len << ": " << st;
العودة 0؛
}

في هذا الإصدار، لا يتم إلغاء الإشارة إلى المؤشر st. لذلك، ليس الحرف الذي يشير إليه st هو الذي يتم التحقق من المساواة 0، ولكن المؤشر نفسه. نظرًا لأن هذا المؤشر كان له في البداية قيمة غير صفرية (عنوان السلسلة)، فلن تصبح صفرًا أبدًا، وستعمل الحلقة إلى ما لا نهاية.
في الإصدار الثاني من البرنامج تمت إزالة هذا الخطأ. ينتهي البرنامج بنجاح، ولكن الإخراج غير صحيح. أين أخطأنا هذه المرة؟

#يشمل const char *st = "سعر زجاجة النبيذ\n"; انت مين()
{
إنت لين = 0;
while (*st++) ++len; cout<< len << ": " << st << endl;
العودة 0؛
}

الخطأ هو أنه بعد انتهاء الحلقة، لا يخاطب المؤشر st الحرف الأصلي للحرف، بل الحرف الموجود في الذاكرة بعد انتهاء ذلك الحرفي. يمكن أن يكون أي شيء في هذا المكان، وسيكون إخراج البرنامج عبارة عن تسلسل عشوائي للأحرف.
يمكنك محاولة إصلاح هذا الخطأ:

ش = ش - لين؛ cout<< len << ": " << st;

الآن ينتج برنامجنا شيئا ذا معنى، ولكن ليس بشكل كامل. الجواب يبدو مثل هذا:

18:سعر زجاجة النبيذ

لقد نسينا أن نأخذ في الاعتبار أن الحرف الفارغ اللاحق لم يتم تضمينه في الطول المحسوب. يجب أن تتم إزاحة st بطول السلسلة زائد 1. وهنا أخيرًا العامل الصحيح:

ش = ش - لين - 1؛

وهذه هي النتيجة الصحيحة:

18: ثمن زجاجة من النبيذ

ومع ذلك، لا يمكننا أن نقول أن برنامجنا يبدو أنيقا. المشغل أو العامل

ش = ش - لين - 1؛

تمت إضافتها لتصحيح خطأ حدث في مرحلة مبكرة من تصميم البرنامج - مما أدى إلى زيادة المؤشر مباشرة. لا يتناسب هذا البيان مع منطق البرنامج، وأصبح من الصعب الآن فهم الكود. غالبًا ما تسمى هذه الأنواع من الإصلاحات بالتصحيحات، وهي شيء مصمم لسد ثغرة في برنامج موجود. الحل الأفضل هو إعادة النظر في المنطق. أحد الخيارات في حالتنا هو تحديد مؤشر ثانٍ تمت تهيئته بالقيمة st:

Const char *p = st;

الآن يمكن استخدام p في حلقة حساب الطول، مع ترك قيمة st دون تغيير:

بينما (*ع++)

3.4.2. فئة السلسلة

كما رأينا للتو، فإن استخدام نوع السلسلة المضمن هو عرضة للخطأ وغير مريح لأنه يتم تنفيذه على هذا المستوى المنخفض. لذلك، كان من الشائع جدًا تطوير فئتك أو فصولك لتمثيل نوع سلسلة - تقريبًا كل شركة أو قسم أو مشروع فردي لديه تطبيق خاص به لسلسلة. ماذا يمكنني أن أقول، في الطبعتين السابقتين من هذا الكتاب فعلنا نفس الشيء! وقد أدى ذلك إلى ظهور مشاكل في توافق البرنامج وقابليته للنقل. كان الهدف من تطبيق فئة السلسلة القياسية بواسطة مكتبة C++ القياسية هو وضع حد لإعادة اختراع الدراجات.
دعنا نحاول تحديد الحد الأدنى لمجموعة العمليات التي يجب أن تحتوي عليها فئة السلسلة:

  • التهيئة بمصفوفة من الأحرف (نوع سلسلة مضمن) أو كائن آخر من نوع السلسلة. لا يحتوي النوع المضمن على الإمكانية الثانية؛
  • نسخ سطر إلى آخر. بالنسبة للنوع المضمن، عليك استخدام الدالة strcpy()؛
  • الوصول إلى الأحرف الفردية لسلسلة للقراءة والكتابة. في المصفوفة المضمنة، يتم ذلك باستخدام عملية الفهرس أو العنونة غير المباشرة؛
  • مقارنة سلسلتين للمساواة. بالنسبة للنوع المضمن، استخدم الدالة strcmp();
  • تسلسل سلسلتين، مما ينتج عنه النتيجة إما كسلسلة ثالثة أو بدلاً من إحدى السلسلتين الأصليتين. بالنسبة للنوع المضمن، يتم استخدام الدالة strcat()، ولكن للحصول على النتيجة في سطر جديد، تحتاج إلى استخدام الدالتين strcpy() وstrcat() بالتسلسل؛
  • حساب طول السلسلة. يمكنك معرفة طول سلسلة الكتابة المضمنة باستخدام الدالة strlen()؛
  • القدرة على معرفة ما إذا كانت السلسلة فارغة. بالنسبة للسلاسل المضمنة، يجب التحقق من شرطين لهذا الغرض: char str = 0; //... if (! str || ! *str) return;

تنفذ فئة السلسلة في مكتبة C++ القياسية كل هذه العمليات (وأكثر من ذلك بكثير، كما سنرى في الفصل السادس). في هذا القسم سوف نتعلم كيفية استخدام العمليات الأساسية لهذا الفصل.
من أجل استخدام كائنات فئة السلسلة، يجب عليك تضمين ملف الرأس المقابل:

#يشمل

فيما يلي مثال لسلسلة من القسم السابق، ممثلة بكائن من النوع سلسلة وتمت تهيئتها إلى سلسلة أحرف:

#يشمل string st("سعر زجاجة النبيذ\n");

يتم إرجاع طول السلسلة بواسطة وظيفة العضو size() (لا يتضمن الطول حرف الإنهاء الفارغ).

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

يحدد النموذج الثاني لتعريف السلسلة سلسلة فارغة:

سلسلة st2؛ // سطر فارغ

كيف نعرف إذا كانت السلسلة فارغة؟ بالطبع يمكنك مقارنة طوله بـ 0:

إذا (! st.size()) // صحيح: فارغ

ومع ذلك، هناك أيضًا طريقة خاصة فارغة () تُرجع صحيحًا لسلسلة فارغة وخطأ لسلسلة غير فارغة:

إذا (st.empty()) // صحيح: فارغ

يقوم النموذج الثالث للمنشئ بتهيئة كائن من النوع string مع كائن آخر من نفس النوع:

سلسلة st3(st);

تتم تهيئة السلسلة st3 باستخدام السلسلة st. كيف يمكننا التأكد من تطابق هذه السلاسل؟ لنستخدم عامل المقارنة (==):

إذا (st == st3) // نجحت التهيئة

كيفية نسخ سطر إلى آخر؟ باستخدام عامل التعيين العادي:

St2 = st3؛ // انسخ st3 إلى st2

لتسلسل السلاسل، استخدم الإضافة (+) أو الإضافة مع عملية الإسناد (+=). دعونا نعطي سطرين:

سلسلة s1("مرحبا،"); سلسلة s2("العالم\n");

يمكننا الحصول على سلسلة ثالثة تتكون من سلسلة من السلسلة الأولى والثانية، بهذه الطريقة:

سلسلة s3 = s1 + s2؛

إذا أردنا إضافة s2 إلى نهاية s1، يجب أن نكتب:

S1 += S2;

يمكن لعملية الإضافة ربط كائنات فئة السلسلة ليس فقط مع بعضها البعض، ولكن أيضًا مع سلاسل من النوع المدمج. يمكنك إعادة كتابة المثال أعلاه بحيث يتم تمثيل الأحرف الخاصة وعلامات الترقيم بواسطة نوع مدمج، ويتم تمثيل الكلمات ذات المعنى بواسطة كائنات من سلسلة الفئة:

Const char *pc = ", "; سلسلة s1("مرحبا"); سلسلة s2("العالم");
سلسلة s3 = s1 + pc + s2 + "\n"؛

تعمل مثل هذه التعبيرات لأن المترجم يعرف كيفية تحويل الكائنات من النوع المضمن تلقائيًا إلى كائنات من فئة السلسلة. من الممكن أيضًا تعيين سلسلة مضمنة لكائن سلسلة ببساطة:

سلسلة S1؛ const char *pc = "مصفوفة أحرف"; s1 = جهاز كمبيوتر؛ // يمين

ومع ذلك، فإن التحويل العكسي لا يعمل. ستؤدي محاولة تنفيذ تهيئة سلسلة النوع المضمنة التالية إلى حدوث خطأ في الترجمة:

شار *ستر = s1; // خطأ في التحويل

لإنجاز هذا التحويل، يجب عليك بشكل صريح استدعاء دالة العضو c_str() والتي تحمل اسمًا غريبًا إلى حد ما:

Char *str = s1.c_str(); // صحيح تقريبا

تقوم الدالة c_str() بإرجاع مؤشر إلى مصفوفة أحرف تحتوي على سلسلة كائن السلسلة كما تظهر في نوع السلسلة المضمنة.
المثال أعلاه لتهيئة مؤشر char *str لا يزال غير صحيح تمامًا. تقوم c_str() بإرجاع مؤشر إلى مصفوفة ثابتة لمنع تعديل محتويات الكائن مباشرة بواسطة هذا المؤشر، الذي يحتوي على نوع

حرف ثابت *

(سنغطي الكلمة الأساسية const في القسم التالي.) يبدو خيار التهيئة الصحيح كما يلي:

Const char *str = s1.c_str(); // يمين

يمكن الوصول إلى الأحرف الفردية لكائن السلسلة، مثل النوع المضمن، باستخدام عملية الفهرس. على سبيل المثال، إليك جزء من التعليمات البرمجية التي تستبدل جميع الفترات بشرطات سفلية:

سلسلة str("fa.disney.com"); حجم صحيح = str.size(); ل(int التاسع = 0؛ التاسع< size; ++ix) if (str[ ix ] == ".")
str[ix] = "_";

هذا كل ما أردنا قوله عن فئة السلسلة الآن. في الواقع، هذه الفئة لديها العديد من الخصائص والقدرات الأكثر إثارة للاهتمام. لنفترض أن المثال السابق تم تنفيذه أيضًا عن طريق استدعاء دالة استبدال () واحدة:

استبدال (str.begin ()، str.end ()، "."، "_")؛

استبدال() هي إحدى الخوارزميات العامة التي قدمناها في القسم 2.8 والتي سيتم مناقشتها بالتفصيل في الفصل 12. تعمل هذه الوظيفة عبر النطاق من begin() إلى end()، والتي تعيد المؤشرات إلى بداية ونهاية الدالة السلسلة، ويستبدل العناصر المساوية لمعلمتها الثالثة بالرابعة.

التمرين 3.12

ابحث عن الأخطاء في العبارات أدناه:

(أ) char ch = "الطريق الطويل والمتعرج"؛ (ب) int ival = (ج) char *pc = (d) string st(&ch); (هـ) الكمبيوتر = 0؛ (ط) الكمبيوتر = "0"؛
(و) ش = جهاز كمبيوتر؛ (ي) ش =
(ز) الفصل = الكمبيوتر؛ (ك) الفصل = *كمبيوتر؛
(ح) الكمبيوتر = ش؛ (ل) *pc = ival؛

التمرين 3.13

اشرح الفرق في سلوك عبارات الحلقة التالية:

بينما (st++) ++cnt;
بينما (* ش ++)
++المركزي;

التمرين 3.14

يتم إعطاء برنامجين متكافئين لغويا. الأول يستخدم نوع السلسلة المضمنة، والثاني يستخدم فئة السلسلة:

// ***** التنفيذ باستخدام سلاسل C ***** #include #يشمل
انت مين()
{
أخطاء كثافة العمليات = 0؛
const char *pc = "سلسلة حرفية طويلة جدًا"; ل(int التاسع = 0؛ التاسع< 1000000; ++ix)
{
int len ​​​​= strlen(pc);
char *pc2 = new char[ len + 1 ];
strcpy(pc2, pc);
إذا (ستركمب (كمبيوتر 2، كمبيوتر))
++ أخطاء؛ حذف pc2؛
}
cout<< "C-строки: "
<< errors << " ошибок.\n";
) // ***** التنفيذ باستخدام فئة السلسلة ***** #include
#يشمل
انت مين()
{
أخطاء كثافة العمليات = 0؛
سلسلة str("سلسلة حرفية طويلة جدًا"); ل(int التاسع = 0؛ التاسع< 1000000; ++ix)
{
int len ​​​​= str.size();
سلسلة str2 = سلسلة؛
إذا (شارع! = str2)
}
cout<< "класс string: "
<< errors << " ошибок.\n;
}

ماذا تفعل هذه البرامج؟
وتبين أن التنفيذ الثاني يعمل أسرع مرتين من الأول. هل كنت تتوقع مثل هذه النتيجة؟ كيف تفسرون ذلك؟

التمرين 3.15

هل يمكنك تحسين أو إضافة أي شيء إلى مجموعة عمليات فئة السلسلة الواردة في القسم الأخير؟ اشرح مقترحاتك

3.5. محدد ثابت

لنأخذ مثال الكود التالي:

من أجل (مؤشر int = 0؛ مؤشر< 512; ++index) ... ;

هناك مشكلتان في استخدام 512 حرفيًا. الأول هو سهولة إدراك نص البرنامج. لماذا يجب أن يكون الحد الأعلى لمتغير الحلقة 512 بالضبط؟ ما هو مخفي وراء هذه القيمة؟ يبدو أنها عشوائية...
المشكلة الثانية تتعلق بسهولة التعديل وصيانة الكود. لنفترض أن البرنامج يحتوي على 10000 سطر وأن العدد الحرفي 512 يحدث في 4% منها. لنفترض أنه في 80٪ من الحالات يجب تغيير الرقم 512 إلى 1024. هل يمكنك تخيل مدى تعقيد هذا العمل وعدد الأخطاء التي يمكن ارتكابها عن طريق تصحيح القيمة الخاطئة؟
نحن نحل هاتين المشكلتين في نفس الوقت: نحتاج إلى إنشاء كائن بالقيمة 512. ومن خلال إعطائه اسمًا ذا معنى، مثل bufSize، نجعل البرنامج أكثر قابلية للفهم: يصبح من الواضح ما هي مقارنة متغير الحلقة بالضبط ل.

فِهرِس< bufSize

في هذه الحالة، تغيير حجم bufSize لا يتطلب المرور عبر 400 سطر من التعليمات البرمجية لتعديل 320 منها. ما مدى تقليل احتمال حدوث الأخطاء عن طريق إضافة كائن واحد فقط! الآن القيمة هي 512 موضعية.

حجم كثافة العمليات = 512; // حجم المخزن المؤقت للإدخال // ... for (int Index = 0; Index< bufSize; ++index)
// ...

تبقى مشكلة واحدة صغيرة: المتغير bufSize هنا هو قيمة l يمكن تغييرها عن طريق الخطأ في البرنامج، مما يؤدي إلى خطأ يصعب اكتشافه. إليك خطأ شائع: استخدام عامل التعيين (=) بدلاً من عامل المقارنة (==):

// تغيير عشوائي في قيمة bufSize إذا (bufSize = 1) // ...

سيؤدي تنفيذ هذا الرمز إلى جعل قيمة bufSize 1، مما قد يؤدي إلى سلوك برنامج غير متوقع تمامًا. عادةً ما يكون من الصعب جدًا اكتشاف الأخطاء من هذا النوع لأنها ببساطة غير مرئية.
يؤدي استخدام محدد const إلى حل هذه المشكلة. من خلال الإعلان عن الكائن كـ

Const int bufSize = 512; // حجم المخزن المؤقت للإدخال

نحول المتغير إلى ثابت بقيمة 512، لا يمكن تغيير قيمته: يتم منع مثل هذه المحاولات بواسطة المترجم: الاستخدام غير الصحيح لعامل التعيين بدلاً من المقارنة، كما في المثال أعلاه، سيؤدي إلى خطأ في الترجمة.

// خطأ: حاول تعيين قيمة لثابت if (bufSize = 0) ...

نظرًا لأنه لا يمكن تعيين قيمة للثابت، فيجب تهيئته في المكان الذي تم تعريفه فيه. يؤدي تعريف ثابت دون تهيئته أيضًا إلى حدوث خطأ في الترجمة:

كونست بي مزدوج؛ // خطأ: ثابت غير مهيأ

الحد الأدنى للأجور المزدوج = 9.60؛ // يمين؟ خطأ؟
مزدوج *بتر =

هل يجب على المترجم السماح بمثل هذه المهمة؟ نظرًا لأن الحد الأدنى للأجور ثابت، فلا يمكن تعيين قيمة له. ومن ناحية أخرى، لا شيء يمنعنا من أن نكتب:

*ptr += 1.40; // تغيير كائن minWage!

كقاعدة عامة، المترجم غير قادر على الحماية من استخدام المؤشرات ولن يتمكن من الإشارة إلى خطأ إذا تم استخدامها بهذه الطريقة. وهذا يتطلب تحليلا عميقا جدا لمنطق البرنامج. لذلك، يحظر المترجم ببساطة تعيين عناوين ثابتة للمؤشرات العادية.
فهل نحن محرومون من القدرة على استخدام المؤشرات للثوابت؟ لا. لهذا الغرض، هناك مؤشرات معلنة باستخدام محدد const:

كونست مزدوج *cptr;

حيث cptr هو مؤشر لكائن من النوع const double. الدقة هي أن المؤشر نفسه ليس ثابتًا، مما يعني أنه يمكننا تغيير قيمته. على سبيل المثال:

Const double *pc = 0; الحد الأدنى للأجور المزدوجة الثابت = 9.60؛ // صحيح: لا يمكننا تغيير minWage باستخدام جهاز الكمبيوتر
الكمبيوتر = مزدوج dval = 3.14؛ // صحيح: لا يمكننا تغيير minWage باستخدام جهاز الكمبيوتر
// على الرغم من أن dval ليس ثابتًا
الكمبيوتر = // صحيح dval = 3.14159؛ //يمين
* الكمبيوتر = 3.14159؛ // خطأ

يتم تعيين عنوان الكائن الثابت فقط لمؤشر ثابت. في الوقت نفسه، يمكن أيضًا تعيين عنوان لمتغير عادي لمثل هذا المؤشر:

جهاز الكمبيوتر =

لا يسمح المؤشر الثابت بتعديل الكائن الذي يعالجه باستخدام العنونة غير المباشرة. على الرغم من أن dval في المثال أعلاه ليس ثابتًا، إلا أن المترجم لن يسمح بتغيير dval عبر الكمبيوتر. (مرة أخرى، لأنه غير قادر على تحديد عنوان الكائن الذي قد يحتوي على مؤشر في أي وقت أثناء تنفيذ البرنامج.)
في البرامج الحقيقية، غالبًا ما تُستخدم مؤشرات الثوابت كمعلمات رسمية للوظائف. يضمن استخدامها أن الكائن الذي تم تمريره إلى دالة كوسيطة فعلية لن يتم تعديله بواسطة تلك الدالة. على سبيل المثال:

// في البرامج الحقيقية، غالبًا ما تُستخدم مؤشرات الثوابت // كمعلمات رسمية للوظائف int strcmp(const char *str1, const char *str2);

(سنتحدث أكثر عن المؤشرات الثابتة في الفصل السابع، عندما نتحدث عن الوظائف.)
هناك أيضًا مؤشرات ثابتة. (لاحظ الفرق بين مؤشر const ومؤشر ثابت!). يمكن لمؤشر const أن يعالج إما ثابتًا أو متغيرًا. على سبيل المثال:

int errNumb = 0; int *const currErr =

هنا curErr هو مؤشر ثابت لكائن غير ثابت. هذا يعني أنه لا يمكننا تعيين عنوان كائن آخر له، على الرغم من أنه يمكن تعديل الكائن نفسه. إليك كيفية استخدام مؤشر curErr:

قم بعمل ما()؛ إذا (*curErr) (
معالج الأخطاء()؛
*curErr = 0; // صحيح: إعادة تعيين قيمة errNumb
}

ستؤدي محاولة تعيين قيمة لمؤشر const إلى حدوث خطأ في الترجمة:

CurErr = // خطأ

المؤشر الثابت إلى الثابت هو اتحاد بين الحالتين اللتين تم النظر فيهما.

كونست بي مزدوج = 3.14159؛ const مزدوج *const pi_ptr = π

لا يمكن تغيير قيمة الكائن المشار إليه بواسطة pi_ptr ولا قيمة المؤشر نفسه في البرنامج.

التمرين 3.16

اشرح معنى التعاريف الخمسة التالية. هل أي منهم على خطأ؟

(أ) كثافة العمليات ط؛ (د) int *const cpi؛ (ب) ثابت int جيم؛ (هـ) const int *const cpic; (ج) ثابت int *pic;

التمرين 3.17

أي من التعريفات التالية صحيحة؟ لماذا؟

(أ) إنت ط = -1؛ (ب) const int ic = i; (ج) const int *pic = (d) int *const cpi = (e) const int *const cpic =

التمرين 3.18

باستخدام التعريفات من التمرين السابق، حدد عوامل التعيين الصحيحة. يشرح.

(أ) أنا = جيم؛ (د) الموافقة المسبقة عن علم = الصورة؛ (ب) الموافقة المسبقة عن علم = (i) Cpic = (ج) CPI = الموافقة المسبقة عن علم؛ (و) ic = *cpic;

3.6. نوع مرجع

يتم استخدام نوع مرجعي، يسمى أحيانًا الاسم المستعار، لإعطاء كائن اسمًا إضافيًا. يسمح لك المرجع بمعالجة كائن بشكل غير مباشر، تمامًا كما يمكنك باستخدام المؤشر. ومع ذلك، لا يتطلب هذا التلاعب غير المباشر بناء الجملة الخاص المطلوب للمؤشرات. عادة، يتم استخدام المراجع كمعلمات رسمية للوظائف. في هذا القسم، سننظر في استخدام كائنات النوع المرجعي بمفردنا.
تتم الإشارة إلى نوع المرجع عن طريق تحديد عامل العنوان (&) قبل اسم المتغير. يجب تهيئة الرابط. على سبيل المثال:

إنت إيفال = 1024؛ // صحيح: refVal - إشارة إلى ival int &refVal = ival; // خطأ: يجب تهيئة المرجع إلى int

إنت إيفال = 1024؛ // خطأ: refVal من النوع int، وليس int* int &refVal = int *pi = // صحيح: ptrVal هو مرجع إلى مؤشر int *&ptrVal2 = pi;

بمجرد تعريف المرجع، لا يمكنك تغييره للعمل مع كائن آخر (ولهذا السبب يجب تهيئة المرجع عند النقطة التي تم تعريفه فيها). في المثال التالي، لا يغير عامل التعيين قيمة refVal؛ حيث يتم تعيين القيمة الجديدة للمتغير ival - الذي يعالجه refVal.

إنت مين_فال = 0؛ // ival يتلقى قيمة min_val، // بدلاً من 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;

يمكن تهيئة المرجع const بواسطة كائن من نوع آخر (بافتراض، بالطبع، أنه من الممكن تحويل نوع إلى آخر)، بالإضافة إلى قيمة بدون عنوان مثل الثابت الحرفي. على سبيل المثال:

مزدوج دفال = 3.14159؛ // صحيح فقط للمراجع الثابتة
كونست إنت &ir = 1024؛
const int &ir2 = dval;
const double &dr = dval + 1.0;

إذا لم نقم بتحديد محدد const، فإن التعريفات المرجعية الثلاثة جميعها قد تسببت في حدوث خطأ في الترجمة. ومع ذلك، فإن سبب عدم قيام المترجم بتمرير مثل هذه التعريفات غير واضح. دعونا نحاول معرفة ذلك.
بالنسبة للقيم الحرفية، هذا أكثر أو أقل وضوحًا: لا ينبغي أن نكون قادرين على تغيير قيمة القيمة الحرفية بشكل غير مباشر باستخدام المؤشرات أو المراجع. أما بالنسبة للكائنات من الأنواع الأخرى، يقوم المترجم بتحويل الكائن الأصلي إلى كائن مساعد. فمثلاً لو كتبنا:

مزدوج دفال = 1024؛ const int &ri = dval;

ثم يقوم المترجم بتحويله إلى شيء مثل هذا:

درجة الحرارة المؤقتة = دفال؛ const int &ri = temp;

إذا تمكنا من تعيين قيمة جديدة لمرجع ri، فلن نغير في الواقع dval، بل درجة الحرارة. ستبقى قيمة dval كما هي، وهو أمر غير واضح تمامًا للمبرمج. لذلك، يحظر المترجم مثل هذه الإجراءات، والطريقة الوحيدة لتهيئة مرجع بكائن من نوع آخر هي الإعلان عنه كـ const.
إليك مثال آخر على رابط يصعب فهمه في المرة الأولى. نريد تحديد مرجع لعنوان كائن ثابت، لكن خيارنا الأول يسبب خطأ في الترجمة:

كونست إنت إيفال = 1024؛ // خطأ: هناك حاجة إلى مرجع ثابت
int *&pi_ref =

فشلت أيضًا محاولة تصحيح الأمر عن طريق إضافة محدد const:

كونست إنت إيفال = 1024؛ // لا يزال هناك خطأ const int *&pi_ref =

ماهو السبب؟ إذا قرأنا التعريف بعناية، فسنرى أن pi_ref هو مرجع لمؤشر ثابت لكائن من النوع int. ونحن بحاجة إلى مؤشر غير ثابت لكائن ثابت، وبالتالي فإن الإدخال التالي سيكون صحيحا:

كونست إنت إيفال = 1024؛ // يمين
int *const &piref =

هناك اختلافان رئيسيان بين الرابط والمؤشر. أولاً، يجب تهيئة الارتباط في المكان الذي تم تعريفه فيه. ثانيًا، أي تغيير في الرابط لا يؤدي إلى تحويل الرابط، بل إلى الكائن الذي يشير إليه. دعونا ننظر إلى الأمثلة. إذا كتبنا:

إنت * بي = 0؛

نقوم بتهيئة المؤشر pi إلى الصفر، مما يعني أن pi لا يشير إلى أي كائن. في نفس الوقت التسجيل

كونست إنت &ري = 0؛
يعني شيئا مثل هذا:
درجة الحرارة المؤقتة = 0؛
const int &ri = temp;

أما بالنسبة لعملية الإسناد ففي المثال التالي:

إنت ival = 1024، ival2 = 2048؛ int *pi = &ival, *pi2 = pi = pi2;

يبقى المتغير ival الذي يشير إليه pi بدون تغيير، ويستقبل pi قيمة عنوان المتغير ival2. يشير كل من pi وpi2 الآن إلى نفس الكائن، ival2.
إذا عملنا مع الروابط:

Int &ri = ival, &ri2 = ival2; ري = ري2؛

// مثال لاستخدام الروابط // يتم إرجاع القيمة في معلمة القيمة التالية
bool get_next_value(int &next_value); // مشغل Matrix ذو التحميل الزائد عامل التشغيل+(const Matrix&, const Matrix&);

إنت إيفال؛ بينما (get_next_value(ival)) ...

Int &next_value = ival;

(تتم مناقشة استخدام المراجع كمعلمات رسمية للوظائف بمزيد من التفصيل في الفصل السابع.)

التمرين 3.19

هل هناك أي أخطاء في هذه التعريفات؟ يشرح. كيف يمكنك اصلاحها؟

(أ) إنت إيفال = 1.01؛ (ب) كثافة العمليات &rval1 = 1.01؛ (ج) int &rval2 = ival; (د) int &rval3 = (e) int *pi = (f) int &rval4 = pi; (ز) int &rval5 = pi*; (ح) int &*prval1 = pi; (ط) const int &ival2 = 1; (ي) const int &*prval2 =

التمرين 3.20

هل أي من عوامل التعيين التالية خاطئة (باستخدام التعريفات من التمرين السابق)؟

(أ) rval1 = 3.14159؛ (ب) prval1 = prval2؛ (ج) prval2 = rval1؛ (د) * prval2 = ival2؛

التمرين 3.21

ابحث عن الأخطاء في التعليمات المقدمة:

(أ) إنت إيفال = 0؛ const int *pi = 0; كونست إنت &ري = 0؛ (ب) بي =
ري =
بي =

3.7. اكتب منطقي

يمكن لكائن من النوع bool أن يأخذ إحدى القيمتين: صحيح وخطأ. على سبيل المثال:

// تهيئة السلسلة string search_word = get_word(); // تهيئة المتغير الموجود
تم العثور على منطقية = خطأ؛ سلسلة next_word؛ بينما (سين >> next_word)
إذا (next_word == search_word)
وجدت = صحيح؛
// ... // الاختصار: إذا (وجد == صحيح)
ان وجد)
cout<< "ok, мы нашли слово\n";
آخر قطع<< "нет, наше слово не встретилось.\n";

على الرغم من أن bool هو أحد أنواع الأعداد الصحيحة، إلا أنه لا يمكن الإعلان عنه على أنه موقع أو غير موقع أو قصير أو طويل، لذا فإن التعريف أعلاه خاطئ:

// تم العثور على خطأ منطقي قصير = خطأ؛

يتم تحويل الكائنات من النوع bool ضمنيًا إلى النوع int. صحيح يصبح 1 وخطأ يصبح 0. على سبيل المثال:

تم العثور على المنطق = خطأ؛ كثافة العمليات_عدد = 0; بينما (/* غمغم */)
{
وجدت = look_for(/* شيء */); // يتم تحويل القيمة الموجودة إلى 0 أو 1
عدد الأحداث += موجود؛ )

وبنفس الطريقة يمكن تحويل قيم الأنواع الصحيحة والمؤشرات إلى قيم من النوع bool. في هذه الحالة، يتم تفسير 0 على أنه خطأ، وكل شيء آخر على أنه صحيح:

// يُرجع عدد التكرارات الخارجية int find(const string&); تم العثور على منطقية = خطأ؛ if (found = find("rosebud")) // صحيح: Found == true // يُرجع مؤشرًا إلى العنصر
int الخارجي * find(قيمة int); إذا (وجدت = وجدت (1024)) // صحيح: وجدت == صحيح

3.8. التحويلات

غالبًا ما يتعين عليك تحديد متغير يأخذ قيمًا من مجموعة معينة. لنفترض أن الملف مفتوح في أي من الأوضاع الثلاثة: للقراءة والكتابة والإلحاق.
وبالطبع يمكن تعريف ثلاثة ثوابت للدلالة على هذه الأوضاع:

إدخال ثابت = 1؛ إخراج ثابت = 2 ؛ إلحاق const int = 3؛

واستخدم هذه الثوابت:

Bool open_file(string file_name, int open_mode); // ...
open_file("Phoenix_and_the_Crane"، إلحاق)؛

هذا الحل ممكن، ولكنه غير مقبول تمامًا، حيث لا يمكننا ضمان أن الوسيط الذي تم تمريره إلى الدالة open_file() هو 1 أو 2 أو 3 فقط.
استخدام نوع التعداد يحل هذه المشكلة. عندما نكتب:

التعداد open_modes( الإدخال = 1, الإخراج, إلحاق );

نحدد نوعًا جديدًا open_modes. تقتصر القيم الصالحة لكائن من هذا النوع على مجموعة 1 و2 و3، مع وجود اسم تذكيري لكل من القيم المحددة. يمكننا استخدام اسم هذا النوع الجديد لتحديد كائن من هذا النوع ونوع المعلمات الرسمية للوظيفة:

Void open_file(string file_name, open_modes om);

الإدخال والإخراج والإلحاق هي عناصر التعداد. تحدد مجموعة عناصر التعداد مجموعة القيم المسموح بها لكائن من نوع معين. تتم تهيئة متغير من النوع open_modes (في مثالنا) بإحدى هذه القيم، ويمكن أيضًا تعيينه لأي منها. على سبيل المثال:

Open_file("فينيكس والرافعة"، إلحاق)؛

ستؤدي محاولة تعيين قيمة غير أحد عناصر التعداد لمتغير من هذا النوع (أو تمريرها كمعلمة إلى دالة) إلى حدوث خطأ في الترجمة. حتى لو حاولنا تمرير قيمة عددية تتوافق مع أحد عناصر التعداد، فسوف نتلقى خطأً:

// خطأ: 1 ليس عنصرًا في التعداد open_modes open_file("Jonah"، 1);

هناك طريقة لتعريف متغير من النوع open_modes، وتعيين قيمة أحد عناصر التعداد له وتمريره كمعلمة إلى الدالة:

Open_modes om = input; // ... أوم = إلحاق؛ open_file("TailTell", om);

ومع ذلك، ليس من الممكن الحصول على أسماء هذه العناصر. إذا كتبنا بيان الإخراج:

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

ثم ما زلنا نحصل على:

تم حل هذه المشكلة عن طريق تحديد مصفوفة سلسلة يحتوي فيها العنصر الذي يحتوي على فهرس يساوي قيمة عنصر التعداد على اسمه. بالنظر إلى مثل هذه المصفوفة، يمكننا أن نكتب:

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

بالإضافة إلى ذلك، لا يمكنك التكرار على كافة قيم التعداد:

// غير مدعوم لـ (open_modes iter = input; iter != append; ++inter) // ...

لتعريف التعداد، استخدم الكلمة الأساسية التعداد، ويتم تحديد أسماء العناصر بين قوسين متعرجين، مفصولة بفواصل. بشكل افتراضي، الأول هو 0، التالي هو 1، وهكذا. يمكنك تغيير هذه القاعدة باستخدام عامل التعيين. في هذه الحالة، كل عنصر لاحق بدون قيمة محددة بشكل صريح سيكون 1 أكثر من العنصر الذي يسبقه في القائمة. في مثالنا، حددنا بشكل صريح القيمة 1 للإدخال، وسيكون الإخراج والإلحاق مساويين لـ 2 و3. وإليك مثال آخر:

// الشكل == 0، المجال == 1، الأسطوانة == 2، المضلع == 3 التعداد Forms( share, spere, Cylinder, Polygon );

لا يجب أن تكون القيم الصحيحة المقابلة لعناصر مختلفة من نفس التعداد مختلفة. على سبيل المثال:

// point2d == 2, point2w == 3, point3d == 3, point3w == 4 نقاط التعداد ( point2d=2, point2w, point3d=3, point3w=4 );

يمكن تعريف الكائن الذي يكون نوعه تعدادًا، واستخدامه في التعبيرات، وتمريره إلى دالة كوسيطة. تتم تهيئة مثل هذا الكائن بقيمة أحد عناصر التعداد فقط، ويتم تعيين هذه القيمة له فقط، إما بشكل صريح أو كقيمة لكائن آخر من نفس النوع. حتى القيم الصحيحة المقابلة لعناصر التعداد الصالحة لا يمكن تخصيصها لها:

Void mumble() ( Points pt3d = point3d; // صحيح: pt2d == 3 // خطأ: تمت تهيئة pt3w بالنوع int Points pt3w = 3; // خطأ: لم يتم تضمين المضلع في تعداد النقاط pt3w = polygon; / / صحيح: كلا الكائنين من النوع Points pt3w = pt3d )

ومع ذلك، في التعبيرات الحسابية، يمكن تحويل التعداد تلقائيًا إلى النوع int. على سبيل المثال:

Const int array_size = 1024؛ // صحيح: يتم تحويل pt2w إلى int
int Chunk_size = array_size * pt2w;

3.9. اكتب "مصفوفة"

لقد تطرقنا بالفعل إلى المصفوفات في القسم 2.1. المصفوفة عبارة عن مجموعة من العناصر من نفس النوع، يتم الوصول إليها عن طريق فهرس - الرقم التسلسلي للعنصر في المصفوفة. على سبيل المثال:

إنت إيفال؛

يعرف ival كمتغير int، والتعليمات

إنت إيا[10]؛

يحدد مصفوفة من عشرة كائنات من النوع int. لكل من هذه الكائنات، أو عناصر المصفوفة، يمكن الوصول إليها باستخدام عملية الفهرس:

Ival = ia[2];

يعين المتغير ival قيمة عنصر الصفيف ia مع الفهرس 2. وبالمثل

Ia[ 7 ] = ival;

يعين العنصر في الفهرس 7 القيمة ival.

يتكون تعريف المصفوفة من محدد النوع واسم المصفوفة وحجمها. يحدد الحجم عدد عناصر الصفيف (1 على الأقل) وهو محاط بين قوسين مربعين. يجب أن يكون حجم المصفوفة معروفًا بالفعل في مرحلة الترجمة، وبالتالي يجب أن يكون تعبيرًا ثابتًا، على الرغم من أنه لا يتم تحديده بالضرورة كتعبير حرفي. فيما يلي أمثلة لتعريفات المصفوفة الصحيحة وغير الصحيحة:

الخارجية int get_size(); // ثوابت buf_size وmax_files
const int buf_size = 512, max_files = 20;
كثافة الموظفين_حجم = 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 صحيحة. لكن حجم الموظفين متغير (على الرغم من أنه تمت تهيئته بالرقم الثابت 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[التاسع] = التاسع;
}

عند تعريف مصفوفة، يمكنك تهيئتها بشكل صريح عن طريق إدراج قيم عناصرها بين قوسين متعرجين، مفصولة بفواصل:

Const int array_size = 3؛ int ia[ray_size] = ( 0, 1, 2);

إذا قمنا بتحديد قائمة من القيم بشكل صريح، فلن نحتاج إلى تحديد حجم المصفوفة: المترجم نفسه سوف يحسب عدد العناصر:

// مصفوفة بالحجم 3 int ia = ( 0, 1, 2 );

عندما يتم تحديد الحجم وقائمة القيم بشكل صريح، تكون هناك ثلاثة خيارات ممكنة. وإذا تزامن حجم القيم وعددها، فكل شيء واضح. إذا كانت قائمة القيم أقصر من الحجم المحدد، تتم تهيئة عناصر المصفوفة المتبقية إلى الصفر. إذا كان هناك المزيد من القيم في القائمة، يعرض المترجم رسالة خطأ:

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

يمكن تهيئة مصفوفة الأحرف ليس فقط باستخدام قائمة قيم الأحرف داخل الأقواس المتعرجة، ولكن أيضًا باستخدام سلسلة حرفية. ومع ذلك، هناك بعض الاختلافات بين هذه الأساليب. دعنا نقول

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

أبعاد مصفوفة ca1 هي 3، ومصفوفة ca2 هي 4 (في السلسلة الحرفية، يتم أخذ الحرف الفارغ النهائي في الاعتبار). التعريف التالي سوف يسبب خطأ في الترجمة:

// خطأ: السلسلة "Daniel" تتكون من 7 عناصر const char ch3[ 6 ] = "Daniel";

لا يمكن تعيين قيمة لمصفوفة أخرى، ولا يُسمح بتهيئة مصفوفة بواسطة مصفوفة أخرى. بالإضافة إلى ذلك، لا يجوز استخدام مجموعة من المراجع. فيما يلي أمثلة على الاستخدام الصحيح وغير الصحيح للمصفوفات:

Const int array_size = 3؛ إنت التاسع، جكس، ككس؛ // صحيح: مجموعة من المؤشرات من النوع int* int *iar = ( &ix, &jx, &kx ); // خطأ: المصفوفات المرجعية غير مسموح بها int &iar = ( ix, jx, kx ); انت مين()
{
int ia3(ray_size]; // صحيح
// خطأ: لا يمكن نسخ المصفوفات المضمنة
ia3 = ia;
العودة 0؛
}

لنسخ مصفوفة إلى أخرى، عليك القيام بذلك لكل عنصر على حدة:

Const int array_size = 7؛ كثافة العمليات ia1 = (0, 1, 2, 3, 4, 5, 6); انت مين() (
int ia3[ray_size]; ل(int التاسع = 0؛ التاسع< array_size; ++ix)
ia2[التاسع] = ia1[التاسع]; العودة 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() ]; (هـ) شار st[ 11 ] = "أساسي"; (ج) كثافة العمليات [ 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++، من الممكن استخدام المصفوفات متعددة الأبعاد، عند الإعلان عنها يجب عليك الإشارة إلى الحد الأيمن لكل بعد بين قوسين مربعين منفصلين. فيما يلي تعريف المصفوفة ثنائية الأبعاد:

إنت إيا[ 4 ] [ 3 ];

تحدد القيمة الأولى (4) عدد الصفوف، والثانية (3) - عدد الأعمدة. يتم تعريف كائن ia على أنه مصفوفة من أربع سلاسل مكونة من ثلاثة عناصر لكل منها. يمكن أيضًا تهيئة المصفوفات متعددة الأبعاد:

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

تعتبر الأقواس المتعرجة الداخلية، التي تقسم قائمة القيم إلى أسطر، اختيارية وتستخدم بشكل عام لتسهيل قراءة التعليمات البرمجية. التهيئة أدناه هي بالضبط نفس المثال السابق، على الرغم من أنها أقل وضوحا:

كثافة العمليات = (0,1,2,3,4,5,6,7,8,9,10,11);

يقوم التعريف التالي بتهيئة العناصر الأولى فقط من كل سطر. العناصر المتبقية ستكون صفراً:

كثافة العمليات[ 4 ][ 3 ] = ( (0), (3), (6), (9) );

إذا قمت بحذف الأقواس الداخلية المتعرجة، فإن النتيجة ستكون مختلفة تماما. ستتلقى جميع العناصر الثلاثة في الصف الأول والعنصر الأول من الثاني القيمة المحددة، وسيتم تهيئة الباقي ضمنيًا إلى 0.

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

عند الوصول إلى عناصر مصفوفة متعددة الأبعاد، يجب عليك استخدام فهارس لكل بعد (وهي محاطة بين قوسين مربعين). هذا ما تبدو عليه عملية تهيئة مصفوفة ثنائية الأبعاد باستخدام الحلقات المتداخلة:

Int main() ( const introwSize = 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;
}

تصميم

يا[ 1, 2 ]

صحيح من وجهة نظر بناء جملة C++، لكنه لا يعني على الإطلاق ما يتوقعه مبرمج عديم الخبرة. هذا ليس إعلانًا عن مصفوفة ثنائية الأبعاد 1 × 2. التجميع بين قوسين مربعين هو قائمة من التعبيرات مفصولة بفواصل والتي ستؤدي إلى القيمة النهائية 2 (انظر عامل الفاصلة في القسم 4.2). لذلك فإن الإعلان ia يعادل ia. هذه فرصة أخرى لارتكاب خطأ.

3.9.2. العلاقة بين المصفوفات والمؤشرات

إذا كان لدينا تعريف صفيف:

إنت أ = ( 0, 1, 1, 2, 3, 5, 8, 13, 21);

إذن ماذا يعني مجرد الإشارة إلى اسمه في البرنامج؟

إن استخدام معرف المصفوفة في البرنامج يعادل تحديد عنوان العنصر الأول الخاص به:

وبالمثل، يمكنك الوصول إلى قيمة العنصر الأول في المصفوفة بطريقتين:

// يعيد كلا التعبيرين العنصر الأول *ia; I ل؛

للحصول على عنوان العنصر الثاني من المصفوفة يجب أن نكتب:

وكما ذكرنا سابقاً فإن التعبير

كما يعطي عنوان العنصر الثاني من المصفوفة. وعليه يأتي معناها على الوجهين التاليين:

*(ia+1); I ل؛

لاحظ الفرق في التعبيرات:

*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) ( cout<< *pbegin <<; ++pbegin; } }

تتم تهيئة مؤشر pbegin إلى عنوان العنصر الأول في المصفوفة. يؤدي كل مرور خلال الحلقة إلى زيادة هذا المؤشر بمقدار 1، مما يعني أنه يتم إزاحته إلى العنصر التالي. كيف تعرف مكان الإقامة؟ في مثالنا، قمنا بتعريف مؤشر تعليق ثانٍ وقمنا بتهيئته بالعنوان الذي يلي العنصر الأخير في المصفوفة ia. بمجرد أن تصبح قيمة pbegin مساوية لـ pend، نعلم أن المصفوفة قد انتهت. دعونا نعيد كتابة هذا البرنامج بحيث يتم تمرير بداية ونهاية المصفوفة كمعلمات إلى دالة معممة معينة يمكنها طباعة مصفوفة بأي حجم:

#يشمل باطلة ia_print(int *pbegin, int *pend) (
بينما (pbegin != pend) (
cout<< *pbegin << " ";
++pbegin;
}
) انت مين()
{
كثافة العمليات = (0, 1, 1, 2, 3, 5, 8, 13, 21);
ia_print(ia, ia + 9);
}

أصبحت وظيفتنا أكثر عالمية، ومع ذلك، لا يمكنها العمل إلا مع صفائف من النوع int. هناك طريقة لإزالة هذا القيد: تحويل هذه الوظيفة إلى قالب (تم تقديم القوالب لفترة وجيزة في القسم 2.5):

#يشمل نموذج طباعة باطلة (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 ); سلسلة sa = ( "خنزير صغير"، " eeyore"، "pooh" ); print(ia, ia+9);
طباعة (دا، دا + 4)؛
طباعة (سا، سا+3)؛
}

كتبنا المعممةوظيفة. توفر المكتبة القياسية مجموعة من الخوارزميات العامة (لقد ذكرنا ذلك بالفعل في القسم 3.4) والتي يتم تنفيذها بطريقة مماثلة. تعد معلمات هذه الوظائف بمثابة مؤشرات لبداية ونهاية المصفوفة التي يتم من خلالها تنفيذ إجراءات معينة. هنا، على سبيل المثال، هو ما تبدو عليه الاستدعاءات إلى خوارزمية الفرز المعممة:

#يشمل int main() ( int ia = ( 107, 28, 3, 47, 104, 76 ); string sa = ( "piglet", "eeyore", "pooh" );sort(ia, ia+6);
فرز(سا,سا+3);
};

(سنتناول المزيد من التفاصيل حول الخوارزميات المعممة في الفصل 12، وسيقدم الملحق أمثلة على استخدامها.)
تحتوي مكتبة C++ القياسية على مجموعة من الفئات التي تتضمن استخدام الحاويات والمؤشرات. (تمت مناقشة هذا في القسم 2.8.) في القسم التالي، سنلقي نظرة على ناقل نوع الحاوية القياسي، وهو تطبيق موجه للكائنات لمصفوفة.

3.10. فئة المتجهات

يعد استخدام فئة المتجه (انظر القسم 2.8) بديلاً لاستخدام المصفوفات المضمنة. توفر هذه الفئة وظائف أكثر بكثير، لذلك يفضل استخدامها. ومع ذلك، هناك حالات عندما لا يمكنك الاستغناء عن صفائف النوع المدمج. إحدى هذه المواقف هي معالجة معلمات سطر الأوامر التي تم تمريرها إلى البرنامج، والتي سنناقشها في القسم 7.8. تعد فئة المتجهات، مثل فئة السلسلة، جزءًا من مكتبة C++ القياسية.
لاستخدام المتجه يجب عليك تضمين ملف الرأس:

#يشمل

هناك طريقتان مختلفتان تمامًا لاستخدام المتجه، دعنا نسميهما مصطلح المصفوفة ومصطلح STL. في الحالة الأولى، يتم استخدام كائن متجه بنفس الطريقة تمامًا مثل مصفوفة من النوع المضمن. يتم تحديد متجه لبعد معين:

المتجه< int >إيفيك (10) ؛

وهو مشابه لتعريف مصفوفة من النوع المدمج:

إنت إيا[10]؛

للوصول إلى العناصر الفردية للمتجه، يتم استخدام عملية الفهرس:

باطلة simp1e_examp1e() ( const int e1em_size = 10; المتجه< int >ivec(e1em_size); int ia[e1em_size]; ل(int التاسع = 0؛ التاسع< e1em_size; ++ix)
ia[ix] = ivec[ix]; // ...
}

يمكننا معرفة أبعاد المتجه باستخدام الدالة size() والتحقق مما إذا كان المتجه فارغًا باستخدام الدالة الفارغة(). على سبيل المثال:

طباعة باطلة_vector(vector ivec) ( if (ivec.empty()) return; for (int ix=0; ix< ivec.size(); ++ix)
cout<< ivec[ ix ] << " ";
}

تتم تهيئة عناصر المتجه بالقيم الافتراضية. بالنسبة للأنواع والمؤشرات الرقمية، هذه القيمة هي 0. إذا كانت العناصر عبارة عن كائنات فئة، فسيتم تحديد البادئ لها بواسطة المنشئ الافتراضي (انظر القسم 2.3). ومع ذلك، يمكن أيضًا تحديد البادئ بشكل صريح باستخدام النموذج:

المتجه< int >ivec(10, -1);

جميع العناصر العشرة للمتجه ستكون مساوية لـ -1.
يمكن تهيئة مصفوفة من النوع المدمج بشكل صريح باستخدام قائمة:

Int ia[ 6 ] = ( -2, -1, O, 1, 2, 1024);

لا يمكن تنفيذ إجراء مماثل لكائن من فئة المتجه. ومع ذلك، يمكن تهيئة مثل هذا الكائن باستخدام نوع مصفوفة مضمن:

// يتم نسخ 6 عناصر ia إلى ناقل ivec< int >ivec(ia, ia+6);

يتم تمرير منشئ المتجه ivec مؤشرين - مؤشر إلى بداية المصفوفة ia وإلى العنصر الذي يلي العنصر الأخير. كقائمة من القيم الأولية، لا يجوز تحديد المصفوفة بأكملها، بل نطاق معين منها:

// يتم نسخ 3 عناصر: ia، ia، ia Vector< int >ivec(&ia[ 2 ], &ia[ 5 ]);

هناك اختلاف آخر بين المتجه والمصفوفة المضمنة وهو القدرة على تهيئة كائن متجه مع آخر واستخدام عامل التعيين لنسخ الكائنات. على سبيل المثال:

المتجه< string >سفيك؛ void init_and_sign() ( // تتم تهيئة أحد المتجهات بواسطة ناقل آخر< string >user_names(svec); // ... // يتم نسخ متجه إلى آخر
svec = user_names;
}

عندما نتحدث عن لغة STL، فإننا نعني نهجًا مختلفًا تمامًا لاستخدام المتجه. بدلاً من تحديد الحجم المطلوب على الفور، نحدد متجهًا فارغًا:

المتجه< string >نص؛

ثم نضيف عناصر إليها باستخدام وظائف مختلفة. على سبيل المثال، تقوم الدالة Push_back() بإدراج عنصر في نهاية المتجه. إليك جزء من التعليمات البرمجية يقرأ سلسلة من الأسطر من الإدخال القياسي ويضيفها إلى المتجه:

كلمة سلسلة؛ بينما (سين >> كلمة) ( text.push_back(word); // ... )

على الرغم من أنه يمكننا استخدام عملية الفهرس للتكرار على عناصر المتجه:

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

الاستخدام الأكثر شيوعًا للمكررات ضمن هذا المصطلح هو:

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

المكرر عبارة عن فئة مكتبة قياسية تعد في الأساس مؤشرًا لعنصر الصفيف.
تعبير

يقوم بإلغاء الإشارة إلى المكرر ويعطي العنصر المتجه نفسه. تعليمات

ينقل المؤشر إلى العنصر التالي. ليست هناك حاجة للخلط بين هذين النهجين. إذا اتبعت لغة STL عند تحديد متجه فارغ:

المتجه ivec;

سيكون من الخطأ أن أكتب:

ليس لدينا عنصر واحد من ناقل ivec حتى الآن؛ يتم تحديد عدد العناصر باستخدام الدالة size().

ويمكن أيضًا ارتكاب الخطأ المعاكس. إذا قمنا بتعريف متجه بحجم معين، على سبيل المثال:

المتجه أنا(10);

ثم يؤدي إدراج العناصر إلى زيادة حجمه، وإضافة عناصر جديدة إلى العناصر الموجودة. على الرغم من أن هذا يبدو واضحًا، إلا أن المبرمج المبتدئ قد يكتب:

حجم ثابت = 7؛ int ia[الحجم] = (0, 1, 1, 2, 3, 5, 8); المتجه< int >ivec(حجم); ل(int التاسع = 0؛ التاسع< size; ++ix) ivec.push_back(ia[ ix ]);

وهذا يعني تهيئة متجه ivec بقيم عناصر ia، مما أدى إلى ظهور متجه ivec بحجم 14.
باتباع لغة STL، لا يمكنك إضافة عناصر من المتجه فحسب، بل يمكنك أيضًا إزالتها. (سنتناول كل هذا بالتفصيل وبالأمثلة في الفصل السادس).

التمرين 3.24

هل هناك أي أخطاء في التعريفات التالية؟
int ia[ 7 ] = ( 0, 1, 1, 2, 3, 5, 8 );

(أ) ناقلات< vector< int >>ivec;
(ب) ناقلات< int >إيفيك = (0, 1, 1, 2, 3, 5, 8);
(ج) ناقلات< int >ivec(ia, ia+7);
(د) ناقلات< string >svec = ivec;
(ه) ناقلات< string >svec(10, string("null"));

التمرين 3.25

تنفيذ الوظيفة التالية:
منطقي is_equal(const int*ia, int ia_size,
ناقل ثابت &ivec);
تقارن الدالة is_equal() بين حاويتين عنصرًا بعنصر. وفي حالة الحاويات ذات الأحجام المختلفة، لا يؤخذ "ذيل" الحاوية الأطول بعين الاعتبار. من الواضح أنه إذا كانت جميع العناصر المقارنة متساوية، فسترجع الدالة صحيحًا، وإذا كان عنصر واحد مختلفًا على الأقل، فسترجع خطأ. استخدم مكررًا للتكرار على العناصر. اكتب دالة main() تستدعي is_equal().

3.11. مجمع الطبقة

فئة الأعداد المركبة المعقدة هي فئة أخرى من المكتبة القياسية. كالعادة، لاستخدامه، يجب عليك تضمين ملف الرأس:

#يشمل

يتكون العدد المركب من جزأين - حقيقي وتخيلي. الجزء التخيلي هو الجذر التربيعي لعدد سالب. عادةً ما يُكتب العدد المركب على الصورة

حيث 2 هو الجزء الحقيقي، و3i هو الجزء التخيلي. فيما يلي أمثلة لتعريفات الكائنات من النوع المعقد:

// رقم وهمي بحت: 0 + 7-i مركب< double >بوري (0، 7)؛ // الجزء التخيلي هو 0: 3 + مجمع Oi< float >rea1_num(3); // كل من الأجزاء الحقيقية والتخيلية تساوي 0: 0 + 0-i complex< long double >صفر؛ // تهيئة رقم مركب مع مركب آخر< double >purei2(بوري);

نظرًا لأن المعقد، مثل المتجه، عبارة عن قالب، فيمكننا إنشاء مثيل له باستخدام الأنواع 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 بنفس طريقة محددات النوع:

// مضاعفة كل ساعة، أسبوعيا؛ الأجور بالساعة والأسبوعية؛ //المتجه فيكل(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؛

الجواب الذي يبدو واضحا هو:

كونست شار *cstr

مهما يكن ... هذه ليست الحقيقة. يشير محدد const إلى cstr، وبالتالي فإن الإجابة الصحيحة هي مؤشر const إلى char:

شار *const cstr;

3.13. محدد متقلب

يتم تعريف الكائن بأنه متقلب إذا كان من الممكن تغيير قيمته دون ملاحظة المترجم، مثل تحديث المتغير بواسطة ساعة النظام. يخبر هذا المحدد المترجم أنه لا يحتاج إلى تحسين التعليمات البرمجية للعمل مع هذا الكائن.
يتم استخدام المحدد المتقلب بشكل مشابه لمحدد const:

متطاير كثافة العمليات disp1ay_register؛ مهمة متقلبة *curr_task; متقلبة int ixa[max_size]; bitmap_buf للشاشة المتقلبة؛

Display_register هو كائن متطاير من النوع int. curr_task هو مؤشر لكائن متطاير من فئة المهمة. ixa عبارة عن مصفوفة غير مستقرة من الأعداد الصحيحة، وكل عنصر في هذه المصفوفة يعتبر غير مستقر. bitmap_buf هو كائن متطاير من فئة الشاشة، ويعتبر كل عضو من بياناته متطايرًا أيضًا.
الغرض الوحيد من استخدام المحدد المتطاير هو إخبار المترجم أنه لا يمكنه تحديد من يمكنه تغيير قيمة كائن معين وكيف. ولذلك، يجب ألا يقوم المحول البرمجي بإجراء تحسينات على التعليمات البرمجية التي تستخدم هذا الكائن.

3.14. زوج الطبقة

تتيح لنا فئة الزوج في مكتبة C++ القياسية تحديد زوج من القيم بكائن واحد إذا كان هناك أي اتصال دلالي بينهما. يمكن أن تكون هذه القيم هي نفسها أو أنواع مختلفة. لاستخدام هذه الفئة يجب عليك تضمين ملف الرأس:

#يشمل

على سبيل المثال، التعليمات

زوج< string, string >المؤلف ("جيمس"، "جويس")؛

ينشئ كائن مؤلف من النوع زوج، يتكون من قيمتين من السلسلة.
يمكن الحصول على الأجزاء الفردية للزوج باستخدام العضوين الأول والثاني:

سلسلة أول كتاب؛ إذا (جويس.فيرست == "جيمس" &&
جويس.ثانية == "جويس")
firstBook = "ستيفن هيرو";

إذا كنت بحاجة إلى تعريف عدة كائنات من نفس النوع من هذه الفئة، فمن الملائم استخدام توجيه typedef:

زوج Typedef< string, string >المؤلفون؛ المؤلفون بروست("مارسيل", "بروست"); المؤلفون جويس ("جيمس"، "جويس")؛ المؤلفون موسيل("روبرت", "musi1");

هنا مثال آخر لاستخدام الزوج. تحتوي القيمة الأولى على اسم بعض الكائنات، والثانية هي مؤشر لعنصر الجدول المقابل لهذا الكائن.

فتحة دخول الفئة؛ فتحة الإدخال الخارجية* 1ook_up(string); زوج typedef< string, EntrySlot* >إدخال الرمز; رمز الإدخال current_entry("author", 1ook_up("author"));
// ... إذا (EntrySlot *it = 1ook_up("editor")) (
current_entry.first = "editor";
current_entry. Second = it;
}

(سنعود إلى فئة الزوج عندما نتحدث عن أنواع الحاويات في الفصل السادس وعن الخوارزميات العامة في الفصل الثاني عشر.)

3.15. أنواع الفصول

تسمح لك آلية الفصل بإنشاء أنواع بيانات جديدة؛ بمساعدتها، تم تقديم أنواع السلسلة والمتجهات والمعقدة والزوجية التي تمت مناقشتها أعلاه. في الفصل الثاني، قدمنا ​​المفاهيم والآليات التي تدعم النهج الكائني والموجه للكائنات، باستخدام فئة Array كمثال. هنا، بناءً على نهج الكائن، سنقوم بإنشاء فئة سلسلة بسيطة، والتي سيساعدنا تنفيذها على فهم التحميل الزائد للمشغل، على وجه الخصوص - تحدثنا عنه في القسم 2.3. (يتم تناول الفصول بالتفصيل في الفصول 13 و14 و15.) لقد قدمنا ​​​​وصفًا موجزًا ​​للفصل من أجل تقديم أمثلة أكثر إثارة للاهتمام. قد يرغب القارئ الجديد في لغة C++ في تخطي هذا القسم وانتظار وصف أكثر منهجية للفئات في الفصول اللاحقة.)
يجب أن تدعم فئة السلسلة الخاصة بنا التهيئة بواسطة كائن من فئة السلسلة، وسلسلة حرفية، ونوع السلسلة المضمنة، بالإضافة إلى عملية تعيين القيم لهذه الأنواع. نحن نستخدم منشئي الفئة ومشغل المهمة المثقلة لهذا الغرض. سيتم تنفيذ الوصول إلى أحرف السلسلة الفردية كعملية فهرس مثقلة. بالإضافة إلى ذلك، سنحتاج إلى: الدالة size() للحصول على معلومات حول طول السلسلة؛ عملية مقارنة كائنات من النوع String وكائنات String بسلسلة من النوع المدمج؛ وكذلك عمليات الإدخال/الإخراج لكائننا. أخيرًا، قمنا بتنفيذ القدرة على الوصول إلى التمثيل الداخلي لسلسلتنا كنوع سلسلة مدمج.
يبدأ تعريف الفئة بالكلمة الأساسية، متبوعة بمعرف - اسم الفئة أو النوع. بشكل عام، يتكون الفصل من أقسام تسبقها الكلمات العامة (مفتوحة) والخاصة (مغلقة). يحتوي القسم العام عادةً على مجموعة من العمليات التي يدعمها الفصل، تسمى الأساليب أو وظائف الأعضاء في الفصل. تحدد وظائف الأعضاء هذه الواجهة العامة للفئة، وبعبارة أخرى، مجموعة الإجراءات التي يمكن تنفيذها على كائنات تلك الفئة. يتضمن القسم الخاص عادةً أعضاء البيانات الذين يوفرون التنفيذ الداخلي. في حالتنا، تتضمن الأعضاء الداخلية _string - مؤشر إلى char، بالإضافة إلى _size من النوع int. سوف يقوم _size بتخزين معلومات حول طول السلسلة، وسيكون _string عبارة عن مجموعة من الأحرف المخصصة ديناميكيًا. هذا ما يبدو عليه تعريف الفئة:

#يشمل سلسلة الطبقة؛ istream& المشغل >>(istream&, String&);
ostream& المشغل<<(ostream&, const String&); class String {
عام:
// مجموعة من المنشئين
// للتهيئة التلقائية
// سلسلة strl؛ // خيط()
// سلسلة str2("literal"); // سلسلة(const char*);
// سلسلة str3(str2); // سلسلة (سلسلة كونست &)؛ خيط()؛
سلسلة (كونست شار *)؛
سلسلة(سلسلة ثابتة&); // المدمرة
~سلسلة(); // عوامل التعيين
// strl = str2
// str3 = "سلسلة حرفية" String&operator=(const String&);
String&operator=(const char*); // عوامل اختبار المساواة
// strl == str2;
// str3 == "سلسلة حرفية"; عامل منطقي ==(const String&);
عامل منطقي ==(const char*); // التحميل الزائد على مشغل الوصول حسب الفهرس
// strl[ 0 ] = str2[ 0 ]; شار&المشغل(int); // الوصول إلى أعضاء الفصل
حجم صحيح () (إرجاع _ الحجم؛)
char* c_str() ( return _string; ) خاص:
int_size;
شار *_string;
}

تحتوي فئة String على ثلاثة مُنشئات. كما تمت مناقشته في القسم 2.3، تتيح لك آلية التحميل الزائد تحديد تطبيقات متعددة للوظائف التي تحمل الاسم نفسه، طالما أنها تختلف جميعًا في عدد و/أو أنواع معلماتها. المنشئ الأول

إنه المُنشئ الافتراضي لأنه لا يتطلب قيمة أولية صريحة. عندما نكتب:

بالنسبة إلى str1، يتم استدعاء هذا المنشئ.
لكل من المنشئين المتبقيين معلمة واحدة. نعم لاجل

سلسلة str2("سلسلة الأحرف");

يسمى المنشئ

سلسلة (كونست شار *)؛

سلسلة str3(str2);

البناء

سلسلة(سلسلة ثابتة&);

يتم تحديد نوع المنشئ الذي يتم استدعاؤه حسب نوع الوسيطة الفعلية. يُطلق على آخر المُنشئات، String(const String&)، مُنشئ النسخ لأنه يقوم بتهيئة كائن بنسخة من كائن آخر.
إذا كتبت:

سلسلة str4(1024);

سيؤدي هذا إلى حدوث خطأ في الترجمة، لأنه لا يوجد مُنشئ بمعلمة من النوع int.
إعلان عامل التحميل الزائد له التنسيق التالي:

عامل تشغيل Return_type op(parameter_list);

حيث أن عامل التشغيل هو كلمة أساسية، وop هو أحد عوامل التشغيل المحددة مسبقًا: +، =، ==، وما إلى ذلك. (راجع الفصل 15 للحصول على تعريف دقيق للتركيب.) فيما يلي إعلان عامل الفهرس المحمل بشكل زائد:

شار&المشغل(int);

يحتوي هذا العامل على معلمة واحدة من النوع int ويقوم بإرجاع مرجع إلى char. قد يتم تحميل عامل التشغيل المثقل بشكل زائد إذا اختلفت قوائم المعلمات الخاصة بعمليات إنشاء مثيلات فردية. بالنسبة لفئة السلسلة الخاصة بنا، سنقوم بإنشاء عاملي إسناد ومساواة مختلفين.
لاستدعاء وظيفة عضو، استخدم عوامل وصول العضو النقطة (.) أو السهم (->). دعونا نحصل على إعلانات لكائنات من النوع String:

كائن سلسلة ("داني")؛
String *ptr = new String("آنا");
مصفوفة السلسلة؛
إليك ما يبدو عليه استدعاء size() على هذه الكائنات:
المتجه الأحجام (3)؛

// عضو الوصول للكائنات (.); // حجم الكائنات هو 5 أحجام[ 0 ] = object.size(); // وصول الأعضاء للمؤشرات (->)
// ptr بحجم 4
الأحجام[ 1 ] = ptr->size(); // الوصول إلى العضو (.)
// حجم المصفوفة 0
الأحجام[2] = array.size();

تقوم بإرجاع 5 و 4 و 0 على التوالي.
يتم تطبيق العوامل المحملة بشكل زائد على الكائن بنفس الطريقة التي يتم بها تطبيق العوامل العادية:

اسم السلسلة ("يادي")؛ اسم السلسلة 2("يودي"); // عامل التشغيل المنطقي ==(سلسلة const&)
إذا (الاسم == الاسم 2)
يعود؛
آخر
// سلسلة & عامل = (سلسلة ثابتة &)
الاسم = الاسم2؛

يجب أن يكون إعلان وظيفة العضو داخل تعريف الفئة، ويمكن أن يكون تعريف الوظيفة داخل تعريف الفئة أو خارجه. (يتم تعريف كل من الدالتين size() وc_str() داخل فئة ما.) إذا تم تعريف دالة خارج فئة ما، فيجب علينا تحديد الفئة التي تنتمي إليها، من بين أمور أخرى. في هذه الحالة، يتم وضع تعريف الوظيفة في الملف المصدر، مثلاً String.C، ويتم وضع تعريف الفئة نفسها في ملف الرأس (String.h في مثالنا)، والذي يجب تضمينه في المصدر :

// محتويات الملف المصدر: String.C // تمكين تعريف فئة السلسلة
#include "String.h" // يتضمن تعريف الدالة strcmp()
#يشمل
منطقي // نوع الإرجاع
السلسلة:: // الفئة التي تنتمي إليها الوظيفة
عامل == // اسم الوظيفة: عامل المساواة
(سلسلة const &rhs) // قائمة المعلمات
{
إذا (_size!= rhs._size)
عودة كاذبة؛
إرجاع strcmp(_strinq, rhs._string) ؟
خطأ: صحيح؛
}

تذكر أن strcmp() هي دالة مكتبة قياسية في لغة C، فهي تقارن سلسلتين مضمنتين، وتعيد 0 إذا كانت السلاسل متساوية وغير صفرية إذا لم تكن متساوية. يقوم العامل الشرطي (؟:) باختبار القيمة قبل علامة الاستفهام. إذا كان صحيحًا، فسيتم إرجاع قيمة التعبير الموجود على يسار النقطتين؛ وإلا، فسيتم إرجاع القيمة الموجودة على اليمين. في مثالنا، تكون قيمة التعبير خاطئة إذا أعادت الدالة strcmp() قيمة غير صفرية، وتكون صحيحة إذا أعادت قيمة صفر. (تمت مناقشة العامل الشرطي في القسم 4.7.)
يتم استخدام عملية المقارنة في كثير من الأحيان، وتبين أن الوظيفة التي تنفذها صغيرة، لذلك من المفيد إعلان هذه الوظيفة مدمجة (مضمنة). يقوم المترجم باستبدال نص الوظيفة بدلاً من استدعائها، لذلك لا يضيع أي وقت في مثل هذا الاستدعاء. (تتم مناقشة الوظائف المضمنة في القسم 7.6.) تكون وظيفة العضو المحددة داخل الفصل مدمجة بشكل افتراضي. إذا تم تعريفها خارج الفصل، فمن أجل إعلانها مدمجة، تحتاج إلى استخدام الكلمة الأساسية المضمنة:

سلسلة منطقية مضمّنة::operator==(const String &rhs) (// نفس الشيء)

يجب أن يكون تعريف الوظيفة المضمنة في ملف الرأس الذي يحتوي على تعريف الفئة. من خلال إعادة تعريف عامل التشغيل == كعامل سطري، يجب علينا نقل نص الوظيفة نفسه من ملف String.C إلى ملف String.h.
فيما يلي تنفيذ عملية مقارنة كائن سلسلة بنوع سلسلة مضمن:

سلسلة منطقية مضمنة::operator==(const char *s) ( return strcmp(_string, s) ? false: true; )

اسم المنشئ هو نفس اسم الفئة. يعتبر عدم إرجاع قيمة، لذلك ليست هناك حاجة لتحديد قيمة إرجاع سواء في تعريفه أو في نصه. يمكن أن يكون هناك العديد من الصانعين. مثل أي وظيفة أخرى، يمكن الإعلان عنها في السطر.

#يشمل // المنشئ الافتراضي المضمّن String::String()
{
_الحجم = 0;
_سلسلة = 0;
) inline String::String(const char *str) ( if (! str) ( _size = 0; _string = 0; ) else ( _size = str1en(str); strcpy(_string, str); ) // نسخ المُنشئ
سلسلة مضمنة::سلسلة(سلسلة const &rhs)
{
size = rhs._size;
إذا (! rhs._string)
_سلسلة = 0;
آخر(
_string = new char[ _size + 1];
} }

نظرًا لأننا قمنا بتخصيص الذاكرة ديناميكيًا باستخدام العامل الجديد، فإننا نحتاج إلى تحريرها عن طريق استدعاء الحذف عندما لا نكون بحاجة إلى كائن السلسلة. تخدم وظيفة عضو خاصة أخرى هذا الغرض - المدمر، الذي يتم استدعاؤه تلقائيًا على الكائن في اللحظة التي يتوقف فيها هذا الكائن عن الوجود. (راجع الفصل السابع حول عمر الكائن.) يتكون اسم المدمر من حرف التلدة (~) واسم الفئة. فيما يلي تعريف مدمر فئة السلسلة. هذا هو المكان الذي نسميه عملية الحذف لتحرير الذاكرة المخصصة في المنشئ:

سلسلة مضمنة: :~String() (حذف _string;)

يستخدم كلا عاملي التعيين المحملين بشكل زائد الكلمة الأساسية الخاصة this.
عندما نكتب:

سلسلة namel("أورفيل"), name2("ويلبر");
الاسم = "أورفيل رايت"؛
هذا هو المؤشر الذي يعالج كائن name1 داخل النص الأساسي لعملية التعيين.
يشير هذا دائمًا إلى كائن الفئة الذي يتم من خلاله استدعاء الوظيفة. لو
ptr->size();
كائن[1024];

ثم داخل size() ستكون قيمة هذا هي العنوان المخزن في ptr. داخل عملية الفهرس، يحتوي هذا على عنوان obj. من خلال إلغاء الإشارة إلى this (باستخدام *this)، نحصل على الكائن نفسه. (تم وصف هذا المؤشر بالتفصيل في القسم 13.4.)

Inline String& String::operator=(const char *s) ( if (! s) ( _size = 0; حذف _string; _string = 0; ) else ( _size = str1en(s); حذف _string; _string = new char[ _size + 1 ]; strcpy(_string, s);

عند تنفيذ عملية إسناد، أحد الأخطاء التي يتم ارتكابها غالبًا هو نسيان التحقق مما إذا كان الكائن الذي يتم نسخه هو نفس الكائن الذي تم إجراء النسخة إليه. سنقوم بإجراء هذا الفحص باستخدام نفس هذا المؤشر:

Inline String& String::operator=(const String &rhs) ( // في التعبير // namel = *pointer_to_string // هذا يمثل name1، // rhs - *pointer_to_string.if (this != &rhs) (

فيما يلي النص الكامل لعملية تعيين كائن من نفس النوع لكائن سلسلة:

سلسلة مضمنة وسلسلة::operator=(const String &rhs) ( if (this != &rhs) (حذف _string; _size = rhs._size; if (! rhs._string)
_سلسلة = 0;
آخر(
_string = new char[ _size + 1];
strcpy(_string, rhs._string);
}
}
العودة * هذا؛
}

إن عملية أخذ الفهرس مطابقة تقريبًا لتطبيقها على مصفوفة Array التي أنشأناها في القسم 2.3:

#يشمل حرف مضمّن&
سلسلة :: المشغل (عنصر int)
{
تأكيد(elem >= 0 && elem< _size);
إرجاع _سلسلة [عنصر]؛
}

يتم تنفيذ عوامل الإدخال والإخراج كوظائف منفصلة بدلاً من كونها أعضاء في الفصل. (سنتحدث عن أسباب ذلك في القسم 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*);

يحتاج مشغل الإخراج إلى الوصول إلى التمثيل الداخلي للسلسلة. منذ المشغل<< не является функцией-членом, он не имеет доступа к закрытому члену данных _string. Ситуацию можно разрешить двумя способами: объявить operator<< дружественным классу String, используя ключевое слово friend (дружественные отношения рассматриваются в разделе 15.2), или реализовать встраиваемую (inline) функцию для доступа к этому члену. В нашем случае уже есть такая функция: c_str() обеспечивает доступ к внутреннему представлению строки. Воспользуемся ею при реализации операции вывода:

مضمنة ostream والمشغل<<(ostream& os, const String &s) { return os << s.c_str(); }

يوجد أدناه برنامج مثال يستخدم فئة السلسلة. يأخذ هذا البرنامج الكلمات من تدفق الإدخال ويحسب العدد الإجمالي لها، بالإضافة إلى عدد الكلمات "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; / / الكلمتين "ال" و"هو"
// سوف نتحقق باستخدام عامل التشغيل==(const char*)
سلسلة لكن، the("the"),it("it"); // عامل التشغيل>>(ostream&، String&)
بينما (سين >> بوف) (
++wdCnt; // المشغل أو العامل<<(ostream&, const String&)
cout<< buf << " "; if (wdCnt % 12 == 0)
cout<< endl; // String::operator==(const String&) and
// String::operator==(const char*);
إذا (buf == the | | buf == "The")
++theCnt;
آخر
إذا (buf == it || buf == "It")
++itCnt; // يستدعي السلسلة::s-ize()
ل (int التاسع = 0؛ التاسع< buf.sizeO; ++ix)
{
// يستدعي String::operator(int)
التبديل (بوف [التاسع])
{
الحالة "أ": الحالة "أ": ++aCnt; استراحة؛
الحالة "e": الحالة "E": ++eCnt; استراحة؛
الحالة "i": الحالة "I": ++iCnt; استراحة؛
الحالة "o": الحالة "0": ++oCnt; استراحة؛
الحالة "u": الحالة "U": ++uCnt; استراحة؛
الافتراضي: ++notVowe1; استراحة؛
}
}
) // المشغل أو العامل<<(ostream&, const String&)
cout<< "\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;
}

لنختبر البرنامج: سنعرض عليه فقرة من قصة أطفال كتبها أحد مؤلفي هذا الكتاب (سنلتقي بهذه القصة في الفصل السادس). وهنا نتيجة البرنامج:

أليس إيما لديها شعر أحمر طويل متدفق. يقول والدها إنه عندما تهب الريح على شعرها، فإنه يبدو حيًا تقريبًا، مثل طائر ناري أثناء طيرانه. أخبرها أنه طائر ناري جميل، سحري لكنه جامح. "أبي، اسكت، لا يوجد شيء من هذا القبيل،" قالت له، وفي نفس الوقت أرادت منه أن يخبرها بالمزيد. تسأل بخجل: "أعني يا أبي، هل هناك؟" الكلمات: 65
/ ال: 2
هو/هو: 1
الحروف الساكنة: 190
ج: 22
ه: 30
ط: 24
س: 10
ش: 7

التمرين 3.26

تحتوي تطبيقاتنا للمنشئين ومشغلي المهام على الكثير من التكرار. فكر في نقل التعليمات البرمجية المكررة إلى وظيفة عضو خاص منفصلة، ​​كما حدث في القسم 2.3. تأكد من أن الخيار الجديد يعمل.

التمرين 3.27

قم بتعديل برنامج الاختبار بحيث يقوم أيضًا بإحصاء الحروف الساكنة b، d، f، s، t.

التمرين 3.28

اكتب دالة عضو تحسب عدد مرات ظهور الحرف في سلسلة باستخدام التصريح التالي:

سلسلة الفئة ( عامة: // ... int count(char ch) const; // ... );

التمرين 3.29

قم بتنفيذ عامل تسلسل السلسلة (+) بحيث يقوم بتسلسل سلسلتين وإرجاع النتيجة في كائن سلسلة جديد. هنا هو إعلان الوظيفة:

سلسلة الفئة ( عامة: // ... مشغل السلسلة+(const String &rhs) const; // ... );

في هذا الدرس سوف تتعلم أبجدية لغة C++، وماذا أيضا أنواع البياناتيمكن للبرنامج معالجتها. قد لا تكون هذه هي اللحظة الأكثر إثارة، ولكن هذه المعرفة ضرورية بالإضافة إلى ذلك، عندما تبدأ في تعلم أي لغة برمجة أخرى، فسوف تمر بمرحلة مماثلة من التعلم بثقة أكبر! يمكن أن يحتوي برنامج C++ على الرموز التالية:

  • الأحرف اللاتينية الكبيرة والصغيرة A، B، C...، x، y، z والشرطة السفلية؛
  • الأرقام العربية من 0 إلى 9؛
  • الأحرف الخاصة: ( ) ، | , () + - / % * . \' : ؟< > = ! & # ~ ; ^
  • المسافة وعلامة التبويب وأحرف السطر الجديد.

في اختبار البرنامج يمكنك استخدامه تعليقات. إذا كان النص يحتوي على حرفين مائلين للأمام // وينتهي بحرف سطر جديد أو كان محاطًا بالحرفين /* و */، فإن المترجم يتجاهله.

البيانات في C++

لحل مشكلة ما، يقوم أي برنامج بمعالجة بعض البيانات. يمكن أن تكون من أنواع مختلفة: الأعداد الصحيحة والأرقام الحقيقية، والأحرف، والسلاسل، والمصفوفات. في لغة C++، يتم وصف البيانات عادة في بداية الوظيفة. ل أنواع البيانات الأساسيةاللغات تشمل:

لتوليد أنواع أخرى من البيانات الأساسية وما يسمى محددات.تحدد لغة C++ أربعة محددات لنوع البيانات:

  • قصير قصير؛
  • طويل طويل؛
  • وقعت - وقعت؛
  • غير موقعة - غير موقعة.

نوع عدد صحيح

نوع المتغير كثافة العملياتفي ذاكرة الكمبيوتر يمكن أن تشغل إما 2 أو 4 بايت. ذلك يعتمد على حجم بت المعالج. افتراضيًا، تعتبر كافة أنواع الأعداد الصحيحة موقعة، أي المحدد وقعتقد لا يتم تحديدها. محدد غير موقعةيسمح لك بتمثيل الأرقام الإيجابية فقط. فيما يلي بعض نطاقات قيم النوع الصحيح

يكتب يتراوح مقاس
كثافة العمليات -2147483648…2147483647 4 بايت
كثافة العمليات غير الموقعة 0…4294967295 4 بايت
وقعت كثافة العمليات -2147483648…2147483647 4 بايت
كثافة العمليات قصيرة -32768…32767 2 بايت
كثافة العمليات طويلة -2147483648…2147483647 4 بايت
كثافة العمليات قصيرة غير موقعة 0…65535 2 بايت

النوع الحقيقي

يتم تمثيل رقم النقطة العائمة في النموذج mE +- p، حيث m هو الجزء العشري (عدد صحيح أو رقم كسري بنقطة عشرية)، p هو الأس (عدد صحيح). عادة القيم مثل يطفويستغرق 4 بايت، و مزدوج 8 بايت. جدول نطاق القيمة الحقيقية:

يطفو 3.4E-38...3.4E+38 4 بايت
مزدوج 1.7E-308…1.7E+308 8 بايت
مزدوج طويل 3.4E-4932...3.4E+4932 8 بايت

نوع منطقي

نوع المتغير منطقييمكن أن تأخذ قيمتين فقط حقيقي (حقيقي ) أو فاسل (كذب ). يتم تفسير أي قيمة لا تساوي الصفر على أنها حقيقي.معنى خطأ شنيعممثلة في الذاكرة كـ 0.

اكتب الفراغ

مجموعة القيمة من هذا النوع فارغة. يتم استخدامه لتحديد الوظائف التي لا تُرجع قيمة، ولتحديد قائمة فارغة لوسائط الوظائف، كنوع أساسي للمؤشرات، وفي عمليات صب الكتابة.

تحويل نوع البيانات

في لغة C++، هناك نوعان من تحويل نوع البيانات: صريح وضمني.

  • التحويل الضمنييحدث تلقائيا. ويتم ذلك أثناء المقارنة أو التعيين أو تقييم التعبيرات بمختلف أنواعها. على سبيل المثال، سيقوم البرنامج التالي بطباعة قيمة مثل إلى وحدة التحكم يطفو

#تتضمن "stdafx.h" #تتضمن استخدام اسم للمحطة؛ int main() ( int i=5; float f=10.12; cout<>باطل"); إرجاع 0;)

#تشمل "stdafx.h"

#يشمل

استخدام اسم للمحطة ؛

انت مين()

إنت ط = 5؛ تعويم و = 10.12؛

cout<< i / f ;

النظام ("إيقاف مؤقت>>باطل")؛

العودة 0 ;

يتم إعطاء الأولوية القصوى للنوع الذي يتم فيه فقدان أقل قدر من المعلومات. يجب ألا تسيء استخدام تحويل النوع الضمني، حيث قد تنشأ مواقف مختلفة غير متوقعة.

  • تحويل صريحعلى عكس الضمني، يتم تنفيذه بواسطة المبرمج. هناك عدة طرق للقيام بهذا التحويل:
  1. التحويل إلى الأنماط ج: (تطفو) أ
  2. التحويل إلى الأنماط سي ++: يطفو()

يمكن أيضًا إجراء صب النوع باستخدام العمليات التالية:

static_cast<>() const_cast<>() reinterpret_cast<>()dynamic_cast<> ()

static_cast<> ()

const_cast<> ()

reinterpret_cast<> ()

Dynamic_cast<> ()

static_cas- تحويل أنواع البيانات ذات الصلة. يقوم هذا العامل بتحويل الأنواع وفقًا للقواعد المعتادة، والتي قد تكون ضرورية عندما لا يقوم المترجم بإجراء التحويل التلقائي. سيبدو بناء الجملة كما يلي:

اكتب static_cast<Тип>(شيء)؛

باستخدام static_cast، لا يمكنك إزالة الثبات من متغير، ولكن يمكن للعامل التالي القيام بذلك. const_cast- يستخدم فقط عندما يكون من الضروري إزالة الثبات من الكائن. سيبدو بناء الجملة كما يلي:

يكتبconst_cast< يكتب> (شيء);

reinterpret_cast- يستخدم لتحويل الأنواع المختلفة، عدد صحيح إلى مؤشر والعكس. إذا رأيت كلمة "فهرس" جديدة - فلا تنزعج! وهذا أيضًا نوع بيانات، لكننا لن نعمل معه قريبًا. بناء الجملة هنا هو نفس بناء الجملة الخاص بالعوامل التي تمت مناقشتها سابقًا:

يكتبإعادة تفسير_يقذف< يكتب> (شيء);

Dynamic_cast- يستخدم في التحويل الديناميكيالأنواع، وتنفذ صب المؤشرات أو المراجع. بناء الجملة:

يكتبمتحرك _يقذف< يكتب> (شيء);

أحرف التحكم

أنت بالفعل على دراية ببعض "أحرف التحكم" هذه (على سبيل المثال، مع ). تبدأ جميعها بشرطة مائلة عكسية وتحيط بها أيضًا علامات الاقتباس المزدوجة.

صورة

رمز سداسي عشري

اسم

صوت الصافرة

العودة خطوة واحدة

ترجمة الصفحة (التنسيق)

ترجمة الخط

إرجاع

الجدولة الأفقية

علامة التبويب العمودية