[C]Pomoc ze zrozumieniem uwag nauczyciela języka c

0

Witam, zarejestrowałem się tu bo jestem początkujący i nauczyciel ze studiów zadał nam pracę do zrobienia, no to ją zrobiłem lecz miał sporo uwag (niektóre wynikały z niedokładnego przeanalizowania kodu). Gdy prosiłem o wyjaśnienie niektórych jego uwag to nie zrobił tego tak, żeby dało się go zrozumieć, dlatego może ktoś będzie mógł mi pomóc w zrozumieniu jego, wytłumaczeni mi moich błędów, a mianowicie:

struct Liczba
{
char cyfry[80];
int ilosc_cyfr;
bool dodatni;
};
struct Liczba liczby[1000];
// Brakuje zdefiniowanego typu Liczby.
// Skąd wiadomo, ile liczb użytkownik wprowadził - brakuje zmiennej, która by to okreslała.
// Struktura opisująca liczby jako zmienna globalna jest akceptowalna (chociaż jest to mniej czytelne),
// Jednak zdefiniowanie tablicy liczb jako zmiennej globalnej a ich liczby (ile zostało wprowadzonych) jako zmiennej lokalnej w funkcji main jest niedopuszczalne.
// Te dwie zmienne są logicznie połaczone i musza być przetwarzane jako całość. Należy zdefiniować typ Liczby (struktura).

Jak dokładnie mam definiować? W wielu przykładach struktur właśnie tak to wyglądało i nic dodatkowo nie było definiowane, informacja ile liczb zostało wprowadzone miałem zadeklarowane lokalnie, potrzebowałem to tylko i wyłącznie do zakończenia pętli przy sortowaniu, dlaczego jest to nie dopuszczalne, czyli mam zadeklarować int ilosc_liczb jako zmienna globalna i już? Dlaczego logicznie połączone, ja tam nie widzę, żadnego logicznego łączenia.

// Zdecydowanie lepszym rozwiązanie będzie zdefiniowanie zmiennej Liczby jako zmiennej lokalnej w funkcji main i przekazywanie jej jako parametr (przez wskazanie) do innych funkcji.

Jeżeli używam struct Liczba liczby[1000] w każdej funkcji w kodzie to dlaczego lepiej będzie przez wskazanie? Doda mi to dodatkowego pisania, dla mnie jest to nielogiczne, że ilosc liczb musi być globalnie bo niby niedopuszczalne inaczej a struct Liczba liczby[1000] to już lokalnie.

// Lepiej będzie jeśli funcja otrzyma wskazanie na strukturę Liczby, którą będzie wypełniać.

Dotyczy ta uwaga funkcji która wczytuje liczbę i Od razu przypisuje do struktury zadeklarowanej globalnie bo przez cały program się do niej odnoszę prawie w każdej funkcji. Po co mam to robić przez wskazanie i se utrudniać?

// Funkcja powinna się nazywać wczytaj liczby i powinna wywoływać funkcję wczytajLiczbe

Dotyczy ta uwaga funkcji która wczytuje liczbę i Od razu przypisuje do struktury, po co mam w funkcji która wczytuje liczbe i przypisuje do struktury wywoływać funkcje która wczytuje? Odpowiedź na to dostałem taką: "funkcja wczytująca liczbę powinna wczytać liczbę i wypełnić odpowiednimi wartościami strukturę liczba" Jak pisałem, moja funkcja aktualnie to robi, po co mam dublować?

"// Funkcja powinna korzystać ze zdefiniowanej funkcji wyswietlLiczbe" JA:nie rozumiem, funkcja która wyświetla ma korzystać z funkcji która też wyświetla?
Powinien Pan napisać funkcję wyświetlającą pojedyncza liczbę i wykorzystać ją (wywołać) w funkcji wyświetlającej liczby.

Że coo?

Podaje cały kod, Od razu zaznaczam, że jestem początkujący i dopiero się uczę, wiem, że muszę nazwy zmiennych poprzemieniać na prostsze, zmienne typu bool przerobić z 0 1 na true false dla czytelności. W funkcji wczytywanie wczytuje wpisane liczby, Od razu przypisuje do struktury bez początkowych zer jakby się wpisało np. 00034 to tylko przypisze 34, wyłapuje czy na początku jest znak - czy + odraz Od razu zlicza ilość cyfr. Porównuje program najpierw po znaku czy liczba na minusie czy na plusie, potem po ilości cyfr a potem po cyfrach (tak powiedział nauczyciel bym zrobił)

Aktualnie jestem w trakcie przerabiania funkcji wczytywanie tak by łapała w locie wciskane klawisze, wszystko w locie przy okazji by mi liczyło jak np ilość cyfr, czy to liczba na minusie czy nie itp. Tylko jeszcze myślę, jak zrobić by się zakończyło po słowie koniec.

Treść zadania: Dany ciąg co najwyżej 80-cyfrowych liczb całkowitych ze znakiem (nie mogą być one reprezen­towane wewnętrznie nawet jako żaden typ liczbowy). Wprowadzanie kończy się słowem KONIEC podanym zamiast liczby. Wynikiem powinien być wyświetlo­ny ciąg liczb danych uporząd­kowany w/g rosnącej wartości.

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

struct Liczba
    {
        char cyfry[80];
        int ilosc_cyfr;
        bool dodatni;
    };
struct Liczba liczby[1000];

struct Liczba temp;

void wprowadzanie(int *x_zwracanie)
{
    int licznik_kopii_tablicy=0;
    int czy_natrafilo_na_liczbe_inna_niz_zero=0;
    int ilosc_usunietych_poczatkowych_zer=0;
    char wczytywanie_liczby[100];
    int licznik_cyfr=0;
    int x=0;
    do
    {
        czy_natrafilo_na_liczbe_inna_niz_zero=0;
        ilosc_usunietych_poczatkowych_zer=0;
        licznik_kopii_tablicy=0;
        licznik_cyfr=0;
        gets(wczytywanie_liczby);
        if (stricmp(wczytywanie_liczby, "KONIEC") == 0)
        {
            break;
        }
        do
        {
            if (wczytywanie_liczby[licznik_cyfr] == '\0')
            {
                break;
            }
            ++licznik_cyfr;
        }
        while(0!=2);
        if (wczytywanie_liczby[0] == '-')
        {
            ++licznik_kopii_tablicy;
            liczby[x].dodatni=0;
        }
        else
        {
            liczby[x].dodatni=1;
        }
        if (wczytywanie_liczby[0] == '+')
        {
            ++licznik_kopii_tablicy;
        }
        int y=0;
        for (;licznik_kopii_tablicy!=licznik_cyfr;)
        {
            if (wczytywanie_liczby[licznik_kopii_tablicy]=='0' && czy_natrafilo_na_liczbe_inna_niz_zero==0)
            {
                ++licznik_kopii_tablicy;
                ++ilosc_usunietych_poczatkowych_zer;
            }
            else
            {
                liczby[x].cyfry[y]=wczytywanie_liczby[licznik_kopii_tablicy];
                ++y;
                ++licznik_kopii_tablicy;
                czy_natrafilo_na_liczbe_inna_niz_zero=1;
            }

        }
        if (wczytywanie_liczby[0] == '-' || wczytywanie_liczby[0] == '+')
        {

            --licznik_cyfr;
        }
        if (licznik_cyfr>=81)
        {
            puts("Za duza liczba, limit cyfr to 80 nie uwzgledniajac znaku - albo +, konczenie programu");
            exit(0);
        }
        licznik_cyfr=licznik_cyfr-ilosc_usunietych_poczatkowych_zer;
        liczby[x].ilosc_cyfr=licznik_cyfr;
        ++x;

    }
    while (1!=2);
    *x_zwracanie=x;
}

void wyswietlenie_jednej_liczby()
{
        if (liczby[0].dodatni==0)
        {
            printf("-%s",liczby[0].cyfry);
        }
        else
        {
            printf("%s",liczby[0].cyfry);
        }
        exit(0);
}
void porownywanie(int x, bool *jest_roznica, int *a_zwracanie, int *c_zwracanie, bool *wszystko_posortowane)
{
    int a=0;
    int b=0;
    int c=1;
    do
    {
        *jest_roznica=0;
        b=0;
        if (liczby[a].dodatni>liczby[c].dodatni)
        {
            *jest_roznica=1;
            *a_zwracanie=a;
            *c_zwracanie=c;
            return;
        }
        else
        {
            if (liczby[a].dodatni==liczby[c].dodatni && liczby[a].dodatni==1)//ma roscac
            {
                if(liczby[a].ilosc_cyfr>liczby[c].ilosc_cyfr)
                {
                    *jest_roznica=1;
                    *a_zwracanie=a;
                    *c_zwracanie=c;
                    return;
                }
                else
                {
                    if(liczby[a].ilosc_cyfr==liczby[c].ilosc_cyfr)
                    {
                        do
                        {
                            if(liczby[a].cyfry[b]>liczby[c].cyfry[b])
                            {
                                *jest_roznica=1;
                                *a_zwracanie=a;
                                *c_zwracanie=c;
                                return;
                            }
                            if(liczby[a].cyfry[b]<liczby[c].cyfry[b])
                            {
                                break;
                            }
                            ++b;
                        }
                        while(b!=liczby[a].ilosc_cyfr);
                    }
                }
            }
            if (liczby[a].dodatni==liczby[c].dodatni && liczby[a].dodatni==0)//ma malec
            {
                if(liczby[a].ilosc_cyfr<liczby[c].ilosc_cyfr)
                {
                    *jest_roznica=1;
                    *a_zwracanie=a;
                    *c_zwracanie=c;
                    return;
                }
                else
                {
                    if(liczby[a].ilosc_cyfr==liczby[c].ilosc_cyfr)
                    {
                        do
                        {
                            if(liczby[a].cyfry[b]<liczby[c].cyfry[b])
                            {
                                *jest_roznica=1;
                                *a_zwracanie=a;
                                *c_zwracanie=c;
                                return;
                            }
                            if(liczby[a].cyfry[b]>liczby[c].cyfry[b])
                            {
                                break;
                            }
                            ++b;
                        }
                        while(b!=liczby[a].ilosc_cyfr);
                    }
                }
            }
        }
        ++c;
        if (c==x+1)
        {
            ++a;
            c=a+1;
        }
    }
    while(a!=x);
    if (*jest_roznica==0)
    {
        *wszystko_posortowane=1;
    }
}
void sortowanie(int a, int b)
{
    temp=liczby[a];
    liczby[a]=liczby[b];
    liczby[b]=temp;
}
void wyswietlanie(int x)
{
    puts("\nPosortowane liczby:\n");
    int g=0;
    for (g=0; g<=x; ++g)
    {
        if (liczby[g].dodatni==0)
        {
            printf("-%s\n",liczby[g].cyfry);
        }
        else
        {
            printf("%s\n",liczby[g].cyfry);
        }
    }

}

int main()
{
    int ilosc_liczb, a=0, c=1;
    bool jest_roznica=0;
    bool wszystko_posortowane=0;


   wprowadzanie(&ilosc_liczb);
    ilosc_liczb=ilosc_liczb-1;
    if (ilosc_liczb==0)
    {
        wyswietlenie_jednej_liczby();
    }
    do
    {
        porownywanie(ilosc_liczb, &jest_roznica, &a, &c, &wszystko_posortowane);
        if (jest_roznica==1)
        {
            sortowanie(a, c);
        }
    }
    while(wszystko_posortowane!=1);
    wyswietlanie(ilosc_liczb);
    return 0;
}
0

struct Liczba liczby[1000];
Co jeśli użytkownik wprowadzi 1001 liczb? Albo jeśli wprowadzi jedną - po co wtedy marnować zasoby na resztę liczb?

Te dwie zmienne są logicznie połaczone i musza być przetwarzane jako całość. Należy zdefiniować typ Liczby (struktura).

Dlaczego logicznie połączone, ja tam nie widzę, żadnego logicznego łączenia.

Jedna zmienna nie ma sensu bez drugiej, czy to nie jest logiczne połączenie? Jeśli nie wiesz, ile danych podał użytkownik, to jak możesz przetwarzać te dane? Jeśli wiesz ile danych podał, ale nie masz tych danych, to chyba też nie za bardzo ma to sens... Dlatego warto połączyć te dwie zmienne w klasę, ewentualnie strukturę.

Zdecydowanie lepszym rozwiązanie będzie zdefiniowanie zmiennej Liczby jako zmiennej lokalnej w funkcji main i przekazywanie jej jako parametr (przez wskazanie) do innych funkcji.

Jeżeli używam struct Liczba liczby[1000] w każdej funkcji w kodzie to dlaczego lepiej będzie przez wskazanie? Doda mi to dodatkowego pisania, dla mnie jest to nielogiczne, że ilosc liczb musi być globalnie bo niby niedopuszczalne inaczej a struct Liczba liczby[1000] to już lokalnie.

Tu chodzi o to, że liczby[1000] i hipotetyczne ilosc_liczb musi mieć identyczny zasięg, a modyfikacja pierwszego powinna mieć wpływ na drugie i vice versa (abstrahując od sensu ręcznej modyfikacji zmiennej ilosc_liczb), dlatego obie zmienne powinny być zapakowane razem w jedną klasę/strukturę.
Każda z funkcji, które działają na tablicy liczb, powinna w argumencie przyjmować właśnie taką strukturę opakowującą tablicę. Dlaczego argumenty, a nie ze zmienna globalna? Wyobraź sobie, że masz dwie takie tablice - przy zmiennych globalnych dla każdej musiałbyś pisać osobny zestaw funkcji albo sprawdzać, na której tablicy masz operować - to drugie wiąże się i tak z dodaniem argumentu.
Jeśli funkcje będą operować na argumentach, a nie zmiennych globalnych, to nie będzie miało znaczenia, czy faktycznych źródłem danych jest zmienna globalna, czy lokalna.

Poza tym masz tu napisane "zdefiniowanie zmiennej Liczby jako zmiennej lokalnej", a nie "zdefiniowanie zmiennej Liczba liczby[1000] jako zmiennej lokalnej".

Nauczyciel usiłuje nauczyć was programować obiektowo w języku nie wspierającym obiektowości, może stąd Twoje problemy ze zrozumieniem jego intencji. Proponuję Ci, żebyć poczytał o obiektowości (np. na podstawie C++ albo objective C) i spróbował zrozumieć zasady SOLID (zwłaszcza S).

wyswietlenie_jednej_liczby - dlaczego zawsze jest to pierwsza wprowadzona liczba, dlaczego ignorujesz pole ilosc_cyfr?
void sortowanie(int a, int b) - z sortowanem to nie ma nic wspólnego, to tylko robi swap danych w tablicy.
Generalnie Twój kod jest nieczytelny, zbyt długi (vide porownywanie()) i niskiej jakości. Poczytaj o programowaniu obiektowym, spróbuj przekuć jego reguły do C, potem wywal to, co napisałeś i napisz od nowa, z uwzględnieniem tego, że funkcje/metody powinny mieć jedną odpowiedzialność (powinny robić jedną rzecz) i w związku z tym być krótkie (kilka linijek). Np. pobieranie danych od użytkownika (pseudokod):

bool CollectNumberFromInput(Numbers numbers)
{
  char[80] userInput;
  bool hasData = getDataFromInput(&userInput);
  if (!hasData)
    return false;

  Number number = CreateNumberFromData(userInput);
  AppendNewNumber(numbers, number);
  return true;
}

Numbers CollectNumbersFromInput()
{
  Numbers numbers = new Numbers();
  do {} while (CollectNumberFromInput(numbers));

  return numbers;
}
0

Zapomniałem dodać, że tablice nie mogą być dynamiczne, więc z góry założyłem, że wprowadzi maksymalnie 1000 liczb (zaakceptowane przez nauczyciela).
Jeśli chodzi o funkcję wyswietlenie_jednej_liczby to ona się odpala tylko i wyłącznie kiedy użytkownik wprowadzi jedną liczbę, wtedy ta funkcja ją wyświetla i kończy program Od razu. Ilosc_cyfr nie jest mi tam potrzebna.
void sortowanie(int a, int b) - wiem, już zmieniłem tak, że w funkcji sortowanie są 2 funkcje o nazwie porównanie i swap.
Czyli mam tworzyć funkcję odpowiedzialne za jedną rzecz. Czyli mógłbym podzielić wprowadzanie na np. wprowadzanie i przypisanie(przypisywałoby do struktury), a porównanie na osobną funkcję porównującą znak, osobną porównującą ilość cyfr i osobną porównującą cyfry? I te 3 funkcje byłyby w funkcji porównanie i jeśli wymagana będzie zamiana to funkcja porównanie zwróciłaby true i zadziałałaby funkcja swap? Sama długość kodu mi się nie skróci a i wydłuży troszkę ale za to każda funkcja będzie dużo krótsza.
Napiszę jeszcze raz wszystko próbując uwzględnić zasadę SOLID, na początku pewnie nie wyjdzie dobrze ale mam nadzieje, że będzie choć trochę lepiej niż aktualnie.
Ale zanim to zrobię czy taka forma jest akceptowalna?:

int main()
{
    wprowadzanie();
    sortowanie();
    wyswietlanie();
    return 0;
}

Te funkcje dzielą się na inne funkcje. Nie mają argumentów bo korzystają z struktóry Liczba która jest globalna. W nich funkcje już przekazują między sobą dane przez przekazanie lub zwracanie.

Tu chodzi o to, że liczby[1000] i hipotetyczne ilosc_liczb musi mieć identyczny zasięg, a modyfikacja pierwszego powinna mieć wpływ na drugie i vice versa (abstrahując od sensu ręcznej modyfikacji zmiennej ilosc_liczb), dlatego obie zmienne powinny być zapakowane razem w jedną klasę/strukturę. - Jak to powinno wtedy prawidłowo wyglądać?

0

Nie używaj zmiennych globalnych. To antywzorzec programistyczny. W internecie znajdziesdz tysiące uzasadnień.

int main()
{
    Numbers numbers = wprowadz();
    sortuj(numbers);
    wyswietl(numbers);
    return 0;
}

A w idealnym świecie:

    Numbers numbers = new Numbers();
    numbers.LoadDataFromInput();
    numbers.Sort();
    numbers.PrintAll();

modyfikacja pierwszego powinna mieć wpływ na drugie i vice versa (abstrahując od sensu ręcznej modyfikacji zmiennej ilosc_liczb), dlatego obie zmienne powinny być zapakowane razem w jedną klasę/strukturę.

  • Jak to powinno wtedy prawidłowo wyglądać?

Pseudokod:

void AppendNewNumber(Numbers numbers, Number number) // dodanie nowej liczby modyfikuje naraz obie zmienne, to dlatego była mowa o tym, że są to zmienne powiązane ze sobą
{
  int offset = numbers.length;
  // tu jeszcze można sprawdzić, czy nie wychodzimy poza rozmiar tablicy i rzucić wyjątkiem/exit'em z odpowiednią treścią
  numbers[offset] = number;
  ++numbers.length;
}

W idealnym (jednowątkowym) świecie:

void AppendNewNumber(Numbers numbers, Number number)
{
  numbers.Append(number);
}


class Numbers
{
  private const MaxLength = 1000;
  private Number[MaxLength] numbers;
  private int length = 0;

  public Add(Number number)
  {
    if (length == MaxLength)
      throw new ArgumentOutOfRangeException();
    if (number == null)
      throw new ArgumentNullException();

    numbers[length] = number; // te dwie linijki nie są thread-safe, ale nie chciałem zbyt komplikować
    ++length;
  }
}

1 użytkowników online, w tym zalogowanych: 0, gości: 1