Programowanie w języku C, część 1.

by gynvael.coldwind//vx

Wstęp

Witam w pierwszej części kursu poświęconego programowaniu w języku C pod systemu Windows oraz Linux. W kursie będę omawiał język C w standardzie C99, a nie Ansi C (C89). Cóż, trzeba iść do przodu =^^=.

Zanim rozpoczniemy naukę programowania, musimy przygotować środowisko do pracy. Użytkownicy systemu Linux są tu na uprzywilowanej pozycji, ponieważ większość dystrubucji dostarcza kompilatory oraz środowisko przygotowane pod programistów. Pod systemem z rodziny Windows musimy niestety ściągnąć kompilator i ustawić środowisko samemu. Cóż, niestety czasy Atari i Commodore, na których naciskało się Power On i można było pisać program, już dawno mineły.

Konfiguracja środowiska (Windows)

Zaczniemy od ściągnięcia kompilatora oraz IDE (Integrated Development Environment, Zintegrowane Środowisko Programistyczne). Zaznaczę odrazu że z IDE będziemy korzystać tylko i wyłącznie do edycji kodu źródłowego. Kompilacja oraz testowanie będzie się odbywać spod konsoli. Powody są dwa: wg. mnie konsola jest dużo wygodniejsza na początek, oraz nie warto tracić czasu na omawianie IDE, wkońcu mamy uczyć się programować, a nie obsługiwać edytor tekstu z tysiącem opcji.
Ściągamy więc IDE+kompilator, w naszym wypadku będzie to Dev-C++, który można pobrać ze strony http://bloodshed.net/dev/devcpp.html. W skład Dev-C++ wchodzi m.in. kompilator MinGW gcc, który jest jednym z portów gcc na systemy z rodziny Windows (drugim jest djgpp).
Po ściągnięciu instalujemy Dev-C++. Na potrzeby tego kursu zakładam że instalujemy do katalogu C:\Dev-Cpp\.
Następną rzeczą jaką musimy zrobić, jest ustawienie ścieżek do kompilatora gcc. Znajduje się on w katalogu C:\Dev-Cpp\bin. Najprostszą metodą jest stworzenie skryptu dev.bat, który umieszczamy w katalogu C:\Windows\Command (w przypadku Win9x), lub w katalogu C:\Windows\System32 (w przypadku Windowsów z rodziny NT/2000/XP), o następującej treści:
@echo off
set path=%path%;C:\Dev-Cpp\bin
Następnie uruchamiamy konsole Menu Start / Uruchom / command (w przypadku Win9x), lub Menu Start / Uruchom / cmd (w przypadku WinXP), i wpisujemy dev (po tym enter), a następnie gcc (i znowu enter). Jeśli środowisko zostało dobrze skonfigurowane, efekt powinien być następujący:
C:\>dev
C:\>gcc
gcc: o input files

C:\>
Jeśli ktoś nie zna standardowych poleceń konsoli Windows, takich jak cd, dir, mkdir, etc, to jest to dobry moment żeby znaleźć na google jakiś kurs i się zapoznać z podstawami.

Witaj świecie (Windows+Linux)

Tradycją stało się że pierwszym programem, jest program wypisujący "Hello World!" na konsole. Cóż, nie mam zamiaru być w tym miejscu oryginalny. Utworzmy katalog ~/code (Linux) lub c:\code (Win), tam będziemy wrzucać nasz kod, po czym utwórzmy, w ulubiony edytorze tekstowym, lub Dev-C++, tam plik hello.c. Rozszerzenie musi być .c, kompilator po tym rozpoznaje z jakim rodzajem pliku ma do czynienia. Utworzony plik będzie zwierał źródło programu. Następnie PRZEPISUJEMY do pliku (w żadnym wypadku nie korzystamy z kopiuj/wklej) następujący kod:
#include <stdio.h>

int main(void)
{
  puts("Hello World!");
  return 0;
}

Proszę zauważyć że na końcu listingu znajduje się dodatkowa pusta linia. Teraz kompilujemy:

(Linux)
# gcc hello.c -o hello -Wall -pedantic -std=c99
# ./hello
Hello World!

#
(Windows)
C:\code\>gcc hello.c -o hello -Wall -pedantic -std=c99
C:\code\>hello
Hello World!

C:\code\>
Coś się skompilowało, coś się uruchomiło, ale o co w tym wszystkim chodzi? Czas na trochę teorii.
Po pierwsze, oczywiste dla wszystkich ludzi którzy już programowali, kod źródłowy programu to coś analogicznego do przepisu kucharskiego (bądź gamebooka jeśli ktoś kojarzy co to =^^=). Czyli jest to zapis (tekstowy), w którym jest napisane co krok po kroku trzeba robić. Za czasów komputerów 8-bitowych, języki programowania miały numerowane linie. Pisało się więc przykładowo:
1 PRINT "ALA MA KOTA"
2 PRINT "KOT MA ALE"
3 GOTO 1
Kod ten można przeczytać jako:
1 Wydrukuj na ekran napis "ALA MA KOTA"
2 Wydrukuj na ekran napis "KOT MA ALE"
3 Idź do kroku pierwszego (czyli powtarzaj dwa powyższe kroki)
Z czasem jednak zrezygnowano z numerowania lini, ponieważ było to sporo dodatkowego pisania, oraz znacznie utrudniało wstawianie kodu między już istniejący. Obecnie napisało by się po prostu:
WYPISYWANIE:
  PRINT "ALA MA KOTA"
  PRINT "KOT MA ALE"
  GOTO WYPISYWANIE
Działanie programu jest identyczne z działaniem wczesniejszego. Znikneły numery linii, ale chyba oczywiste jest dla nas że "przepisy" czyta się od góry do dołu (nooo przynajmniej w większości języków programowania =^^=). Zauważyć można, że w tym przepisie/kodzie pojawiła się również etykieta "WYPISYWANIE:". Etykiety służą po prostu do oznaczenia jakiegoś miejsca w kodzie. Z czasem, gdy etykiet było dużo, wyewoluowały z nich funkcje, czyli zgrupowane kawałki kodu:
GOTO PETLA

WYPISZ_ALA:
  PRINT "ALA MA KOTA"
  RETURN

WYPISZ_KOT
  PRINT "KOT MA ALE"
  RETURN

PETLA:
  FUNC WYPISZ_ALA
  FUNC WYPISZ_KOT
  GOTO PETLA
O ile w powyższym przykładzie funkcje (podprogramy) są jedynie dodatkiem, zupełnie zbędnym, o tyle w języku C funkcje (a przynajmniej jedna funkcja) są konieczne. Mianowicie w powyższych przykładach wykonanie programu zaczynało się zawsze od pierwszej instrukcji na samej górze, natomiast w języku C wykonanie programu zaczyna się od funkcji main (z angielskiego "(funkcja) główna"). Dodatkowo, kod funkcji jest zawarty między klamrami { a }. Przyjżyjmy się naszemu programowi Hello World:
#include <stdio.h>

int main(void)
{
  puts("Hello World!");
  return 0;
}
Zaznaczony fragmane to właśnie funkcja main. Zauważyć można pewne "ozdoby" przy nazwie funkcji. Mianowicie, pierwsza linia funkcji (przed klamrami) określa prototyp funkcji. Cała linia int main(void) to właśnie prototyp. Dzięki prototypowi wiadomo jak się funkcja nazywa (main), co potrzebuje za informacje potrzebuje do działania (tj argumenty, o nich dokładnie później) ((void)) oraz, co zwraca (tj jaką informacje zwrotną wywołującej ją funkcji podaje, w przypadku funkcji main informacja zwrotna trafia do systemu operacyjnego).
Następnie mamy ciało funkcji, na które składa się cały kod zawarty między klamrami. Ciało funkcji to jest ten nasz kucharski przepis, czyli instukcja krok po kroku co ma być wykonane. Przeanalizujmy dokładnie obie linie.
Pierwszą linie, czyli puts("Hello World!");, można przeczytać (taak.. języki programowania można czytać "po ludzku"... zapewne każdy spotkał się ze zdaniem że na matematyce przy tablicy trzeba "opowiadać" co się pisze.. cóż, w programowaniu też tak jest i też wypada to robić) to jako "Wypisz na ekran Hello World!". Rozbijmy tą linie na elementy składowe. Pierwszym elementem linii jest słowo puts, jest to nazwa funkcji, jednej z wielu gotowych funkcji, udostępnianych przez tak zwany libc (bibliotekę języka C). puts to skrót od put string, a dokładniej put string on screen, czyli "wsadź/wypisz łańcuch znaków (napis) na ekran". Dodam że do napisu funkcja puts dodaje znak nowej linii, czyli "enter", czyli wypisanie napisu tą funkcją spowoduje przejście do następnej linii (osoby piszące w Pascalu powinny znać analogiczną funkcję, writeln).
Następnie jest nawias, a w nim napis Hello World! w cudzysłowiu, po czym nawias zamykający. Jeśli nawias występuje po nazwie funkcji, to mówimy o liście parametrów funkcji. Jest to informacja przekazywana do funkcji, dzięki niej funkcja staje się niejako uniwersalna. Gdyby nie było parametrów funkcji, trzeba by zrobić inną funkcje do wypisywania napisu "Hello World!", i inną do wypisywania "Ala ma kota". A tak, dzięki parametrom, możemy powiedzieć funkcji co dokładnie chcemy wypisać.
Pierwszym (i ostatnim w tym wypadku) parametrem jest "Hello World!". Jest to tzw. string ASCIIZ, co na polski można przetłumaczyć jako łańcuch znaków, w formacie ASCII, zakończony zerem binarnym. Wszystkie stałe parametry tekstowe muszą być w cudzysłowiu (nie, nie mogą być w apostrofie).
Po liście parametrów jest średnik ;, jest to terminator linii, czyli znak który mówi kompilatorowi, że po tym znaku zaczyna się nowa linia, a konkretniej, nowa instrukcja.
W linii drugiej mamy keyword (słowo kluczowe, jest to wewnętrzna instrukcja języka, która ma jakieś swoje określone zadanie) return (powrót, albo zwróć). A zaraz po nim parametr 0. Całą linie możemy przeczytać jako return zero czyli zwróć zero. Keyword return nie wymaga nawiasów do listy parametrów, jako że zawsze ma tylko i wyłącznie jeden parametr (lub wogóle nie ma parametrów). Przy napotkaniu tej instrukcji następuje natychmiastowe wyjście z funkcji (w tym wypadku jest to wyjście z programu).
Dodam jeszcze że prototyp + ciało funkcji to tzw. definicja funkcji.

Teoria była, czas na troche praktyki i eksperymentów z kodem.
Zacznijmy od końca. Pisałem wcześniej że return zwraca informacje (zero w naszym wypadku) do system. Jak to sprawdzić? Pod Windowsem 9x sprawa jest odrobine skomplikowana, ponieważ system ten pozwala nam sprawdzić jedynie jaka liczba została zwrócona, nie pozwala natomiast nam jej po prostu wypisać. Można to obejść za pomocą prostego skryptu error.bat (którego umieszczamy w C:\Windows\Command).
@ECHO OFF
IF ERRORLEVEL 10 IF NOT ERRORLEVEL 11 ECHO 10
IF ERRORLEVEL 9 IF NOT ERRORLEVEL 10 ECHO 9
IF ERRORLEVEL 8 IF NOT ERRORLEVEL 9 ECHO 8
IF ERRORLEVEL 7 IF NOT ERRORLEVEL 8 ECHO 7
IF ERRORLEVEL 6 IF NOT ERRORLEVEL 7 ECHO 6
IF ERRORLEVEL 5 IF NOT ERRORLEVEL 6 ECHO 5
IF ERRORLEVEL 4 IF NOT ERRORLEVEL 5 ECHO 4
IF ERRORLEVEL 3 IF NOT ERRORLEVEL 4 ECHO 3
IF ERRORLEVEL 2 IF NOT ERRORLEVEL 3 ECHO 2
IF ERRORLEVEL 1 IF NOT ERRORLEVEL 2 ECHO 1
IF ERRORLEVEL 0 IF NOT ERRORLEVEL 1 ECHO 0
Użytkownicy Linuxa oraz nowszych Windowsów są w lepszej pozycji. Ci pierwsi wystarczy że napiszą echo $?, a ci drudzy echo %ERRORLEVEL%. Przetestujmy więc to:

(Linux)
# gcc hello.c -o hello -Wall -pedantic -std=c99
# ./hello
Hello World!

# echo $?
0
#
(Windows 9x)
C:\code\>gcc hello.c -o hello -Wall -pedantic -std=c99
C:\code\>hello
Hello World!

C:\code\>error
0
C:\code\>
(Windows NT/2000/XP)
C:\code\>gcc hello.c -o hello -Wall -pedantic -std=c99
C:\code\>hello
Hello World!

C:\code\>echo %ERRORLEVEL%
0
C:\code\>
Zmieńmy teraz w kodzie return 0; na return 2;, po czym skompilujmy i przetestujmy.

(Linux)
# gcc hello.c -o hello -Wall -pedantic -std=c99
# ./hello
Hello World!

# echo $?
2
#
(Windows 9x)
C:\code\>gcc hello.c -o hello -Wall -pedantic -std=c99
C:\code\>hello
Hello World!

C:\code\>error
2
C:\code\>
(Windows NT/2000/XP)
C:\code\>gcc hello.c -o hello -Wall -pedantic -std=c99
C:\code\>hello
Hello World!

C:\code\>echo %ERRORLEVEL%
2
C:\code\>
Po co program miał by coś systemowi zwracać? Dobry programista dba o to, by program który wykonał się CAŁKOWICIE POPRAWNIE, zwracał zawsze 0 do systemu, a program który wykonał się błędnie, zwracał wartość niezerową, najlepiej dodatnią. Pozwala to na integracja naszego programu ze skryptami (.bat, Bash).

Teraz trzy słowa na temat samego wyglądu kodu. Proszę zauważyć, że w moim przypadku, dałem "ENTER" przed {, oraz "ENTER" po niej. Również po średniku dałem "ENTER". Język C ma dość elastyczną składnie. Poniżej prezentuje pare metod zapisu kodu Hello World w języku C. Wszystkie z nich są poprawne.
#include <stdio.h>

int main(void) { puts("Hello World!"); return 0; }

#include <stdio.h>

int
main
(
void
)
{
puts
(
"Hello World!"
)
;
return 
0
;
}

#include <stdio.h>

int          main   (     void     )
{
  puts    (   "Hello World!"    )     ; 
                 return 0; 
}

#include <stdio.h>

int 
main(void)
{
  puts("Hello World!"); 
  return 0; 
}

#include <stdio.h>

int main(void) {
  puts("Hello World!"); 
  return 0; 
}

#include <stdio.h>

int main(void) {
  puts("Hello World!"); 
  return 0; 
}

#include <stdio.h>

int main(void) 
{
  puts ( "Hello World!" ); 
  return ( 0 ); 
}

Po mimo faktu że każdy z tych zapisów jest w 100% poprawny, należy pamiętać że nie każdy jest czytelny. Jaki styl używać? Zależy to od własnego gustu, i nie jest narzucone z góry. Dobry programista po wybraniu stylu w jakim napisza dany program, powinien konsekwętnie się tego stylu trzymać.
Można zauważyć że język C jest bardzo elastyczny jeśli chodzi o znaki białe (spacje, taby, entery). Jest jednak pare zasad, które trzeba zachować:
1) Nie wolno robić enterów w stringu (napisie)! Zapis:
  puts("Hello
  World!");
jest błędny i spowoduje wygenerowania błędu error: missing terminating " character (błąd: brakuje zakańczającego znaku ").
Co zrobić natomiast jeśli chcemy żeby na ekranie pojawiło się Hello World! oddzielone "enterem"? Do tego służy specjalna sekwencja znaków (tzw sekwencja ucieczkowa, escape sequence) \n. W miejsce \n w napisie zostanie wstawiony "enter".
  puts("Hello\nWorld!");
A co zrobić jeśli koniecznie chcieli byśmy żeby w kodzie Hello było w innej linii niż World? Wystarczy dodać " na koniec pierwszej linii, i na początek drugiej:
  puts("Hello "
       "World!");
Ten kod jest już poprawny. Mimo iż w kodzie ten napis jest w dwóch linia, to prosze zauważyć że nie ma tam znaku "enter" (\n), więc napis mimo wszystko zostanie wypisany w jednej linii.
Dobry programista stara się tak zapisywać kod, żeby był czytelny. Zazwyczaj ogranicza się długość linii do 70 lub 100 znaków..

2) Linie rozpoczynające się od znaku hash # MUSZĄ być w jednej linii (i nie mają średnika na końcu). Zapis:
#include <stdio.h> int main(void) { puts("Hello World!"); return 0; }
Jest błędny, i wygeneruje ostrzeżenie warning: extra tokens at end of #include directive (ostrzeżenie: dodatkowe elementy na końcu dyrektywy #include) lub, jeśli linie z # dokleimy po linii kodu, błąd error: syntax error at '#' token (błąd: błąd składni przy elemencie '#'). Oba błedy spowodują prawdopodobnie lawinowo dużą ilość kolejnych.
Co to są za linie rozpoczynające się od #? Są to tak zwane dyrektywy preprocesora. Są to instrukcje wykonywane PRZED kompilacją, które zazwyczaj mają charakter operacji tekstowych, takich jak doklejanie plików, czy zamiana pewnych nazw na inne. Dyrektywa #include służy do włączania danego pliku do źródła (w sensie "weź plik, skopiuj jego zawartość, i wklej w miejsce #include"). O dyrektywnie #include oraz innych będzie mowa później.

Pare słów jeszcze o kompilacji. Mianowicie kompilujemy nasz kod za pomocą linii gcc hello.c -o hello -Wall -pedantic -std=c99. Pewnie niektórzy z was pomyślą "po co, skoro można napisać gcc hello.c, i też skompiluje". Ma to jednak pewien cel. Przeanalizujmy parametry:
Pierwszym parametrem jest hello.c. Jak łatwo można się domyślić jest to nazwa pliku źródłowego.
Drugim jest -o hello. Jest to nazwa pliku wynikowego. W systemach Windows dodane zostanie automagicznie rozszerzenie .exe, więc nie trzeba go dawać.
Następnie są dwa parametry, -Wall -pedantic. Pierwszy z nich to skrót od Warning all (wszystkie ostrzeżenia). Powoduje to że kompilator przy kompilacji wypisuje wszystkie niejasne dla niego rzeczy.
Dobry programista stara się aby przy kompilacji programu nie został wypisany ŻADEN warning i error.
Ostatnim parametrem jest -std=c99, jest to informacja dla kompilatora że będziemy używać standardu C 99.

Od źródła do binarki (Windows+Linux)

Ponownie czas na trochę teorii. Jak wspomniałem wczesniej, dyrektywy preprocesora są wykonywane PRZED kompilacją. Pytanie się pojawia, z czego tak właściwie składa się proces tłumaczenia źródła programu do jego postaci binarnej (wykonywalnej). Postaram się odpowiedzieć na to pytanie.

Pierwszym krokiem, jest tzw. preprocesing (eng. przetworzenie wstępne). W tym kroku wykonywane są wszystkie dyrektywy rozpoczynające się znakiem hash #, oraz wykonywane są makra preprocesora. Co dokładnie wykonuje preprocesor?
Stwórzmy dwa pliki pre.c oraz pre.txt. pre.c niech zawiera:
#define IMIE Ala
#define ZWIERZAK Kota
#include "pre.txt"
#undef IMIE
#undef ZWIERZAK
#define IMIE Tomek
#define ZWIERZAK Psa
#include "pre.txt"	

A pre.txt niech zawiera:
IMIE ma ZWIERZAK.
I wymuśmy na gcc aby wykonał tylko i wyłącznie pierwszy krok (tj preprocesing). Służy do tego flaga -E.
>gcc -E pre.c
# 1 "pre.c"
# 1 "<built-in>"
# 1 "<command line>"
# 1 "pre.c"


# 1 "pre.txt" 1
Ala ma Kota.
# 4 "pre.c" 2




# 1 "pre.txt" 1
Tomek ma Psa.
# 9 "pre.c" 2

>
Wszystkie linie zaczynające się od # w tym wypadku, to po prostu komentarze, możemy się ich pozbyć dla zwiększenia czytelności.
Ala ma Kota.
Tomek ma Psa.
Pierwszym zauważalnym faktem, jest to że preprocesor nie wymaga aby kod źródłowy był koniecznie w języku C (w powyższym przykładzie posłużyłem się językiem naturalnym, ale to chyba widać =^^=). Druga sprawa wymaga wyjaśnienia trzech dyrektyw preprocesora:
Dyrektywa #include "nazwa pliku" powoduję włączenie podanego pliku do kodu źródłowego, a dokładniej mówiąc, "wklejenie" treści danego pliku w miejsce tej dyrektywy. Nazwy pliku w cudzysłowiu, oznaczają że dany pliku jest w tym samym katalogu co kod źródłowy. Jeśli nazwa pliku była by podana nawiasach trójkątnych, np. #include <stdio.h>, oznacza to że plik jest w jednym z domyślnych katalogów z nagłówkami (/usr/include,/usr/localinclude, lub C:\Dev-Cpp\include).
Dyrektywa #define NAZWA tresc służy do definiowania makr preprocesora. Najprostsze (użyte powyżej) makro to po prostu podmiana jednego słowa (NAZWA) na inne (tresc). Bardzo często używane jest to do nadawania nazw znaczących jakimś stałym, np. #define PI 3.14. Przyjęło się że nazwy makr oraz nazwy stałych są pisane WIELKIMI LITERAMI..
Dyrektywa #undef NAZWA (undefine) to po prostu "usunięcie" wcześniej zdefiniowanego makra.
Przeanalizujmy przebieg preprocesingu w tym wypadku:
#define IMIE Ala - definiowane jest makro IMIE o treści "Ala"
#define ZWIERZAK Kota - definiowane jest makro ZWIERZAK o treści "Kota"
#include "pre.txt" - włączany jest w to miejsce plik "pre.txt", który ODRAZU jest "wrzucany" w preprocesor
IMIE ma ZWIERZAK. - IMIE to makro, o wartości "Ala", ZWIERZAK to również makro, o wartości "Kota". Oba makra są "egzekwowane", czyli IMIE jest podmieniane na Ala, a ZWIERZAK na Kota. Wyjściowym zdaniem jest więc Ala ma Kota.
#undef IMIE - zapominamy IMIE
#undef ZWIERZAK - zapominamy ZWIERZAK
Etc. Reszta jest analogiczna.
Tematyką preprocesora zajmiemy się dokładniej w przyszłości.

Drugim krokiem jest kompilacja, czyli tłumaczenie kodu języka C na assembler. Aby gcc poprzestało na tym kroku kompilacji, należy użyć flagi -S (gcc domyślnie tworzy plik w składni AT&T, aby wygenerować plik w sładni Intel, należy dodać flagę -masm=intel). Przetestujmy:
<gcc -S hello.c
<
Powstał plik hello.s o treści zbliżonej do:
	.file	"hello.c"
	.def	___main;	.scl	2;	.type	32;	.endef
	.section .rdata,"dr"
LC0:
	.ascii "Hello World!\0"
	.text
.globl _main
	.def	_main;	.scl	2;	.type	32;	.endef
_main:
	pushl	%ebp
	movl	%esp, %ebp
	subl	$8, %esp
	andl	$-16, %esp
	movl	$0, %eax
	addl	$15, %eax
	addl	$15, %eax
	shrl	$4, %eax
	sall	$4, %eax
	movl	%eax, -4(%ebp)
	movl	-4(%ebp), %eax
	call	__alloca
	call	___main
	movl	$LC0, (%esp)
	call	_puts
	movl	$0, %eax
	leave
	ret
	.def	_puts;	.scl	2;	.type	32;	.endef 
Flaga -S to cudowne narzędzie do debuggowania wstawek assemblera, oraz do analizy wygenerowanego kodu / sposobów optymalizacji. Ale o tym przy okazji =^^=.

Następnym krokiem jest skompilowanie kodu w assemblerze do postacji pliku obiektowego. Jest to plik, w którym wszystkie funkcje są już pokompilowane, ale jeszcze nie są połączone z innymi plikami z projektu (jeśli projekt ma wiele plików) oraz z bibliotekami (np. libc). Aby uzyskać plik obiektowy, możemy uruchomić gcc -c hello.c (zostanie utworzony plik hello.o), albo możemy skompilować wcześniej wygenerowanego przez flagę -S kod assemblera, za pomocą kompilatora assemblera GNU Assembler (jest razem z gcc w pakiecie) pisząc as hello.s -o hello.o.

Ostatnim krokiem jest likowanie (konsolidacja) (wykonuje ją linker, w przypadku gcc jest to ld). Linkowanie jest to połączenie wszystkich bibliotek, obiektów etc etc w jedną całość, czyli plik binarny, ostateczny plik wykonywalny. Jednak z uwagi na to że gcc przy linkowaniu (szczególnie pod Windows) dodaje sporą liste libów, to najprościej używać po prostu gcc hello.o do linkowania (powstanie wtedy plik a.exe lub a.out).

Słowo na koniec.

I tak dobiega końca pierwsza część mojego kursu języka C, która jest żywym dowodem na to, że na temat Hello World! można napisać 10 stron =^^=. Wiem że trudno było by przeczytać to od deski do deski, ale nie na tym powinna polegać nauka programowania. Programowanie to przede wszystkim pisanie, więc jeśli chcesz się nauczyć dobrze programować Drogi Czytelniku, to pisz, pisz i jeszcze raz pisz. A powyższy kurs może stanowić jedynie wyjaśnienie dlaczego coś działa, oraz ewentualną wskazówkę gdzie iść dalej.
Wszelkie komentarze są mile widziane. Można je wysyłać na adres e-mail gynvael@vexillium.org, lub zostawiać na moim blogu w księdze gości (http://gynvael.lunarii.org).

Jeśli pierwsza lekcja kursu się sposoba, będzie następna. A w niej: zmienne, stałe, siła printf i scanf, funkcje, tablice, if, while, switch, for, oraz operacje na łańcuchach znaków w języku C, czyli jak stworzyć prostą grę tekstową.

Kilka zadanek na koniec:
1) Przetłumacz program hello.c z języka C na język naturalny dla ludzi
2) Napisz program który wypisze na ekran:
Ala ma kota
Kot ma Ale
Ala ma psa
Pies ma Ale
Za pomocą:
2a) Czterech puts()
2b) Jednego puts() zapisanego w jednej linii
2c) Jednego puts() zapisanego w czterech liniach
3) Znajdź plik stdio.h> na swoim dysku, i znajdź w nim instrukcję puts oraz printf. Jaki typ zwracają obie instrukcje?
4) Napisz program demonstrujący że wszystko co jest po return nie jest wykonywane
5) Sprawdź który styl kodu ci najbardziej odpowiada pisząć zadanie 2a w każdym jaki ci przyjdzie do głowy

Gynvael Coldwind
Team Vexillium