المتغيرات وأنواع البيانات في لغة البرمجة C
أنواع البيانات في لغة البرمجة C، كيفية إنشاء المتغيرات في لغة البرمجة سي، الفرق بين signed و unsigned، أنواع البيانات الأساسية int, float, double, char, int8_t, wchar_t, int16_t, int32_t, int64_t, short, long وأنواع البيانات المركبة المصفوفات والبيانات الهيكلية Arrays, struct data types
لغة البرمجة سي من اللغات التي تتعامل مع البيانات بشكل محدد أو صارم حيث تعرف بـ Strict Typed Language، أي أنه يجب عليك تحديد نوع البيانات قبل التعامل معها، فلا يمكنك تعريف متغير في لغة البرمجة سي دون تحديد نوع البيانات التي سوف يتم تخزينها في هذا المتغير.
في لغة البرمجة سي يمكن تقسيم البيانات إلى قسمين
- البيانات الأساسية أو الأولية، وتعرف بـ Basic Types أو Primitive Types، ويحتوي هذا القسم على أنواع البيانات
int
وchar
وfloat
وdouble
وvoid
، بالإضافة إلى الأشكال الخاصة من هذه الأنواع كما سنتناولها بالتفصيل في هذا المقال. - البيانات المشتقة أو المركبة، وهي عبارة عن أنواع للبيانات تتكون من نوع واحد أو أكثر من البيانات الأساسية، مثل
struct
وunion
و Array و المؤشرات Pointers.
كيفية إنشاء متغير في لغة البرمجة سي
عند إنشاء متغير في لغة البرمجة سي فإنه يتم حجز مساحة محددة في الذاكرة العشوائية RAM لكي يتم حفظ قيمة المتغير بداخلها، ويتم تحديد حجم هذه المساحة بناء على نوع البيانات المستخدم، وقد يختلف حجم المتغير لنوع البيانات الواحد طبقا لنظام التشغيل المستخدم ومعمارية وحدة المعالجة المركزية.
لكي تقوم بإنشاء متغير في لغة البرمجة سي فإنه يجب إتباع بعض القواعد والتي تتمثل في
- تحديد نوع البيانات: ويتم ذلك بكتابة الكلمة التي تميز نوع البيانات، مثال int, float, struct.
- تحديد إسم للمتغير: وهو الإسم الذي يمكنك من التعامل مع المتغير، ويجب أن يبدأ هذا الإسم بحرف من حروف اللغة الإنجليزية سواء كان حرف كبير أو صغير أو يبدأ بعلامة الشرطة التحتية Underscore، ويمكن أن يحتوي إسم المتغير على أكثر من حرف أو رقم أو علامة الشرطة التحتية بعد أول حرف، ولكن لا يمكن أن يبدأ إسم المتغير برقم أو رمز خلاف ذلك.
- تحديد قيمة للمتغير: وتعتبر هذه الخطوة إختيارية حيث يمكنك إنشاء متغير دون تحديد قيمة مبدئية.
الشكل التالي يوضح القاعدة الأساسية لإنشاء متغير في لغة البرمجة سي
<Data-Type> <Variable-Name> [= <value>];
من الشكل السابق يتم إستبدال الكلمة Data-Type بنوع المتغير، والكلمة Variable-Name بإسم المتغير، وما بين الأقواس المربعة لتحديد قيمة المتغير وهي خطوة إختيارية، وفي النهاية يتم كتابة علامة الفاصلة المنقوطة Semicolon.
الشكل التالي يوضح بعض الأمثلة لإنشاء مجموعة من المتغيرات من أنواع مختلفة
int x;int y = 10;double d = 7.3;char letter = 'A';struct s {int i;char *p;};
قبل التعرف على أنواع البيانات سوف نتعرف على بعض الكلمات التي يمكن إستخدامها مع أنواع البيانات والتي تغير من خصائص البيانات
الفرق بين signed و unsigned
كما تعرف أنه يتم تخزين البيانات في الحاسوب على هيئة أعداد ثنائية، وفي بعض أنواع البيانات وخاصة البيانات الرقمية يوجد بيانات موجبة وبيانات سالبة، لذلك يقوم الحاسوب بحجز بت واحد من حجم البيانات لتحديد إن كان موجب أو سالب، وعادة ما يكون البت الأخير أو تخزين البيانات السالبة بطريقة المكمل لإثنين Two's Complement.
على سبيل المثال إذا أردنا تمثيل الرقم 8 وهو رقم موجب في متغير بحجم واحد بايت فإنه يتم تمثيله في النظام الثنائي بـ 00001000 وإذا أردت تمثيل الرقم -8 في النظام الثنائي بطريقة الـ Signed Magnitude فإنه يتم تمثيله بـ 10001000، حيث يتم حجز البت الأخير لتحديد إشارة الرقم، واحد تعني أن الرقم سالب وصفر للرقم الموجب أو بطريقة المكمل لإثنين ليصبح الرقم 11111000.
يعتبر الوضع الإفتراضي في نوع البيانات int
و char
هو مراعاة إشارة الرقم أي signed
، لذلك إذا ما رغبت في تغيير ذلك يمكنك إضافة الكلمة unsigned
قبل نوع البيانات لتحديد طريقة تمثيل البيانات في الذاكرة، لكي تتمكن من فهم الفرق بوضوح بين signed و unsigned يمكنك تشغيل الكود التالي
#include <stdio.h>#include <stdlib.h>int main() {char i = 0b11111111;unsigned char u = 0b11111111;printf("i=%d, u=%d\n", i, u);system("pause");return 0;}
الكود في المثال السابق تم كتابة الرقم 255 بالنظام الثنائي وذلك بإضافة 0b قبل الرقم الثنائي 11111111، عند تشغيل الكود سوف يتم طباعة قيمة المتغير i=-1 والمتغير u=255 وهو ما يوضح الفرق بين إستخدام كلمة unsigned أو إستخدام كلمة signed وهو الوضع الإفتراضي لذلك لا يتم كتابتها.
في المثال السابق إستخدمنا الأكواد الأساسية لكتابة برنامج بلغة البرمجة سي، والدالة printf() في لغة البرمجة سي، يمكنك الإطلاع على المقالتين لمزيد من المعلومات.
البيانات الرقمية الصحيحة Integer
في لغة البرمجة سي تستخدم الكلمة int
لترمز إلى البيانات الرقمية الصحيحة، وهي الأعداد التي لا تحتوي على كسور، مثال الرقم 1 أو الرقم 1001.
عادة ما يتم حجز مساحة 4-Byte للمتغير من النوع int
، حيث يتم تحديد الحد الأدنى و الحد الأقصى أو ما يعرف بـ المدى الخاص بالبيانات على حسب المساحة المحجوزة في الذاكرة للمتغير.
تحتوي لغة البرمجة سي على مجموعة من الكلمات المحجوزة (Reserved Words) التي يمكن أن تستخدم لإنشاء متغيرات للبيانات الرقمية الصحيحة، وهي كالتالي
الكلمة short
حيث يمكن إنشاء متغير من النوع short
عن طريق كتابة الأمر short x = 10;
، وهي إختصارا للأمر short int x = 10;
، وكلا الأمرين يقوم بإنشاء متغير رقمي صحيح، حجمه 2-Byte.
الكلمة long
في الأجهزة ذات المعمارية 32-bit أو 64-bit تعتبر هذه الكلمة إختصارا للكلمة int، حيث عند إنشاء متغير من النوع int
أو long
أو long int
تحصل على متغير بنفس الحجم، وهو 4-Byte.
ولكن يمكنك إنشاء متغير رقمي صحيح بحجم أكبر عن طريق إستخدام الأمر long long int
، ومن خلال هذا الأمر يمكنك الحصول على متغير رقمي بحجم 8-Byte.
البيانات الرقمية الصحيحة بحجم محدد Fixed-Width Integers
تمت إضافة أنواع البيانات الرقمية ذات الحجم المحدد من أول الإصدار C99 من لغة البرمجة سي، يمكنك التعرف على الإصدارات المختلفة في مقالة مقدمة في لغة البرمجة C، ولكي تتمكن من إستخدام أي من هذه الأنواع يجب إستدعاء الملف inttypes.h عن طريق كتابة الأمر #include <inttypes.h>
في بداية الكود.
وتتمثل هذه الأنواع في int8_t
و int16_t
و int32_t
و int64_t
حيث يمثل الرقم بعد كلمة int
حجم البيانات بوحدة الـ bit، بالإضافة إلى الأنواع uint8_t
و uint16_t
و uint32_t
و uint64_t
وهي الأنواع التي لا تدعم الإشارة أو المماثلة للنوع unsigned int
، ويوجد أيضا مجموعة إضافية من الأنواع التي يمكن أن نتناولها في موضوعات متقدمة.
الجدول التالي يوضح الأنواع المختلفة للبيانات الرقمية الصحيحة مع تحديد حجم كل نوع ومدي الأرقام المسموح به
نوع البيانات | الحجم بـ Byte | المدى |
---|---|---|
int signed int long signed long long int signed long int int32_t | 4 | من -2,147,483,648 إلى 2,147,483,647 |
unsigned int unsigned long unsigned long int uint32_t | 4 | من 0 إلى 4 294 967 295 |
short signed short short int signed short int int16_t | 2 | من -32,768 إلى 32,767 |
unsigned short unsigned short int uint16_t | 2 | من 0 إلى 65,535 |
long long signed long long long long int signed long long int int64_t | 8 | من -9,223,372,036,854,775,808 إلى 9,223,372,036,854,775,807 |
unsigned long long unsigned long long int uint64_t | 8 | من 0 إلى 18,446,744,073,709,551,615 |
int8_t | 1 | من -128 إلى 127 |
uint8_t | 1 | من 0 إلى 255 |
البيانات الرقمية العشرية Floating Point Type
تسمى أحيانا بـ الأعداد الحقيقية أو Real Numbers، وهي الأعداد التي تحتوي على كسور مثال 1.01 و 0.107، في لغة البرمجة C يتم إستخدام الكلمات float
أو double
لإنشاء متغيرات يمكنها التعامل مع الأرقام الحقيقية والفرق بينهم يتمثل في حجم المتغير، وفي معظم الأحيان يتم تجاهل الكلمة float
ويتم إستخدام الكلمة double
حتى تتمكن من التعامل مع أرقام أكبر.
في معظم الأحيان يتم حجز مساحة 4-Byte للنوع float
و 8-Byte للنوع double
.
في معظم الأجهزة الحديثة يتم تخزين الأرقام الحقيقية في الذاكرة طبقا لطريقة IEEE 754 الخاصة بتحويل الأعداد الحقيقة إلى نظام العد الثنائي، وفي هذا الوقت سوف نكتفي بأن تعرف أنها طريقة مختلفة عن طريقة تمثيل الأعداد الصحيحة بالنظام الثنائي.
يمكن أن يتم كتابة الأرقام الحقيقية أو العشرية بطريقتين
طريقة النقطة العائمة Floating Point
وهي الطريقة التقليدية وتتمثل في كتابة الجزء الصحيح من الرقم متبوعا بنقطة ثم الجزء الكسري من الرقم، مثال 101.987 حيث يمثل العدد 101 الجزء الصحيح من الرقم، والعدد 987 الجزء الكسري من الرقم.
الطريقة العلمية Scientific Notation
وفي هذه الطريقة يتم كتابة الرقم الحقيقي طبقا للمعادلة التالية
m x 10n
حيث يمثل m قيمة رقم حقيقي مضروب في 10 مرفوعة لأس رقم صحيح قيمته n، وقد تكون قيمة n موجبة أو سالبة وكذلك m.
لتبسيط الأمر إذا أردت كتابة الرقم 10,000 بالطريقة العلمية، فإنه يمكنك أن تكتب الرقم كالتالي 1 x 104، كمثال آخر إذا أردت تمثيل الرقم 179.08 بالطريقة العلمية فيمكنك كتابته بالشكل التالي 1.7908 x 102، في هذا المثال قمت بترحيل العلامة العشرية برقمين إلى اليسار، وبالتالي يصبح الأس وهو قيمة n تساوي 2 موجبة لأن تحريك العلامة إلى اليسار.
مثال آخر إذا أردت تمثيل الرقم الحقيقي 0.00023 بالطريقة العلمية فإنه يكتب 2.3 x 10-4، في هذا المثال قمنا بتحريك العلامة العشرية أربع خطوات إلى اليمين وبالتالي قيمة n تصبح -4.
في لغات البرمجة يصعب كتابة الأس بجوار رقم الأساس، وبالتالي يتم إستبدال الرقم 10 بالرمز E أو e ويكتب بجواره قيمة الأس ليصبح الرقم من المثال السابق 2.3E-4، ومن المثال السابق له يكتب الرقم 1.7908E2.
البيانات من الأرقام المركبة Complex Numbers
بداية من الإصدار C99 للغة البرمجة سي، تم إضافة نوع البيانات complex
لدعم العمليات الحسابية على الأرقام المركبة، ولكي تتمكن من إستخدام هذا النوع يتوجب عليك إستدعاء الملف math.h عن طريق إستخدام الأمر #include <math.h>
في بداية الكود.
سوف نكتفي فقط بالإشارة إلى دعم العمليات الحسابية على الأعداد المركبة في لغة البرمجة C، فهو موضوع رياضي يحتاج لعدة مقالات لشرحه.
البيانات الحرفية أو النصية Character Types
لغة البرمجة C تدعم فقط البيانات الحرفية أو التي تحتوي على حرف واحد وذلك عن طريق إستخدام الكلمة char
، ولكي تتمكن من كتابة أكثر من حرف أو ما يعرف بـ String فإنه يتم إنشاء مصفوفة Array من النوع char
ليتم حفظ سلسة الأحرف المكونة للنص.
يتم حجز مساحة واحد بايت للمتغير من النوع char
، ويمكن كتابة أي رمز من مجموعة الرموز ASCII كقيمة للمتغير من هذا النوع، ولا يمكن كتابة أي حرف أو رمز غير موجود بمجموعة الـ ASCII كقيمة لهذا النوع من المتغيرات، فلا يمكنك كتابة أي حرف باللغة العربية لحفظه في متغير من النوع char.
لذلك تم إضافة نوع جديد للبيانات لتمثيل الأحرف العريضة والتي تعرف بـ Wide Character وهي تتمثل في أي حرف أو رمز بخلاف مجموعة الـ ASCII، أو عادة ما يطلق عليها مجموعة Unicode، ويجب الأخذ في الإعتبار أن مجموعة الـ Unicode تدعم أيضا رموز مجموعة الـ ASCII، وتستخدم الكلمة wchar_t
لإنشاء متغيرات قادرة على تمثيل بيانات ذات ترميز Unicode.
عند إنشاء متغير حرفي يجب أن يتم كتابة قيمة المتغير بين علامات تنصيص مفردة Single Quotes ويجب أن تكون القيمة هي حرف واحد فقط، المثال التالي يوضح كيفية إنشاء متغيرات حرفية
char c = 'A';wchar_t w = 'س';
عند إنشاء متغيرات من النوع wchar_t
يجب إستدعاء الملف wchar.h عن طريق كتابة الأمر #include <wchar.h>
في بداية الكود، في بعض الأحيان يمكنك إستخدام الكلمة دون إستدعاء أي ملفات ولكن لتجنب ظهور أي مشكلات أثناء تشغيل الكود يفضل إستدعاء هذا الملف.
في نظام التشغيل ويندوز يتم حجز 2-Byte للمتغير من النوع wchar_t
أما في باقي أنظمة التشغيل يتم حجز 4-Byte لهذا النوع من المتغيرات، والفرق في أن نظام التشغيل ويندوز يستخدم الترميز UTF-16.
البيانات المنطقية Boolean Type
هذا النوع من البيانات يمكن أن يحتوي على قيمة صحيحة أو ما يعرف بـ True أو قيمة خطأ وهي ما تعرف بـ False، وعادة ما تكون القيمة صفر هي الـ False وأي قيمة أخرى تعتبر True.
لغة البرمجة C لم تكن تدعم هذا النوع من البيانات حتى الإصدار C99، في الإصدارات السابقة كان يتم إستخدام نوع البيانات unsigned char ليمثل البيانات المنطقية فهو يعتبر أقل نوع بيانات في مساحة الذاكرة وبالتالي يعتبر كافي لتخزين قيمة البيانات المنطقية.
ولكن من عيوب إستخدام النوع unsigned char أنه في حالة إضافة قيمة أكبر من المدى الخاص بهذا النوع من البيانات أحيانا يحدث ما يعرف بـ Overflow مما يؤدي إلى قراءة قيمة المتغير على أنها صفر بالرغم من أنها يفترض أن تكون True، على سبيل المثال إذا ما قمت بتخزين القيمة 256 بداخل متغير من النوع unsigned char سوف يتم قراءة هذا المتغير على أنه يساوي صفر.
ولتجنب هذا النوع من المشاكل تم إنشاء نوع البيانات _Bool
ليتفادى هذه المشكلة حيث يتم تخزين القيمة 1 لأي قيمة بخلاف الصفر، هذا النوع من البيانات حجمه واحد بايت، ولتوضيح الفرق بين الطريقتين يمكنك تجربة الكود التالي
#include <stdio.h>#include <stdlib.h>int main() {_Bool x = 256;unsigned char c = 256;printf("unsigned char c = %d\n_Bool x = %d\n", c, x);system("pause");return 0;}
المصفوفات Arrays
المصفوفات تعتبر نوع مركب من البيانات، حيث أنها تحتوي على مجموعة محددة من العناصر التي تنتمي إلى نوع واحد من أنواع البيانات الأساسية أو الأولية.
أو بمعنى آخر لكي تتمكن من إنشاء مصفوفة فإنه يجب تحديد نوع البيانات لهذه المصفوفة ويجب أن تكون كل عناصر المصفوفة من نفس النوع ويجب تحديد عدد العناصر التي تحتوي عليها المصفوفة.
نظرا لأهمية المصفوفات في لغة البرمجة سي فقد قمنا بتخصيص مقال متكامل لشرح المصفوفات في لغة البرمجة C يمكنك قراءته لتعلم كل التفاصيل الخاصة بالمصفوفات.
لكي تتمكن من إنشاء مصفوفة فإنه يجب كتابة نوع البيانات ثم تحديد إسم المتغير يلية زوج من الأقواس المربعة Square Braces وبداخل هذه الأقواس يتم كتابة عدد عناصر المصفوفة، ولتحديد قيم هذه العناصر يتم إستخدام الأقواس المعقوفة Curly Braces ويكتب بداخلهم عناصر المصفوفة ويفصلهم علامة الفاصلة.
عناصر المصفوفة تكون مرتبة من صفر أي أن أول عنصر في المصفوفة يكون ترتيبه أو ما يعرف بـ index الخاص به هو صفر، وآخر عنصر في المصفوفة ترتيبه هو عدد العناصر مطروح منه واحد.
كمثال سوف نقوم بإنشاء عدة مصفوفات لعدد من أنواع البيانات
int i_arr[10];int i2[7] = { 7 };char name[11] = { 'A', 'b', 'u', 'e', 'l', 'f', 'a', 't', 'e', 'h', '\0' };double d[5] = { 2.3, 0.1, 4.3, 8.9, 2.01 };char site[] = "abuelfateh.com";i_arr[0] = 1;i_arr[8] = 7;i2[1] = 2;site[10] = '|';
من الأمثلة قمنا بإنشاء متغير من النوع int وتم تحديد إسمه بـ i_arr ثم تم تحديد عدد عناصر هذه المصفوفة بـ 10 عناصر، سوف تلاحظ أننا قمنا بتحديد قيم بعض عناصر المصفوفة بداية من السطر السابع حيث قمنا بتحديد قيمة العنصر الأول (الـ index صفر) بالقيمة واحد، والعنصر التاسع (الـ index يساوي 8) بالقيمة 7.
وسوف تلاحظ أننا قمنا بإنشاء المصفوفة i2 من النوع int وهي تحتوي على 7 عناصر، وقد قمنا بتحديد القيمة 7 بين الأقواس المعقوفة مرة واحدة، وهذه الطريقة تستخدم لتحديد قيمة العنصر الأول من المصفوفة وتصفير باقي العناصر أو جعل قيمة باقي العناصر مساوية لصفر.
سوف تلاحظ أننا أنشأنا المصفوفة site من النوع char ولم نقم بتحديد عدد عناصر المصفوفة، وتم كتابة عنوان الموقع بين علامات تنصيص مزدوجة، وهذه الطريقة تستخدم لإنشاء الجمل النصية وتعد إختصارا لطريقة إنشاء المصفوفة من هذا النوع والتي كان يفترض أن يتم إنشاؤها كما في مثال المصفوفة name.
النصوص في لغة البرمجة C يجب أن تنتهي بالحرف \n وهو ما يعرف بـ NULL، وهو قيمة خاصة يتم من خلالها تحديد عدد حروف الجملة، لذلك إذا ما قمت بإنشاء مصفوفة نصية كمثال المصفوفة name يجب أن تكتب آخر حرف في المصفوفة بهذه القيمة، وإذا قمت بإستخدام الطريقة التي تم بها إنشاء المصفوفة site فإن هذا الرمز يتم إدراجه بواسكة المترجم، ويجب مراعاة هذا العنصر عند تحديد حجم المصفوفة بإضافة عنصر على عدد الحروف التي ترغب في كتابتها.
البيانات الهيكلية struct Type
هذا النوع من البيانات يعتبر من البيانات المركبة، حيث يحتوي على نوع واحد أو أكثر من البيانات الأساسية أو المركبة.
ويستخدم هذا النوع من البيانات لتسهيل التعامل مع البيانات ذات الصلة، على سبيل المثال إذا أردت التعامل مع بيانات الوقت والتاريخ فإنه يتوجب عليك إنشاء عدد كبير من المتغيرات لحفظ الوقت والتاريخ، على أقل تقدير سوف تقوم بإنشاء متغير لتاريخ اليوم وآخر للشهر وآخر للعام ومتغير لحفظ الساعات وآخر للدقائق وآخر للثواني، وفي كل مرة تريد التعامل مع الوقت والتاريخ سوف تقوم بإنشاء نفس العدد من المتغيرات بأسماء مختلفة مما سوف يصعب عملية تتبع هذه المتغيرات.
للتغلب على مشكلة تتبع المتغيرات يمكنك إنشاء هيكل للبيانات لحفظ الوقت والتاريخ، هيكل البيانات لا يقوم بحجز مساحة في الذاكرة إلا عند إستخدامه، فهو يعد طريقة لتنظيم كتابة الأكواد البرمجية.
لكي تتمكن من إنشاء هذا النوع من البيانات فإنه يتم كتابة الكلمة struct يليها إسم هيكل البيانات متبوعا بزوج من الأقواس المعقوفة، وبدخل هذه الأقواس يتم تعريف أنواع البيانات التي ترغي في إنشاؤها كما تعودنا في البيانات الأساسية.
وعند الرغبة في إنشاء بيانات من النوع الذي تم تحديده في هيكل البيانات يتم كتابة الكلمة struct متبوعة بإسم هيكل البيانات متبوعة بإسم المتغير، والمثال التالي يوضح كيفية إنشاء هيكل بيانات لحفظ الوقت والتاريخ
#include <stdio.h>#include <stdlib.h>struct DateTime {int Year;int Month;int Day;int Hour;int Minut;int Second;};int main() {struct DateTime d1;d1.Year = 2020;d1.Month = 2;d1.Day = 22;d1.Hour = 12;d1.Minut = 24;d1.Second = 48;printf("%d-%d-%d %d:%d:%d\n", d1.Year, d1.Month, d1.Day, d1.Hour, d1.Minut, d1.Second);system("pause");return 0;}