تا اینجا مثالهامون یا هیچ مقداری برنمیگردوندن (یعنی void) یا یه عدد صحیح (int).
اما اگه یه تابع قرار باشه مقدار دیگهای برگردونه چی؟
خیلی از توابع عددی مثل sqrt, sin, و cos مقدار double برمیگردونن.
توابع تخصصیتر هم ممکنه انواع دیگهای از دادهها رو برگردونن.
برای اینکه بفهمیم چطور باید با این موضوع برخورد کنیم،
بیایید تابع atof(s) رو بنویسیم — این تابع رشتهی s رو به معادل عددی با دقت مضاعف (double-precision floating-point) تبدیل میکنه.
تابع atof در واقع نسخهی گسترشیافتهی atoi هست،
که نمونههایی از اون رو تو فصلهای ۲ (Types, Operators and
Expressions) و ۳ (Control Flow) دیدیم.
این تابع میتونه علامت مثبت یا منفی و نقطهی اعشار رو هم تشخیص بده،
و هم بخش صحیح و هم بخش اعشاری عدد رو در نظر بگیره (چه وجود داشته باشن چه نه).
البته نسخهای که ما مینویسیم یه مبدل ورودی خیلی حرفهای نیست،
چون نسخهی کاملش جا و توضیح بیشتری لازم داره.
کتابخانهی استاندارد C خودش یه atof داره که در <stdlib.h> تعریف شده.
اول از همه، چون atof مقدار int برنمیگردونه،
باید نوع خروجی تابع رو صراحتاً اعلام کنیم.
نام نوع داده قبل از نام تابع میاد:
#include <ctype.h>
/* atof: convert string s to double */
double atof(char s[])
{
double val, power;
int i, sign;
for (i = 0; isspace(s[i]); i++) /* skip white space */
;
sign = (s[i] == '-') ? -1 : 1;
if (s[i] == '+' || s[i] == '-')
i++;
for (val = 0.0; isdigit(s[i]); i++)
val = 10.0 * val + (s[i] - '0');
if (s[i] == '.')
i++;
for (power = 1.0; isdigit(s[i]); i++) {
val = 10.0 * val + (s[i] - '0');
power *= 10;
}
return sign * val / power;
}
دومین نکته مهم اینه که تابع فراخواننده (caller) هم باید بدونه atof مقدار double برمیگردونه، نه int.
برای اینکه این موضوع مشخص باشه، باید atof رو توی تابع فراخواننده هم اعلام (declare) کنیم.
تو مثال زیر که یه ماشینحساب خیلی سادهست (در حد جمعزدن چند عدد)،
ورودیها یکییکی خونده میشن (هر خط یه عدد، ممکنه با علامت مثبت یا منفی)،
و بعد از هر ورودی، مجموع فعلی چاپ میشه:
#include <stdio.h>
#define MAXLINE 100
/* rudimentary calculator */
main()
{
double sum, atof(char []);
char line[MAXLINE];
int getline(char line[], int max);
sum = 0;
while (getline(line, MAXLINE) > 0)
printf("\t%g\n", sum += atof(line));
return 0;
}
عبارت
double sum, atof(char []);
یعنی:
-
sum یه متغیر از نوع double هست
-
atof یه تابعه که یه آرایهی char[] به عنوان ورودی میگیره و یه double برمیگردونه
تابع atof باید بهصورت سازگار اعلام (declare) و تعریف (define) بشه.
اگه نوع دادهها توی خود فایل منبع یکی نباشن، کامپایلر خطا میگیره.
اما اگه atof جداگانه کامپایل شده باشه، ممکنه خطا مشخص نشه.
در اون حالت، atof یه مقدار double برمیگردونه
ولی main اون رو به عنوان int در نظر میگیره — و نتیجهاش میتونه کاملاً بیمعنی باشه
شاید برات عجیب باشه که چرا همچین ناهماهنگیای ممکنه پیش بیاد.
علتش اینه که اگه تابع prototype نداشته باشه،
کامپایلر بهصورت ضمنی از اولین جایی که اسمش توی یه عبارت بیاد اون رو معرفی میکنه.
مثلاً توی این خط:
sum += atof(line);
چون atof قبلاً معرفی نشده، کامپایلر فرض میکنه یه تابعه که یه مقدار int برمیگردونه،
و هیچ اطلاعی از نوع آرگومانها نداره!
حتی اگه بهصورت زیر هم بنویسی:
double atof();
باز هم یعنی «در مورد آرگومانها چیزی نمیدونیم».
این فقط برای سازگاری با نسخههای قدیمی C نگهداشته شده،
اما در کدهای جدید نباید ازش استفاده کرد.
پس اگه تابع آرگومان داره، اونها رو توی اعلانش بنویس.
و اگه آرگومانی نداره، از void استفاده کن.
حالا که atof درست تعریف شده،
میتونیم تابع atoi (تبدیل رشته به عدد صحیح) رو هم با استفاده از اون بنویسیم:
/* atoi: convert string s to integer using atof */
int atoi(char s[])
{
double atof(char s[]);
return (int) atof(s);
}
به ساختار اعلان و عبارت return دقت کن:
عبارت داخل return به نوع بازگشتی تابع تبدیل میشه.
اینجا atof یه double برمیگردونه،
اما چون atoi باید یه int برگردونه،
اون مقدار به int تبدیل میشه.
البته این تبدیل ممکنه باعث از دست رفتن اطلاعات بشه (مثل بخش اعشاری)،
به همین خاطر بعضی کامپایلرها هشدار میدن.
نوشتن (int) قبل از atof(s) یعنی ما عمداً این تبدیل رو انجام میدیم،
و این هشدار رو بیاثر میکنیم.
تمرین ۲-۴:
تابع atof رو طوری گسترش بده که نماد علمی (scientific notation) رو هم پشتیبانی کنه، مثل:
یعنی عدد اعشاری ممکنه با e یا E و یه توان با علامت مثبت یا منفی دنبال بشه.