gcc
Program gcc
jest tylko interfejsem, sterownikiem. GCC (jako pakiet) składa się z
wielu programów. Każda faza budowania ma przydzieloną osobną aplikację.
cpp
(C PreProcessor), tworzy plik gotowy do kompilacji (-E)cc
(lub cc1
, C Compiler), kompiluje do assemblera (-S)as
, assembluje kod do kodu maszynowego platformy docelowej (-c)ld
– tworzy końcowy program z utworzonych obiektów.Każdemu etapowi tworzenia obrazu procesu w pamięci towarzyszy odpowiednie przekształcanie adresów, począwszy od etykiet i innych symboli, a skończywszy na fizycznych adresach komórek pamięci. Jeśli w programie źródłowym występują adresy, to mają one najczęściej postać symboliczną (etykiety w asemblerze) lub abstrakcyjną (wskaźniki w C). Adresy związane z lokalizacją pojawiają się na etapie translacji i są odpowiednio przekształcane aż do uzyskania adresów fizycznych.
Skompilowany program tworzy 4 osobne obszary pamięci:
Styl programowania to sposób pisania kodu, który będzie czytelny zarówno dla jego twórcy, jak i innych programistów. Dobry styl jest podstawą dobrego programowania. Dobrego stylu programowania można nauczyć się wyłącznie poprzez zdobycie praktyki. Nie ma uniwersalnej reguły gwarantującej pisanie dobrych programów. Istnieje kilka prostych i przydatnych zasad.
#include <stdio.h>
/*
to jest komentarz
kilkuwierszowy
*/
int main(void) {
printf("Hello, world\n"); // komentarz jednowierszowy
return 0;
}
bool
, tylko w
wersji C99 (po włączeniu nagłówka <stdbool.h>),char / unsigned char / signed char
,int / unsigned int
,short / unsigned short
,long / unsigned long
,long long / unsigned long long
(C99),float
,double
,long double
,Rozmiary poszczególnych typów są zależne od implementacji (operator
sizeof()
).
Specyfikacja języka C zawiera również typy dodatkowe (zdefiniowane w
<stddef.h>
) size_t
oraz ptrdiff_t
.
Ich rozmiar jest zdefiniowany w zależnosci od architektury procesora.
size_t
jest typem całkowitym bez znaku używanym do reprezentacji
rozmiaru dowolnego obiektu (włączając tablice) w danej implementacji.
size_t
jest typem zwracanym przez operator sizeof
.
ptrdiff_t
jest typem całkowitym ze znakiem używanym do
reprezentowania różnicy wskaźników (tego samego typu, różnica wskaźników różnych
typów nie jest zdefiniowana).
int: 536230
unsigned int: 536230u
int: 012300
wartość oktalnaint: 0xff, 0xab12
wartość heksadecymalnalong: 536230l
unsigned long: 536230ul
long long: 536230ll
unsigned long long: 536230ull
1e1f, 2.f, .3f
1e1, .3, 0.0, 2.d
.3l, 0.0l, 2.e5l
true, false
'a'
'\t'
tab'\n'
linefeed'\f'
form feed'\r'
carriage return'\"'
double quote'\\'
backslash'\''
single quote'\xff'
kod heksadecymalny znaku'\177'
kod oktalny znaku"to jest tekst\n"
literał tekstowy (tablica znakowa)Zmienne są (zwykle) nazwanymi pojemnikami na pojedyncze wartości typu z jakim zostały zadeklarowane.
Wyróżniamy następujące rodzaje zmiennych:
Przy deklaracji zmiennych można jawnie podawać ich wartości początkowe. To bardzo dobra praktyka. Jeśli w deklaracji zmiennych statycznych lub globalnych nie podano ich wartość początkowej, to taka zmienna zostanie automatycznie zainicjalizowana wartością 0 odpowiedniego typu. W przypadku zmiennych lokalnych programista musi sam zadbać o zainicjalizowanie zmiennej przed jej odczytaniem.
auto | break | case | char |
const | continue | default | do |
double | else | enum | extern |
float | for | goto | if |
int | long | register | return |
short | signed | sizeof | static |
struct | switch | typeset | union |
unsigned | void | volatile | while |
+, −
unarny*, ⁄, %
dwuargumentowe+, −
dwuargumentowe++, −−
inkrementacja i dekrementacja (pre i postfiksowa)<, <=, >, >=, ==, !=
&&, ||
<<, >>
operatory przesunięcia (tylko dla typów całkowitych),~
operator dopełnienia bitowego&, |, ^
(and, or, xor)+=, −=, *=, ⁄=, &=, |=, ^=, %=, <<=, >>=
operatory modyfikacji?:
operator warunkowyDzielenie całkowite daje wynik całkowity, np. 5 ⁄ 2
daje w wyniku 2 (a nie 2.5)
dla operandów całkowitych daje wynik taki, że (a ⁄ b) * b +
(a % b)
jest równe a
E1 ? E2 : E3
wartością wyrażenia jest E2
jeżeli E1
jest true
i E3
jeżeli
E1
jest false
np. c = (a > b) ? a : b
oblicza maximum liczb
a, b
n++, n−−
postfixowy
++n, −−n
prefixowy
n = 5; m = n++;
daje m = 5
n = 5; m = ++n;
daje m = 6
Postać: E1 op= E2
jest równoważne:
E1 = (T)((E1) op (E2))
T
jest typem E1
, op
jest jednym z:
+, −, *, ⁄, %, &, |, ^, <<, >>
Precedence | Operator | Description | Associativity |
---|---|---|---|
1 | ++ --
|
Suffix/postfix increment and decrement | Left-to-right |
()
|
Function call | ||
[]
|
Array subscripting | ||
.
|
Structure and union member access | ||
->
|
Structure and union member access through pointer | ||
(type){list}
|
Compound literal(C99) | ||
2 | ++ --
|
Prefix increment and decrement | Right-to-left |
+ -
|
Unary plus and minus | ||
! ~
|
Logical NOT and bitwise NOT | ||
(type)
|
Type cast | ||
*
|
Indirection (dereference) | ||
&
|
Address-of | ||
sizeof
|
Size-of | ||
3 | * / %
|
Multiplication, division, and remainder | Left-to-right |
4 | + -
|
Addition and subtraction | |
5 | << >>
|
Bitwise left shift and right shift | |
6 | < <=
|
For relational operators < and ≤ respectively | |
> >=
|
For relational operators > and ≥ respectively | ||
7 | == !=
|
For relational = and ≠ respectively | |
8 | &
|
Bitwise AND | |
9 | ^
|
Bitwise XOR (exclusive or) | |
10 | |
|
Bitwise OR (inclusive or) | |
11 | &&
|
Logical AND | |
12 | ||
|
Logical OR | |
13 | ?:
|
Ternary conditional | Right-to-Left |
14 | =
|
Simple assignment | |
+= -=
|
Assignment by sum and difference | ||
*= /= %=
|
Assignment by product, quotient, and remainder | ||
<<= >>=
|
Assignment by bitwise left shift and right shift | ||
&= ^= |=
|
Assignment by bitwise AND, XOR, and OR | ||
15 | ,
|
Comma | Left-to-right |
Uwagi:
?
a
:
) jest traktowane jak ujęte w nawiasy: jego priorytet
względem ?:
jest ignorowany.a = b = c
jest parsowane jak a = (b = c)
a
nie (a = b) = c
ponieważ operator =
jest
prawostronnie łączny, ale a + b - c
jest równoważne (a +
b) - c
a nie a + (b - c)
z powodu lewostronnej
łączności operatorów dodawania i odejmowania.++*p
oznacza (++(*p))
, natomiast unarne operatory
postfiksowe są zawsze lewostronnie łączne: a[1][2]++
oznacza
((a[1])[2])++
. Łączność jest jednak istotna dla operatorów
dostępu do składowych: a.b++
jest parsowane jako
(a.b)++
.Łączność operatorów jest wykorzystywana kiedy w wyrażeniu występują co najmniej dwa operatory o tym samym priorytecie. Istotne jest aby zauważyć, że łączność nie definiuje kolejności, w jakiej wyliczane są operandy pojedynczego operatora. Na przykład w następującym fragmencie programu
int x = 0;
int f1() {
x = 5;
return x;
}
int f2() {
x = 10;
return x;
}
int main(void) {
int p = f1() + f2();
printf("%d ", x);
return 0;
}
nie ma gwarancji, że f1()
zostanie obliczone przed f2()
(mimo, że operator + jest lewostronnie łączny). Wynik wykonania tego programu jest
zależny od kompilatora.
x = 2*6 + 6*9;
Mnożenie wykonuje się przed dodawaniem, ale które jako pierwsze zależy od kompilatora
a[i] = i++; // Nie wiemy czy zostanie zmodyfikowana komórka a[i] czy a[i+1].
...
int i = 7;
printf("%d\n", i * i++); // Wypisuje 56, a wydaje się, że powinno być 49
Pojedyncze wyrażenie nie powinno modyfikować dwa razy tego samego obiektu, ani jednocześnie modyfikować i pobierać wartości.
Punkt sekwencyjny to miejsce, w którym wszystkie efekty uboczne danego wyrażenia zostały zrealizowane. Punkty sekwencyjne są ustawiane:
return
if
, switch
, while
, do-while
for
|| , && , ? :
, przecinekZasady:
Antyprzykłady, czyli jak nie postępować
z = i++ * i++; // 1
i = i++; // 1
a[i] = i++; // 2
Ale
z = i++ && i++;
Jest jednoznaczne, ponieważ operator &&
jest punktem
sekwencyjnym.
Dokonywane dla operandów następujących operatorów dwuargumentowych:
+, −, *, ⁄, %
<, <=, >, >=, ==, !=
&, |, ^
? :
Konwersja typu jest zawsze możliwa dla dowolnych dwóch typów numerycznych. Kompilator automatycznie dokonuje takiej konwersji w razie potrzeby. Czasami taka konwersja może doprowadzić do zmiany wartości zmiennej.
Hierarchia typów
Gdy operandy mają różne typy, niejawna konwersja zależy od ich hierarchii.
Zasady hierarchii:
char
na tę samą pozycję co signed
char
i unsigned char
.char < short < int < long < long long
float < double < long double
Promocje całkowite
W każdym wyrażeniu można użyć wartości niżej w hierarchii niż
int
w miejsce wartości int
lub unsigned
int
. W takich przypadkach kompilator stosuje promocje całkowite: każdy
operand o pozycji niższej niż int
jest automatycznie konwertowany
do typu int
(gdy int
może pomieścić wszystkie wartości
oryginalnego typu). Gdy typ int
jest zbyt mały to wartość jest
konwertowana do unsigned int
.
Promocje całkowite zawsze zachowują wartość operandu.
Standardowe konwersje arytmetyczne
Zwykłe konwersje arytmetyczne to konwersje niejawne, które są automatycznie stosowane do operandów o różnych typach. Celem tych konwersji jest:
Zasady konwersji
T
,
którego pozycja jest nie mniejsza niż pozycja typu drugiego
operandu, to drugi operand jest konwertowany do typu
T
.T
, którego pozycja jest wyższa niż pozycja drugiego
operandu. Drugi operand jest konwertowany do typu T
tylko wtedy gdy typ T
może reprezentować wszystkie
wartości swojego poprzedniego typu. Jeżeli nie, oba operandy są
konwertowane do typu bez znaku, który odpowiada typowi
T
Przykładowo:
int si = -20;
long sl = -20;
unsigned int ui = 10;
printf("si < ui = %d\n", si < ui); // (1)
printf("si + ui = %u\n", si + ui);
printf("sl < ui = %d\n", sl < ui); // (2)
printf("sl + ui = %ld\n", sl + ui);
drukuje (przyjmując, że sizeof(int) == 4
i sizeof(long) ==
8
):
si < ui = 0
si + ui = 4294967286
sl < ui = 1
sl + ui = -10
Aby wyznaczyć wartość wyrażenia warunkowego (1) si < ui
,
wartość si
(-20) jest konwertowana na typ unsigned int
(reguła 2a). Wynikiem jest duża liczba dodatnia (większa niż wartość zmiennej
ui
). W zwiazku z tym warunek będzie fałszywy i program
konsekwentnie wyprowadzi wartość 0. Podobna konwersja ma miejsce w przypadku
sumy si + ui
.
W przypadku wyrażeń (2), stosujemy regułę 2b. Wartość zmiennej
ui
jest konwertowana do typu zmiennej sl
(czyli
long
) jeżeli zakres wartości typu long
zawiera cały
zakres wartości typu unsigned int
(jak w prezentowanym powyżej
przykładzie). Jeżeli nie (np. oba typy są tej samej długości), to oba czynniki
są konwertowane do typu unsigned long
i program wyprowadzi
wartości podobne do przypadku (1).
instrukcja - wyrażenie zakończone średnikiem;
blok - grupa instrukcji ujęta w nawiasy klamrowe {} (składniowo równoważna jednej instrukcji)
if (wyrażenie)
instrukcja-1
else
instrukcja-2
switch (wyrażenie) {
case stała-1: instrukcja-1
case stała-2: instrukcja-2
...
case stała-n: instrukcja-n
default: instrukcja
}
while (wyrażenie) instrukcja;
do {
instrukcja;
} while (wyrażenie);
for (wyrażenie1; wyrażenie2; wyrażenie3)
instrukcja;
Równoważne:
wyrażenie1;
while (wyrażenie2) {
instrukcja;
wyrażenie3;
}
Definicja funkcji
<type> name(<type> arg1, <type> arg2, ...) {
definitions and instructions;
...
}
Deklaracja funkcji
<type> name(<type> arg1, <type> arg2, ...);
Przykłady:
double sqr(double x);
double sqr(double x) {
return x*x;
}
int add(int a, int b);
int add(int a, int b) {
return a + b;
}
void print (char* s, double x);
void print (char* s, double x) {
printf("%s %f\n", s, x);
}
void message(void);
void message(void) {
printf("Function with empty parameter list\n");
}
Funkcja o typie różnym od
Parametry do funkcji są przekazywane przez wartość. Oznacza to, że aktualne wartości parametrów są kopiowane do obszaru roboczego funkcji, a sama funkcja nie ma dostępu do oryginału. Wniosek: Funkcja nie może zmienić oryginalnych wartości parametrów. Inne sposoby przekazywania parametrów są opisane w rozdziale o wskaźnikach.
According to the C Standard, subclause 6.7.6.3, paragraph 14 [ISO/IEC 9899:2011]
An identifier list declares only the identifiers of the parameters of the function. An empty list in a function declarator that is part of a definition of that function specifies that the function has no parameters. The empty list in a function declarator that is not part of a definition of that function specifies that no information about the number or types of the parameters is supplied.
Subclause 6.11.6 states that
The use of function declarators with empty parentheses (not prototype-format parameter type declarators) is an obsolescent feature.
Consequently, functions that accept no arguments should explicitly declare a void parameter in their parameter list. This holds true in both the declaration and definition sections (which should match). Defining a function with a void argument list differs from declaring it with no arguments because, in the latter case, the compiler will not check whether the function is called with parameters at all [TIGCC, void usage]. Consequently, function calling with arbitrary parameters will be accepted without a warning at compile time.
Deklaracja tablicy
int ia[10];
#define DIM 10
char ca[DIM];
Tworzenie tablicy:
Inicjalizacja
int silnia[] = { 1, 1, 2, 6, 24};
char as[] = "Tablica";
Tablice muszą być indeksowane wyrażeniami całkowitymi. Tablica o długości
W C mamy do dyspozycji kilka form zapisu przekazywania tablic do funkcji. Zapisy te są względem siebie równoważne, a więc można ich używać zamiennie. Zapisy te wyglądają następująco:
void sposob_1(int tablica[123]);
void sposob_2(int tablica[]);
void sposob_3(int* tablica);
Z punktu widzenia funkcji znajomość liczby elementów dla tablic
jednowymiarowych jest zbędna. Wspomniany zapis zadziała więc tak samo jak zapis
int tablica[]
, który informuje kompilator o przekazywaniu tablicy
do funkcji. Ostatni zapis natomiast mówi, że jest to wskaźnik. Kompilator
wszystkie omówione zapisy zinterpretuje jako wskaźnik.
char *itoa(int n) {
static char retbuf[25]; // użycie 'static' jest ważne!!!
sprintf(retbuf, "%d", n);
return retbuf;
}
...............
printf("i = %s, j = %s\n", itoa(i), itoa(j));
Niezależnie od tego, które z wywołań funkcji itoa()
zostanie
wykonane wcześniej (standard tego nie specyfikuje), printf
wydrukuje dwie takie same wartości (albo i
albo j
). W
tablicy retbuf
znajdzie sie wartość z drugiego wywołania.
Jeżeli funkcja nie może użyć lokalnej tablicy statycznej (bo np. przewidujemy możliwość wielokrotnych wywołań, j.w.), kolejną możliwością jest użycie tablicy uprzednio zaalokowanej przez funkcję wołającą.Wtedy nasza funkcja przyjmuje kolejny argument: wskaźnik do obszaru, gdzie należy umieścić wartości zwracanej tablicy.
char *itoa(int n, char buf[]) {
sprintf(buf, "%d", n);
return buf;
}
// wywołanie ...
int i = 23;
char buf[25];
char *str = itoa(i, buf);
Wadą powyższego rozwiązania jest fakt, że funkcja atoi()
nie
posiada informacji o aktualnym rozmiarze przekazanej jej tablicy. W związku z
tym nie może ona sprawdzić, czy zakres tej tablicy nie zostanie
przekroczony.
Trzecia możliwością jest dynamiczna alokacja pamięci na zwracaną tablicę przez nasza funkcję.
char *itoa(int n) {
char *retbuf = malloc(25);
if(retbuf == NULL) return NULL;
sprintf(retbuf, "%d", n);
return retbuf;
}
Metoda ta również posiada wady:
malloc()
zawiedzie. Funkcja wołająca powinna zatem
sprawdzać (zawsze przed użyciem zwróconego wskaźnika), czy nie jest on
zerowy. Tablica 2D w języku C jest tablicą 1D, której elementami są tablice 1D
(wiersze). Na przyklad tablica
a[0] | ---> | a[0][0] | a[0][1] | a[0][2] |
a[1] | ---> | a[1][0] | a[1][1] | a[1][2] |
a[2] | ---> | a[2][0] | a[2][1] | a[2][2] |
a[3] | ---> | a[3][0] | a[3][1] | a[3][2] |
Elementy tablicy są przechowywane w pamięci wierszami, tak więc adres
elementu
address(a[i][j]) = address(a[0][0]) + (i * n + j) * size(T)
K&R stworzyli zunifikowane pojęcie tablicy i wskaźnika. Ich rozwiązanie można przedstawić w postaci pięciu reguł:
ptr # n = ptr + n * size(type-pointed-into)
"#" oznacza dodawanie "wskaźnikowe" (wskaźnik + int), by odróżnić go od zwykłego dodawania.
tab[i] == *(tab # i)
Powyższe reguły prowadzą do równania tablic. Dla tablicy 2D elementów typu
T a[m][n];
a[i] = a # i = a + i * sizeof(row) // no '*' since it is an address (rule 5.)
a[i][j] = *(a[i] # j)
a[i][j] = *((a + i * sizeof(row)) # j)
a[i][j] = *(a + i * sizeof(row) + j * sizeof(T))
a[i][j] = *(a + (i * n + j) * size(T)) // since sizeof(row) = n * sizeof(T)
address(a[i][j]) = a + (i * n + j) * size(T)
Uzyskaliśmy więc równanie jak wyżej.
"Wskaźnik do wskaźnika typu
Deklaracja "wskaźnika-do-wskaźnika" wygląda następująco:
int **ipp;
gdzie '**' oznacza dwa "poziomy" wskaźników.
Proste przykłady:
int i = 5, j = 6; k = 7;
int *ip1 = &i, *ip2 = &j;
Przypisanie
ipp = &ip1;
powoduje, że ipp
wskazuje na ip1
, który wskazuje na
i
. (*ipp
jest równy ip1
a
**ipp
jest równy i
, czyli 5).
Jeżeli napiszemy:
*ipp = ip2;
to zmienimy wskaźnik wskazywany przez ipp
(ip1
) na
ip2
, tak, że ip1
wskazuje teraz na j
.
Przypisanie:
*ipp = &k;
zmieni wskaźnik wskazywany przez ipp
(ip1
)
ponownie, tym razem na k
.
Zastosowanie: Przekazywanie wskaźników do funkcji "przez wskaźnik", gdy np. chcemy, aby funkcja mogła zmienić wartość wskaźnika
Stos to obszar pamięci w przestrzeni adresowej programu wykorzystywany do specyficznych celów. Stos obsługiwany jest w sposób automatyczny, obsługa ta nie wymaga ingerencji programisty. Na stosie dostępny jest wyłącznie element położony na jego wierzchołku, a elementy zdejmowane są ze stosu w odwrotnej kolejności niż były na nim umieszczane (rejestr LIFO ).
Na stosie przechowywane są:
Sterta to obszar pamięci udostępniany przez system operacyjny wszystkim działającym programom (procesom). Na stercie przechowywane są dynamicznie przydzielane obszary pamięci
Wszystkie zmienne globalne oraz zmienne statyczne. Dodatkowo, pamięć w której przechowywany jest binarny kod wykonywalny programu również jest statycznie zaalokowana. Pamięć statyczna jest alokowana w momencie uruchomienia procesu (zawsze w tym samym miejscu).
Składnia:
int (*foo)(int);
foo jest wskaźnikiem do funkcji posiadającej jeden argument typu int i zwracającej wartość typu int.
Deklarując wskaźniki funkcyjne postepujemy jak przy deklaracji funkcji, zamieniając nazwę funkcji (np. foo) na (*foo) (pamiętając o nawiasach).
Inicjalizacja wskaźników funkcyjnych:
Aby zainicjalizować wskaźnik funkcyjny musimy podać adres funkcji o parametrach zgodnych z deklaracją
#include <stdio.h>
void my_int_func(int x) {
printf( "%d\n", x );
}
int main(void) {
void (*foo)(int);
foo = &my_int_func; // the ampersand is optional
foo(2); // call my_int_func; you do not need to write (*foo)(2)
(*foo)(2); // but if you want to, you may
return 0;
}
powyższego przykładu wynika, że składnia wskaźników funkcyjnych jest elastyczna: można stosować konwencję 'wskaźnikową', z '*' i '&', albo ominąć tę część.
Reguła "right-left" służu do analizy deklaracji C; może także być pomocna w ich tworzeniu.
Symbole tłumacz następująco:
Algorytm postępowania:
Powtarzaj kroki 2 i 3 do wyczerpania symboli.
Przykład 1:
Przykład 2:
Niektóre deklaracje wyglądają na bardziej skomplikowane, ponieważ zawierają
rozmiary tablic i listy argumentów funkcji. W tym przypadku
"
Przykład 3:
Przykład 4:
f jest wskaźnikiem do funkcji o parametrach
int printf (const char* format, ...);
Funkcja formatuje tekst zgodnie z podanym formatem opisanym poniżej i wypisuje tekst na standardowe wyjście (tj. do stdout). Jeżeli format zawiera specyfikacje formatu, dodatkowe argumenty są formatowane i wstawiane do stringu, zastępując odpowiadające im specyfikacje.
Specyfikator formatu ma następującą postać:
%[flags][width][.precision][length]specifier
specifier jest obowiązkowy i definiuje typ i interpretację odpowiadającego mu argumentu.
specifier | Output | Example |
---|---|---|
d or i | Signed decimal integer | 392 |
u | Unsigned decimal integer | 7235 |
o | Unsigned octal | 610 |
x | Unsigned hexadecimal integer | 7fa |
X | Unsigned hexadecimal integer (uppercase) | 7FA |
f | Decimal floating point, lowercase | 392.65 |
F | Decimal floating point, uppercase | 392.65 |
e | Scientific notation (mantissa/exponent), lowercase | 3.9265e+2 |
E | Scientific notation (mantissa/exponent), uppercase | 3.9265E+2 |
g | Use the shortest representation: %e or %f | 392.65 |
G | Use the shortest representation: %E or %F | 392.65 |
a | Hexadecimal floating point, lowercase | -0xc.90fep-2 |
A | Hexadecimal floating point, uppercase | -0XC.90FEP-2 |
c | Character | a |
s | String of characters | sample |
p | Pointer address | b8000000 |
n | Nothing printed. The corresponding argument must be a pointer to a signed int. The number of characters written so far is stored in the pointed location. |
|
% | A % followed by another % character will write a single % to the stream. | % |
flags | description |
---|---|
- | Left-justify within the given field width; Right justification is the default (see width sub-specifier). |
+ | Forces to preceed the result with a plus or minus sign (+ or -) even for positive numbers. By default, only negative numbers are preceded with a - sign. |
(space) | If no sign is going to be written, a blank space is inserted before the value. |
# | Used with o, x or X specifiers the value is preceeded with 0,
0x or 0X respectively for values different than zero. Used with a, A, e, E, f, F, g or G it forces the written output to contain a decimal point even if no more digits follow. By default, if no digits follow, no decimal point is written. |
0 | Left-pads the number with zeroes (0) instead of spaces when padding is specified (see width sub-specifier). |
width | description |
---|---|
(number) | Minimum number of characters to be printed. If the value to be printed is shorter than this number, the result is padded with blank spaces. The value is not truncated even if the result is larger. |
* | The width is not specified in the format string, but as an additional integer value argument preceding the argument that has to be formatted. |
.precision | description |
---|---|
.number | For integer specifiers (d, i, o, u, x, X): precision
specifies the minimum number of digits to be written. If the value to
be written is shorter than this number, the result is padded with
leading zeros. The value is not truncated even if the result is longer. A
precision of 0 means that no character is written for the value 0. For a, A, e, E, f and F specifiers: this is the number of digits to be printed after the decimal point (by default, this is 6). For g and G specifiers: This is the maximum number of significant digits to be printed. For s: this is the maximum number of characters to be printed. By default all characters are printed until the ending null character is encountered. If the period is specified without an explicit value for precision, 0 is assumed. |
.* | The precision is not specified in the format string, but as an additional integer value argument preceding the argument that has to be formatted. |
Specyfikator length modyfikuje długość danego typu. Tabela przedstawia typy argumentów oczekiwane przez funkcję. W przypadku niezgodności typów, dokonywana jest konwersja (w dopuszczalnych sytuacjach).
specifiers | |||||||
---|---|---|---|---|---|---|---|
length | d i | u o x X | f F e E g G a A | c | s | p | n |
(none) | int | unsigned int | double | int | char* | void* | int* |
hh | signed char | unsigned char | signed char* | ||||
h | short int | unsigned short int | short int* | ||||
l | long int | unsigned long int | wint_t | wchar_t* | long int* | ||
ll | long long int | unsigned long long int | long long int* | ||||
j | intmax_t | intmax_t | intmax_t* | ||||
z | size_t | size_t | size_t* | ||||
t | ptrdiff_t | ptrdiff_t | ptrdiff_t* | ||||
L | long double |
Uwaga: Żółte wiersze oznaczają specyfikatory wprowadzone w standardzie C99.
Funkcja zwraca liczbę wyprowadzonych znaków.
int scanf ( const char * format, ... );
Funkcja odczytuje dane ze standardowego wejścia (stdin) zgodnie z podanym formatem opisanym niżej. Dodatkowe argumenty powinny wskazywać na istniejące obiekty o typie określonym przez odpowiedni specyfikator formatu zawarty w parametrze format.
Format składa się ze zwykłych znaków (innych niż znak '%') oraz sekwencji sterujących, zaczynających się od symbolu procenta, po którym następuje:
Wystąpienie w formacie białego znaku powoduje, że funkcje z rodziny scanf będą odczytywać i odrzucać znaki, aż do napotkania pierwszego znaku nie będącego białym znakiem.
Wszystkie inne znaki (tj. nie białe znaki oraz nie sekwencje sterujące) muszą dokładnie pasować do danych wejściowych.
Wszystkie białe znaki z wejścia są ignorowane, chyba że sekwencja sterująca określa format [], c lub n.
Jeżeli w sekwencji sterującej występuje gwiazdka to dane z wejścia zostaną pobrane zgodnie z formatem, ale wynik konwersji nie zostanie nigdzie zapisany. W ten sposób można pomijać część danych.
Maksymalna szerokość pola przyjmuje postać dodatniej liczby całkowitej zaczynającej się od cyfry różnej od zera. Określa ona ile maksymalnie znaków dany format może odczytać. Jest to szczególnie przydatne przy odczytywaniu ciągu znaków, gdyż dzięki temu można podać wielkość tablicy (minus jeden) i tym samym uniknąć błędów przepełnienia bufora.
specifier | Description | Characters extracted |
---|---|---|
i | Integer | Any number of digits, optionally preceded by a sign (+ or -). Decimal digits assumed by default (0-9), but a 0 prefix introduces octal digits (0-7), and 0x hexadecimal digits (0-f). Signed argument. |
d or u | Decimal integer | Any number of decimal digits (0-9), optionally preceded by a sign (+ or -). d is for a signed argument, and u for an unsigned. |
o | Octal integer | Any number of octal digits (0-7), optionally preceded by a sign (+ or -). Unsigned argument. |
x | Hexadecimal integer | Any number of hexadecimal digits (0-9, a-f, A-F), optionally
preceded by 0x or 0X, and all optionally preceded by a sign (+ or -). Unsigned argument. |
f, e, g | Floating point number | A series of decimal digits, optionally containing a decimal point,
optionally preceeded by a sign (+ or -) and optionally followed by the
e or E character and a decimal integer (or some of the other sequences
supported by strtod). Implementations complying with C99 also support hexadecimal floating-point format when preceded by 0x or 0X .
|
a | ||
c | Character | The next character. If a width other than 1 is specified, the function reads exactly width characters and stores them in the successive locations of the array passed as argument. No null character is appended at the end. |
s | String of characters | Any number of non-whitespace characters, stopping at the first whitespace character found. A terminating null character is automatically added at the end of the stored sequence. |
p | Pointer address | A sequence of characters representing a pointer. The particular format used depends on the system and library implementation, but it is the same as the one used to format %p in fprintf. |
[characters] | Scanset | Any number of the characters specified between the brackets. A dash (-) that is not the first character may produce non-portable behavior in some library implementations. |
[^characters] | Negated scanset | Any number of characters none of them specified as characters between the brackets. |
n | Count | No input is consumed. The number of characters read so far from stdin is stored in the pointed location. |
% | % | A % followed by another % matches a single %. |
Z wyjątkiem n, przynajmniej jeden znak powinien zostać pobrany przez dany specyfikator, w przeciwnym przypadku dopasowanie zawodzi i skanowanie wejścia się kończy.
Specyfikator może także zawierać podspecyfikatory *, width and length (w tej kolejności), które są opcjonalne.
Specyfikator '%[' pozwala na podanie zbioru znaków, które powinny znaleźć się na wejściu. Konwersja (pobieranie znaków) kończy się po natrafieniu na pierwszy znak różny od opisanego.
Na przykład %[0-9] oznacza "pobierz wszystkie cyfry od 0 do 9", a %[AD-G34] "pobierz A, D do G, 3, lub 4".
Można też wypisać znaki, które NIE powinny znaleźć się na wejściu, poprzez użycie znaku '^' bezpośrednio po '%['. Na przykład %[^A-K] oznacz wszystkie znaki poza literami z zakresu A-K.
Aby umieścić na liście nawias zamykający (]), wpisujemy go jako pierwszy, np. %[]A-C] lub %[^]A-C]. Z kolei myślnik powinien być ostatni, np. %[A-C-]. Czyli jeżeli chcemy by na wejściu mogły się pojawiać wszystkie litery z wyjątkiem "%", "^", "]", "B", "C", "D", "E", i "-" można użyć następującego formatu: %[^]%^B-E-].
sub-specifier | description |
---|---|
* | An optional starting asterisk indicates that the data is to be read from the stream but ignored (i.e. it is not stored in the location pointed by an argument). |
width | Specifies the maximum number of characters to be read in the current reading operation (optional). |
length | One of hh, h, l, ll, j, z, t,
L (optional). This alters the expected type of the storage pointed by the corresponding argument (see below). |
Tabela przedstawia typy argumentów oczekiwane przez funkcję
specifiers | |||||||
---|---|---|---|---|---|---|---|
length | d i | u o x | f e g a | c s [] [^] | p | n | |
(none) | int* | unsigned int* | float* | char* | void** | int* | |
hh | signed char* | unsigned char* | signed char* | ||||
h | short int* | unsigned short int* | short int* | ||||
l | long int* | unsigned long int* | double* | wchar_t* | long int* | ||
ll | long long int* | unsigned long long int* | long long int* | ||||
j | intmax_t* | uintmax_t* | intmax_t* | ||||
z | size_t* | size_t* | size_t* | ||||
t | ptrdiff_t* | ptrdiff_t* | ptrdiff_t* | ||||
L | long double* |
Uwaga: Żółte wiersze oznaczają specyfikatory wprowadzone w standardzie C99.
W zależności od stringu formatującego, funkcja oczekuje sekwencji dodatkowych argumentów, każdy będący wskaźnikiem do uprzednio zaalokowanej pamięci, gdzie wartości odpowiadające polom wejściowym zostaną zapamiętane.
Funkcja powinna mieć co najmniej tyle dodatkowych argumentów ile wynika i liczby specyfikatorów formatu. Dodatkowe argumenty są ignorowane.
W przypadku sukcesu, funkcja zwraca liczbę poprawnie wczytanych pozycji. Mniejsza wartość zwykle oznacza błąd dopasowania, błąd odczytu lub koniec pliku wejściowego.