Diskuze: Type, ve funkci va_start
V předchozím kvízu, Online test znalostí C++, jsme si ověřili nabyté zkušenosti z kurzu.

Člen

Zobrazeno 11 zpráv z 11.
//= Settings::TRACKING_CODE_B ?> //= Settings::TRACKING_CODE ?>
V předchozím kvízu, Online test znalostí C++, jsme si ověřili nabyté zkušenosti z kurzu.
Je třeba uvést, že kód má souvislost s vlastní implementací funkce printf(). Jinak kód nikomu nic neřekne. Jinak va_start není funkce ale makro. Ukazatel na char (char *) nemusí nutně znamenat ukazatel na znak. V drtivé většině případů a je to i tento případ, se jedná o ukazatel na řetězec. Prvním argumentem vlastní funkce printf() je konstantní ukazatel na řetězec. Je to zároveň i poslední známý fixní parametr. Proto pro párování s va_list je použit ukazatel.
Proto pro parovani je s va_list je pouzit ukazatel.
Pochopil bych ukazatel pouzit kvuli adresy zacatku pole (string), ale ne jako hodnotu argumentu fukce (jakou hodnotu ma pointer? - pokud neni dereferencovan - coz ve vyse zmineme prikladu neni - ma hodnotu adresy), takze jak muze byt 'TYPE' pouzit jako adresa?
dokumentace:
The argument last is the name of the last argument before the variable
ar‐
gument list, that is, the last argument of which the calling function
knows
the type.
Type adresy (jak je to ve funkci pouzito)? To se mi nejak nezda
V C se pole nebo řetězec nepředává jako celek, ale předává se ukazatel na začátek pole. Pokud chci funkci předat pole, musí být argument deklarován jako ukazatel.
char p[] = "Test", *pp = p;
char *p2 = "Test2";
p, pp, p2 - vše jsou ukazatele na začátek pole (na první znak řetězce).
void vypis(char *pole);
// Volání funkce
vypis(p);
vypis(pp);
toto ale opet neni odpoved na muj dotaz, to ze se pole predava snad vim, jak sem zacal delat v c. Ale ja se ptam, jak to, ze ve funkci va_start(va_list ap, type), je jako hodnota type pouzit pointer bez dereference? Nehlede na to ze ukazuje na zacatek pole. Ale v te funkci se bere jako TYPE POSLEDNI ZNAMY TYP - ted sem to psal, je to z dokumentace. JAKY je posledni znamy typ Pointeru (NE HODNOTA - ALE TYP)? Co je typ pointeru? Jeho adresa nebo dereferencovana hodnota? Tak prosim odpovez na tuto otazku, a ne co znamena pointer ve stringu
Trochu pokory, pokud požaduješ odpověď. Mnoho programátorů v C netuší, že když se předává pole předává se ukazatel na pole a ne celé pole. To co řešíš je základní syntax ukazatelů.
char *p;
V definici ukazatelova proměnná je p, nikoli *p. Hvězdička v definici značí pouze odlišnost od klasické proměnné. Pokud je ukazatel použít ve funkci, hvězdička se nepoužívá. Pokud chci získat hodnotu na kterou ukazatel ukazuje, dereferencuje se ukazatel a před ukazatel se dává hvězdička.
Jelikož typ prvního parametrů ve funkci s proměnným počtem parametrů je ukazatel na char, musí být druhy argument macra va_start také ukazatel. Jelikož se nejedná o definici, tak v něm žádná hvězdička není.
A jeste jedna vec. Pokud bych zmenil tento argument:
va_start(ap, fmt)
na
va_start(ap *fmt)
-- Bude to fungovat stejne.... Takze to je na co se ptam, ve druhem pripade je pointer dereferencovany (takze ted je tam konecne hodnota, kterou funkce vyzaduje - aby mela znamy typ a vedel kdy skoncit), ale v prvnim pripade pointer dereferncovany neni.... JAK JE TO MOZNE? Ze funguji oba? A obzvlast kdyz pointer NENI dereferencovany, takze 'hodnota' kterou funkce va_start dostane je hexadecimalni cislo reprezentujici adresu pointeru (protoze neni dereferencovany).
otazka teda je, proc je potreba 'odlisovat' promenne mezi sebou - a tedy zavadet v definici '*' - hvezdicku, ale nikoli jako 'dereferenci', pouze na odliseni v definici. Celkem je to matouci a nechapu smysl nebo duvod proc je tato potreba. Kdyz mam v definici funkci pouzite argumenty, kazdy ma svuj nazev a svuj datovy typ. Proc je teda tato konvence 'rozslisovat' (cim?) mezi sebou promenne hvezdickou? To je fakt matouci
Není na tom nic matoucího. Jak jinak by si chtěl oddělit 2 typy proměnné, kde jedná obsahuje adresu a druhá hodnotu? Proto je v C v definici pro ukazatelovou proměnnou použitá hvězdička před jménem a pro běžnou proměnnou pouze jméno.
int i = 0, *pi = NULL;
pi = &i; // zde se * nepouziva
*pi = 100; // nebo
i = 100;
printf("%d %d %p %p\n", i, *pi, &i, pi);
Vypis hodnot a adres dvěma způsoby. Přiřazení adresy proměnné ukazateli a hodnoty proměnné i přímo a pomocí ukazatele.
A jeste jedna vec. Pokud bych zmenil tento argument:
Makro va_start opravdu nemusí dereferenceovat (přistupovat) na pointer předávaný v druhém argumentu. Jeho úkolem totiž je nastavit první parametr (va_list) tak, aby ukazoval na první z parametrů skrývajících se v "argumentu" .... fmt musí být poslední parametr před ..., protože na zásobníku jsou parametry při volání variadické funkce uloženy následovně:
(počítáno zleva doprava. Jelikož zásobník roste směrem k nižším adresám, obvykle se parametry předávají v opačném pořadí (zprava doleva), ale chtěl jsem číslovat pořadí tak, jak jej vidíš ve zdrojovém kódu).
Makro va_start ti pak prostě vrátí
(void **)&fmt + 1
Do parametru fmt tedy nepřistupuje (nečte jeho hodnotu). Jde jen o to získat jeho adresu na zásobníku.
Z implementace va_start a jemu podobných je zároveň vidět, jak je to celé potenciálně nebezpečné, protože:
Ano, úkolem makra va_start je nastavení pozice na zásobníku. To je důvod, proč se do va_start zadává poslední známý parametr před .... Následné argumenty lze pak snadno přečíst pomocí va_arg. Doplním praktickou ukázku.
void printargs(int n, ...) {
va_list list;
va_start(list, n);
for (; n; n--) printf("%d ", va_arg(list, int));
va_end(list);
}
s voláním např.
printargs(5, 10, 20, 30, 40, 50);
Přesně tak, celé to nebezpečí spočívá v tom, že překladač skutečně nekontroluje hodnotu odpovídající počtu parametrů, nehlásí varování ani nepozastaví překlad. Je jen na programátorovi jak se k tomu celému postaví. Obvykle však existuje nějaká vazba mezi počtem argumentů které budou následovat a argumenty a takto "natvrdo" se to nezadává popř. výjimečně. Je to spíše jen experimentální ukázka na které je princip činnosti dobře vidět. Dobrý programátor by měl vědět, jak si s tím poradit. Funkce s variabilním počtem parametrů s jedním parametrem představujícím počet variadických parametrů je jedním ze způsobů jak taková funkce vypadá.
Tím druhým způsobem, o poznání frekventovanějším, je tvorba variadické funkce za pomocí formátovacího řetězce, díky kterému lze specifikovat typy parametrů (viz. zmíněná funkce printf() a další). Zde je jen na programátorovi jak správně napáruje specifikace formátu k danému typu.
Existují další způsoby ale tyto dva jsou nejčastější neboť jsou nejznámější.
Zobrazeno 11 zpráv z 11.