Mikuláš je tu! Získaj 90 % extra kreditov ZADARMO s promo kódom CERTIK90 pri nákupe od 1 199 kreditov. Len do nedele 7. 12. 2025! Zisti viac:
NOVINKA: Najžiadanejšie rekvalifikačné kurzy teraz s 50% zľavou + kurz AI ZADARMO. Nečakaj, táto ponuka dlho nevydrží! Zisti viac:

Diskusia – 13. diel - Funkcie s variabilným počtom a Typo argumentovať (stdarg.h)

Späť

Upozorňujeme, že diskusie pod našimi online kurzami sú nemoderované a primárne slúžia na získavanie spätnej väzby pre budúce vylepšenie kurzov. Pre študentov našich rekvalifikačných kurzov ponúkame možnosť priameho kontaktu s lektormi a študijným referentom pre osobné konzultácie a podporu v rámci ich štúdia. Toto je exkluzívna služba, ktorá zaisťuje kvalitnú a cielenú pomoc v prípade akýchkoľvek otázok alebo projektov.

Komentáre
Posledné komentáre sú na spodnej časti poslednej stránky.
Avatar
Patrik Pastor:10.9.2019 15:01
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

Avatar
DarkCoder
Člen
Avatar
DarkCoder:20.1.2024 14:15

„Napíšte funkciu, ktorá zo zadaných celých čísiel (argumentov) typu 'int' vyberie najväčšie a vráti ho. Prvé číslo stanovuje počet čísiel, z ktorých sa bude vyberať najväčšie. Počet zadaných čísiel môže byť rôzny.“

Proč ty složitosti ve funkci? Přímočaře..

#include <stdarg.h>
#include <stdio.h>

int maxInt(const unsigned count, ...);

int main(void) {
        int result1 = maxInt(5, 10, 5, 8, 20, 15);
        int result2 = maxInt(3, -5, 0, -3);
        int result3 = maxInt(1, 42);

        printf("Nejvetsi cislo: %d\n", result1);
        printf("Nejvetsi cislo: %d\n", result2);
        printf("Nejvetsi cislo: %d\n", result3);

        return 0;
}

int maxInt(const unsigned count, ...) {
        va_list args;
        int max, current;

        va_start(args, count);

        max = va_arg(args, int);

        for (unsigned i = 1; i < count; ++i) {
                current = va_arg(args, int);
                if (current > max) {
                        max = current;
                }
        }

        va_end(args);

        return max;
}
Odpovedať
"I ta nejlepší poučka postrádá na významu, není-li patřičně předána." - DarkCoder
Avatar
Yveta Kršková:10. novembra 13:16

Nechce se mi ani věřit, že v Céčku je zapojena tak 'nebezpečná' součástka :D
Možná bych uživatele nechala zadávat čísla po jednom, pokaždé si prověřila, co mi zadal a pokud by šlo o číslo, srovnala bych ho s dosavadním nejvyšším nalezeným číslem, které mi zadal předtím (pokud už mi nějaké zadal). Pak bych měla jistotu, že mi ta opičárna nepřepíše někde paměť :D

Odpovedať
:D :D :D
Avatar
DarkCoder
Člen
Avatar
Odpovedá na Yveta Kršková
DarkCoder:10. novembra 14:11

To je v C naprosto normální, C je nízkoúrovňový jazyk a spousta zodpovědnosti je na programátorovi. Ano, práce s variadickými funkcemi a stdarg.h je nebezpečná, ale mocná. Je dobré je používat s rozumem, popřípadě dobře definovat konvenci jako je to u funkcí printf() a scanf().

Ano, jsou místa kde to jde elegantně obejít. A zpracování vstupu jeden po druhém je právě tento případ. A správně si naznačila vhodné řešení. Je to jednoduché ale stále ne neprůstřelné. Ale i tvorba spojového seznamu není stoprocentní. Vždy se bude balancovat mezi jednoduchostí, bezpečností a efektivitou. V C je možné vše, ale také vše může selhat. :-)

Odpovedať
"I ta nejlepší poučka postrádá na významu, není-li patřičně předána." - DarkCoder
Avatar
DarkCoder
Člen
Avatar
DarkCoder:10. novembra 15:24

Jinak funkci maxInt() lze vylepšit o detekci žádného čísla k porovnání a validní paměti pro výsledek. Stejně tak aby indikovala zda vše proběhlo v pořádku či nastala chyba.

int maxInt(const unsigned count, int *result, ...) {
    if (count == 0) return 1;       // Žádné číslo k porovnání
    if (result == NULL) return 2;   // Ukazatel result je NULL

    va_list args;
    va_start(args, result);

    int max = va_arg(args, int);
    for (unsigned i = 1; i < count; ++i) {
        int current = va_arg(args, int);
        if (current > max) max = current;
    }

    va_end(args);

    *result = max;
    return 0;
}

Kompilátor stále ale nemůže detekovat validní typy argumentů ...

Takže:
Pečlivě dokumentovat funkci, že přijímá pouze int. (Moc nepomůže :D)
Používat wrapper funkce, která typově kontroluje vstup před zavoláním variadické funkce.

Alternativa: nepoužívat variadické funkce, místo toho předat pole int a jeho délku.

Odpovedať
"I ta nejlepší poučka postrádá na významu, není-li patřičně předána." - DarkCoder
Avatar
Odpovedá na DarkCoder
Yveta Kršková:13. novembra 15:45

Ale DC, to byla ironie, přijmout od uživatele jeden údaj za druhým a nikoliv všechny naráz a důkladně si ověřovat, co zadal, nebo mu omezit možnosti zadávání, je přece standard pro každé uživatelské rozhraní. Pokud nám přicházejí opakovaně výsledky měření nějakého přístroje, ty ukládáme a vypočítáváme např. odchylku měření, nebo změnu koordinátu ukazatele, postupujeme zase jinak.
Jedna věc je, když si v úloze hrajeme, a jiná, když řešíme opravdu skutečný praktický příklad a píšeme pro někoho program.

Odpovedať
:D :D :D
Avatar
DarkCoder
Člen
Avatar
Odpovedá na Yveta Kršková
DarkCoder:13. novembra 15:56

To ano, postupy se liší dle situace.. efektivita a důslednost přesto nepřechází do pozadí.. A to ať už je to hraní si nebo ostrý program.. Ale vím jak si to zamýšlela.. :-)

Odpovedať
"I ta nejlepší poučka postrádá na významu, není-li patřičně předána." - DarkCoder
Avatar
DarkCoder
Člen
Avatar
DarkCoder:17. novembra 15:21

Existují i jiné způsoby práce s variadickými funkcemi než předávání počtu argumentů předem. Jedním z nejčastějších je použití tzv. sentinelu.

Sentinel je speciální hodnota vložená mezi argumenty, která slouží jako zarážka a říká:

„Od této hodnoty dál už žádné další argumenty nepokračují.“

Je to čistá konvence: samotný jazyk C nemá žádný mechanismus, jak určit konec variadické části, takže hodnotu poznávacího znamení si musí definovat samotná funkce.

Mezi nejčastěji používané sentinely patří:

NULL — ideální pro ukazatele,
speciální hodnota enum (např. TOKEN_END),
integer mimo doménu běžných hodnot (např. -1),
tzv. tagový systém (páry „typ–hodnota“ zakončené END tagem).

Příklad:

Vytvořme funkci, která vypíše libovolný počet řetězců, dokud nenarazí na NULL:

void print_strings(const char *first, ...) {
    va_list ap;
    va_start(ap, first);

    const char *s = first;
    while (s != NULL) {
        puts(s);
        s = va_arg(ap, const char *);
    }

    va_end(ap);
}

a volání:

print_strings("Hello", "world", "C", NULL);

Funkce postupně čte všechny stringy, dokud nenarazí na sentinel NULL, který ukončí zpracování variadických argumentů.

Odpovedať
"I ta nejlepší poučka postrádá na významu, není-li patřičně předána." - DarkCoder
Posledné komentáre sú na spodnej časti poslednej stránky.
Robíme čo je v našich silách, aby bola tunajšia diskusia čo najkvalitnejšia. Preto do nej tiež môžu prispievať len registrovaní členovia. Pre zapojenie sa do diskusie sa zaloguj. Ak ešte nemáš účet, zaregistruj sa, je to zadarmo.

Zobrazené 8 správy z 18.