کار این سیستم کال در اصل نوشتنه و چیزی که manual اون به ما
میگه(man -s2 write $
) اینه که ۳ تا آرگومنت از ما میگیره که این آرگومنت ها
به ترتیب file descriptor (یا همون fd) و پوینتر به بافر و تعداد
بایت هایین که میخوایم برامون بنویسه.
WRITE(2) Linux Programmer's Manual WRITE(2)
NAME
write - write to a file descriptor
SYNOPSIS
#include <unistd.h>
ssize_t write(int fd, const void *buf, size_t count);
DESCRIPTION
write() writes up to count bytes from the buffer pointed buf to the
file referred to by the file descriptor fd.
آرگومنت یکم
اگه نمیدونید file descriptor چیه، یه عدده که راه اتصال ما به یه “فایل” بحساب میاد و از اونجایی که تو یونیکس “همه چیز فایله”(!) اون سر این عدد میتونه هر چیزی باشه! از یه فایل متنی بگیر تا یه سوکت شبکه از نوع TCP یا حتی یه قسمت(قطعه) سخت افزاری از کامپیوتر.
حالا این عدد رو از کجا گیر میاریم؟
زمانی که یه “فایل” رو با استفاده از یه سیستم کال دیگه به اسم open
(که بعدا
باهاش آشنا میشید) باز میکنیم.
این عدد از صفر بزرگتره و ۳تا از این عدد ها هم بصورت پیشفرض(by default) برای هر(همه) برنامه(ها) قابل دسترسی هستن.
اون ۳ عدد کدوم اعدادن؟
اولیش صفره که بهش stdin
هم میگن. بعدیش یکه که بهش stdout
هم میگن
و آخریش هم دو (است!) که بهش stderr
هم گفته میشه. (دقیقا همونایی که
تو shell scripting ازشون استفاده میکنیم.)
عددی که تو این پست ازش استفاده میکنیم یکه و هرچیزی که به اون بفرستیم(یا به عبارت دیگه، تو اون فایل بنویسیم) روی صفحه(ترمینال / shell) نشون داده میشه.
آرگومنت دوم
یه پوینتر به اون چیزی که تو “فایل”مون قراره بنویسیمش.
آرگومنت سوم
یه عدده که مشخص میکنه چند بایت رو میخوایم تو اون “فایل” بنویسیم.
اگر قرار باشه متن “!hello, world” رو روی صفحه(ترمینال / shell) چاپ کنیم، یا
باید از یه تابع(()strlen
) برای بدست آوردن طول متنمون استفاده کنیم، یا
اینکه خودمون تعداد کاراکتر(بایت) هاشو بدونیم و اون عدد رو برای مقدار دهی
این آرگومنت استفاده کنیم. (و برای درک بهتر این موضوع هم قطعا از روش دوم
استفاده میکنیم!)
هر کاراکتر ASCII
چند بایت رو اشغال میکنه؟ یک
متن ما چند کاراکتر داره؟
- ۵ تا برای hello
- ۱ دونه برای ویرگول
- ۱ دونه برای فاصله(space)
- ۵ تا برای world
- ۱ دونه برای علامت تعجب
و..
همین؟ نه! ولی برای اینکه هیچوقت یادتون نره که کار اون کاراکتر آخریه چیه(و اگه همچین “باگ”ی رو دیدید بدونید که مشکل کجاست) عدد ۱۳ رو برای آرگومنت سوم استفاده میکنیم.
اسم فایلمون رو میزاریم write.c
و بعد از include کردن کتابخانه unistd.h
میتونیم سیستم کال مورد نظرمون رو استفاده کنیم: (حتی بدون include هم ممکنه
ولی توصیه نمیشه به هیچ وجه.)
#include <unistd.h>
int main(void){
write(1, "hello, world!", 13);
return 0;
}
برای کامپایل کردن این کد(و اکثر کدهایی که بعدا قراره بنویسیم) از دستور زیر استفاده میکنیم:
$ cc -Wall -pedantic -std=c89 write.c
اجرای اون هم مثل بقیه برنامه هاست و نسبت به مسیر جاری(current working director) میتونید از یکی از این دستورها استفاده کنید:
$ /path/to/a.out
یا
$ ./a.out
خوب؟ درست کار کرد؟
بستگی داره منظورمون از درست چی باشه!
سیستم کال write
کارشو کاملا درست انجام داد و هرچیزی که ما بهش گفتیم
رو اون جوری که ما گفتیم چاپ کرد.(Garbage In, Garbage Out)
مشکل اینجاست که ما بهش نگفتیم که بعد از چاپ کردن متنمون بره به خط بعدی.
برای حل این مشکل نیاز به استفاده از newline داریم و کاراکتری که برای این
منظور استفاده میکنیم n\
(است!) اما همیشه یادتون باشه که n\
یه کاراکتر
حساب میشه و ما(انسان ها) برای اینکه به ماشین(کامپیوتر) بفهمونیم که این کاراکتر
با حرف(کاراکتر) n انگلیسی تفاوت داره، نیاز داریم که اون رو اصطلاحا escape
کنیم.
پس؟
#include <unistd.h>
int main(void){
write(1, "hello, world!\n", 13);
return 0;
}
کامپایل کردید و اجرا هم کردید ولی هیچ تغییری نکرد که نکرد!
آرگومنت سوم چی میگه؟ یا بهتره بگیم ما چی گفتیم به ماشین؟
با اینکه در اصل ۱۴ تا کاراکتر داریم هنوز هم داریم به write میگیم که ۱۳ کاراکتر بنویسه که قطعا شامل newline نمیشه و برای همینه که هنوز هم خروجی این برنامه خیلی زیبا و اونجوری که ما میخواستیم نیست. پس:
#include <unistd.h>
int main(void){
write(1, "hello, world!\n", 14);
return 0;
}
موفق باشید.