world:
:%-20s: :hello, world :
:%20.10s: : hello, wor:
:%-20.10s: :hello, wor :
:%.10s: :hello, wor:
PREDOSTEREZHENIE: printf ispol'zuet svoj pervyj argument dlya
opredeleniya chisla posleduyushchih argumentov i ih tipov. Esli
kolichestvo argumentov okazhetsya nedostatochnym ili oni budut
imet' nesootvetstvuyushchie tipy, to vozniknet putanica i
rezul'taty budut nevernymi.
13.4. Formatnyj vvod - funkciya scanf
Osushchestvlyayushchaya vvod funkciya scanf yavlyaetsya analogom
printf i pozvolyaet provodit' v obratnom napravlenii mnogie
iz teh zhe samyh preobrazovanij. Funkciya
scanf(control, arg1, arg2, ...)
chitaet simvoly iz standartnogo vvoda, interpretiruet ih v
sootvetstvii s formatom, ukazannom v argumente control, i
pomeshchaet rezul'taty v ostal'nye argumenty. Upravlyayushchaya
stroka opisyvaetsya nizhe; drugie argumenty, kazhdyj iz kotoryh
dolzhen byt' ukazatelem, opredelyayut, kuda sleduet pomestit'
sootvetstvuyushchim obrazom preobrazovannyj vvod.
Upravlyayushchaya stroka obychno soderzhit specifikacii preob-
razovaniya, kotorye ispol'zuyutsya dlya neposredstvennoj interp-
retacii vhodnyh posledovatel'nostej. Upravlyayushchaya stroka
-55-
mozhet soderzhat':
- probely, tabulyacii ili simvoly novoj stroki ("simvoly
pustyh promezhutkov"), kotorye ignoriruyutsya;
- obychnye simvoly (ne %), kotorye predpolagayutsya sovpa-
dayushchimi so sleduyushchimi otlichnymi ot "simvolov pustyh
promezhutkov" simvolami vhodnogo potoka;
- specifikacii preobrazovaniya, sostoyashchie iz simvola %,
neobyazatel'nogo simvola podavleniya prisvaivaniya *,
neobyazatel'nogo chisla, zadayushchego maksimal'nuyu shirinu
polya i simvola preobrazovaniya.
Specifikaciya preobrazovaniya upravlyaet preobrazovaniem
sleduyushchego polya vvoda. Obychno rezul'tat pomeshchaetsya v pere-
mennuyu, kotoraya ukazyvaetsya sootvetstvuyushchim argumentom.
Esli, odnako , s pomoshch'yu simvola * ukazano podavlenie pris-
vaivaniya, to eto pole vvoda prosto propuskaetsya i nikakogo
prisvaivaniya ne proizvoditsya. Pole vvoda opredelyaetsya kak
stroka simvolov, kotorye otlichny ot "simvolov prostyh prome-
zhutkov"; ono prodolzhaetsya libo do sleduyushchego simvola pustogo
promezhutka, libo poka ne budet ischerpana shirina polya, esli
ona ukazana. Otsyuda sleduet, chto pri poiske nuzhnogo ej
vvoda, funkciya scanf budet peresekat' granicy strok, pos-
kol'ku simvol novoj stroki yavlyaetsya odnim iz simvolov pustyh
promezhutkov.
Imeetsya vozmozhnost' zadaniya bolee slozhnogo algoritma
vydeleniya polej vvoda, kotoraya opisana v rukovodstve prog-
rammista ("scanf(3)").
Simvol preobrazovaniya opredelyaet interpretaciyu polya
vvoda; poskol'ku v Si argumenty peredayutsya po znacheniyu,
argumenty scanf dolzhny byt' ukazatelyami. Dopuskayutsya sleduyu-
shchie simvoly preobrazovaniya:
d - Na vvode ozhidaetsya desyatichnoe celoe; sootvetstvuyushchij
argument dolzhen byt' ukazatelem na celoe.
o - Na vvode ozhidaetsya vos'merichnoe celoe (s lidiruyushchim
nulem ili bez nego); sootvetstvuyushchij argument dolzhen
byt' ukazatelem na celoe.
x - Na vvode ozhidaetsya shestnadcatirichnoe celoe (s lidiru-
yushchimi 0h ili bez nih); sootvetstvuyushchij argument dol-
zhen byt' ukazatelem na celoe.
h - Na vvode ozhidaetsya celoe tipa short; sootvetstvuyushchij
argument dolzhen byt' ukazatelem na celoe tipa short.
c - Ozhidaetsya otdel'nyj simvol; sootvetstvuyushchij argument
dolzhen byt' ukazatelem na simvoly; sleduyushchij vvodimyj
-56-
simvol pomeshchaetsya v ukazannoe mesto. Obychnyj propusk
simvolov pustyh promezhutkov v etom sluchae podavlya-
etsya; dlya chteniya sleduyushchego simvola, kotoryj ne yavlya-
etsya simvolom pustogo promezhutka, pol'zujtes' speci-
fikaciej preobrazovaniya %1s.
s - Ozhidaetsya simvol'naya stroka; sootvetstvuyushchij argument
dolzhen byt' ukazatelem simvolov, kotoryj ukazyvaet na
massiv simvolov, dostatochno bol'shoj dlya prinyatiya
stroki i dobavlyaemogo v konce simvola \0.
f - Ozhidaetsya chislo s veshchestvennoj tochkoj; sootvetstvuyu-
shchij argument dolzhen byt' ukazatelem na peremennuyu
tipa float.
e - Simvol preobrazovaniya e yavlyaetsya sinonimom dlya f.
Format vvoda peremennoj tipa float vklyuchaet neobyaza-
tel'nyj znak, stroku cifr, vozmozhno soderzhashchuyu desya-
tichnuyu tochku i neobyazatel'noe pole eksponenty, sosto-
yashchee iz bukvy e, za kotoroj sleduet celoe, vozmozhno
imeyushchee znak.
Pered simvolami preobrazovaniya d, o i x mozhet stoyat'
bukva l, kotoraya oznachaet, chto v spiske argumentov dolzhen
nahodit'sya ukazatel' na peremennuyu tipa long, a ne tipa int.
Analogichno, bukva l mozhet stoyat' pered simvolami preobrazo-
vaniya e ili f, govorya o tom, chto v spiske argumentov dolzhen
nahodit'sya ukazatel' na peremennuyu tipa double, a ne tipa
float.
Naprimer, obrashchenie
int 1;
float h;
char name[50];
scanf("%d %f %s", &i, &h, name);
so strokoj na vvode
25 54.32e-1 thompson
privodit k prisvaivaniyu i znacheniya 25, h - znacheniya 5.432 i
name - stroki "thompson", nadlezhashchim obrazom zakonchennoj
simvolom \0. |ti tri polya vvoda mozhno razdelit' proizvol'-
nym chislom probelov, tabulyacij i simvolov novoj stroki,
skol'ko vy pozhelaete. Obrashchenie
int i;
float h;
char name[50];
scanf("%2d %f %*d %2s", &i, &h, name);
s vvodom
-57-
56789 0123 45a72
prisvoit i znachenie 56, h - 789.0, propustit 0123 i pomestit
v name stroku "45". Pri sleduyushchem obrashchenii k lyuboj proce-
dure vvoda rassmotrenie nachnetsya s bukvy a. V etih dvuh pri-
merah name yavlyaetsya ukazatelem i, sledovatel'no, pered nim
ne nuzhno pomeshchat' znak &.
V kachestve drugogo primera privedem programmu dlya sum-
mirovaniya chisel, vvodimyh s terminala:
#include <stdio.h>
main() /* Primitivnyj kal'kulyator */
{
double sum, v;
sum =0;
while (scanf("%lf", &v) !=EOF)
printf("\t%.2fFI\n", sum += v);
}
Vypolnenie funkcii scanf zakanchivaetsya libo togda, kogda ona
ischerpyvaet svoyu upravlyayushchuyu stroku, libo kogda nekotoryj
element vvoda ne sootvetstvuet ocherednoj specifikacii preob-
razovaniya. V kachestve svoego znacheniya ona vozvrashchaet chislo
pravil'no raspoznannyh elementov vvoda. |to chislo mozhet byt'
ispol'zovano dlya opredeleniya kolichestva najdennyh elementov
vvoda. Pri vyhode na konec fajla vozvrashchaetsya EOF; podcherk-
nem, chto eto znachenie otlichno ot 0, oznachayushchego, chto sleduyu-
shchij vvodimyj simvol ne udovletvoryaet pervoj specifikacii v
upravlyayushchej stroke. Pri sleduyushchem obrashchenii k scanf poisk
vozobnovlyaetsya neposredstvenno za poslednim vvedennym simvo-
lom.
PREDOSTEREZHENIE: argumenty funkcii scanf dolzhny byt'
ukazatelyami. Nesomnenno, naibolee rasprostranennaya oshibka
sostoit v napisanii
scanf("%d", n);
vmesto
scanf("%d", &n);
13.5. Formatnoe preobrazovanie v pamyati
Ot funkcii scanf i printf proishodyat funkcii sscanf i
sprintf, kotorye osushchestvlyayut analogichnye preobrazovaniya, no
operiruyut so strokoj, a ne s fajlom. Obrashcheniya k etim funk-
ciyam imeyut vid:
-58-
sprintf(string, control, arg1, arg2, ...)
sscanf(string, control, arg1, arg2, ...)
Kak i ran'she , funkciya sprintf preobrazuet svoi argumenty
arg1, arg2 i t.d. v sootvetstvii s formatom, ukazannym v
control, no pomeshchaet rezul'taty v string, a ne v standartnyj
vyvod. Konechno, stroka string dolzhna byt' dostatochno velika,
chtoby prinyat' rezul'tat. Naprimer, esli name - eto simvol'-
nyj massiv, a n - celoe, to
sprintf(name, "temp%d", n);
sozdaet v name stroku vida "tempnnn", gde nnn - znachenie n.
Funkciya sscanf vypolnyaet obratnye preobrazovaniya - ona
prosmatrivaet stroku string v sootvetstvii s formatom v
argumente control i pomeshchaet rezul'tiruyushchie znacheniya v argu-
menty arg1, arg2 i t.d. |ti argumenty dolzhny byt' ukazate-
lyami. V rezul'tate obrashcheniya
sscanf(name, "temp%d", &n);
peremennaya n poluchaet znachenie stroki cifr, sleduyushchih za
temp v name.
13.6. Dostup k fajlam
Opisannye v nachale dannogo razdela programmy chitayut iz
standartnogo vvoda i pishut v standartnyj vyvod, kotorye pre-
dostavlyayutsya programme operacionnoj sistemoj.
Dlya programm, kotorye sami dolzhny organizovyvat' svyaz'
s fajlami, v biblioteke vvoda/vyvoda dejstvuyut sleduyushchie
pravila.
Prezhde chem schityvat' iz nekotorogo fajla ili zapisy-
vat' v nego, etot fajl dolzhen byt' otkryt s pomoshch'yu funkcii
open iz standartnoj biblioteki. Funkciya fopen beret vneshnee
imya (podobnoe h.c ili "temp002") i vozvrashchaet vnutrennee
imya, kotoroe dolzhno ispol'zovat'sya pri posleduyushchih chteniyah
iz fajla ili zapisyah v nego.
|to vnutrennee imya, nazyvaemoe "ukazatelem fajla", fak-
ticheski yavlyaetsya ukazatelem struktury, kotoraya soderzhit
informaciyu o fajle, takuyu kak mesto razmeshcheniya bufera, teku-
shchaya poziciya simvola v bufere, proishodit li chtenie iz fajla
ili zapis' v nego i tomu podobnoe. Pol'zovateli ne obyazany
znat' eti detali, potomu chto sredi opredelenij, poluchaemyh
iz fajla stdio.h, soderzhitsya opredelenie etoj struktury.
Edinstvennoe neobhodimoe dlya ukazatelya fajla opisanie
demonstriruetsya primerom: FILE *fp;
-59-
Zdes' govoritsya, chto fp yavlyaetsya ukazatelem na FILE.
Obratite vnimanie, chto file yavlyaetsya imenem tipa, podobnym
int, a ne yarlykom struktury; eto realizovano cherez
"#define".
Obrashchenie k funkcii fopen v programme imeet vid:
fp=fopen(name,mode);
Pervym argumentom funkcii fopen yavlyaetsya imya fajla, kotoroe
zadaetsya v vide simvol'noj sroki "name". Vtoroj argument
mode (rezhim) takzhe yavlyaetsya simvol'noj strokoj, kotoraya uka-
zyvaet, kak etot fajl budet ispol'zovat'sya. Dopustimymi
rezhimami yavlyayutsya: chtenie (r), zapis' (w) i dobavlenie (a),
vozmozhen eshche simvol + sprava (naprimer, r+), kotoryj ozna-
chaet, chto vozmozhno i chtenie, i zapis' v fajl.
Esli vy otkroete fajl, kotoryj eshche ne sushchestvuet, dlya
zapisi ili dobavleniya, to takoj fajl budet sozdan (esli eto
vozmozhno). Otkrytie sushchestvuyushchego fajla na zapis' privodit
k otbrasyvaniyu ego starogo soderzhimogo. Popytka chteniya nesu-
shchestvuyushchego fajla yavlyaetsya oshchibkoj. Oshibki mogut byt' obus-
lovleny i drugimi prichinami (naprimer, popytka chteniya iz
fajla, ne imeya na to razresheniya). Pri nalichii kakoj-libo
oshibki funkciya vozvrashchaet nulevoe znachenie ukazatelya NULL
(kotoroe dlya udobstva takzhe opredelyaetsya v fajle stdio.h).
Drugoj neobhodimoj veshch'yu yavlyaetsya sposob chteniya ili
zapisi, esli fajl uzhe otkryt. Zdes' imeetsya neskol'ko voz-
mozhnostej, iz kotoryh getc i putc yavlyayutsya prostejshimi.
Funkciya getc schityvaet iz fajla sleduyushchij simvol; ej neobho-
dim ukazatel' fajla, chtoby znat', iz kakogo fajla chitat'.
Obrashchenie: c=getc(fp)
pomeshchaet v c sleduyushchij simvol iz fajla, ukazannogo posredst-
vom fp, i EOF, esli dostignut konec fajla.
Funkciya putc: putc(c,fp)
pomeshchaet simvol c v fajl fp i vozvrashchaet c. Podobno funkciyam
getchar i putchar, getc i putc mogut byt' makrosami, a ne
funkciyami.
Pri zapuske programmy avtomaticheski otkryvayutsya tri
fajla, kotorye snabzheny opredelennymi ukazatelyami fajlov.
|timi fajlami yavlyayutsya standartnyj vvod, standartnyj vyvod i
standartnyj vyvod oshibok; sootvetstvuyushchie ukazateli fajlov
nazyvayutsya stdin, stdout i stderr. Obychno vse eti ukazateli
svyazany s terminalom, no stdin i stdout mogut byt' perenap-
ravleny na fajly ili v mezhprocessnyj kanal.
Funkcii getchar i putchar mogut byt' opredeleny v ter-
minah getc, putc, stdin i stdout sleduyushchim obrazom:
#define getchar() getc(stdin)
#define putchar(c) putc(c, stdout)
-60-
Pri rabote s fajlami dlya formatnogo vvoda i vyvoda mozhno
ispol'zovat' funkcii fscanf i fprintf. Oni identichny funk-
ciyam scanf i printf, za isklyucheniem togo, chto pervym argu-
mentom yavlyaetsya ukazatel' fajla, opredelyayushchij tot fajl,
kotoryj budet chitat'sya ili kuda budet vestis' zapis'; uprav-
lyayushchaya stroka budet vtorym argumentom.
Ukazateli fajlov stdin i stdout zaranee opredeleny v
biblioteke vvoda-vyvoda kak standartnyj vvod i standartnyj
vyvod; oni mogut byt' ispol'zovany v lyubom meste, gde mozhno
ispol'zovat' ob容kt tipa FILE *. Oni, odnako, yavlyayutsya kons-
tantami, a ne peremennymi, tak chto ih nel'zya izmenyat'.
Funkciya fclose yavlyaetsya obratnoj po otnosheniyu k fopen;
ona razryvaet svyaz' mezhdu ukazatelem fajla i vneshnim imenem,
ustanovlennuyu funkciej fopen, i vysvobozhdaet ukazatel' fajla
dlya drugogo fajla. V operacionnoj sisteme imeyutsya ograniche-
niya na chislo odnovremenno otkrytyh fajlov, kotorymi mozhet
rasporyazhat'sya programma. Funkciya fclose zakryvaet fajl, a
takzhe vyzyvaet vydachu informacii iz bufera, v kotorom putc
sobiraet vyvod (pri normal'nom zavershenii programmy funkciya
fclose vyzyvaetsya avtomaticheski dlya kazhdogo otkrytogo
fajla).
13.7. Obrabotka oshibok - stderr i exit
Pri pechati diagnosticheskih soobshchenij zhelatel'no, chtoby
oni postupali na terminal, dazhe esli standartnyj vyvod pos-
tupaet v nekotoryj fajl ili v mezhprocessnyj kanal.
CHtoby luchshe obrabatyvat' takuyu situaciyu, k programme
tochno takim zhe obrazom, kak stdin i stdout, avtomaticheski
prisoedinyaetsya vtoroj vyhodnoj fajl, nazyvaemyj stderr. Esli
eto voobshche vozmozhno, vyvod, zapisannyj v fajle stderr, poyav-
lyaetsya na terminale pol'zovatelya, dazhe esli standartnyj
vyvod napravlyaetsya v drugoe mesto (na samom dele imeetsya
vozmozhnost' napravit' takie soobshcheniya v fajl, no etogo ne
proishodit pri prostom perenapravlenii standartnogo vyvoda).
Programma mozhet takzhe ispol'zovat' funkciyu exit iz
standartnoj biblioteki, obrashchenie k kotoroj vyzyvaet zaver-
shenie vypolneniya programmy. Argument funkcii exit dostupen
programme, vyzvavshej programmu pol'zovatelya v kachestve pod-
zadachi, tak chto ona mozhet proverit' uspeshnoe ili neudachnoe
zavershenie dannoj programmy. Po soglasheniyu, velichina 0 v
kachestve vozvrashchaemogo znacheniya svidetel'stvuet o tom, chto
vse v poryadke, a razlichnye nenulevye znacheniya yavlyayutsya priz-
nakami nenormal'nyh situacij.
Funkciya exit vyzyvaet funkciyu fclose dlya kazhdogo otkry-
togo vyhodnogo fajla, s tem chtoby vyvesti vsyu pomeshchennuyu v
bufery vyhodnuyu informaciyu, a zatem vyzyvaet funkciyu _exit.
Funkciya _exit privodit k nemedlennomu zaversheniyu bez ochistki
-61-
kakih-libo buferov; konechno, pri zhelanii k etoj funkcii
mozhno obratit'sya neposredstvenno.
13.8. Vvod i vyvod strok
Standartnaya biblioteka soderzhit funkciyu fgets. V
rezul'tate obrashcheniya
fgets(line, maxline, fp)
sleduyushchaya stroka vvoda (vklyuchaya simvol novoj stroki) schity-
vaetsya iz fajla fp v simvol'nyj massiv line; samoe bol'shee
maxline-1 simvol budet prochitan. Rezul'tiruyushchaya stroka
zakanchivaetsya simvolom \0. Obychno funkciya fgets vozvrashchaet
line; v konce fajla ona vozvrashchaet NULL.
Prednaznachennaya dlya vyvoda funkciya fputs zapisyvaet
stroku (kotoraya ne obyazana soderzhat' simvol novoj stroki) v
fajl:
fputs(line, fp)
Funkcii gets i puts yavlyayutsya uproshchennymi variantami
fgets i fputs, kotorye rabotayut s fajlami standartnogo
vvoda i vyvoda i ne proveryayut dlinu stroki; gets ne zapisy-
vaet simvol novoj stroki v pamyat', a puts dopisyvaet etot
simvol v fajl v konce stroki:
gets(line)
puts(line)
13.9. Funkciya ungetc
Standartnaya biblioteka soderzhit funkciyu, vozvrashchayushchuyu
poslednij schitannyj simvol. V rezul'tate obrashcheniya
ungetc(c,fp)
simvol c vozvrashchaetsya v fajl fp. Pozvolyaetsya vozvrashchat' v
kazhdyj fajl tol'ko odin simvol.
13.10. Raznye standartnye funkcii
Standartnaya biblioteka predostavlyaet mnozhestvo raznoob-
raznyh funkcij, nekotorye iz kotoryh okazyvayutsya osobenno
poleznymi.
13.10.1. Upravlenie pamyat'yu
Funkciya calloc sluzhit dlya zaprosov pamyati. V rezul'-
tate obrashcheniya
-62-
calloc(n, sizeof(object))
vozvrashchaetsya libo ukazatel' prostranstva, dostatochnogo dlya
razmeshcheniya n ob容ktov ukazannogo razmera, libo NULL, esli
zapros ne mozhet byt' udovletvoren. Otvodimaya pamyat' inicia-
liziruetsya nulevymi znacheniyami. Funkciya malloc delaet to zhe
samoe, no pamyat' zadaetsya v bajtah:
malloc(size)
Ukazatel' obladaet nuzhnym dlya rassmatrivaemyh ob容ktov
vyravnivaniem, no emu sleduet pripisyvat' sootvetstvuyushchij
tip, kak v sleduyushchem primere
char *calloc();
int *ip;
ip=(int*) calloc(n,sizeof(int));
Funkciya free(p) osvobozhdaet prostranstvo, na kotoroe
ukazyvaet p, prichem ukazatel' p pervonachal'no dolzhen byt'
poluchen v rezul'tate obrashcheniya k calloc. Zdes' net nikakih
ogranichenij na poryadok osvobozhdeniya prostranstva, no osvo-
bozhdenie chego libo, ne poluchennogo s pomoshch'yu calloc ili mal-
loc, privodit k tyazhelym oshibkam.
13.10.2. Standartnye funkcii yazyka Si
V standartnuyu biblioteku funkcij na yazyke Si vhodit,
pomimo opisannyh, mnozhestvo samyh raznyh funkcij. Podrobnoe
opisanie ih privedeno v rukovodstve programmistu po OS
DEMOS, chast' 4 (bibliotechnye funkcii), i v operativnoj doku-
mentacii (man(3)). Nizhe v skobkah privedeny nazvaniya razde-
lov operativnoj dokumentacii, v kotoryh imeyutsya sootvetstvu-
yushchie opisaniya:
- operacii so strokami (string);
- preobrazovanie dannyh bez sscanf i sprintf" (atoi,
itoa, atof, ftoa);
- matematicheskie funkcii (sin, exp, ...);
- proverka i preobrazovanie simvolov (ctype);
- i mnogoe drugoe.
* 14. VZAIMODEJSTVIE S OPERACIONNOJ SISTEMOJ
-63-
14.1. Podgotovka programm na Si v OS DEMOS
V operacionnoj sisteme DEMOS programmy mogut sostoyat'
iz odnogo ili neskol'kih modulej, napisannyh na yazykah Si,
Fortran-77, Assembler. Dlya translyacii i sborki programm na
yazyke Si sluzhit komanda cc. V prostejshem sluchae translyaciya
osushchestvlyaetsya po komande:
cc fajl1.c fajl2.c ...
gde fajl1.c, fajl2.c, ... - imena fajlov, soderzhashchih prog-
rammy na yazyke Si (imena takih fajlov dolzhny okanchivat'sya na
suffiks .c). Komanda osushchestvlyaet translyaciyu perechislennyh
programm i ih ob容dinenie redaktorom svyazej. Esli translya-
ciya proshla bez oshibok, sozdaetsya ispolnyaemyj fajl a.out,
kotoryj mozhno zapustit' na schet, vvedya komandu:
a.out
(to est' nabrav prosto imya etogo fajla). Translyaciyu chasto
provodyat v dva etapa: snachala transliruyut otdel'nye prog-
rammy, poluchaya ob容ktnye moduli, a zatem ob容dinyayut ih
vmeste (v predydushchem primere eto bylo sdelano avtomaticheski
komandoj cc). Razdel'naya translyaciya vyglyadit primerno tak:
cc fajl1.c fajl2.c ...
cc fajlN.c fajlN1.c ...
cc fajl1.o fajl2.o ... fajlN.o ...
V bolee slozhnom sluchae programma mozhet sostoyat' iz modulej
na raznyh yazykah, rezul'tat translyacii mozhet byt' zapisan v
fajl, otlichnyj ot a.out, mozhno ottranslirovat' programmu dlya
otladki s pomoshch'yu otladchika cdeb, i t.p. Podrobnoe opisanie
vyzova kompilyatora imeetsya v rukovodstve programmista
(cc(1), ld(1)). V obshchem sluchae programmy na Si zapuskayutsya
interpretatorami shell ili cshell komandoj:
imya_fajla argumenty naznachenie_vvoda_vyvoda
gde lyubaya chast', krome imeni fajla, mozhet otsutstvovat'.
Lyubaya programma na Si v OS DEMOS dolzhna soderzhat'
golovnuyu funkciyu s imenem main. Rabota programmy nachinaetsya
s etoj funkcii, prichem informaciya o argumentah komandy pere-
daetsya cherez ee formal'nye parametry.
14.2. Dostup k argumentam komandy
Operacionnaya sistema DEMOS pozvolyaet peredavat' argu-
menty komandy nachinayushchej vypolnyat'sya programme. Kogda funk-
ciya main vyzyvaetsya sistemoj, ona vyzyvaetsya s dvumya argu-
mentami. Pervyj argument (tipa int, uslovno nazyvaemyj argc)
ukazyvaet chislo argumentov v komandnoj stroke, s kotorymi
-64-
proishodit obrashchenie k programme; vtoroj argument (argv)
yavlyaetsya ukazatelem na massiv simvol'nyh strok, soderzhashchih
eti argumenty, po odnomu v stroke.
Samuyu prostuyu illyustraciyu etoj vozmozhnosti i neobhodi-
myh pri etom opisanij daet programma echo, kotoraya prosto
pechataet v odnu stroku argumenty komandnoj stroki, razdelyaya
ih probelami. , Esli dana komanda
echo hello, world
to v rezul'tate poluchim:
hello, world
Po soglasheniyu argv[0] yavlyaetsya imenem, po kotoromu
vyzyvaetsya programma, tak chto argc po men'shej mere raven 1.
V privedennom vyshe primere argc raven 3, a argv[0], argv[1]
i argv[2] ravny sootvetstvenno echo, hello, i world. Pervym
fakticheskim agumentom yavlyaetsya argv[1], a poslednim -
argv[argc-1]. Esli argc raven 1, to za imenem programmy ne
sleduet nikakoj komandnoj stroki argumentov. Vse eto poka-
zano v echo:
main(argc, argv)
int argc;
char *argv[];
{
int i;
for (i = 1; i < argc; i++)
printf("%s%c", argv[i],
(i<argc-1) ? ' ' : '\n');
}
Poskol'ku argv yavlyaetsya ukazatelem na massiv ukazatelej, to
sushchestvuet neskol'ko sposobov napisaniya etoj programmy,
ispol'zuyushchih rabotu s ukazatelem, a ne s indeksaciej mas-
siva. Sleduyushchij primer demonstriruet drugoj variant:
main(argc, argv)
int argc;
char **argv;
{
while (--argc > 0)
printf("%s%c",*++argv,
(argc > 1) ? ' ' : '\n');
}
Krome stroki argumentov, programma poluchaet ot sistemy
nabor peremennyh, opisyvayushchih sredu, v kotoroj ona vypolnya-
etsya. Kazhdaya peremennaya sostoit iz imeni i znacheniya
-65-
(tekstovoj stroki). Naprimer, peremennaya TERM peredaet tip
terminala, s kotorogo programma zapushchena. Dlya zaprosa znache-
niya peremennoj po imeni ispol'zuetsya funkciya getenv:
char *getenv();
par = getenv("imya_peremennoj")
Funkciya vozvrashchaet ukazatel' na stroku - znachenie peremen-
noj, libo NULL, esli imya ne najdeno v opisanii sredy.
* 15. INTERFEJS SISTEMY DEMOS
Vse bez isklyucheniya vozmozhnosti operacionnoj sistemy
DEMOS dostupny iz programm na yazyke Si. Material etoj glavy
otnositsya k interfejsu mezhdu Si-programmami i operacionnoj
sistemoj DEMOS. Material delitsya na sleduyushchie chasti:
vvod/vyvod, sistema fajlov, processy, signaly. Predpolaga-
etsya znanie osnovnyh koncepcij OS DEMOS, a takzhe ponyatij
"fajl", "process", "signal". Podrobnoe opisanie sistemnyh
vyzovov i sootvetstvuyushchih im funkcij iz standartnoj biblio-
teke imeetsya v rukovodstve programmista po OS DEMOS i v ope-
rativnoj dokumentacii (chasti 2 i 3). Naprimer, esli v opi-
sanii govoritsya o funkcii popen(3), to podrobnoe opisanie
sleduet iskat' v rukovodstve programmista, chast' 4, ili v
operativnoj dokumentacii, chast' 3; spravku o funkcii mozhno
poluchit' na terminal, nabrav man 3 popen.
15.1. Vvod/vyvod
V opisanii biblioteki vvoda/vyvoda byl opisan univer-
sal'nyj interfejs, kotoryj odinakov dlya vsego mnogoobraziya
operacionnyh sistem. Na kazhdoj konkretnoj operacionnoj sis-
teme funkcii standartnoj biblioteki dolzhny byt' napisany v
terminah vvoda-vyvoda, dostupnyh na dannoj mashine. V sleduyu-
shchih razdelah opisan nabor funkcij vvoda/vyvoda nizhnego
urovnya, podderzhivaemyh yadrom operacionnoj sistemy DEMOS.
15.1.1. Deskriptory fajlov
V operacionnoj sisteme DEMOS ves' vvod i vyvod osu-
shchestvlyaetsya posredstvom chteniya fajlov ili ih zapisi, potomu
chto vse periferijnye ustrojstva, vklyuchaya terminal pol'zova-
telya, yavlyayutsya fajlami opredelennoj fajlovoj sistemy. |to
oznachaet, chto odin odnorodnyj interfejs upravlyaet vsemi svya-
zyami mezhdu programmoj i periferijnymi ustrojstvami.
V naibolee obshchem sluchae pered chteniem iz fajla ili
zapis'yu v fajl neobhodimo soobshchit' sisteme o namerenii sde-
lat' eto; etot process nazyvaetsya "otkrytiem" fajla. Sistema
vyyasnyaet, imeet li programma pravo postupat' takim obrazom
(sushchestvuet li etot fajl? imeetsya li razreshenie na obrashche-
nie k nemu?), i esli vse v poryadke, vozvrashchaet v programmu
nebol'shoe polozhitel'noe celoe chislo, nazyvaemoe deskriptorom
-66-
fajla. Vsyakij raz, kogda etot fajl ispol'zuetsya dlya vvoda
ili vyvoda, dlya identifikacii fajla upotreblyaetsya deskriptor
fajla, a ne ego imya (zdes' sushchestvuet primernaya analogiya s
ispol'zovaniem read (5,...) i write (6,...) v Fortrane). Vsya
informaciya ob otkrytom fajle soderzhitsya v sisteme; programma
pol'zovatelya obrashchaetsya k fajlu tol'ko cherez deskriptor
fajla.
Dlya udobstva vypolneniya obychnyh operacij vvoda i vyvoda
s pomoshch'yu terminala pol'zovatelya sushchestvuyut special'nye sog-
lasheniya. Kogda interpretator komand (shell) progonyaet prog-
rammu, on otkryvaet tri fajla, nazyvaemye standartnym vvo-
dom, standartnym vyvodom i standartnym vyvodom oshibok, koto-
rye imeyut sootvetstvenno chisla 0, 1 i 2 v kachestve deskrip-
torov etih fajlov. V normal'nom sostoyanii vse oni svyazany s
terminalom, tak chto esli programma chitaet s deskriptorom
fajla 0 i pishet s deskriptorami fajlov 1 i 2, to ona mozhet
osushchestvlyat' vvod i vyvod s pomoshch'yu terminala, ne zabotyas'
ob otkrytii sootvetstvuyushchih fajlov.
Pol'zovatel' programmy mozhet perenapravlyat' vvod i
vyvod na fajly, ispol'zuya v interpretatore komand simvoly <
i >:
prog < infile > outfile
V etom sluchae interpretator komand izmenit opredelenie
deskriptorov fajlov 0 i 1 s terminala na ukazannye fajly.
Obychno deskriptor fajla 2 ostaetsya svyazannym s terminalom,
tak chto soobshcheniya ob oshibkah mogut postupat' tuda. Podobnye
zamechaniya spravedlivy i togda, kogda vvod i vyvod svyazan s
mezhprocessnym kanalom. Sleduet otmetit', chto v etom sluchae
svyaz' programmy s fajlami izmenyaetsya interpretatorom shell
(ili cshell), a ne programmoj. Sama programma, poka ona
ispol'zuet fajl 0 dlya vvoda i fajly 1 i 2 dlya vyvoda, ne
znaet ni otkuda prihodit ee vvod, ni kuda postupaet ee
vydacha.
15.1.2. Nizkourovnevyj vvod/vyvod.
Samyj nizkij uroven' vvoda/vyvoda v sisteme DEMOS ne
predusmatrivaet ni kakoj-libo buferizacii, ni kakogo-libo
drugogo servisa; on po sushchestvu yavlyaetsya neposredstvennym
obrashcheniem k operacionnoj sisteme. Ves' vvod i vyvod osu-
shchestvlyaetsya dvumya funkciyami: read i write. Pervym argumen-
tom obeih funkcij yavlyaetsya deskriptor fajla. Vtorym argumen-
tom yavlyaetsya bufer v vashej programme, otkuda ili kuda dolzhny
postupat' dannye. Tretij argument - eto chislo podlezhashchih
peresylke bajtov. Obrashcheniya k etim funkciyam imeyut vid:
n_read=read(fd,buf,n);
n_written=write(fd,buf,n);
-67-
Pri kazhdom obrashchenii vozvrashchaetsya schetchik bajtov, ukazyvayu-
shchij fakticheskoe chislo peredannyh bajtov. Pri chtenii vozvra-
shchennoe chislo bajtov mozhet okazat'sya men'she, chem zaproshennoe
chislo. Vozvrashchennoe nulevoe chislo bajtov oznachaet konec
fajla, a "-1" ukazyvaet na nalichie kakoj-libo oshibki. Pri
zapisi vozvrashchennoe znachenie ravno chislu fakticheski zapisan-
nyh bajtov; nesovpadenie etogo chisla s chislom bajtov, koto-
roe predpolagalos' zapisat', obychno svidetel'stvuet ob
oshibke.
Kolichestvo bajtov, podlezhashchih chteniyu ili zapisi, mozhet
byt' sovershenno proizvol'nym. Dvumya samymi rasprostranennymi
velichinami yavlyayutsya "1", chto oznachaet peredachu odnogo sim-
vola za obrashchenie (t.e. bez ispol'zovaniya bufera), i "512",
chto sootvetstvuet fizicheskomu razmeru bloka na mnogih peri-
ferijnyh ustrojstvah. |tot poslednij razmer budet naibolee
effektivnym, no dazhe vvod ili vyvod po odnomu simvolu za
obrashchenie ne budet slishkom dorogim.
Primer. Kopirovanie vvoda na vyvod. V sisteme DEMOS
eta programma budet kopirovat' chto ugodno kuda ugodno,
potomu chto vvod i vyvod mogut byt' perenapravleny na lyuboj
fajl ili ustrojstvo.
#define BUFSIZE 512
main() /*copy input to output*/
{
char buf[BUFSIZE];
int n;
while((n=read(0,buf,BUFSIZE))>0)
write(1,buf,n);
}
Esli razmer fajla ne budet kraten BUFSIZE, to pri ocherednom
obrashchenii k read budet vozvrashcheno men'shee chislo bajtov,
kotorye zatem zapisyvayutsya s pomoshch'yu write; pri sleduyushchem
posle etogo obrashchenii k read budet vozvrashchen nul'.
15.1.3. Otkrytie, sozdanie, zakrytie i udalenie
Vo vseh sluchayah, esli tol'ko ne ispol'zuyutsya opredelen-
nye po umolchaniyu standartnye fajly vvoda, vyvoda i oshibok,
vy dolzhny yavno otkryvat' fajly, chtoby zatem chitat' iz nih
ili pisat' v nih. Dlya etoj celi sushchestvuyut dve funkcii: open
i creat.
Funkciya open ves'ma shodna s funkciej fopen, rassmot-
rennoj vyshe, za isklyucheniem togo, chto vmesto vozvrashcheniya
ukazatelya fajla ona vozvrashchaet deskriptor fajla, kotoryj
yavlyaetsya prosto celym tipa int.
int fd;
fd=open(name,rwmode);
-68-
Kak i v sluchae fopen, argument name yavlyaetsya simvol'noj
strokoj, sootvetstvuyushchej vneshnemu imeni fajla. Odnako argu-
ment, opredelyayushchij rezhim dostupa, otlichen: rwmode ravno: 0 -
dlya chteniya, 1 - dlya zapisi, 2 - dlya chteniya i zapisi. Esli
proishodit kakaya-to oshibka, funkciya open vozvrashchaet "-1"; v
protivnom sluchae ona vozvrashchaet neotricatel'nyj deskriptor
fajla.
Popytka otkryt' fajl, kotoryj ne sushchestvuet, yavlyaetsya
oshibkoj. Funkciya creat predostavlyaet vozmozhnost' sozdaniya
novyh fajlov ili perezapisi staryh. V rezul'tate obrashcheniya:
fd=creat(name,pmode);
vozvrashchaet deskriptor fajla, esli okazalos' vozmozhnym soz-
dat' fajl s imenem name, i "-1" v protivnom sluchae. Sozda-
nie fajla, kotoryj uzhe sushchestvuet, ne yavlyaetsya oshibkoj:
creat usechet ego do nulevoj dliny.
Esli fajl ranee ne sushchestvoval, to creat sozdaet ego s
opredelennym rezhimom zashchity, specificiruemym argumentom
pmode. V sisteme fajlov OS DEMOS s fajlom svyazyvayutsya devyat'
bitov zashchity informacii, kotorye upravlyayut razresheniem na
chtenie, zapis' i vypolnenie dlya vladel'ca fajla, dlya gruppy
vladel'cev i dlya vseh ostal'nyh pol'zovatelej. Takim obra-
zom, trehznachnoe vos'merichnoe chislo naibolee udobno dlya
zapisi rezhima zashchity. Naprimer, chislo 0755 svidetel'stvuet o
razreshenii na chtenie, zapis' i vypolnenie dlya vladel'ca i o
razreshenii na chtenie i vypolnenie dlya gruppy i vseh ostal'-
nyh.
Sushchestvuet ogranichenie (obychno 15 - 25) na kolichestvo
fajlov, kotorye programma mozhet imet' otkrytymi odnovre-
menno. V sootvetstvii s etim lyubaya programma, sobirayushchayasya
rabotat' so mnogimi fajlami, dolzhna byt' podgotovlena k pov-
tornomu ispol'zovaniyu deskriptorov fajlov. Procedura close
preryvaet svyaz' mezhdu deskriptorom fajla i otkrytym fajlom i
osvobozhdaet deskriptor fajla dlya ispol'zovaniya s drugim faj-
lom. Zavershenie vypolneniya programmy cherez exit ili v
rezul'tate vozvrata iz golovnoj funkcii privodit k zakrytiyu
vseh otkrytyh fajlov.
Funkciya udaleniya unlink(filename) udalyaet iz sistemy
fajl s imenem filename (Tochnee, udalyaet imya filename, fajl
udalyaetsya, esli na nego ne ostaetsya ssylok pod drugimi ime-
nam).
15.1.4. Proizvol'nyj dostup - lseek
Obychno pri rabote s fajlami vvod i vyvod osushchestvlyaetsya
posledovatel'no: pri kazhdom obrashchenii k funkciyam read i
write chtenie ili zapis' nachinayutsya s pozicii, neposredst-
venno sleduyushchej za predydushchej obrabotannoj. No pri
-69-
neobhodimosti fajl mozhet chitat'sya ili zapisyvat'sya v lyubom
proizvol'nom poryadke. Obrashchenie k sisteme s pomoshch'yu funkcii
lseek pozvolyaet peredvigat'sya po fajlu, ne proizvodya fakti-
cheskogo chteniya ili zapisi. V rezul'tate obrashcheniya
lseek(fd,offset,origin);
tekushchaya poziciya v fajle s deskriptorom fd peredvigaetsya na
poziciyu offset (smeshchenie), kotoraya otschityvaetsya ot mesta,
ukazyvaemogo argumentom origin (nachalo otscheta). Posleduyushchee
chtenie ili zapis' budut teper' nachinat'sya s etoj pozicii.
Argument offset imeet tip long; fd i origin imeyut tip int.
Argument origin mozhet prinimat' znacheniya 0, 1 ili 2, ukazy-
vaya na to, chto velichina offset dolzhna otschityvat'sya soot-
vetstvenno ot nachala fajla, ot tekushchej pozicii ili ot konca
fajla. Naprimer, chtoby dopolnit' fajl, sleduet pered zapis'yu
najti ego konec:
lseek(fd,0l,2);
chtoby vernut'sya k nachalu, mozhno napisat':
lseek(fd,0l,0);
Obratite vnimanie na argument 0l; ego mozhno bylo by zapisat'
i v vide (long) 0.
Funkciya lseek pozvolyaet obrashchat'sya s fajlami primerno
tak zhe, kak s bol'shimi massivami, tol'ko cenoj bolee medlen-
nogo dostupa.
Primer. Funkciya, schityvayushchaya lyuboe kolichestvo bajtov, nachi-
naya s proizvol'nogo mesta v fajle.
/*chitat' n bajtov s pozicii pos v buf */
get(fd,pos,buf,n)
int fd, n;
long pos;
char *buf;
{
lseek(fd,pos,0); /*get to pos */
return(read(fd,buf,n));
}
15.2. Upravlenie processami
V operacionnoj sisteme DEMOS chasto trebuetsya vyzvat' iz
programmy i vypolnit' v vide otdel'nogo processa druguyu
programmu. Sleduyushchij razdel opisyvaet prostejshij sposob
sdelat' eto, a dalee budut rassmotreny bazovye sredstva
upravleniya processami, imeyushchiesya v OS DEMOS.
-70-
15.2.1. Funkciya system
Prostejshij sposob vyzvat' druguyu programmu - ispol'zo-
vat' standartnuyu funkciyu system:
system("komandnaya stroka")
Funkciya imeet odin parametr - stroku, kotoruyu ona analizi-
ruet i vypolnyaet tochno tak zhe, kak vypolnyayutsya komandy, vvo-
dimye interpretatorom shell s terminala. Funkciya vypolnyaet
komandu i vozvrashchaet celoe chislo - kod otveta vypolnennoj
komandy (0, esli vse konchilos' normal'no). V komandnoj
stroke vosprinimayutsya lyubye simvoly upravleniya
vvodom/vyvodom >, <, i t.p.
Sleduet uchest', chto, esli v programme vyvod buferizu-
etsya, to pered vyzovom funkcii system neobhodimo vytolknut'
bufera, naprimer vyzvav funkciyu fflush.
15.2.2. Vyzov programmy na nizkom urovne - execl
Vyzov programmy v OS DEMOS osushchestvlyaetsya s pomoshch'yu
neskol'kih elementarnyh funkcij, odna iz kotoryh - funkciya
execl - osushchestvlyaet vyzov novoj programmy vmesto uzhe vypol-
nyayushchejsya, bez vozvrata v vyzyvayushchuyu programmu. Obrashchenie v
nej imeet vid:
execl(komanda,arg0,arg1,...,argN,NULL);
gde "komanda" - stroka simvolov, tochno imenuyushchaya fajl vyzy-
vaemoj komandy. Naprimer, dlya vyzova komandy pr neobhodimo
ukazat' imya /bin/pr. Ostal'nye argumenty takzhe predstavlyayut
soboj stroki simvolov i prosto peredayutsya komande v kachestve
argumentov, pri etom arg0 obychno predstavlyaet soboj prosto
sokrashchennoe imya komandy, a ostal'nye argumenty - parametry
dannoj komandy.
Vyzov execl v sluchae normal'nogo zapuska novoj prog-
rammy zamenyaet eyu tekushchuyu programmu, upravlenie iz funkcii
execl vozvrashchaetsya tol'ko v sluchae oshibki (naprimer, ne naj-
dena komanda s ukazannym imenem). V biblioteke imeetsya
celyj nabor funkcij, osushchestvlyayushchih to zhe samoe i otlichayu-
shchihsya tol'ko predstavleniem parametrov (execl(2), execv(2),
execvp(2), ...) i tem, chto nekotorye funkcii osushchestvlyayut
poisk komandy v standartnom nabore spravochnikov.
15.2.3. Porozhdenie novogo processa - fork
Dlya togo, chtoby zapustit' parallel'no novuyu programmu,
neobhodimo prezhde vsego umet' zapuskat' parallel'nyj pro-
cess. Dlya etogo v OS DEMOS sluzhit funkciya "fork" (razvet-
vit'sya):
-71-
proc_id = fork()
Programma razdelyaetsya na dve identichnye kopii, kotorye pro-
dolzhayut vypolnyat'sya kak dva nezavisimyh processa. Odna iz
programm - process "syn" - poluchaet ot funkcii fork kod
otveta 0, drugaya - "roditel'" - poluchaet nomer, pod kotorym
zapushchen process "syn". V prostejshem sluchae dlya zapuska
parallel'noj programmy vyzov fork kombiniruetsya s execl sle-
duyushchim obrazom:
if( fork() == 0)
{ /* |to process - syn */
... nastrojka fajlov ...
execl(... );
/*Syuda my popadaem pri oshibke v execl */
perror("Ne mogu zapustit' process");
exit(1);
}
... prodolzhenie osnovnoj programmy ...
Zdes' programma posle vyzova fork analiziruet, v kakom pro-
cesse ("roditel'" ili "syn") ona vypolnyaetsya i, v zavisi-
mosti ot etogo, vedet sebya po raznomu. Esli osnovnaya prog-
ramma dolzhna zhdat' okonchaniya "syna", to ona dolzhna vyzvat'
funkciyu wait:
int status;
...
if( fork() == 0)
{ ... execl(...); exit(1);
}
wait(&status));
Funkciya wait vozvrashchaet identifikator processa - "syna", i
zasylaet v peremennuyu status kod zaversheniya etogo processa.
Kod zaversheniya sostoit iz dvuh chastej - mladshie 8 bitov for-
miruyutsya sistemoj i oboznachayut prichinu okonchaniya processa; v
sluchae normal'nogo okonchaniya po funkcii exit" oni soderzhat
0. Starshie 8 bitov v sluchae, esli programma okonchilas' v
rezul'tate vyzova exit, berutsya iz argumenta vyzova funkcii
exit; obychno peredaetsya 0 pri normal'nom zavershenii i chislo,
otlichnoe ot nulya, v sluchae kakih libo oshibok.
Ni fork, ni execl ne zatragivayut otkrytyh fajlov, posle
fork ranee otkrytye fajly nachinayut ispol'zovat'sya oboimi
processami sovmestno, to est' ispol'zuyut