بناء جملة لغة C

مقتطف من كود C من نواة لينكس

تُعرف بنية لغة C بأنها الشكل الذي يجب أن يتخذه النص ليكون كودًا مكتوبًا بلغة C. صُممت قواعد بنية اللغة للسماح بكتابة كود موجز، يرتبط ارتباطًا وثيقًا بالكود الناتج ، مع توفير تجريد بيانات عالي المستوى نسبيًا . كانت C أول لغة برمجة عالية المستوى حققت نجاحًا واسعًا في تطوير أنظمة التشغيل المحمولة . [ 1 ] تعتمد بنية C على مبدأ "التلخيص الأقصى" . وباعتبارها لغة حرة الشكل ، يمكن تنسيق كود C بطرق مختلفة دون التأثير على طبيعته النحوية. وقد أثرت بنية C على بنية اللغات اللاحقة، بما في ذلك C++ و Java و C# .

الهيكل عالي المستوى

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

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

نقطة الدخول

كود C لبرنامج يطبع "Hello, World!"

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

int main (); int main ( void ); int main ( int argc , char * argv []); int main ( int argc , char ** argv ); int main ( void ); int main ( int argc , char * argv [], char * envp []); int main ( int argc , char ** argv , char ** envp ); int main ( int argc , char * argv [], char ** envp ); int main ( int argc , char ** argv , char * envp []);

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

تُستخدم القيمة المُعادة، المكتوبة بصيغة int، كمؤشر حالة لبيئة النظام. <stdlib.h>توفر المكتبة القياسية، المُعرّفة في ، وحدات ماكرو لقيم الحالة القياسية: EXIT_SUCCESSو EXIT_FAILURE. مع ذلك، يمكن للبرنامج الإشارة إلى الحالة باستخدام أي قيم. على سبيل المثال، killيُعيد الأمر القيمة العددية للإشارة مضافًا إليها 128.

يتكون البرنامج البسيط من mainدالة فارغة، مثل:

int main () {}

على عكس الدوال الأخرى، تتطلب اللغة [ أ ] أن يتصرف البرنامج كما لو أنه يُرجع القيمة 0 حتى لو لم ينتهِ بعبارة return. [ 2 ]

في بيئة مستقلة (غير مستضافة)، مثل نظام بدون نظام تشغيل ، يسمح المعيار بمعالجة بدء التشغيل بشكل مختلف. ولا يتطلب ذلك بالضرورة mainوجود وظيفة.

وسائط سطر الأوامر

يتم تمرير الوسائط المضمنة في سطر الأوامر لبدء برنامج ما إلى البرنامج كقيمتين - عدد الوسائط (يسمى عادةً argc) ومصفوفة من السلاسل المنتهية بـ null (يسمى عادةً argv) مع اسم البرنامج كأول عنصر.

يقوم الكود التالي بطباعة قيمة معلمات سطر الأوامر.

#include <stdio.h>int main ( int argc , char * argv []) { printf ( "argc \t = %d \n " , argc ); for ( int i = 0 ; i < argc ; ++ i ) { printf ( "argv[%i] \t = %s \n " , i , argv [ i ]); } }
$ ./a.out abc def argc = 3 argv[0] = ./a.out argv[1] = abc argv[2] = def

الكلمات المحجوزة

الكلمات التالية محجوزة - غير مسموح بها كمعرفات، ويبلغ عددها 43 كلمة.

  • alignas
  • alignof
  • auto
  • bool
  • break
  • case
  • char
  • const
  • constexpr
  • continue
  • default
  • do
  • double
  • else
  • enum
  • extern
  • float
  • for
  • goto
  • if
  • inline
  • int
  • long
  • register
  • restrict
  • return
  • short
  • signed
  • sizeof
  • static
  • static_assert
  • struct
  • switch
  • thread_local
  • typedef
  • typeof
  • typeof_unqual
  • union
  • unsigned
  • void
  • volatile
  • while

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

  • _Alignas(مهجور)
  • _Alignof(مهجور)
  • _Atomic
  • _BitInt
  • _Bool(مهجور)
  • _Complex
  • _Countof
  • _Decimal32
  • _Decimal64
  • _Decimal128
  • _Generic
  • _Noreturn(مهجور)
  • _Static_assert(مهجور)
  • _Thread_local(مهجور)

_Imaginaryتمت إزالة الكلمة المفتاحية في C2Y .

تشير الكلمات التالية إلى القيم الحرفية المستخدمة في اللغة، والتي يوجد منها 3.

  • nullptr
  • true
  • false

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

توجيهات المعالج المسبق

فيما يلي توجيهات للمعالج المسبق ، وعددها 19 توجيهاً.

  • #if
  • #elif
  • #else
  • #endif
  • #ifdef
  • #ifndef
  • #elifdef
  • #elifndef
  • #define
  • #undef
  • #include
  • #embed
  • #line
  • #error
  • #warning
  • #pragma
  • #__has_include
  • #__has_embed
  • #__has_c_attribute

يوفر هذا _Pragmaالمعامل صيغة بديلة للوظائف التي يوفرها #pragma.

تعليقات

التعليق - وهو نص توضيحي للمبرمج يتجاهله مترجم اللغة - يمكن تضمينه في الكود إما كتعليق سطري أو تعليق كتلة. يبدأ التعليق السطري وينتهي في نهاية السطر نفسه. أما التعليق الكتلة فيبدأ وينتهي ، ويمتد على أي عدد من الأسطر (أو سطر واحد فقط).///**/

في بعض الحالات، يتم تجاهل علامات التعليق. يُستثنى نص السلسلة النصية من اعتباره بداية تعليق. ولا يمكن تداخل التعليقات. على سبيل المثال، /*لا يُعامل التعليق في سطر كما //يُعامل بداية تعليق كتلة، والعكس صحيح.

نشأت صيغة التعليقات السطرية، والتي تُسمى أحيانًا بنمط C++، في لغة BCPL وأصبحت صيغةً صالحةً في C99 . وهي غير متوفرة في إصدار K&R الأصلي ولا في ANSI C.

يوضح الكود التالي كيفية استخدام التعليقات. يحتوي السطر 1 على تعليق سطري، بينما يحتوي السطران 3 و4 على تعليق كتلة. يوضح السطر 4 إمكانية تضمين تعليق كتلة في سطر يحتوي على كود قبله وبعده.

int i ; // تعليق سطري /*  تعليق كتلة */ int ii = /* صفر دائمًا */ 0 ;

يوضح المثال التالي مشكلة محتملة في صيغة التعليقات. ما يُفترض أن يكون عامل القسمة /ثم عامل فك المرجع *، يُقيّم على أنه بداية تعليق كتلة.

x = * p /*q;

النص التالي لا يُمثل صيغة صحيحة للغة C، إذ لا يمكن تضمين التعليقات داخل بعضها. يبدو أن الأسطر من 3 إلى 5 عبارة عن تعليق مُضمن داخل كتلة التعليقات الممتدة من 1 إلى 7. ولكن في الواقع، يُنهي السطر 5 التعليق الذي بدأ في السطر 1. وهذا يجعل السطر 6 يُفسر على أنه كود، وهو ما يُعد بوضوح غير صحيح لصيغة C.

/* أول سطر من كتلة التعليقات /* أول سطر مما يُفترض أن يكون كتلة داخلية */ يتعامل المترجم مع هذا السطر على أنه كود ، ولكنه غير صالح ! */

المعرفات

يدعم هذا النظام معرّفات يُحددها المستخدم. يجب أن يبدأ المعرّف بحرف (AZ، az) أو شرطة سفلية (_)، ويمكن أن تكون الأحرف اللاحقة أحرفًا أو أرقامًا (من 0 إلى 9) أو شرطات سفلية، ويجب ألا يكون كلمة محجوزة. المعرّفات حساسة لحالة الأحرف، مما يجعل fooو FOOو و Fooمختلفة.

أمر التقييم

توجد طرق متعددة لتقييم تعبير رياضي يتوافق مع الترميز الرياضي . على سبيل المثال، يمكن تقييم التعبير بالترتيب التالي : ، ، ، ، أو بالترتيب التالي : ، ، ، .(1+1)+(3+3)(1+1)+(3+3)(2)+(3+3)(2)+(6)(8)(1+1)+(3+3)(1+1)+(6)(2)+(6)(8)

لتقليل مشاكل وقت التشغيل المتعلقة بترتيب التقييم مع توفير بعض التحسينات، ينص المعيار على أنه يمكن تقييم التعبيرات بأي ترتيب بين نقاط التسلسل التي يتم تعريفها على أنها أي مما يلي:

  • نهاية البيان
  • عامل التسلسل : الفاصلة؛ الفواصل التي تفصل وسائط الدالة ليست نقاط تسلسل.
  • عوامل الدائرة المختصرة : العامل المنطقي " و " ( &&والذي يمكن قراءته " ثم ") والعامل المنطقي " أو " ( ||والذي يمكن قراءته " أو وإلا ").
  • عامل الشرط الثلاثي ( ?:): يقوم هذا العامل بتقييم تعبيره الفرعي الأول أولاً، ثم الثاني أو الثالث (لا يقوم بتقييم كليهما أبدًا) بناءً على قيمة الأول.
  • الدخول إلى استدعاء دالة والخروج منه (ولكن ليس بين عمليات تقييم الوسائط)

تُقيّم التعبيرات التي تسبق نقطة التسلسل دائمًا قبل تلك التي تليها. في حالة التقييم المختصر، قد لا يُقيّم التعبير الثاني اعتمادًا على نتيجة التعبير الأول. على سبيل المثال، في التعبير (a() || b())، إذا كانت قيمة الوسيط الأول غير صفرية (صحيح)، فلا يمكن أن تكون نتيجة التعبير بأكمله إلا صحيحة، وبالتالي b()لا يُقيّم. وبالمثل، في التعبير ، إذا كانت قيمة الوسيط الأول صفرية (خطأ)، فلا يمكن أن تكون نتيجة التعبير بأكمله إلا خاطئة، وبالتالي لا يُقيّم.(a()&&b())b()

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

printf ( "%s %s \n " , argv [ i = 0 ], argv [ ++ i ]);

تضمين الكود

العناوين

يتم تضمين التعليمات البرمجية من ملفات أخرى باستخدام #includeالتوجيه الذي ينسخ محتويات الملف نصيًا في مكانه. تقليديًا، تُقسّم تعليمات لغة C البرمجية بين ملف رأس (بامتداد .h ) وملف مصدر (بامتداد .c ). يحتوي ملف الرأس على تعريفات الرموز، بينما يحتوي ملف المصدر على التنفيذ الكامل. يُفرض هذا الفصل لمنع المُصرّف من إعادة ترجمة نفس التعليمات البرمجية المصدرية بشكل متكرر في كل مرة يتم فيها تضمين مكتبة، مما يؤدي إلى زيادة وقت الترجمة. لا تحتوي ملفات الرأس ووحدات الترجمة المنفصلة على مفهوم مساحات الأسماء ، مما يعني أن جميع أسماء الرموز عامة، وستحدث تعارضات إذا وُجد الاسم نفسه لكائنات متعددة في وحدة الترجمة نفسها.

لمنع تضمين رأس الملف أكثر من مرة، يمكن استخدام #include guards أو #pragma once .

يمكن تجميع ملفات رأس C في ملفات رأس مُجمّعة مسبقًا ، مما يُسرّع عملية المعالجة بواسطة المُجمّع. عادةً، يمكن تجميع المكتبات التي لا تتغير كثيرًا (مثل ملفات رأس مكتبة C القياسية) مسبقًا لتسريع عملية التجميع في المشروع.

للتمييز بين البحث في دليل تضمين والبحث في مسار نسبي، استخدم الأقواس الزاوية لأدلة التضمين وعلامات الاقتباس للمسارات النسبية.

دبراغما مرة واحدة// تضمينات المكتبة القياسية #include <stdio.h> #include <stdlib.h>// local #include "Personalities.h" #include "Math/SpecialFunctions.h"// المكتبات الخارجية #include <sqlite3.h>

وحدات Clang C

يُقدّم Clang ميزة غير قياسية تُسمى الوحدات ، وهي مشابهة لوحدات C++ ولكنها تختلف عنها دلاليًا. تُستخدم كلتاهما لتقليل وقت الترجمة من خلال ترجمة وحدة الترجمة مرة واحدة.

تضمين

يمكن استخدام هذا #embedالتوجيه لتضمين محتوى ثنائي في ملف، حتى لو لم يكن كود C صالحًا.

constexpr char ICON_DISPLAY_DATA [] = { #embed "art.png" };// حدد أي نوع يمكن تهيئته من خلال تعبيرات الثوابت العددية الصحيحة constexpr char RESET_BLOB [] = { #embed "data.bin" };// تعمل السمات بنفس الكفاءة alignas ( 8 ) constexpr char ALIGNED_DATA_STRING [] = { #embed "attributes.xml" };int main () { return #embed </dev/urandom> limit(1) ; }

نظام الكتابة

الأنواع الأولية

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

تتضمن أنواع الأعداد الصحيحة متغيرات من النوعين signedو unsigned. إذا لم يُحدد أي منهما، signedيُفترض النوع في معظم الحالات. مع ذلك، ولأسباب تاريخية، charيُعد النوع نوعًا منفصلاً عن كل من signed charو unsigned char. قد يكون النوع موجبًا أو سالبًا، وذلك بحسب المُصرّف ومجموعة الأحرف (يشترط المعيار أن تكون قيم عناصر مجموعة الأحرف الأساسية موجبة). كذلك، قد تكون أنواع حقول البتات المُحددة كـ intموجبة أو سالبة، وذلك بحسب المُصرّف.

أنواع الأعداد الصحيحة

تأتي أنواع الأعداد الصحيحة بأحجام ثابتة مختلفة، قادرة على تمثيل نطاقات متنوعة من الأرقام. يشغل النوع بايتًاchar واحدًا بالضبط (أصغر وحدة تخزين قابلة للعنونة)، والذي يبلغ عرضه عادةً 8 ​​بتات. (على الرغم من إمكانية تمثيل أي حرف "أساسي"، قد يتطلب الأمر نوعًا أوسع لمجموعات الأحرف الدولية). تحتوي معظم أنواع الأعداد الصحيحة على نوعين: مُوَقَّع وغير مُوَقَّع ، ويُشار إليهما بالكلمتين المفتاحيتين `in` و` in`. تستخدم أنواع الأعداد الصحيحة المُوَقَّعة دائمًا تمثيل المُتمِّم الثنائي ، منذ معيار C23 [ 3 ] (وفي الواقع العملي قبل ذلك؛ في الإصدارات السابقة لمعيار C23، كان من الممكن أن يكون التمثيل بديلًا عن ذلك هو المُتمِّم الأحادي ، أو تمثيل الإشارة والمقدار ، ولكن في الواقع العملي لم يعد هذا هو الحال لعقود على الأجهزة الحديثة). في كثير من الحالات، توجد طرق متكافئة متعددة لتحديد النوع؛ على سبيل المثال، `in` و` in` مترادفان.charsignedunsignedsigned short intshort

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

أنواع الأعداد الصحيحة القياسية
اسمالحد الأدنى للعرض (بت)
bool1
char8
signed char8
unsigned char8
short16
unsigned short16
int16
unsigned int16
long32
unsigned long32
long long[ ملاحظة 1 ]64
unsigned long long[ ملاحظة 1 ]64

charيختلف هذا النوع عن كل من النوعين `and` signed charو`and` unsigned char، ولكنه يضمن أن يكون له نفس التمثيل الذي يمثله أحدهما. تم توحيد النوعين ` _Booland` و`and` long longمنذ عام 1999، وقد لا تدعمهما المترجمات القديمة. _Boolعادةً ما يتم الوصول إلى النوع `and` عبر typedefالاسم boolالمحدد في ملف الرأس القياسي `<stdbool.h>` ، ولكن منذ معيار C23، تمت إعادة تسمية النوع `and` ، وأصبح استخدامه قديمًا._Boolbool<stdbool.h>

بشكل عام، يتم اختيار عرض النوع ونظام التمثيل المُطبق على أي منصة معينة بناءً على بنية الجهاز، مع مراعاة سهولة استيراد الشيفرة المصدرية المُطورة لمنصات أخرى. intيختلف عرض النوع بشكل خاص بين المترجمين؛ وغالبًا ما يتوافق مع حجم الكلمة "الأكثر طبيعية" للمنصة. يُحدد ملف الترويسة القياسي <limits.h> وحدات ماكرو للحد الأدنى والحد الأقصى للقيم القابلة للتمثيل لأنواع الأعداد الصحيحة القياسية كما هي مُطبقة على أي منصة محددة.

بالإضافة إلى أنواع الأعداد الصحيحة القياسية، قد توجد أنواع أخرى "موسّعة" للأعداد الصحيحة، والتي يمكن استخدامها typedefفي ملفات الرأس القياسية. ولتحديد العرض بدقة أكبر، يمكن للمبرمجين، بل ينبغي عليهم، استخدام typedefأنواع الأعداد الصحيحة من ملف الرأس القياسي <stdint.h> .

يمكن تحديد الثوابت العددية في شفرة المصدر بعدة طرق. يمكن تحديد القيم العددية كعشرية (مثال: 10220)، أو ثمانية مع 0إضافة الصفر (0) كبادئة (0 01776)، أو ست عشرية مع 0xإضافة (صفر ×) كبادئة (0 0x3FE). يُمثل الحرف الموجود بين علامتي اقتباس مفردتين (مثال: 0 'R')، والذي يُسمى "ثابت حرفي"، قيمة ذلك الحرف في مجموعة أحرف التنفيذ، من النوع int. باستثناء الثوابت الحرفية، يُحدد نوع الثابت العددي بالعرض المطلوب لتمثيل القيمة المحددة، ولكنه يكون دائمًا على الأقل بعرض int. يمكن تجاوز ذلك بإضافة مُعدِّل طول و/أو إشارة صريح؛ على سبيل المثال، 12luيكون نوع unsigned long. لا توجد ثوابت عددية سالبة، ولكن يمكن غالبًا الحصول على نفس التأثير باستخدام عامل النفي الأحادي " -".

نوع مُعدّد

النوع المُعدّد ، المُحدد بالكلمة المفتاحية، والذي يُطلق عليهenum غالبًا اسم "enum" (ويُنطق عادةً / ˈiːnʌm/ إي-نوم أو /ˈiːnuːm / إي - نوم ) ، هو نوع مُصمم لتمثيل القيم عبر سلسلة من الثوابت المُسماة. لكل ثابت من الثوابت المُعدّدة نوع .int كل enumنوع مُتوافق مع نوع عدد صحيح مُوقّع أو غير مُوقّع، ولكن كل تطبيقchar يُحدد قواعده الخاصة لاختيار النوع.

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

يُصرَّح عن النوع المُعدَّد باستخدام enumالمُحدِّد واسم (أو وسم ) اختياري للنوع المُعدَّد، متبوعًا بقائمة من ثابت واحد أو أكثر داخل أقواس معقوفة مفصولة بفواصل، وقائمة اختيارية من أسماء المتغيرات. تستخدم المراجع اللاحقة لنوع مُعدَّد مُحدَّد الكلمة enumالمفتاحية واسم النوع المُعدَّد. افتراضيًا، يُعيَّن للثابت الأول في التعداد القيمة صفر، وتُزاد كل قيمة لاحقة بمقدار واحد عن القيمة السابقة. يمكن أيضًا تعيين قيم مُحدَّدة للثوابت في التصريح، وأي ثوابت لاحقة بدون قيم مُحدَّدة ستُعطى قيمًا متزايدة من تلك النقطة فصاعدًا. على سبيل المثال، انظر إلى التصريح التالي:

enum Color { RED , GREEN , BLUE = 5 , YELLOW } paint_color ;

يُحدد هذا enum Colorالنوع؛ intوالثوابت RED(التي قيمتها 0)، و GREEN(التي قيمتها أكبر من 1 بواحد RED، أي 1)، و BLUE(التي قيمتها هي القيمة المُعطاة، أي 5)، و YELLOW(التي قيمتها أكبر من 6 بواحد BLUE، أي 6)؛ والمتغير enum Color. paint_colorيمكن استخدام الثوابت خارج سياق enum(حيث يُسمح بأي قيمة عددية صحيحة)، ويمكن إسناد قيم أخرى غير الثوابت إلى paint_color، أو أي متغير آخر من النوع enum Color.

على عكس لغة C++، فإنّ تعدادات C غير مُحدّدة النطاق، إذ لا يوجد في C مفهوم مساحات الأسماء. في C، enumيمكن تحويل القيم ضمنيًا إلى أنواع عددية، وهو أمر غير آمن من ناحية النوع.

typedef enum Color { RED , ORANGE , YELLOW , GREEN , BLUE , INDIGO , VIOLET } Color ;Color c = RED ; // في لغة C Color d = Color :: RED ; // في لغة C++، ولكن ليس في لغة C

منذ C23، أصبح من الممكن تحديد النوع الأساسي يدويًا لـ enum، كما هو الحال في C++.

enum CardSuit : char { HEARTS , CLUBS , SPADES , DIAMONDS };

أنواع الفاصلة العائمة

تُستخدم صيغة الفاصلة العائمة لتمثيل الأعداد التي تحتوي على جزء كسري. مع ذلك، فهي لا تُمثل معظم الأعداد النسبية بدقة تامة، بل تُقدم تقريبًا دقيقًا لها. توجد ثلاثة أنواع قياسية من القيم الحقيقية، يُشار إليها بمُحدداتها (ومنذ معيار C23، أُضيفت ثلاثة أنواع عشرية أخرى): الدقة المفردة ( float)، والدقة المزدوجة ( double)، والدقة المزدوجة الموسعة ( long double). يُمكن لكل نوع من هذه الأنواع تمثيل القيم بصيغة مختلفة، غالبًا ما تكون إحدى صيغ الفاصلة العائمة المعتمدة من IEEE .

أنواع الفاصلة العائمة
محددات النوعالدقة (الأرقام العشرية)مدى الأس
الحد الأدنىIEEE 754الحد الأدنىIEEE 754
float67.2 (24 بت)±37±38 (8 بت)
double1015.9 (53 بت)±37±307 (11 بت)
long double1034.0 (113 بت)±37±4931 (15 بت)

يمكن كتابة الثوابت ذات الفاصلة العائمة بالصيغة العشرية ، على سبيل المثال 1.23. يمكن استخدام الصيغة العلمية العشرية بإضافة eأو Eمتبوعةً بأس عشري، والمعروفة أيضًا بصيغة E ، على سبيل المثال 1.23e2(والتي قيمتها 1.23 × 10² = 123.0). يُشترط وجود فاصلة عشرية أو أس (وإلا، يُفسر العدد كثابت صحيح). تتبع الثوابت ذات الفاصلة العائمة السداسية عشرية قواعد مماثلة، باستثناء أنه يجب أن تُسبق بـ 0xواستخدام pأو Pلتحديد أس ثنائي، على سبيل المثال 0xAp-2(والتي قيمتها 2.5، لأن A h × 2 −2 = 10 × 2 −2f = 10 ÷ 4). يمكن إضافة لاحقة أو إلى كل من الثوابت ذات الفاصلة العائمة العشرية والسداسية عشرية Fللإشارة إلى ثابت من النوع float، أو إضافة لاحقة l(الحرف l) أو Lللإشارة إلى ثابت من النوع long double، أو تركها بدون لاحقة doubleلثابت.

يُحدد ملف الرأس القياسي <float.h> القيم الدنيا والقصوى لأنواع الأعداد العشرية المستخدمة في التنفيذ . كما يُحدد حدودًا أخرى ذات صلة بمعالجة الأعداد العشرية.floatdoublelong double

يقدم C23 ثلاثة أنواع إضافية من الأعداد العشرية (بدلاً من الثنائية) الحقيقية ذات الفاصلة العائمة: _Decimal32، _Decimal64، و _Decimal128.

ملاحظة: لا تحدد لغة C أساسًا لـ floatو doubleو long double. يمكن للتطبيق اختيار تمثيل floatو doubleو long doubleليكون مطابقًا لأنواع الأعداد العشرية العائمة. [ 4 ]

مع ذلك، كان الأساس تاريخيًا ثنائيًا (الأساس 2)، مما يعني أن أعدادًا مثل 1/2 أو 1/4 تكون دقيقة، لكن ليس 1/10 أو 1/100 أو 1/3. في نظام الفاصلة العائمة العشري، تكون جميع الأعداد نفسها دقيقة بالإضافة إلى أعداد مثل 1/10 و1/100، لكنها لا تزال غير دقيقة مثل 1/3. لا يوجد تطبيق معروف يستخدم الأساس العشري للأعداد التي كانت تُعرف سابقًا بأنها ثنائية. نظرًا لأن معظم أجهزة الكمبيوتر لا تحتوي حتى على المكونات المادية اللازمة للأعداد العشرية، وتلك القليلة التي تحتوي عليها (مثل أجهزة IBM الرئيسية منذ نظام IBM z10 )، يمكنها استخدام الأعداد العشرية بشكل صريح.

فئة التخزين

يصف الجدول التالي المحددات التي تحدد سمات التخزين المختلفة بما في ذلك المدة - ثابتة (افتراضية للمساحة العالمية)، تلقائية (افتراضية للمساحة المحلية)، أو ديناميكية (مخصصة).

فئات التخزين
المحددحياةنِطَاقالمُهيئ الافتراضي
autoكتلة (مكدس)حاجزغير مهيأ
registerكتلة (مكدس أو سجل وحدة المعالجة المركزية)حاجزغير مهيأ
staticبرنامجوحدة تجميع أو كتلةصفر
externبرنامجالبرنامج العالمي (البرنامج بأكمله)صفر
thread_localخيط
(لا شيء) 1ديناميكي (كومة)غير مهيأ (يتم تهيئته إلى القيمة الافتراضية 0في حالة الاستخدام calloc())
1. تم تخصيص الذاكرة وإلغاء تخصيصها باستخدام وظائف malloc()المكتبة free().

تُخزَّن المتغيرات المُصرَّح عنها داخل كتلة برمجية تلقائيًا، وكذلك تلك المُصرَّح عنها صراحةً باستخدام مُحدِّدات فئة التخزين [ ملاحظة 2 ] . لا يُمكن استخدام مُحدِّدات فئة التخزين إلا داخل الدوال وتصريحات وسائطها؛ ولذلك، فإن مُحدِّد فئة التخزين دائمًا ما يكون زائدًا. تتمتع الكائنات المُصرَّح عنها خارج جميع الكتل البرمجية وتلك المُصرَّح عنها صراحةً باستخدام مُحدِّد فئة التخزين بمدة تخزين ثابتة. يُهيِّئ المُصرِّف المتغيرات الثابتة إلى الصفر افتراضيًا.autoregisterautoregisterautostatic

الكائنات ذات التخزين التلقائي محلية ضمن الكتلة التي تم تعريفها فيها، وتُحذف عند الخروج من الكتلة. بالإضافة إلى ذلك، registerقد يُعطي المُصرّف أولوية أعلى للكائنات المُعرّفة باستخدام فئة التخزين للوصول إلى السجلات ، مع العلم أنه قد يختار عدم تخزين أي منها في سجل. لا يُمكن استخدام الكائنات من هذه الفئة مع عامل العنوان الأحادي ( &). أما الكائنات ذات التخزين الثابت، فتستمر طوال مدة البرنامج. وبهذه الطريقة، يُمكن الوصول إلى الكائن نفسه بواسطة دالة عبر استدعاءات متعددة. يتم إنشاء الكائنات ذات مدة التخزين المُخصصة وحذفها بشكل صريح باستخدام الدوال المُخصصة malloc، freeوالدوال ذات الصلة.

يشير مُحدِّد فئة externالتخزين إلى أن مساحة تخزين الكائن قد تم تعريفها في مكان آخر. عند استخدامه داخل كتلة برمجية، فإنه يشير إلى أن مساحة التخزين قد تم تعريفها بتصريح خارج تلك الكتلة. وعند استخدامه خارج جميع الكتل البرمجية، فإنه يشير إلى أن مساحة التخزين قد تم تعريفها خارج وحدة الترجمة. externيُعد مُحدِّد فئة التخزين زائداً عن الحاجة عند استخدامه في تصريح دالة، حيث يشير إلى أن الدالة المُصرَّح عنها قد تم تعريفها خارج وحدة الترجمة.

يُستخدم مُحدِّد فئة التخزين ( قبل C23 ، وفي الإصدارات السابقة من لغة C إذا تم تضمين ملف الرأس thread_local) الذي تم تقديمه في C11 ، لتعريف متغير محلي خاص بالخيط. ويمكن دمجه مع أو لتحديد الارتباط._Thread_local<threads.h>staticextern

لاحظ أن مُحدِّدات التخزين تنطبق فقط على الدوال والكائنات؛ أما الأمور الأخرى مثل تعريفات الأنواع والتعدادات فهي خاصة بوحدة الترجمة التي تظهر فيها. من ناحية أخرى، تحتوي الأنواع على مُؤهِّلات (انظر أدناه).

منذ الإصدار C23، يمكن استخدام C autoللإعلان عن متغير يتم استنتاج نوعه .

محددات النوع

يمكن تحديد أنواع البيانات للإشارة إلى خصائصها المميزة. constيشير مُحدِّد النوع إلى أن القيمة لا تتغير بعد تهيئتها. محاولة تعديل constقيمة مُحدَّدة تُؤدي إلى سلوك غير مُعرَّف، لذا تقوم بعض المُترجمات بتخزينها في بيانات عامة (rodata) أو (للأنظمة المُدمجة) في ذاكرة القراءة فقط (ROM). وبالمثل، constexprيُمكن اعتبارها شكلاً "أقوى" من const، حيث يجب أن تكون القيمة معروفة وقت الترجمة (مما يجعلها بديلاً آمناً من حيث النوع للثوابت الكلية). constexprوبالمثل، يجب أن تكون الدالة قابلة للتقييم وقت الترجمة. volatileيُشير مُحدِّد النوع إلى المُترجم المُحسِّن أنه قد لا يُزيل عمليات القراءة أو الكتابة الزائدة ظاهرياً، حيث قد تتغير القيمة حتى لو لم يتم تعديلها بواسطة أي تعبير أو عبارة، أو قد تكون هناك حاجة إلى عمليات كتابة مُتعددة، كما هو الحال في عمليات الإدخال /الإخراج المُرتبطة بالذاكرة .

أنواع غير مكتملة

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

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

struct Integer * pt ;

يُصرّح هذا ptعن استخدام المؤشر struct Integer(وكذلك نوع البنية غير المكتمل). وبما أن جميع المؤشرات لها نفس الحجم (بغض النظر عما تشير إليه)، يمكن للبرنامج استخدام ptالمؤشر على الرغم من أنه لا يمكنه الوصول إلى حقول البنية struct Integer.

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

struct Integer { int num ; };

تُستخدم الأنواع غير المكتملة لتنفيذ الهياكل المتكررة ؛ ويمكن تأجيل نص تعريف النوع إلى وقت لاحق في وحدة الترجمة:

typedef struct Bert Bert ; typedef struct Wilma Wilma ;struct Bert { Wilma * wilma ; };struct Wilma { Bert * bert ; };

تُستخدم الأنواع غير المكتملة أيضًا لإخفاء البيانات . يُعرَّف النوع غير المكتمل في ملف رأس ، ويُخفى التعريف الكامل في ملف نصي واحد.

نصائح

في تعريف المتغيرات، يمكن اعتبار علامة النجمة ( * ) بمثابة "مؤشر إلى". على سبيل المثال، يُعرّف متغيرًا من النوع int، ويُعرّف متغيرًا آخر كمؤشر إلى عدد صحيح. يرى البعض، استنادًا إلى تعريف اللغة، أن المؤشر أقرب إلى المتغير من نوعه ، وبالتالي يُنسقون الكود على النحو التالي: أو حتى . هذا لأن علامة النجمة ( * ) جزء من المُعرّف [ 5 ] وليست مُحدد النوع؛ ومن نتائج ذلك أن يُعرّف متغيرين بحيث يكون المؤشر مؤشرًا إلى عدد صحيح، ويكون المتغير عددًا صحيحًا بحد ذاته.int xint* pxpx*int *pxint * pxint* a,b;ab

تربط قيمة المؤشر بين معلومتين: عنوان الذاكرة ونوع البيانات.

الإشارة

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

يُحدد المعامل &عنوان كائن البيانات الذي يليه. في المثال التالي، ptrيُسند العنوان التالي إلى a:

int a = 0 ; int * ptr = &a a ؛

إلغاء المرجعية

تُستخدم علامة النجمة (*) قبل اسم المتغير (عندما لا يكون في تعريف أو تعبير رياضي) لفك مرجعية المؤشر، مما يسمح بالوصول إلى القيمة التي يشير إليها. في المثال التالي، bتم تعيين قيمة المتغير الصحيح إلى قيمة المتغير الصحيح a، وهي 10:

int a = 10 ; int * p ; p = &a a ; int b = * p ;

المصفوفات

تعريف المصفوفة

تُخزّن المصفوفات عناصر متتالية من نفس النوع. يُعرّف الكود التالي مصفوفة من 100 عنصر، تُسمى a، من النوع int.

int a [ 100 ];

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

يتوفر عدد العناصر كـ ، ولكن إذا تم تمرير القيمة إلى دالة أخرى، فإن عدد العناصر لا يكون متاحًا عبر متغير المعامل الرسمي.sizeof(a)/sizeof(int)

الوصول إلى العناصر

تُعدّ أداة فهرسة المصفوفة الوسيلة الأساسية للوصول إلى عناصر المصفوفة. على سبيل المثال، a[i]تُستخدم هذه الأداة للوصول إلى العنصر الموجود في الفهرس ` i< ...a

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

فهارس المصفوفات مقابل حساب المؤشرات
عنصرأولاًثانيةثالثن
فهرس المصفوفةa[0]a[1]a[2]a[n-1]
مؤشر تم فك مرجعه*a*(a+1)*(a+2)*(a+n-1)

بما أن التعبير a[i]مكافئ دلاليًا لـ *(a + i)، والذي بدوره مكافئ لـ *(i + a)، يمكن أيضًا كتابة التعبير على النحو التالي i[a]، على الرغم من أن هذا الشكل نادرًا ما يستخدم.

المصفوفات ذات الأطوال المتغيرة

قامت معيارية C99 بتوحيد مصفوفة الطول المتغير (VLA) في نطاق الكتلة، والتي تُنتج مصفوفة يتم تحديد حجمها بناءً على معلومات وقت التشغيل (وليس قيمة ثابتة)، ولكن بحجم ثابت حتى نهاية الكتلة. [ 2 ] اعتبارًا من معيار C11 ، لم يعد مطلوبًا من المُصرّف تنفيذ هذه الميزة.

int n = 20 ; // يمكن أن تكون n أي قيمة عشوائية int a [ n ]; a [ 3 ] = 10 ;

المصفوفات متعددة الأبعاد

تدعم اللغة المصفوفات متعددة الأبعاد ، المخزنة بترتيب الصفوف، وهي في الأساس مصفوفة أحادية البعد تحتوي عناصرها على مصفوفات. وبما أن ROWS و COLUMNS ثابتتان، فإن ما يلي يُعرّف مصفوفة ثنائية الأبعاد بطول ROWS ، كل عنصر منها عبارة عن مصفوفة من COLUMNS عددًا صحيحًا.

int array2d [ ROWS ][ COLUMNS ];

فيما يلي مثال على كيفية الوصول إلى عنصر عدد صحيح:

array2d [ 4 ][ 3 ]

عند القراءة من اليسار إلى اليمين، يتم الوصول إلى الصف الخامس، والعنصر الرابع فيه. التعبير array2d[4]عبارة عن مصفوفة، ويتم استخدام [3] للوصول إلى العدد الصحيح الرابع.

فهارس المصفوفات مقابل حساب المؤشرات [ 6 ]
عنصرأولاًالصف الثاني، العمود الثانيالصف رقم i ، العمود رقم j
فهرس المصفوفةarray[0][0]array[1][1]array[i-1][j-1]
مؤشر تم فك مرجعه*(*(array+0)+0)*(*(array+1)+1)*(*(array+i-1)+j-1)

يمكن تعريف المصفوفات ذات الأبعاد الأعلى بطريقة مماثلة.

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

نص

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

سلسلة حرفية

السلسلة النصية الحرفية هي نص برمجي محاط بعلامات اقتباس مزدوجة، مثل "Hello world!". يتم تجميع السلسلة النصية الحرفية إلى مصفوفة من charالقيم المحددة مع حرف إنهاء فارغ للإشارة إلى نهاية السلسلة.

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

printf ( __FILE__ ": %d: Hello " "world \n " );

يصبح الشكل التالي بعد توسيع المعالج المسبق __FILE__:

printf ( "helloworld.c" ": %d: Hello " "world \n " );

وهو ما يعادل:

printf ( "helloworld.c: %d: Hello world \n " );

ثوابت الأحرف

يُحاط الحرف الحرفي، المسمى ثابت الحرف، بعلامة اقتباس مفردة، على سبيل المثال 'A'، وله نوع int. لتوضيح الفرق بين سلسلة حرفية وثابت حرف، ضع في اعتبارك أن "A"عبارة عن حرفين، 'A'و '\0'، بينما 'A'يمثل حرفًا واحدًا (65 في ASCII).

لا يمكن أن يكون الثابت الحرفي فارغًا (أي ''أن هذا بناء جملة غير صحيح). الثوابت متعددة الأحرف (مثل `____________ 'xy') صالحة، وإن كانت نادرة الاستخدام - فهي تسمح بتخزين عدة أحرف في عدد صحيح (على سبيل المثال، يمكن تخزين 4 أحرف ASCII في عدد صحيح 32 بت، و8 أحرف في عدد صحيح 64 بت). نظرًا intلعدم تحديد ترتيب الأحرف في `_____________ (يُترك ذلك للتنفيذ)، فإن استخدام الثوابت متعددة الأحرف في بيئات متعددة يصعب.

مع ذلك، في حالات تقتصر على منصة محددة وتطبيق مُصرّف معين، تُستخدم الثوابت متعددة الأحرف في تحديد التوقيعات. ومن الأمثلة الشائعة على ذلك نوع البيانات OSType ، حيث يعني الجمع بين مُصرّفات نظام التشغيل Mac OS الكلاسيكي ونظام Big-Endian المتأصل فيه أن البايتات في العدد الصحيح تظهر بالترتيب الدقيق للأحرف المُحددة في القيمة الحرفية. في الواقع، يكون تعريفها في التطبيقات الشائعة متسقًا: ففي GCC وClang و Visual C++ ، '1234'يُنتج قيمةً تحت ASCII. [ 7 ] [ 8 ]0x31323334

مثل السلاسل النصية الحرفية، يمكن أيضًا تعديل الثوابت الحرفية بواسطة البادئات، على سبيل المثال L'A'لها نوع wchar_tوتمثل قيمة الحرف "A" في ترميز الأحرف العريض.

الهروب من الضربة الخلفية

لا يمكن تضمين أحرف التحكم مباشرةً في سلسلة نصية أو حرف حرفي. بدلاً من ذلك، يمكن ترميزها عبر تسلسل هروب يبدأ بشرطة مائلة عكسية (\ \). على سبيل المثال، تشير الشرطات المائلة العكسية في "This string contains \"double quotes\"."إلى أن زوج علامات الاقتباس الداخلي يُقصد به أن يكون جزءًا فعليًا من السلسلة النصية، وليس كما هو مُفترض افتراضيًا كفاصل (نقطة نهاية) للسلسلة.

تتضمن مشاهد الهروب ما يلي:

تسلسلمعنى
\\رد فعل عكسي حرفي
\"علامات اقتباس مزدوجة
\'اقتباس واحد
\nسطر جديد (تغذية السطر)
\rإرجاع السطر
\bمسافة للخلف
\tعلامة تبويب أفقية
\fتغذية النموذج
\aتنبيه (جرس)
\vعلامة تبويب عمودية
\?علامة الاستفهام (كانت تستخدم للهروب من الأحرف الثلاثية ، وهي ميزة قديمة تم إسقاطها في C23)
\OOOالحرف ذو القيمة الثمانية OOO (حيث OOO عبارة عن 1-3 أرقام ثمانية، '0'-'7')
\xhhالحرف ذو القيمة السداسية العشرية hh (حيث hh عبارة عن رقم سداسي عشري واحد أو أكثر، من '0' إلى '9'، ومن 'A' إلى 'F'، ومن 'a' إلى 'f')
\uhhhhرمز يونيكود أقل من 10000 سداسي عشري (أضيف في C99)
\Uhhhhhhhhنقطة رمز يونيكود حيث hhhhhhhh هي ثمانية أرقام سداسية عشرية (أضيفت في C99)

لا يُحدد المعيار استخدام رموز الهروب الأخرى باستخدام الشرطة المائلة العكسية، مع أن المترجمات غالبًا ما توفر رموز هروب إضافية كامتدادات للغة. على سبيل المثال، لم تُضَف سلسلة الهروب \eلحرف الهروب ذي القيمة السداسية العشرية 1B في نظام ASCII إلى المعيار لعدم وجود تمثيل له في مجموعات الأحرف الأخرى (مثل EBCDIC ). وهي متوفرة في GCC و clang و tcc .

لاحظ أن دالة المكتبة القياسية printf()تستخدم %%لتمثيل %الحرف الحرفي.

سلاسل الأحرف العريضة

بما أن نوع البيانات charيبلغ عرضه بايتًا واحدًا، فإن charقيمة واحدة عادةً ما يمكنها تمثيل 255 رمزًا حرفيًا مختلفًا كحد أقصى، وهو عدد غير كافٍ لجميع الأحرف المختلفة المستخدمة في جميع أنحاء العالم. ولتوفير دعم أفضل للأحرف الدولية، قدم المعيار الأول (C89) الأحرف العريضة (المشفرة في نوع البيانات wchar_t) وسلاسل الأحرف العريضة، والتي تُكتب على النحو التالي:L"Hello world!"

تكون الأحرف العريضة عادةً إما بايتين (باستخدام ترميز ثنائي البايت مثل UTF-16 ) أو أربعة بايتات (عادةً UTF-32 )، لكن معيار لغة C لا يُحدد عرض الأحرف العريضة wchar_t، تاركًا الخيار للمُنفذ. يستخدم نظام التشغيل Microsoft Windows عمومًا ترميز UTF-16، وبالتالي سيكون طول السلسلة المذكورة أعلاه 26 بايتًا بالنسبة لمُترجم Microsoft؛ بينما يُفضل نظام Unix ترميز UTF-32 ، وبالتالي ستُنتج مُترجمات مثل GCC سلسلةً طولها 52 بايتًا. wchar_tيُعاني الحرف العريض ذو البايتين من نفس القيد الذي يُعاني منه الحرف العادي، حيث لا يُمكن تمثيل charبعض الأحرف (تلك التي تقع خارج نطاق الأحرف العريضة الأساسيةwchar_t ) في حرف عريض واحد ، ويجب تمثيلها باستخدام أزواج بديلة .

لم يحدد المعيار الأصلي سوى الحد الأدنى من الوظائف للتعامل مع سلاسل الأحرف العريضة؛ وفي عام 1995، عُدِّل المعيار ليشمل دعمًا أوسع بكثير، يُضاهي الدعم المُقدَّم charللسلاسل النصية. تُسمى الوظائف ذات الصلة في الغالب بأسماء مُكافئاتها char، مع إضافة "w" أو استبدال "str" ​​بـ "wcs"؛ وهي مُحدَّدة في [مرجع] <wchar.h>، الذي <wctype.h>يحتوي على وظائف تصنيف وتعيين الأحرف العريضة.

الطريقة الموصى بها الآن بشكل عام [ ملاحظة 3 ] لدعم الأحرف الدولية هي من خلال UTF-8 ، والتي يتم تخزينها في charمصفوفات، ويمكن كتابتها مباشرة في شفرة المصدر إذا كنت تستخدم محرر UTF-8، لأن UTF-8 هو امتداد ASCII مباشر .

سلاسل ذات عرض متغير

wchar_tيُعد استخدام ترميز متغير العرض بديلاً شائعاً ، حيث يمكن أن يمتد الحرف المنطقي على عدة مواضع في السلسلة. يمكن ترميز السلاسل متغيرة العرض حرفياً، مما قد يُربك المُصرّف، أو باستخدام علامات الهروب الرقمية (مثل "\xc3\xa9"" é " في UTF-8). صُمم ترميز UTF-8 خصيصاً (ضمن الخطة 9 ) ليتوافق مع دوال السلاسل في المكتبة القياسية؛ وتشمل ميزاته عدم وجود أصفار مضمنة، وعدم وجود تفسيرات صحيحة للتسلسلات الفرعية، وإمكانية إعادة التزامن البسيطة. من المرجح أن تكون الترميزات التي تفتقر إلى هذه الميزات غير متوافقة مع دوال المكتبة القياسية؛ لذا تُستخدم غالباً دوال السلاسل المُراعية للترميز في مثل هذه الحالات.

بناء

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

يُعلن عن البنية باستخدام الكلمة structالمفتاحية متبوعةً باسم مُعرّف اختياري، يُستخدم لتحديد شكل البنية. يلي ذلك تعريفات الحقول، التي يتكون كل منها من اسم نوع، واسم حقل، وينتهي بفاصلة منقوطة.

يُعرّف ما يلي بنيةً باسم MyStructتحتوي على ثلاثة عناصر. كما يُعرّف نسخةً باسم tee:

struct MyStruct { int x ; float y ; char * z ; } tee ;

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

بمجرد تعريف المتغير، يمكن تعريف متغير من نوع البنية. فيما يلي تعريف لنسخة جديدة من البنية MyStructالمسماة r:

struct MyStruct r ;

على الرغم من أن البعض يفضل تعريف متغيرات البنية باستخدام الكلمة structالمفتاحية `struct`، إلا أن البعض الآخر يستخدم `struct` typedefلتعريف اسم بديل لنوع البنية في نطاق اسم النوع الرئيسي. يُعرّف المثال التالي نوعًا باسم `struct` Integerوالذي يمكن استخدامه بعد ذلك كما يلي : `struct` Integer n.

typedef struct { int i ; } Integer ;

الوصول إلى الأعضاء

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

عادةً ما يتم الوصول إلى بنية ما عبر مؤشر. لنفترض أن لدينا مؤشرًا إلى بنية بيانات ، يُسمى . يمكن الوصول إلى عنصر من عناصر عن طريق فك مرجعية المؤشر واستخدام النتيجة كمعامل أيسر، كما يلي : . [ ب ] ولأن هذه العملية شائعة، توفر اللغة صيغة مختصرة للوصول إلى عنصر مباشرةً من مؤشر، (على سبيل المثال، ).structMyStruct*ptee=&teeteepteeyteeptee(*ptee).y->ptee->y

تكليف

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

يمكن أيضًا إسناد بنية كاملة إلى بنية أخرى من نفس النوع، وذلك بنسخها كمعامل دالة أو كقيمة مُعادة. على سبيل المثال، tee.x = 74تُسند القيمة 74 إلى العنصر المسمى x في البنية tee ، ptee->x = 74وتُسند القيمة 74 إلى العنصر ptee في البنية ptee .

عمليات أخرى

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

حقول البت

تُوفّر اللغة نوعًا خاصًا من الأعضاء يُعرف باسم حقل البت ، وهو عدد صحيح ذو حجم مُحدد بالبتات. يُصرّح عن حقل البت كعضو من النوع ( signed) unsigned، intأو bool، أو _BitInt(N) . [ ملاحظة 4 ] متبوعًا بلاحقة بعد اسم العضو تتكون من نقطتين رأسيتين وعدد البتات. يجب ألا يتجاوز إجمالي عدد البتات في حقل بت واحد إجمالي عدد بتات نوعه الأساسي. على عكس قواعد بناء جملة لغة C المعتادة، يُحدد التنفيذ ما إذا كان حقل البت مُوقّعًا أم غير مُوقّع إذا لم يُحدد ذلك صراحةً. لذلك، يُفضّل تحديد أو .signedunsigned

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

تُعد حقول البت محدودة مقارنة بالحقول العادية من حيث أن عوامل العنوان ( &) و sizeofغير مدعومة.

يُعرّف ما يلي نوع بنية باسم FlagStatusومثيلًا له باسم g. الحقل الأول، flag، هو بت واحد، لا يمكن أن يكون إلا 1 أو 0. الحقل الثاني، num، هو حقل ذو إشارة مكون من 4 بتات؛ نطاقه من -7 إلى 7 أو من -8 إلى 7. يُضيف الحقل الأخير 3 بتات للحشو لتقريب البنية إلى 8 بتات.

struct FlagStatus { unsigned int flag : 1 ; signed int num : 4 ; signed int : 3 ; } g ;

مساحات الأسماء

لا تدعم لغة C مساحات الأسماء بشكلٍ أصلي ، على عكس لغتي C++ و Java. وهذا يجعل أسماء رموز C عرضةً لتضارب الأسماء. مع ذلك، من الممكن استخدام هياكل البيانات المجهولة لمحاكاة مساحات الأسماء.

Math.h :

دبراغما مرة واحدةconst struct { double PI ; double ( * sin )( double ); } Math ;

Math.c :

#include <math.h>دالة ثابتة مزدوجة _sin ( مزدوجة arg ) { إرجاع sin ( arg ); }const struct { double PI ; double ( * sin )( double ); } Math = { M_PI , _sin };

Main.c :

#include <stdio.h> #include "Math.h"int main () { printf ( "sin(0) = %d \n " , Math . sin ( 0 )); printf ( "pi is %f \n " , Math . PI ); }

الاتحاد

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

يُعلن عن الاتحاد باستخدام unionالكلمة المفتاحية. فيما يلي تعريف لاتحاد باسم معين MyUnionومثيل له باسم آخر n:

اتحاد MyUnion { int x ; float y ; char * z ; } n ;

التهيئة

كمية قياسية

تتضمن عملية تهيئة متغير، بالإضافة إلى تعريفه، إضافة علامة يساوي (=) ثم بنية متوافقة مع نوع البيانات. فيما يلي مثال لتهيئة متغير من نوع عدد صحيح (int):

int x = 12 ;

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

int y = { 23 }; int z = { { 34 } };

قائمة التهيئة

يمكن تهيئة الهياكل والاتحادات والمصفوفات بعد الإعلان عنها عبر قائمة التهيئة.

بما أن العناصر غير المتطابقة تُعيّن إلى 0، فإن القائمة الفارغة تُعيّن جميع العناصر إلى 0. على سبيل المثال، ما يلي يُعيّن جميع عناصر المصفوفة a وجميع حقول s إلى 0:

int a [ 10 ] = {}; struct MyStruct s = {};

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

int x [] = { 0 , 1 , 2 };

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

struct MyStruct { int x ; float y ; char * z ; };struct MyStruct pi = { 3 , 3.1415 , "Pi" };

المُهيئات المُخصصة

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

struct MyStruct pi = { . z = "Pi" , . x = 3 , . y = 3.1415 };

يؤدي استخدام مُعرِّف في المُهيئ إلى تحريك "مؤشر" التهيئة. في المثال أدناه، إذا MAXكانت قيمة أكبر من 10، فستكون هناك بعض العناصر ذات القيمة الصفرية في منتصف المصفوفة a؛ أما إذا كانت أقل من 10، فسيتم استبدال بعض القيم المُقدمة من المُهيئات الخمسة الأولى بالقيم المُقدمة من المُهيئات الخمسة الثانية. وإذا MAXكانت قيمة أقل من 5، فسيحدث خطأ في الترجمة.

int a [ MAX ] = { 1 , 3 , 5 , 7 , 9 , [ MAX - 5 ] = 8 , 6 , 4 , 2 , 0 };

في لغة C89 ، كان يتم تهيئة الاتحاد بقيمة واحدة تُطبق على عضوه الأول. أي أن الاتحاد MyUnion المُعرّف أعلاه لا يمكن تهيئة عضوه x إلا بقيمة واحدة .

الاتحاد MyUnion القيمة = { 3 };

باستخدام مُهيئ مُحدد، لا يشترط أن يكون العضو المراد تهيئته هو العضو الأول:

الاتحاد MyUnion القيمة = { . y = 3.1415 };

يمكن استخدام المُعرِّفات المركبة لتوفير تهيئة صريحة عندما يُساء فهم قوائم التهيئة غير المُزخرفة. في المثال أدناه، wتم تعريف كمصفوفة من الهياكل، يتكون كل هيكل من عنصر a(مصفوفة من 3 int) وعنصر b( عنصر int). تُحدد المُهيئ حجم wإلى 2 وتُحدد قيم العنصر الأول من كل عنصر a.

struct { int a [ 3 ], b ; } w [] = { [ 0 ]. a = { 1 }, [ 1 ]. a [ 0 ] = 2 };

هذا يعادل:

struct { int a [ 3 ], b ; } w [] = { { { 1 , 0 , 0 }, 0 }, { { 2 , 0 , 0 }, 0 } };

الحروف المركبة

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

// تم إنشاء المؤشر من مصفوفة حرفية. int * ptr = ( int []){ 10 , 20 , 30 , 40 };// مؤشر إلى مصفوفة. float ( * foo )[ 3 ] = & ( float []){ 0.5f , 1.f , -0.5f };struct MyStruct pi = ( struct MyStruct ){ 3 , 3.1415 , "Pi" };

غالباً ما يتم دمج القيم الحرفية المركبة مع القيم الأولية المحددة لجعل الإعلان أكثر قابلية للقراءة: [ 2 ]

pi = ( struct MyStruct ){ . z = "Pi" , . x = 3 , . y = 3.1415 };

مؤشرات الدوال

يمكن تعريف مؤشر إلى دالة على النحو التالي :

اسم النوع (* اسم الدالة )( قائمة المعاملات );

يوضح كود البرنامج التالي استخدام مؤشر دالة للاختيار بين الجمع والطرح. يُعرّف السطر 12 متغير مؤشر دالة باسم operationيدعم نفس واجهة كل من الدالتين addو subtract. بناءً على الشرط (argc)، operationيتم تعيين قيمة إلى عنوان إما addأو subtract. في السطر 14 ، يتم استدعاء الدالة التي operationيشير إليها المؤشر.

[[ nodiscard ]]constexpr int add ( int x , int y ) {أعد x + y ;}[[ nodiscard ]]constexpr int subtract ( int x , int y ) {أعد x - y ;}int main ( int argc , char * argv []) {int ( * operation )( int x , int y );العملية = argc ؟ إضافة : طرح ؛int result = operation ( 1 , 1 );أعد النتيجة ؛}

المشغلون

هذه قائمة بالمعاملات في لغتي البرمجة C و C++ .

جميع المعاملات المذكورة مكتوبة بلغة C++، وما لم يُذكر خلاف ذلك، فهي مكتوبة بلغة C أيضًا. تتضمن بعض الجداول عمودًا بعنوان "مكتوب بلغة C" يُشير إلى ما إذا كان المعامل مكتوبًا بلغة C أيضًا. تجدر الإشارة إلى أن لغة C لا تدعم تحميل المعاملات الزائد .

عندما لا يتم تحميلها بشكل زائد، بالنسبة للمعاملات &&، ||و ,( معامل الفاصلة )، توجد نقطة تسلسل بعد تقييم المعامل الأول.

معظم العوامل المتاحة في لغتي C و C++ متاحة أيضًا في لغات أخرى من عائلة C مثل C# و D و Java و Perl و PHP بنفس الأسبقية والترابط والدلالات.

تُعرف العديد من المعاملات المحددة بتسلسل من الرموز باسم يتألف من اسم كل رمز. على سبيل المثال، يُطلق على +=و -=غالبًا اسم "الجمع والتساوي" و"الطرح والتساوي"، بدلاً من المصطلحات الأكثر تفصيلاً مثل "الإسناد بالجمع" و"الإسناد بالطرح".

تدفق التحكم

العبارة المركبة

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

{ { إفادة } }

بما أن العبارة المركبة نوع من أنواع العبارات، فعند استخدامها ، يمكن أن تكون عبارةً واحدة (بدون أقواس) أو عبارةً مركبة (مع أقواس). وتُستخدم العبارة المركبة في جسم الدالة وفي فرع بنية التحكم الذي لا يُمثل عبارةً واحدة.

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

عبارة شرطية

العبارة الشرطية هي كالتالي:if

إذا ( كان التعبير صحيحًا ) { العبارة } وإلا { العبارة }

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

والجدير بالذكر أن العبارة الثانية يمكن أن تكون ifعبارة أخرى. على سبيل المثال:

إذا كان ( i == 1 ) { printf ( "إنه 1" ); } وإذا كان ( i == 2 ) { printf ( "إنه 2" ); } وإلا { printf ( "إنه شيء آخر" ); }

عبارة التبديل

ينقل هذا الأمر switchالتحكم إلى مُعرِّف الحالة الذي يحمل قيمةً تُطابق تعبير النوع الصحيح ، أو إلى defaultالمُعرِّف (إن وُجد) في غير ذلك. ويستمر التحكم في العبارات التي تلي المُعرِّف caseحتى breakعبارةٍ ما أو حتى نهاية العبارة switch. ويكون بناء الجملة كالتالي:

switch ( expression ) { case label-name : إفادة اسم الحالة : إفادة . . . تقصير: إفادة }

يجب أن تكون كل caseقيمة فريدة ضمن العبارة. لا يجوز أن يكون هناك أكثر من defaultتسمية واحدة.

يستمر التنفيذ من caseعلامة إلى أخرى إذا لم breakيُصادف أي عبارة. يُعرف هذا بالتنفيذ المتتالي ، وهو مفيد في بعض الحالات، ولكنه غير مرغوب فيه في كثير من الأحيان.

من الممكن، وإن كان غير شائع، تحديد caseمواقع العلامات في الكتل الفرعية لهياكل التحكم الداخلية. ومن الأمثلة على ذلك جهاز داف وتنفيذ سيمون تاتام للروتينات الفرعية في برنامج Putty . [ 10 ]

فيما يلي مثال على استخدام a switchفوق an int:

#include <stdio.h>// ... int num = 2 ;switch ( num ) { case 1 : printf ( "الرقم هو 1 \n " ); break case 2 : printf ( "الرقم هو 2 \n " ); break ; case 3 : printf ( "الرقم هو 3 \n " ); break ; default : printf ( "الرقم ليس 1 أو 2 أو 3 \n " ); }

ابتداءً من الإصدار C2Y ، يُمكن استخدام " caseنطاق" بين عددين صحيحين ثابتين، باستخدام علامة ...الحذف (...). [ 11 ] يجب وجود مسافة بين القيمة وعلامة الحذف. هذا النطاق شامل. على سبيل المثال:

#include <stdio.h>// ... int num = 2 ;// تبديل النمط الجديد ( الرقم ) { الحالة 1 ... 3 : printf ( "الرقم هو 1 أو 2 أو 3 \n " ); break الافتراضي : printf ( "الرقم ليس 1 أو 2 أو 3 \n " ); }// النمط القديم، باستخدام عبارة switch ( num ) { case 1 : case 2 : case 3 : printf ( "الرقم هو 1 أو 2 أو 3 \n " ); break default : printf ( "الرقم ليس 1 أو 2 أو 3 \n " ); }

بيان التكرار

توجد ثلاثة أشكال لعبارة التكرار :

بينما ( تعبير ) { عبارة }

نفّذ { العبارة } بينما ( التعبير )for ( init ; test ; next ) { statement }

بالنسبة لعبارتي while`and` و` do، تُنفَّذ العبارة الفرعية بشكل متكرر طالما أن قيمة التعبير غير صفرية. أما بالنسبة whileلعبارة `، فيتم إجراء الاختبار، بما في ذلك أي آثار جانبية، قبل كل تكرار. وبالنسبة لعبارة ` do، فيتم إجراء الاختبار بعد كل تكرار. وبالتالي، doتُنفِّذ عبارة ` دائمًا عبارتها الفرعية مرة واحدة على الأقل، بينما whileقد لا تُنفِّذ عبارة ` على الإطلاق`.

forيمكن وصف منطق ذلك من حيث whileأن هذا:

for ( e1 ; e2 ; e3 ) { s ; }

يعادل ما يلي:

e1 ; while ( e2 ) { s ; cont : e3 ; }

باستثناء سلوك عبارة (التي تقفز continue;في الحلقة إلى بدلاً من ). إذا كانت فارغة، فيجب استبدالها بـ .fore3e2e21

يمكن حذف أي من التعبيرات الثلاثة في forالحلقة. يؤدي حذف التعبير الثاني إلى جعل whileنتيجة الاختبار غير صفرية دائمًا، مما يشير إلى حلقة لا نهائية.

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

for ( int i = 0 ; i < limit ; ++ i ) { // ... }

لا توجد حلقة foreach في لغة C، كما هو الحال في لغتي Java و C++. ومع ذلك، يمكن محاكاتها باستخدام وحدات الماكرو.

بيان القفز

هناك أربع عبارات قفز (نقل التحكم بشكل غير مشروط): goto، continue، break، و return.

تُمرر هذه gotoالعبارة التحكم في البرنامج إلى عبارة مُعَلَّمة. ولها الصيغة التالية:

انتقل إلى اسم التسمية

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

بينما ( صحيح ) { // ... استمر ؛ }نفّذ { // ... استمر ; } بينما ( صحيح );for (;;) { // ... continue ; }

العبارة breakالتي هي ببساطة كلمة breakتُنهي عبارة for, while, do, أو switchعبارة تحكم. ينتقل التحكم إلى العبارة التي تلي عبارة التحكم المحيطة.

ينقل هذا returnالبيان التحكم إلى الدالة المستدعِية. عندما returnيتبعه تعبير، تُعاد القيمة إلى الدالة المستدعِية. يُعادل الوصول إلى نهاية الدالة استخدام عبارة returnبدون تعبير. في هذه الحالة، إذا تم تعريف الدالة على أنها تُعيد قيمة وحاولت الدالة المستدعِية استخدام القيمة المُعادة، فسيكون السلوك غير مُحدد.

الملصقات

يشير الوسم إلى نقطة في الكود يمكن نقل التحكم إليها. الوسم عبارة عن مُعرِّف متبوع بنقطتين رأسيتين. على سبيل المثال:

إذا كان ( i == 1 ) { انتقل إلى النهاية ؛ } // نهاية الكود الآخر :

لا يُحدد المعيار طريقةً لاسترجاع عنوان تسمية، لكن لغة GCC تُضيف مُعاملًا أحاديًا &&يُعيد عنوان التسمية. يُمكن تخزين العنوان في void*مُتغير واستخدامه لاحقًا goto. تُتيح هذه الميزة إمكانية تنفيذ جدول قفز .

على سبيل المثال، تتم طباعة ما يلي hiبشكل متكرر:

void * ptr = && J1 ; J1 : printf ( "hi" ); goto * ptr ;

منذ إصدار C2Y ، أصبحت لغة C تدعم الحلقات المُعَلَّمة، على غرار لغة Java. وهذا يسمح بإضافة تسميات إلى الحلقات for، ونقل التحكم من خلالها breakوباستخدامها continue( فواصل متعددة المستويات ). [ 12 ]

// باستخدام break: outer : for ( int i = 0 ; i < n ; ++ i ) { switch ( i ) { case 1 : break ; // ينتقل إلى 1 case 2 : break outer ; // ينتقل إلى 2 default : continue ; } // 1 } // 2// باستخدام continue: outer : for ( int i = 0 ; i < m ; ++ i ) { for ( int j = 0 ; j < n ; ++ j ) { continue ; // ينتقل إلى 1 continue outer ; // ينتقل إلى 2 // 1 } // 2 }

الوظائف

تعريف

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

اسم النوع اسم الدالة ( قائمة المعاملات ) { قائمة العبارات القيمة المرجعة ؛ }

يتم تعريف الدالة التي لا تُرجع أي قيمة باستخدام voidبدلاً من اسم النوع، مثل:

void function-name ( parameter-list ) { statement-list }

لا يتضمن المعيار وظائف لامدا ، لكن بعض المترجمين يفعلون ذلك.

حدود

قائمة المعاملات هي قائمة مفصولة بفواصل تتضمن تعريفات رسمية للمعاملات؛ كل عنصر عبارة عن اسم نوع متبوعًا باسم متغير:

اسم النوع اسم المتغير { , اسم النوع اسم المتغير }

لا يمكن أن يكون نوع القيمة المُعادة مصفوفة أو دالة. على سبيل المثال:

int f ()[ 3 ]; // خطأ: دالة تُرجع مصفوفة int ( * g ())[ 3 ]; // صحيح: دالة تُرجع مؤشرًا إلى مصفوفةvoid h ()(); // خطأ: دالة تُرجع دالة void ( * k ())(); // صحيح: دالة تُرجع مؤشر دالة

إذا لم تقبل الدالة أي معاملات، فقد تكون قائمة المعاملات فارغة أو تحتوي على الكلمة المفتاحية `name` void، ولكن لكل منهما دلالات مختلفة. استدعاء دالة بمعاملات عند تعريفها voidبقائمة معاملات فارغة يُعدّ خطأً في الصياغة. أما استدعاء دالة بمعاملات عند تعريفها بقائمة معاملات فارغة ، فلا يُعدّ خطأً في الصياغة، ولكنه قد يؤدي إلى سلوك غير متوقع. voidلذا، يُنصح باستخدام `name`.

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

int printf ( const char * , ...);

يمكن استخدام الوسائط ذات الطول المتغير عبر وظائف المكتبة القياسية المعلنة في <stdarg.h>.

الاتصال

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

يُشبه الوصول إلى دالة مُعرَّفة من قِبل المستخدم في ملف آخر استخدام دالة من مكتبة. يُصرِّح الكود المُستخدِم عن الدالة إما بتضمين ملف رأس أو مباشرةً في ملفه. ويتم ربط تعريف الدالة في الملف الآخر عند ربط ملفات الكائنات.

يُعد استدعاء دالة مُعرّفة في نفس الملف أمرًا بسيطًا نسبيًا. يجب أن يكون تعريفها أو إعلانها أعلى من استدعاء الدالة.

تمرير الوسائط

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

xفي الكود التالي، يتم تمرير عنوان الدالة عن طريق تحديده &xفي الاستدعاء. تستقبل الدالة المستدعاة العنوان كـ yوتصل إليه xكـ *y.

void incInt ( int * y ) { ( * y ) ++ ; }int main ( void ) { int x = 7 ; incInt ( & x ); return 0 ; }

يوضح الكود التالي استخدامًا متقدمًا للمؤشرات ، وهو تمرير مؤشر إلى مؤشر. aتم تعريف مؤشر من نوع int باسم `<input>` في السطر 9، وتم تمرير عنوانه إلى الدالة في السطر 10. تستقبل الدالة مؤشرًا إلى مؤشر إلى عدد صحيح باسم `<input>` a_p. ثم تُسند قيمة `<input>` إلى a`<input> *a_p`. بعد استدعاء الدالة، في السطر 11، يتم تحرير الذاكرة المخصصة والمُسندة إلى `<input>` a.

#include <stdio.h> #include <stdlib.h>void allocate_array ( int ** const p , const int count ) { * p = malloc ( sizeof ( int ) * count ); }int main ( void ) { int * a ; allocate_array ( &a a , 42 ); free ( a ); return 0 ; }

تمرير المصفوفات

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

#include <stdio.h>void setArray ( int a [], int index ) { array [ index ] = 123 ; }int main ( void ) { int a [ 1 ] = { 1 }; setArray ( a , 0 ); printf ( "a[0]=%d \n " , a [ 0 ]); return 0 ; }

مع ذلك، ثمة سبب آخر لهذا السلوك. يُعامل مُعامل المصفوفة كمؤشر. النموذج الأولي التالي يُكافئ النموذج الأولي للدالة أعلاه:

void setArray ( int * a , int index );

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

منذ إصدار C99، يُمكن للمبرمج تحديد أن الدالة تأخذ مصفوفة بحجم مُعين باستخدام الكلمة المفتاحية ` static. يجب أن يكون المُعامل الأول مؤشرًا إلى العنصر الأول من مصفوفة طولها 4 على الأقل. كما يُمكن استخدام مُحددات (` ،` و` ) لنوع المؤشر الذي تُحوّل إليه المصفوفة.voidsetArray(intarray[static4],intindex)constvolatilerestrict

صفات

Added in C23 and originating from C++11, C supports attribute specifier sequences.[13] Attributes can be applied to any symbol that supports them, including functions and variables, and any symbol marked with an attribute will be specifically treated by the compiler as necessary. These can be thought of as similar to Java annotations for providing additional information to the compiler, however they differ in that attributes in C are not metadata that is meant to be accessed using reflection. Furthermore, one cannot create custom attributes in C, unlike in Java where one may define custom annotations in addition to the standard ones. However, C does have implementation/vendor-specific attributes which are non-standard. These typically have a namespace associated with them. For instance, GCC and Clang have attributes under the gnu:: namespace, and all such attributes are of the form [[gnu::*]], though C does not have support for namespacing in the language.

The syntax of using an attribute on a function is like so:

[[nodiscard]]boolsatisfiesProperty(conststructMyStruct*s);

The standard defines the following attributes:

NameDescription
[[noreturn]]Indicates that the specified function will not return to its caller.
[[deprecated]][[deprecated("reason")]]Indicates that the use of the marked symbol is allowed but discouraged/deprecated for the reason specified (if given).
[[fallthrough]]Indicates that the fall through from the previous case label is intentional.
[[maybe_unused]]Suppresses compiler warnings on an unused entity.
[[nodiscard]][[nodiscard("reason")]]Issues a compiler warning if the return value of the marked symbol is discarded or ignored for the reason specified (if given).
[[unsequenced]]Indicates that a function is stateless, effectless, idempotent and independent.
[[reproducible]]Indicates that a function is effectless and idempotent.

Dynamic memory

C dynamic memory allocation refers to performing manual memory management for dynamic memory allocation in the C programming language via a group of functions in the C standard library, mainly malloc, realloc, calloc, aligned_alloc and free.[14][15][16]

تتضمن لغة البرمجة C++ هذه الدوال؛ ومع ذلك، فإن المعاملين new و delete يوفران وظائف مماثلة ويوصي بهما مطورو تلك اللغة. [ 17 ] ومع ذلك، توجد عدة حالات لا يكون فيها استخدام / مناسبًا، مثل كود جمع البيانات المهملة أو الكود الحساس للأداء، وقد يكون من الضروري استخدام مزيج من المعاملين بدلًا من المعامل ذي المستوى الأعلى.newdeletemallocnewnew

تتوفر العديد من التطبيقات المختلفة لآلية تخصيص الذاكرة الفعلية التي تستخدمها دالة malloc . ويختلف أداؤها من حيث وقت التنفيذ والذاكرة المطلوبة.

انظر أيضاً

ملحوظات

  1. 1 2 تم إدخال المعدِّل فيمعيار C99 .long long
  2. معنى كلمة auto هو مُحدد نوع وليس مُحدد فئة تخزين في لغة C++0x
  3. انظر القسم الأول من UTF-8 للاطلاع على المراجع
  4. تسمح لغة C++ باستخدام جميع الأنواع الصحيحة والأنواع المُعدّدة، والعديد من مُجمّعات لغة C تفعل الشيء نفسه.
  1. منذ عام 1999
  2. نظرًا لأن أسبقية المعاملات (."" أعلى من "*")،*ptee.yفإن هذا غير صحيح؛ يتم تحليله على أنه*(ptee.y)وبالتالي فإن الأقواس ضرورية.

مراجع

  1. "ج | التعريف، التاريخ، التطبيقات، والحقائق | بريتانيكا" . موسوعة بريتانيكا . مؤرشف من الأصل بتاريخ 11 سبتمبر 2025. تم الاطلاع عليه بتاريخ 3 فبراير 2026 .
  2. 1 2 3 كليمنس، بن (2012). القرن الحادي والعشرون ج . أورايلي وسائل الإعلام . رقم ISBN 978-1449327149.
  3. "WG14-N2412: تمثيل الإشارة بنظام المتمم الثنائي" (ملف PDF) . open-std.org . 11 أغسطس 2019. مؤرشف (ملف PDF) من الأصل في 27 ديسمبر 2022.
  4. "WG14-N2341: ISO/IEC TS 18661-2 - ملحقات الفاصلة العائمة للغة C - الجزء 2: العمليات الحسابية العشرية ذات الفاصلة العائمة" (ملف PDF) . open-std.org . 26 فبراير 2019. مؤرشف (ملف PDF) من الأصل في 21 نوفمبر 2022.
  5. "فك تشفير التصريحات في لغة C" (ملف PDF) . Ptgmedia.pearsoncmg.com . تم الاطلاع عليه بتاريخ 17 يوليو 2026 .
  6. ^ Balagurusamy، E. البرمجة في ANSI C. تاتا ماكجرو هيل. ص. 366. 
  7. "معالج C المسبق: السلوك المحدد بالتنفيذ" . gcc.gnu.org .
  8. "السلاسل النصية والقيم الحرفية (C++)" . وثائق Visual C++ 19. تم الاطلاع عليه بتاريخ 20 نوفمبر 2019 .
  9. كيرنيغان وريتشي
  10. تاتام، سيمون (2000). "الروتينات الفرعية في لغة سي" . تم الاسترجاع في 30 أبريل 2017 .
  11. "WG14-N3370: تعبيرات نطاق الأحرف، الإصدار 3.1" . open-std.org . 1 أكتوبر 2024. مؤرشف من الأصل في 4 أغسطس 2025.
  12. أليكس سيليست (18 سبتمبر 2024). "الحلقات المسماة، الإصدار 3" . open-std.org . WG 14.
  13. "تسلسل مُحدِّدات السمات (منذ الإصدار C23)" . cppreference.com . تم الاطلاع عليه بتاريخ 6 يونيو 2025 .
  14. 7.20.3 وظائف إدارة الذاكرة (ملف PDF) . مواصفات ISO/IEC 9899:1999 (تقرير فني). صفحة 313. 
  15. سوميت، ستيف. "الفصل 11: تخصيص الذاكرة" . ملاحظات برمجة لغة سي . تم الاطلاع عليه بتاريخ 11 يوليو 2020 .
  16. "aligned_alloc(3) - صفحة دليل لينكس" .
  17. ستروستروب، بيارن (2008). البرمجة: المبادئ والتطبيق باستخدام لغة C++ . أديسون ويسلي. ص 1009. ISBN  978-0-321-54372-1.
عام