تا اینجا با انواع دادههای عددی کار کردیم، ولی توی زبان C یکی از پراستفادهترین نوع آرایهها، آرایهی کاراکتری (character array) هست — همون چیزی که رشتهها (strings) رو باهاش میسازیم.
برای درک بهتر، بیایید با یه مثال واقعی جلو بریم:
میخوایم چند خط متن از ورودی بخونیم و طولانیترین خط رو چاپ کنیم.
طرح کلی برنامه
اول از همه، طرح منطقی برنامهمون به این شکل خواهد بود
while (یه خط جدید وجود داره)
اگه طولش از طولانیترین خط قبلی بیشتر بود
اون رو ذخیره کن
و طولش رو نگه دار
در نهایت، طولانیترین خط رو چاپ کن
میبینی؟ برنامه بهصورت طبیعی به چند قسمت تقسیم میشه:
-
یه بخش که خط جدید رو میگیره
-
یه بخش که اون خط رو ذخیره میکنه
-
و یه بخش اصلی (main) که کل فرآیند رو کنترل میکنه
نوشتن تابع برای خواندن خطوط ورودی (getline)
اولین تابعی که مینویسیم، getline هست.
این تابع یه خط ورودی رو میخونه و طول اون خط رو برمیگردونه.
اگر به انتهای فایل (EOF) برسیم، طول ۰ برمیگردونه تا بدونیم دیگه ورودیای وجود نداره.
برنامه کامل: پیدا کردن طولانیترین خط ورودی
#include <stdio.h>
#define MAXLINE 1000 /* حداکثر طول مجاز برای هر خط ورودی */
int getline(char line[], int maxline);
void copy(char to[], char from[]);
/* چاپ طولانیترین خط ورودی */
main()
{
int len; /* طول فعلی خط */
int max; /* بیشترین طولی که تا حالا دیده شده */
char line[MAXLINE]; /* خط فعلی ورودی */
char longest[MAXLINE]; /* جایی برای ذخیره طولانیترین خط */
max = 0;
while ((len = getline(line, MAXLINE)) > 0)
if (len > max) {
max = len;
copy(longest, line);
}
if (max > 0) /* اگه حداقل یه خط وجود داشت */
printf("%s", longest);
return 0;
}
تابع getline
/* getline: خواندن یک خط و بازگرداندن طول آن */
int getline(char s[], int lim)
{
int c, i;
for (i = 0; i < lim-1 && (c=getchar())!=EOF && c!='\n'; ++i)
s[i] = c;
if (c == '\n') {
s[i] = c;
++i;
}
s[i] = '\0'; /* اضافه کردن کاراکتر null برای پایان رشته */
return i;
}
تابع copy
/* copy: کپی کردن رشتهی from در to */
void copy(char to[], char from[])
{
int i = 0;
while ((to[i] = from[i]) != '\0')
++i;
}
توضیح کامل برنامه
بیایید مرحلهبهمرحله بررسیش کنیم
تعریف ثابت MAXLINE
در ابتدای برنامه با دستور #define MAXLINE 1000 گفتیم که هر خط حداکثر میتونه ۱۰۰۰ کاراکتر داشته باشه.
این عدد فقط یه حد بالاست، برای جلوگیری از پر شدن حافظه.
تابع main
در تابع main چند تا متغیر داریم:
-
len: طول خط فعلی که از تابع getline میگیریم
-
max: طول طولانیترین خط تا این لحظه
-
line: آرایهای برای ذخیرهی خط فعلی
-
longest: آرایهای برای ذخیرهی طولانیترین خط
درون حلقهی while:
-
هر بار یه خط جدید با getline خونده میشه.
-
اگه طول اون از max بیشتر بود،
با copy اون خط رو به longest منتقل میکنیم و max رو آپدیت میکنیم.
در آخر هم، اگه max > 0 بود (یعنی حداقل یه خط خونده شده)،
اون خط طولانی رو چاپ میکنیم.
تابع getline
تابع getline یه خط از ورودی میخونه تا وقتی:
-
یا به انتهای فایل (EOF) برسه
-
یا به کاراکتر \n (پایان خط) برسه
-
یا آرایه پر بشه (تا lim-1 کاراکتر)
در پایان، '\0' (کاراکتر null) رو اضافه میکنه تا رشته بهدرستی تموم بشه.
نکته:
هر رشته (string) در C با '\0' تموم میشه تا تابعهایی مثل printf بدونن کجا باید چاپ رو متوقف کنن.
تابع copy
تابع copy بهسادگی کاراکتر به کاراکتر رشتهی مبدأ (from) رو به مقصد (to) کپی میکنه
تا به '\0' برسه، که نشونهی پایان رشته است.
نوع بازگشتی تابعها
-
getline مقدار int برمیگردونه (طول خط)
-
copy مقدار void داره، یعنی چیزی برنمیگردونه
چون فقط یه عمل انجام میده و نتیجهاش مستقیم دیده نمیشه.
نکات مهم طراحی
حتی یه برنامهی ساده مثل این هم چند چالش داره:
مثلاً اگه یه خط از MAXLINE بلندتر باشه چی؟
در این حالت getline بهصورت ایمن متوقف میشه تا آرایه پر نشه،
ولی ممکنه اون خط نصفه ذخیره بشه.
ما در این نسخه برای سادهتر شدن کد، از این حالت صرفنظر کردیم،
اما در تمرینهای بعدی بهش میپردازیم.
نحوهی کار رشتهها در C
وقتی در C یه رشته مثل "hello\n" مینویسی،
در واقع اینطوری ذخیره میشه
| کاراکتر | h | e | l | l | o | \n | \0 |
|---|---|---|---|---|---|---|---|
| مقدار ASCII | 104 | 101 | 108 | 108 | 111 | 10 | 0 |
اون \0 در آخر خیلی مهمه — چون به C میفهمونه رشته کجا تموم میشه.
تابعهایی مثل printf (با %s) هم دقیقاً دنبال همین \0 میگردن.
تمرینها
تمرین ۱–۱۶
تابع main برنامهی بالا رو طوری بازنویسی کن که
طول خطوط خیلی بلندتر از MAXLINE رو هم درست چاپ کنه
(یعنی حتی اگه خط نصفه خونده شد، طول کلش حساب بشه).
تمرین ۱–۱۷
برنامهای بنویس که فقط خطهایی رو چاپ کنه که طولشون بیشتر از ۸۰ کاراکتره.
تمرین ۱–۱۸
برنامهای بنویس که از هر خط ورودی:
-
فاصلهها و تبهای اضافی انتهای خط رو حذف کنه
-
و خطهایی که کاملاً خالی هستن رو کلاً حذف کنه.
تمرین ۱–۱۹
تابعی به نام reverse(s) بنویس که رشتهی s رو برعکس کنه.
ازش استفاده کن تا برنامهای بسازی که هر خط ورودی رو بهصورت برعکس چاپ کنه.