Body zdarma Body zdarma
Využij podzimních slev a získej od nás až 40 % bodů zdarma! Více zde

Diskuze: Type, ve funkci va_start

Aktivity (2)
Avatar
Patrik Pastor:10. září 20:29
char* fmt;
va_start(ap, fmt);

jak to, ze je druhy argument typ pointer?(adresa). V dokumentaci je druhy argumeny typu posledniho znameho typu (ale typ "adresa", je prece v jakemkoliv bytu). Rad bych chtel pochopit, jak funguje druhy argument "fmt" (typu char* - adresa na jeden byte) v teto funkci. Dik za odpoved

 
Odpovědět 10. září 20:29
Avatar
DarkCoder
Člen
Avatar
Odpovídá na Patrik Pastor
DarkCoder:11. září 9:02

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.

Nahoru Odpovědět 11. září 9:02
"„Učíš-li se proto, aby sis zapamatoval, zapomeneš. Učíš-li se proto, abys porozuměl, zapamatuješ si."
Avatar
Odpovídá na DarkCoder
Patrik Pastor:11. září 9:22

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

Editováno 11. září 9:23
 
Nahoru Odpovědět 11. září 9:22
Avatar
DarkCoder
Člen
Avatar
Odpovídá na Patrik Pastor
DarkCoder:11. září 10:03

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);
Nahoru Odpovědět 11. září 10:03
"„Učíš-li se proto, aby sis zapamatoval, zapomeneš. Učíš-li se proto, abys porozuměl, zapamatuješ si."
Avatar
Odpovídá na DarkCoder
Patrik Pastor:11. září 10:10

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

 
Nahoru Odpovědět 11. září 10:10
Avatar
DarkCoder
Člen
Avatar
Odpovídá na Patrik Pastor
DarkCoder:11. září 10:28

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í.

Nahoru Odpovědět  +1 11. září 10:28
"„Učíš-li se proto, aby sis zapamatoval, zapomeneš. Učíš-li se proto, abys porozuměl, zapamatuješ si."
Tento výukový obsah pomáhají rozvíjet následující firmy, které dost možná hledají právě tebe!
Avatar
Odpovídá na DarkCoder
Patrik Pastor:11. září 10:29

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).

 
Nahoru Odpovědět 11. září 10:29
Avatar
Odpovídá na DarkCoder
Patrik Pastor:11. září 10:37

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

Editováno 11. září 10:37
 
Nahoru Odpovědět 11. září 10:37
Avatar
DarkCoder
Člen
Avatar
Odpovídá na Patrik Pastor
DarkCoder:11. září 11:27

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.

Nahoru Odpovědět 11. září 11:27
"„Učíš-li se proto, aby sis zapamatoval, zapomeneš. Učíš-li se proto, abys porozuměl, zapamatuješ si."
Avatar
Martin Dráb
Redaktor
Avatar
Odpovídá na Patrik Pastor
Martin Dráb:11. září 23:51

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ě:

  • 1. normální parametr,
  • . . .
  • N-1. normální parametr,
  • fmt,
  • první argument v ...
  • . . .
  • poslední argument v ....

(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:

  1. programátor musí vědět, zda-li do variadické funkce předává správný počet parametrů (protože ta funkce to zjistit sama nemůže),
  2. programátor musí specifikovat parametry správného typu (překladač toto nedokáže zkontrolovat, pokud se nejdná o funkce, které zná (např. printf), a tedy ví, jak např. z formátovacího řetězce odhadnout počet variadických parametrů).
Akceptované řešení
+20 Zkušeností
+1 bodů
Řešení problému
Nahoru Odpovědět  +2 11. září 23:51
2 + 2 = 5 for extremely large values of 2
Avatar
DarkCoder
Člen
Avatar
DarkCoder:12. září 1:27

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ší.

Nahoru Odpovědět  +1 12. září 1:27
"„Učíš-li se proto, aby sis zapamatoval, zapomeneš. Učíš-li se proto, abys porozuměl, zapamatuješ si."
Děláme co je v našich silách, aby byly zdejší diskuze co nejkvalitnější. Proto do nich také mohou přispívat pouze registrovaní členové. Pro zapojení do diskuze se přihlas. Pokud ještě nemáš účet, zaregistruj se, je to zdarma.

Zobrazeno 11 zpráv z 11.