1. المقدمة
لغة C لا تزال تُستخدم على نطاق واسع في مجال تطوير الأنظمة وتطوير الأنظمة المدمجة. ومن بين هذه، تُعد “مصفوفة الأحرف” أحد العناصر التركيبية الأساسية والأكثر أهمية للتعامل مع السلاسل النصية.
في لغة C لا يوجد نوع سلسلة نصية قياسي. بدلاً من ذلك، يتم تمثيل السلاسل النصية باستخدام مصفوفة من الأحرف (char مصفوفة). وهذا قد لا يكون بديهيًا جدًا للمبتدئين، لذا فإن فهم مصفوفة char قد يصبح عائقًا كبيرًا في تعلم لغة C.
بالإضافة إلى ذلك، إذا لم يتم فهم الفروق الدقيقة بين مصفوفة char و مؤشر char(char*
), وكذلك وجود حرف النهاية (null)(\0
), فقد يؤدي ذلك إلى حدوث أخطاء غير متوقعة.
في هذه المقالة، سنركز على موضوع “مصفوفة char في لغة C“، وسنشرح بطريقة واضحة من الاستخدام الأساسي إلى التقنيات المتقدمة، بالإضافة إلى طرق تجنب الأخطاء الشائعة.
إذا كنت ترغب في تعلم لغة C بجدية أو تريد مراجعة مصفوفة char، يرجى متابعة القراءة حتى النهاية. في الفصل التالي سنشرح أولاً تعريف مصفوفة char وآليته الأساسية.
2. ما هو مصفوفة char؟
في لغة C، «مصفوفة char» هي حروف (من نوع char) تُجمع وتُخزن في مصفوفة. هذه بنية أساسية عند التعامل مع السلاسل النصية.
ما هو نوع char؟
في لغة C، النوع char
هو نوع بيانات يُستخدم لتمثيل حرف واحد. على سبيل المثال، يمكن تعريف متغيّر حرفي كما يلي.
char c = 'A';
بهذه الطريقة، الحرف 'A'
المحاط بـ علامات اقتباس مفردة يُعرّف كنوع char
.
الصياغة الأساسية لمصفوفة char
لتخزين عدة أحرف، نستخدم مصفوفة من النوع char
. يتم تعريفها كما يلي:
char str[6] = {'H', 'e', 'l', 'l', 'o', '\0'};
هذه المصفوفة تحجز مساحة ذاكرة تكفي 6 أحرف، أي السلسلة "Hello"
المكوّنة من 5 أحرف مضافًا إليها حرف النهاية null ( \0
).
أهمية حرف النهاية null ( 'حجم المصفوفة والنقاط التي يجب الانتباه إليها
'
)
حجم المصفوفة والنقاط التي يجب الانتباه إليها
'في لغة C، يُستخدم حرف النهاية null '\0'
للإشارة إلى نهاية السلسلة. إذا غاب هذا الرمز، قد لا تعمل دوال معالجة السلاسل بشكل صحيح وقد يحدث سلوك غير متوقع.
char str[] = "Hello"; // يتم إضافة '\0' تلقائيًا في النهاية
كما في المثال أعلاه، إذا استخدمت حرفي السلسلة المحاطين بعلامات اقتباس مزدوجة، سيضيف المترجم تلقائيًا '\0'
في النهاية.
3. إعلان ومبادرة مصفوفة char
عند استخدام مصفوفة char، من الأساسي حجز حجم يساوي عدد الأحرف المطلوبة + 1 (لحرف النهاية null) . إذا تم إدخال سلسلة في مصفوفة أصغر من الحجم المطلوب، قد يتسبب ذلك في تجاوز سعة الذاكرة (buffer overflow) وقد يؤدي إلى توقف البرنامج بشكل غير طبيعي.
الإعلان والمبادرة الساكنة
لاستخدام مصفوفة char، يجب أولاً إعلان ومبادرة مناسبين مطلوب. هنا، سنشرح طريقة الإعلان الأساسية لمصفوفة char في لغة C، وكيفية المبادرة، وكذلك استخدام الذاكرة الديناميكية.
المبادرة باستخدام حرفية السلسلة
كطريقة أساسية، هناك طريقة للإعلان مع تحديد حجم المصفوفة ومبادرة كل حرف على حدة.
char str[6] = {'H', 'e', 'l', 'l', 'o', '\0'};
بهذه الطريقة، يمكن التعامل مع str
كسلسلة نصية "Hello"
. المهم هو تضمين '\0'
في النهاية دائمًا.
تحديد الحجم وإسناد الحرفية
في لغة C، يمكن أيضًا القيام بمبادرة بسيطة باستخدام حرفية السلسلة كما يلي.
char str[] = "Hello";
في هذه الحالة، يتم تحديد حجم المصفوفة تلقائيًا إلى 6 أحرف: "Hello"
+ '\0'
. إذا رغبت في تعديل محتوى السلسلة، فإن الإعلان كمصفوفة char يتيح تعديلًا آمنًا.
استخدام تخصيص الذاكرة الديناميكية (malloc)
من الممكن أيضًا تحديد حجم المصفوفة ومبادرتها بحرفية السلسلة، لكن يجب الانتباه إلى نقص الحجم.
char str[5] = "Hello"; // ❌ سبب الخطأ (نقص الحجم)
كما هو موضح أعلاه، "Hello"
تحتاج إلى 5 أحرف + حرف واحد (‘\0’) أي ما مجموعه 6 أحرف. لذلك، يجب أن يكون على الأقل char str[6]
.
4. عمليات السلسلة النصية
إذا رغبت في التعامل مع السلاسل بمرونة أكبر، هناك طريقة لاستخدام malloc
لتخصيص مصفوفة char ديناميكيًا.
#include
#include
char* str = malloc(6 * sizeof(char));
strcpy(str, "Hello");
في هذه الطريقة، يتم تخصيص الذاكرة ديناميكيًا، ويتم التعامل مع السلسلة عبر المؤشر. بعد الاستخدام، لا تنسَ استخدام free(str);
لتحرير الذاكرة.
نسخ السلسلة النصية: strcpy
في لغة C، عند التعامل مع سلاسل النصوص باستخدام مصفوفة char
، من الشائع الاستفادة من دوال المكتبة القياسية. هنا سنشرح دوال العمليات الأساسية على السلاسل النصية وكيفية استخدامها مع أمثلة عملية.
دمج السلسلة النصية: strcat
strcpy
هو دالة تنسخ سلسلة نصية إلى مصفوفة char أخرى.
#include
char src[] = "Hello";
char dest[10];
strcpy(dest, src);
نقطة الانتباه: إذا لم يتم تخصيص حجم كافٍ للـ dest
، سيتسبب ذلك في حدوث تجاوز للذاكرة. يجب تخصيص حجم يساوي طول السلسلة المنسوخة + 1 (حرف null).
الحصول على طول السلسلة النصية: strlen
strcat
يدمج سلسلتين نصيتين. يضيف محتوى الوسيط الثاني إلى نهاية الوسيط الأول.
#include
char str1[20] = "Hello";
char str2[] = " World";
strcat(str1, str2);
نتيجة ذلك، 49__ يصبح "Hello World"
. كذلك، منترض تكونوفةيط الأول لديها مساحة كافية لاستيعاب الحجم الكلي بعد الدمج .
مقارنة السلاسل النصية: strcmp
__135__strlen
تُعيد طول السلسلة (عدد الأحرف بدون حرف null).
#include
char str[] = "Hello";
int len = strlen(str); // len = 5
نظرًا لأن حرف null لا يُحسب، يجب على غير المألوفين بلغة C أن ينتبهوا.
البحث في السلسلة النصية: strchr
و strstr
لمقارنة ما إذا كانت سلسلتان متساويتان، استخدم strcmp
.
#include
char str1[] = "Hello";
char str2[] = "World";
if (strcmp(str1, str2) == 0) {
// متساوي
} else {
// مختلف
}
هذه الدالة تُعيد 0 إذا كانت السلسلتان متساويتين، وتُعيد فرق رموز الأحرف إذا كانت مختلفة.
5. الاختلاف بين مصفوفة char والمؤشر
للبحث عن حرف معين أو جزء من السلسلة، يمكن استخدام الدوال التالية.
#include
char str[] = "Hello World";
// البحث عن حرف
char *ptr1 = strchr(str, 'o'); // مؤشر أول 'o'
// البحث عن جزء من السلسلة
char *ptr2 = strstr(str, "World"); // مؤشر بداية "World"
إذا لم يتم العثور، كلا الدالتين تُعيدان NULL
.
اختلاف التصريح
عند التعامل مع السلاسل النصية في لغة C، مصفوفة char
و مؤشر char(char*)
قد تبدو متشابهة، لكن في الواقع خصائص مختلفة تمتلك. من خلال فهم هذا الاختلاف بشكل صحيح، يمكنك منع الاستخدام الخاطئ للذاكرة والأخطاء غير المتوقعة.
اختلاف إمكانية التعديل
أولاً، دعونا نلقي نظرة على اختلاف طرق التصريح.
char str1[] = "Hello"; // مصفوفة char
char *str2 = "Hello"; // مؤشر char
str1
هو مصفوفة لها كيان فعلي، ويتم تخصيص 6 بايت في الذاكرة (“Hello” + ‘\\0’). من ناحية أخرى، str2
هو مؤشر إلى منطقة الذاكرة التي تخزن فيها السلسلة النصية الثابتة.
اختلاف بنية الذاكرة
مصفوفة char str1
يمكنها تعديل الأحرف داخل المصفوفة بحرية.
str1[0] = 'h'; // OK
لكن، char* str2 = "Hello";
كما في الوصول إلى السلسلة النصية الثابتة عبر مؤشر، فإن تعديل محتواها يُعد سلوكًا غير معرف.
str2[0] = 'h'; // ❌ سلوك غير معرف (قد يحدث خطأ وقت التنفيذ)
اختلاف في الحصول على الحجم
مصفوفة char
على الstackمؤشر char
منطقة الثوابت (قابلة للقراءة فقط)منطقة الذاكرة المخصصة (مثل malloc)يجب الانتباه إلى التعامل مع الذاكرة
ملخص: نقاط التمييز
في حالة المصفوفة، sizeof(str1)
تُعيد عدد البايتات الكلي للمصفوفة.
char str1[] = "Hello";
printf("%lu", sizeof(str1)); // → 6 (تشمل '\\0')
من ناحية أخرى، في حالة المؤشر، sizeof(str2)
تُعيد حجم المؤشر نفسه (عادةً 4 إلى 8 بايت)، لذا لا يمكن استخدامها لغرض الحصول على حجم المصفوفة.
char *str2 = "Hello";
printf("%lu", sizeof(str2)); // → 8 (بيئة 64 بت)
6. طريقة تمرير مصفوفة char إلى الدالة
العنصر | مصفوفة char | char مؤشر |
---|---|---|
تغيير المحتوى | مُمكن | بشكل عام غير مسموح (في حالة القيم الحرفية) |
الحصول على الحجم | sizeof() | strlen() |
غرض إعادة الكتابة | مناسب | غير مناسب (قابل للقراءة فقط) |
المرونة | حجم ثابت | مرن، لكن يجب الحذر |
مثال أساسي: تمرير مصفوفة char كمعامل
في لغة C، عند تمرير مصفوفة إلى دالة يتم تمريرها عبر المؤشر وليس عبر القيمة. وهذا ينطبق أيضًا على مصفوفة char
، حيث يتم تمرير عنوان البداية (المؤشر) إلى الدالة.
فهم هذه الآلية مهم جدًا في الحالات التي تحتاج فيها إلى تعديل أو معالجة السلاسل النصية بين الدوال.
دالة تعديل المحتوى
#include
void printString(char str[]) {
printf("السلسلة: %s\n", str);
}
int main() {
char greeting[] = "Hello";
printString(greeting);
return 0;
}
في هذا المثال، يتم تمرير معامل من نوع char[]
إلى الدالة printString
، في الواقع يتم استقباله كـ char*
. وبالتالي، char str[]
تعني نفس الشيء كـ char *str
.
إدارة حجم المصفوفة
حتى عند تعديل محتوى المصفوفة داخل الدالة، يمكن التعامل مباشرة مع البيانات عبر العنوان الممرَّر.
#include
void toUpperCase(char str[]) {
for (int i = 0; str[i] != ' '; i++) {
if ('a' <= str[i] && str[i] <= 'z') {
str[i] = str[i] - ('a' - 'A');
}
}
}
int main() {
char text[] = "hello world";
toUpperCase(text);
printf("%s\n", text); // الإخراج: HELLO WORLD
return 0;
}
بهذه الطريقة، حتى إذا أردت تعديل محتوى المصفوفة، لأنها تُعامل كمؤشر، فإن العمليات داخل الدالة تنعكس على المستدعي .
معامل للقراءة فقط باستخدام const
في لغة C، عند تمرير مصفوفة إلى دالة معلومات الحجم لا تُمرَّر مع المصفوفة . لذلك، من الأفضل تمرير الحجم كمعامل لضمان عملية آمنة.
void printChars(char str[], int size) {
for (int i = 0; i < size; i++) {
printf("%c ", str[i]);
}
printf("\n");
}
كما أنه من الشائع استخدام الدالة strlen
للحصول على طول السلسلة بشكل ديناميكي. ومع ذلك، يجب تجنب استخدامها مع المصفوفات غير المنتهية بـ null.
7. مثال عملي: عرض السلسلة بترتيب عكسي
إذا لم يكن من المقصود تعديل السلسلة داخل الدالة عدم التغيير، فمن الأفضل استخدام const char*
لتوضيح أنها للقراءة فقط.
void printConstString(const char *str) {
printf("%s\n", str);
}
هذا يمنع التعديل غير المقصود ويُوضح مواصفات الدالة بوضوح.
الهدف
هنا، سنستفيد من المعرفة حول char hairetsu
لإنشاء برنامج يعرض السلسلة بترتيب عكسي فعليًا.
كود العينة
يقوم بطباعة السلسلة التي أدخلها المستخدم حرفًا بحرف من النهاية إلى البداية. وهذا فعال جدًا كتمرين على عمليات المصفوفة والحصول على طول السلسلة ومعالجة الحلقات.
شرح
#include
#include
void printReversed(char str[]) {
int len = strlen(str);
for (int i = len - 1; i >= 0; i--) {
putchar(str[i]);
}
putchar('\n');
}
int main() {
char text[100];
printf("الرجاء إدخال السلسلة: ");
fgets(text, sizeof(text), stdin);
// إزالة حرف السطر الجديد (إجراء وقائي عند استخدام fgets)
size_t len = strlen(text);
if (len > 0 && text[len - 1] == '\n') {
text[len - 1] = '\0';
}
printf("عند العرض بترتيب عكسي: ");
printReversed(text);
return 0;
}
مثال التنفيذ
fgets
- يتم أيضًا إجراء معالجة لإزالة (حرف سطر جديد) المضاف في نهاية الإدخال.
strlen()
نصائح للتطبيق
الرجاء إدخال السلسلة: OpenAI
عند العرض بترتيب عكسي: IAnepO
8. الأخطاء الشائعة وطرق التعامل معها
هذه العملية يمكن أن تُستَخدم لتحديد ما إذا كانت السلسلة تحديد palindrome أو لفهم بنية الستاك، مما يساهم في تطبيقات تعلم الخوارزميات. كما يمكن تعديلها باستخدام عمليات المؤشر، لتصبح مادة تمرين أكثر تقدمًا.
1. نسيان إضافة النهاية الصفرية(2. نقص حجم الذاكرة المؤقتة
)
2. نقص حجم الذاكرة المؤقتة
في لغة C مصفوفة char
عند التعامل، هناك بعض الفخاخ التي قد يقع فيها المبتدئون وحتى المتقدمون. هنا سنشرح بالتفصيل الأخطاء المتكررة وإجراءات المنع والحلول لها.
3. تعديل literals السلسلة
أحد أكثر الأخطاء شيوعًا هو نسيان إضافة حرف النهاية الصفرية(\0
)في نهاية السلسلة.
char str[5] = {'H', 'e', 'l', 'l', 'o'}; // ❌ لا توجد '\0' في النهاية
printf("%s\n", str); // سلوك غير معرف
الحل:
char str[6] = {'H', 'e', 'l', 'l', 'o', '\0'}; // ✅ صحيح
أو، باستخدام literals السلسلة يتم إضافة حرف النهاية الصفرية تلقائيًا.
4. نسيان معالجة حرف السطر الجديد في fgets
strcpy
و strcat
باستخدام الدوال، إذا كان حجم مصفوفة النسخ إليها غير كافٍ، سيتسبب ذلك في تلف الذاكرة (تجاوز سعة الذاكرة المؤقتة).
char str1[5];
strcpy(str1, "Hello"); // ❌ نسخ 6 أحرف إلى مصفوفة بحجم 5
الحل:
- في الوجهة المستنسخة(مثال: 「عدد الأحرف + 1」)
- أفكر أيضًا في استخدام الأكثر أمانًا
char str1[6];
strncpy(str1, "Hello", sizeof(str1) - 1);
str1[5] = '\0'; // لتأكيد إضافة حرف النهاية الصفرية في النهاية
5. الخلط بين المؤشر والمصفوفة
char *str = "Hello";
كما هو معلن، قد يشير المؤشر إلى إمكانية الإشارة إلى منطقة غير قابلة للتعديل، وإذا تم الكتابة فيه سيحدث خطأ أثناء التنفيذ.
char *str = "Hello";
str[0] = 'h'; // ❌ يحدث Segmentation fault أثناء التنفيذ
الحل:
char str[] = "Hello"; // ✅ تم الإعلان كمصفوفة قابلة للتعديل
str[0] = 'h';
9. الملخص
fgets
باستخدامه للحصول على سلسلة، يجب الانتباه إلى بقاء حرف السطر الجديد(\n
)في النهاية.
fgets(str, sizeof(str), stdin);
// str يحتوي على محتوى مثل "Hello\n\0"
الحل:
size_t len = strlen(str);
if (len > 0 && str[len - 1] == '\n') {
str[len - 1] = '\0';
}
ما تعلمته في هذه المقالة
نظرًا لتشابه الشكل، قد يتم الخلط بين char*
و char[]
، مما قد يؤدي إلى سلوك غير معرف. الفرق في الحصول على الحجم (sizeof
) و الفرق في إمكانية التعديل يجب الانتباه إليه.
نصائح للتعلم المستقبلي
في هذه المقالة، قمنا بشرح 「مصفوفة char في لغة C」 حول، من الأساسيات إلى التطبيقات بشكل تدريجي. أخيرًا، سنستعرض محتوى المقالة ونقدم إرشادات للتعلم المستقبلي.
ما تعلمته في هذه المقالة
- دور وتحديد مصفوفة من نوع char
- أهمية القيم النصية الثابتة والرمز الصفري (
\0
) - التعامل مع السلاسل باستخدام دوال مكتبة القياسية
strcpy
strcat
strlen
strcmp
- مصفوفة حرف ومؤشر حرف
- طريقة تمرير المصفوفة إلى الدالة والاعتبارات الأمنية
- تثبيت الفهم من خلال أمثلة تطبيقية وأخطاء شائعة
نصائح للتعلم المستقبلي
فهم كيفية استخدام مصفوفة char هو، في لغة C، الخطوة الأولى لفهم أساسيات عمليات الذاكرة. المعرفة التي اكتسبتها هنا يمكن أن تُستَخدم في الخطوات التالية كما هو موضح أدناه.
- عمليات المؤشرات
- إدارة الذاكرة الديناميكية
malloc
free
- التركيب مع الهيكل
بالإضافة إلى ذلك، من خلال قراءة الشيفرة التي كتبها الآخرون يمكنك استيعاب وجهات نظر وأساليب كتابة لم تكن لديك. عادة قراءة المشاريع الفعلية أو OSS(البرمجيات مفتوحة المصدر)مفيدة جدًا.
لغة C تتمتع بمرونة عالية، وبالتالي قد تكون خطرة إذا لم تُعامل بشكل صحيح، لكن إذا بنيت الأساس بعناية تصبح سلاحًا قويًا جدًا. فهم مصفوفة char هو الخطوة الأولى. يرجى الاستفادة من هذه المقالة كمرجع، والعمل على تحسين مهاراتك بثبات.