Vydělávej až 160.000 Kč měsíčně! Akreditované rekvalifikační kurzy s garancí práce od 0 Kč. Více informací.
Hledáme nové posily do ITnetwork týmu. Podívej se na volné pozice a přidej se do nejagilnější firmy na trhu - Více informací.
Avatar
Patrik Pastor:29.8.2019 19:18

funguje je to jak je v priloze, ale neprijde mi to spravne.

Zkusil jsem:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

typedef struct{
   char zelenina[10];
   char ovoce[10];
} PLODINA;

int main (void) {

   char exi[] = "exit";

   char odpoved[10];
   char volba;
   int pokracovat2 = 0;
   PLODINA p[20];

   while(pokracovat2 < 20)
   {
      printf("zadejte potravinu (exit pro ukonceni)\n");
      scanf(" %s",odpoved);
      if(strcmp(exi, odpoved) == 0)
         break;

      for(int i =0; i<20 ; i++)
      {
         if(strcmp(p[i].zelenina, odpoved) == 0)
         {
            printf("%s je zelenina\n",odpoved);
            break;
         }

         else if(strcmp(p[i].ovoce, odpoved) == 0)
         {
            printf("%s je ovoce\n",odpoved);
            break;
         }

         else
         {
            printf("%s je ovoce[O] nebo zelenina[Z]?\n", odpoved);
            scanf(" %s",&volba);
            if (volba == 'O')
            {
               strcpy(p[i].ovoce, odpoved);
               break;
            }
            else if(volba == 'Z')
            {
               strcpy(p[i].zelenina, odpoved);
               break;
            }
            else
            {
               printf("spatna odpoved\n");
               break;
            }
            break;
         }
      }
      pokracovat2++;
   }

   return 0;
}

Chci docílit: Funguje to tak jak to je v reseni, ale nezda se mi ze kod je efektivni, nebo "uhledny". Davam to sem spise pro rady, jakych psanych nebo nepsanych pravidel bych se mel drzet, aby byl kod vice citelnejsi nebo efektivnejsi. Mam tam dva loopy - while a for, prijde mi ze to je strasne moc strojovych instrukci. Jak by se to tedy dalo zefektivnit. Jeste jednou pripominam ze mi jde spis o rady a zkusenosti.

Editováno 29.8.2019 19:21
 
Odpovědět
29.8.2019 19:18
Avatar
DarkCoder
Člen
Avatar
Odpovídá na Patrik Pastor
DarkCoder:29.8.2019 20:51

Několik rad k programu

  • Nevkládej do programy knihovny které nejsou potřeba (z knihovny stdlib.h nevyužíváš žádné funkce ani makra, je tedy zbytečné ji do programu přikládat).
#include <stdio.h>
#include <string.h>
  • je třeba správně definovat objekt. Potravina má svůj název a typ. Je chybou deklarovat typ potraviny ve dvou položkách. Jedním prvkem bude tedy dostatečně dlouhý název, druhým prvkem bude znakový příznak určující typ potraviny. Popřípadě to můžeš vyjádřit pomocí bitového pole.
struct SPLODINA {
        char nazev[LIMIT];      // nazev potraviny
        char typ;                       // O - ovoce, Z - zelenina, J - jine
};
  • Pokud pracuješ s rozsahy, vytvářej konstanty pro tyto rozsahy. Vyhneš se tak mnoha problémům a ušetříš práci při změně hodnoty (nemusíš ji měnit všude, stačí na jednom místě v programu).
#define POCET 20                // velikost databaze
#define LIMIT 30                // delka nazvu potraviny
#define EXIT "exit"             // ukoncení prikazu
  • Používej vhodné cykly. Ty nevíš, kolik potravin budeš zadávat a kontrolovat. Ale víš že to budeš dělat alespoň 1x, použij tedy cyklus do-while.
  • Pro čtení příkazu používej samostatný buffer na který poté aplikuj patřičné úkony (Otestování na ukončovací řetězec a následné porovnávání s daty v databázi. To máš správně.
  • Kdykoli v daném okamžiku bys měl znát aktuální velikost databáze. Ať už si hodnotu někde uchovávat, nebo brát jako poslední položku prázdnou hodnotu nebo mít pro ni stanovenou hodnotu. Nemusíš tak procházet maximální velikost databáze. Tedy opět správná volba cyklu. Pokud budeš udržovat databázi setříděnou, budeš moci využít dalších možností (vyhledávací algoritmy, např. půlení databáze apod.).
  • Po zjištění, že potravina se v databázi nevyskytuje přichází na řadu vkládání dat do databáze. Zadání názvu potraviny pomocí fgets() a úkony nutnými k čištění bufferu a úpravě tohoto řetězce. A poté zadání typu potraviny. Typ potraviny je typu char takže nepoužíváme funkci scanf() ale funkci getchar(). Pro výběr z více alternativ pro znakovou volbu používáme řídící příkaz switch namísto schodů if-else-if. V tomto případě to až tak nevadí. Co ale nelze je míchat řetězcovou volbu se znakovou analýzou.
Akceptované řešení
+20 Zkušeností
+2,50 Kč
Řešení problému
Nahoru Odpovědět
29.8.2019 20:51
"I ta nejlepší poučka postrádá na významu, není-li patřičně předána." - DarkCoder
Avatar
Odpovídá na DarkCoder
Patrik Pastor:29.8.2019 21:04

jakoze jsi celkem frajer :D, nevim jak dlouho programujes, a kde ses to vsechno naucil, ale dik za rady. Jenom bych se chtel zeptap, nerozumim presne tomu, jak myslis "pouzivej samostatny buffer na cteni prikazu" - buffer je mysleno pole? A jak bych potom do pole vkladal prikazy? jinak mas pravdu - ze jsem uvazoval o switchi, kdyz doslo na zapisovani dat do databaze (ale uz jsem mel 3 podminky v if, tak jsem je nechtel mazat).

PS: reseni tady je ale vice "rozkouskovano" - je tedy kod napsan ve 3 pomocnych funkcich - ja mam vse v main funkci a spoliham se na cykly. To sem mel na mysli, zda to je semanticky spravne (protoze me muj kod prijde jako "spagety" - nudla). Tedy jestli radeji psat funkce - predpokladam ze ano.

 
Nahoru Odpovědět
29.8.2019 21:04
Avatar
DarkCoder
Člen
Avatar
Odpovídá na Patrik Pastor
DarkCoder:29.8.2019 21:42

Příkazový buffer je znakové pole, které by po veškerých úpravách mělo obsahovat to co uživatel zadal při splnění všech limitujících faktorů plynoucí z programu. Po výzvě uživatel zadává tzv. RAW data a je na programátorovi, aby se s nimi vypořádal. Upravil je, popřípadě vyhodnotil jako neadekvátní vstup a informoval uživatele o tom. Po veškerých potřebných úpravách nyní pole obsahuje to co by mělo.

část kódu by pak mohla vypadat takto:

do {
        printf("Zadejte potravinu (exit pro ukonceni): ");
        fgets(odpoved, LIMIT, stdin);

        // zde je třeba analyzovat a vyhodnotit vstupní data
        // a provést potřebné úkony pro zamezení nesprávné funkci programu
        ...

Pro představu, když zadám delší řetězec než povolený rozsah, je třeba čistit buffer. Funkce fgets() může ale nemusí (dle zadané délky vstupních dat) přidávat do řetězce znak nového řádku. Ten tam nechceme a je třeba ho najít a nahradit nulovým znakem. Poté třeba víš že očekáváš text. Můžeš analyzovat buffer na nesprávný obsah, třeba proto že obsahuje číslici apod. Na základě toho vypíšeš na obrazovku hlášení a použiješ řídící příkaz continue pro opakování volby. Rozhodně je to lepší, něž-li ukončovat program při chybném vstupu. Je třeba naučit se psát programy uživatelsky přátelské a vědět na co testovat a ošetřovat chyby.

A pak už můžeš pokračovat v programu dál:

if (!strcmp(EXIT, odpoved)) break;

// Zde hledání položky v databázi (porovnání příkazového bufferu s prvky databáze za použití vyhledávacích technik).

Je naprosto správné rozdělit si program na samostatné části. Vytvořit si funkce popř. makra a ta volat. Důvod není jen ten, že se daná část může volat vícekrát. Ale umožňuje to mít větší kontrolu na programem. Dosáhnout snadné úpravy funkci či makra a měnit je dle potřeby. Je to rozhodně snazší než hledat celou část podprogramu v programu a měnit ji tam. Na tyto podprogramy si poté můžeš vytvořit knihovnu. Skutečné programy se skládají z funkcí, funkce jsou alfa a omega programování v C. Tedy rozhodně vše rozdělit než vytvářet "vše v jednom". A to ani nemluvím o výhodách odděleného překladu.

Nahoru Odpovědět
29.8.2019 21:42
"I ta nejlepší poučka postrádá na významu, není-li patřičně předána." - DarkCoder
Avatar
Odpovídá na DarkCoder
Patrik Pastor:29.8.2019 22:28

proc vlastne pouzivas fgets, misto scanf? Je to jenom kvuli bezpecnosti, ze si muzes kontrolovat pocet bytu, ktere uzivatel zada? Ale co kdyz nezada nic? nebude to potom undefined behaviour? Tusim ze scanf se nijak nebrani proti overflow, ackoliv tady na siti se hodne pouziva, ale neznam lepsi funkce na input data od uzivatale. Navic posledni argument fgets 'stream' - definujes jako stdin - je standarni input uz definovany? nebo jej inicializujes nekde nahore (asi blba otazka, ale nevim, jestli 'stdin' neexpanduje jak nejake makro nebo jinak, jeste sem se 'streamy' nesetkal (ale chapu ze to je neco jak prostor, ze ktereho dostavam data - proste proud).

 
Nahoru Odpovědět
29.8.2019 22:28
Avatar
DarkCoder
Člen
Avatar
Odpovídá na Patrik Pastor
DarkCoder:29.8.2019 22:50

Protože scanf() se pro čtení samotných řetězců nepoužívá. scanf() je univerzální funkce a velice pomalá. Používá se tam, kde vstup obsahuje vícero typů dat. Kontrolu vrácených hodnot a analýzu vstupu by se měla provádět vždy. Jen nikde se to moc neuvádí z hlediska jednoduchosti, přitom je to na tom to nejdůležitější. Jen tak bude program odolný vůči různým vnějším vlivům.

Když je program spuštěn, jsou s ním automaticky spuštěny 4 proudy (vstupní stdin, výstupní stdout, chybový stderr a tiskový stdprn). Nejsou tedy nikde implicitně vytvořeny a programátor se o ně nemusí starat. Co je ale třeba, tyto datové proudy nesmí být programátorem implicitně zavírány.

Nahoru Odpovědět
29.8.2019 22:50
"I ta nejlepší poučka postrádá na významu, není-li patřičně předána." - DarkCoder
Avatar
Odpovídá na DarkCoder
Patrik Pastor:29.8.2019 22:54
  1. nerozumim presne, jak bys mohl brat "vicero typu" (kdyz se prece formatovym specifikatorem typ definuje)
  2. k proudum - jak se lisi stdout a stdprn, kdyz pouzivam funkci printf() jde vysledek na output nebo print? (kompiluju v gcc - a output mam do terminalu, tak jsem predpokladat ze prave terminal je povazovan za standardni vystup - stdout, nevim potom co je stdprn)
  3. jak by si programator dokazal tyto proudy zavirat? na to je snad nejaka funkce? - A hlavne proc by to vubec nekdo delal.
 
Nahoru Odpovědět
29.8.2019 22:54
Avatar
DarkCoder
Člen
Avatar
Odpovídá na Patrik Pastor
DarkCoder:29.8.2019 23:11
  1. scanf("%f %d %c", &fval, &ival, &cval); funkce scanf() nyní čte tři různé datové typy v jednom kroce.
  2. stdin je standartní vstup a je defaultně brán pro čtení z klávesnice, stdout je standartní výstup a je defaultně brán jako obrazovka. Tyto datové proudy lze přesměrovat, že mohu data brát ze souboru a zapisovat je také do souboru. stdprn je přímo tiskový proud, nevypsiuje se na obrazovku. Všechny tyto datové proudy lze použít tam kde lze použít standartní souborý proud, jsou to ukazatele na FILE *.

Např. dvě následující funkce jsou totožné:

printf("%d\n", i); //

fprintf(stdout, "%d\n", i);
  1. Souborové proudy vytvořené pomocí fopen() lze zavřít pomocí funkce fclose(). Standartní proudy (stdin, stdout, …) může programátor využívat ale ne měnit.
Nahoru Odpovědět
29.8.2019 23:11
"I ta nejlepší poučka postrádá na významu, není-li patřičně předána." - DarkCoder
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 8 zpráv z 8.