Błędy w moich programach do ćwiczeń z książki Język ANSI C

0

Witajcie, postanowiłem założyć nowy temat, tym razem w odpowiednim dziale. Nie tytułuje konkretnie (nr zadania) tematu, bo problemów napotkam z pewnością jeszcze multum.

Chodzi o zadanie:
"Napisz program, który każde słowo wejściowe zapisze w osobnym wierszu"

mój program:

#include <stdio.h>

main()
    {
        int c;
        while ((c=getchar())!=EOF)
            {
                if (c==' ' || c=='\t' || c=='\n')
                    {
                        printf("\n");

                        while(c==' ' || c=='\t' || c=='\n')
                            {
                                c=getchar();
                            }
                    }

                else
                    putchar(c);
            }
    }

Problem polega na tym, że program ucina pierwsze literki wyrazów, np.:
Dwa słowa -> Dwa(/n)łowa
Dwa słowa ->wa(/n)łowa

Gdzie popełniłem błąd? Wydaje mi się, że program powinien działać poprawnie (schemat blokowy).
Ponadto program "działa" po wciśnięciu klawisza ENTER, zamiast po wciśnięciu EOF. Dlaczego tak się dzieje, podczas gdy w niektórych programach trzeba wcisnąć EOF?

Dzięki za pomoc!

0

Wywal:
else

0

Ok, działa - super :D. Tylko teraz chciałbym wiedzieć dlaczego tak się działo (bo ewidentnie nie rozumiem tutaj działania if/else.
W/g mnie (i mojej logiki):

  1. Program pobiera znak
  2. Sprawdza czy znak jest równy ' ', '\t', '\n'.
  3. Jeśli tak, na wypisać '\n'.
  4. W pozostałych przypadkach (czyli znak różny od w/w) ma wypisać to, co mu się przytrafiło.
    I tu się rodzi pytanie - skąd się wzięło, że nie ma wypisać w pozostałych wypadkach, tylko w każdym(!) wypadku?
0
x153 napisał(a):

Ok, działa - super :D. Tylko teraz chciałbym wiedzieć dlaczego tak się działo (bo ewidentnie nie rozumiem tutaj działania if/else.
W/g mnie (i mojej logiki):

  1. Program pobiera znak
  2. Sprawdza czy znak jest równy ' ', '\t', '\n'.
  3. Jeśli tak, na wypisać '\n'.
  4. W pozostałych przypadkach (czyli znak różny od w/w) ma wypisać to, co mu się przytrafiło.
    I tu się rodzi pytanie - skąd się wzięło, że nie ma wypisać w pozostałych wypadkach, tylko w każdym(!) wypadku?

Ja bym zamiast *else *wywalił całe drugie while, bo to z tego powodu źle działa i zupełnie nie jest tam potrzebne.
Bo po wywaleniu else, program będzie działa dobrze, ale jeżeli będą koło siebie dwie spacje, to pojawi się pusta linia (bo drugi *while *pobiera znak, ale nie sprawdza co to za znak)
Wywal ten drugi *while * i wtedy myślę, że bez problemu odpowiesz sobie na powyższe pytania.

0

while działa dopóki nie pobierze normalnego znaku, a ten normalny znak (pobrany w while) się nie wydrukuje.

0

Najpierw odpaliłem program bez drugiego while, ale wtedy gdy miałem 2 spacje miałem dwa '/n', a istotą programu jest chyba to, by niezależnie od ilości znaków odstępu układał te wyrazy w nowej linii.

I teraz nie do końca rozumiem chyba co robi mój drugi while.
Wrzuciłem to po to, by zapętlić program w oczekiwaniu na znak inny od znaku odstępu. Dlaczego to while ma wpływ na dalsze działanie programu? Po zakończeniu działania tego while (otrzymaniu w c=getchar() innego znaku niż odstęp) program powinien pokazać '\n' (po spełnieniu warunku if oczywiście), a w przeciwnym przypadku (nie spełnienia warunku if) po prostu wklepać otrzymany znak.

Czemu wyrzucenie else pomaga? Czego nie łapię w tym mechanizmie, bo mnie zakręciliście trochę chłopaki teraz ;)

0

"while (otrzymaniu w c=getchar() innego znaku niż odstęp) " i właśnie tego innego znaku nie wyświetlasz.
Bez else ten znak się wyświetli przez putchar();

0

A - kurde, faktycznie!
Przecież znak pobrany przez drugie while znajduje się w dalszej części programu a nie pierwszy, kurcze ;x.
No i to else chyba tam jest zupełnie zbędne, jak dam na początku znak "x" to program ominie if z pętlą while i po prostu wyświetli "x". Takie proste.
Cóż, kiedyś chyba nawet ja się nauczę, przekombinowałem.
Dzięki wielkie za pomoc!

0

Tak na zakończenie tematu, żeby uniknąć dwóch getchar-ów i pojawiania się pustych linii wprowadziłbym pomocniczą zmienną i zrobił to tak.

#include <stdio.h>

main()
{
    int c;
    int is_new_line = 1 ;
    
    while ((c=getchar())!=EOF)
    {
        if (c==' ' || c=='\t' || c=='\n')
        {
            if (is_new_line == 0)
            {
                printf("\n");
                is_new_line = 1;
            }
        } else {
            putchar(c);
            is_new_line = 0;
        }
    }
}
0

Ładny kod :).

Teraz... Cóż. Męczę się ciągle z zadaniem pt. "napisz program wyświetlający histogram długości słów wejściowych".
Po pierwsze - zdaje mi się, że program jest zrobiony okrutnie na około. Ten problem chciałbym póki co jednak zostawić.
Bardziej interesuje mnie to, dlaczego poniżej wklejony program NIE DZIAŁA.

#include <stdio.h>

main()

{
    int c, i, n3, n4, n5, n6, n7, n8, n9, n10;
    i=n3=n4=n5=n6=n7=n8=n9=n10=0;
    while ((c=getchar())!=EOF)
    {
        while(c!='\t' && c!='\n' && c!=' ')
            {
                ++i;
                c=getchar();
            }
                if (i>=10)
                    ++n10;
                else if (i=9)
                    ++n9;
                else if (i=8)
                    ++n8;
                else if (i=7)
                    ++n7;
                else if (i=6)
                    ++n6;
                else if (i=5)
                    ++n5;
                else if (i=4)
                    ++n4;
                else if (i<=3 && i>0)
                    ++n3;
                i=0;

    }
    printf("<=3 : ");
    while(n3>0)
        {
            printf("O");
            --n3;
        }
    printf("\n");

    printf("4   : ");
    while(n4>0)
        {
            printf("O");
            --n4;
        }
    printf("\n");

    printf("5   : ");
    while(n5>0)
        {
            printf("O");
            --n5;
        }
    printf("\n");

    printf("6   : ");
    while(n6>0)
        {
            printf("O");
            --n6;
        }
    printf("\n");

    printf("7   : ");
    while(n7>0)
        {
            printf("O");
            --n7;
        }
    printf("\n");

    printf("8   : ");
    while(n8>0)
        {
            printf("O");
            --n8;
        }
    printf("\n");

    printf("9   : ");
    while(n9>0)
        {
            printf("O");
            --n9;
        }
    printf("\n");

    printf("10>=: ");
    while(n10>0)
        {
            printf("O");
            --n10;
        }
    printf("\n");
}

Nie działanie objawia się tym, że wyrazy o liczbie wyrazów co najmniej 10 są przedstawiane prawidłowo, natomiast wszystkie pozostałe są przedstawiane jako 9.
Zdaje mi się, że problem tutaj powinien znajdować się w tej części programu:

    while ((c=getchar())!=EOF)
    {
        while(c!='\t' && c!='\n' && c!=' ')
            {
                ++i;
                c=getchar();
            }
                if (i>=10)
                    ++n10;
                else if (i=9)
                    ++n9;
                else if (i=8)
                    ++n8;
                else if (i=7)
                    ++n7;
                else if (i=6)
                    ++n6;
                else if (i=5)
                    ++n5;
                else if (i=4)
                    ++n4;
                else if (i<=3 && i>0)
                    ++n3;
                i=0;

    }

Chciałem (dla pewności) sprawdzić, czy jeśli usunę else coś się stanie i tak - stało się. Pokazuje dowolne słowo jako słowo o liczbie znaków 4, 5, 6, 7, 8 i 9. Nie ważne czy ma jeden znak, czy pięćdziesiąt. Czysto logicznie - zdaje mi się, że program powinien być ok. Więc... Gdzie popełniłem błąd tym razem?

0

nie * i=9* , tylko i==9 , itd.

0

Ok, mój błąd :D. Wielkie dzięki ;).
To teraz trzeba zająć się drugim problemem - jak to powinienem zrobić? Tj w jaki sposób tutaj mogę uprościć kod? W tym temacie były tablice, ale tablice, gdzie były liczby wypisane po kolei, nie wiem jak wykonać operacje na poszczególnych "komórkach" tabeli. Powinienem wymyślić coś prostszego?

0

Zdecydowanie wynik powinien być przechowywany w tablicy.
Wtedy można chociażby usunąc te paskudne 16 printf i wrzucić w jedną pętle.

0

hm... Czyli powinienem stworzyć np. 15-el tablicę, indeks = ilość liter (sposób liczenia chyba muszę zostawić?), dorzucić zabezpieczenie, że jeśli jest więcej jak 15 to programma wyświetlić tam O (na tej samej zasadzie co teraz z 3 i 10) i będzie okej?

0
#include <stdio.h>
 
int main()
  {
   int c,k,i=0,n[10]={0};
   while((c=getchar())!=EOF)
     {
      for(i=0;c!='\t' && c!='\n' && c!=' ';++i) c=getchar();
      if(i>=10) i=0;
      ++n[i];
     } 
   for(k=1;k<10;++k)
     {
      printf("%d   : ",k);
      while(n[k]--) printf("O");
      printf("\n");
     }
   printf(">10 : ");
   while(n[0]--) printf("O");
   return 0; 
  }
0

Zerżnąłem zastosowanie tabeli i napisałem od nowa :D. Tj program niewiele się różni (bo wzorowany na twoim pomyśle) - zastąpiłem tylko w niektórych miejscach twoje założenia na moje (bo nie wiedziałem skąd się wziął zapis --/ -->) i zmieniłem składnie na czytelniejszą dla mnie.

#include <stdio.h>

int main()
  {
      int c,i,k=0,n[10]={0};
      while((c=getchar())!=EOF)
      {
          for(i=0; c!=' ' && c!='\n' && c!='\t'; ++i)
            c=getchar();
          if (i>=10)
            i=0;
          ++n[i];
      }
      for(k=1; k<10;++k)
      {
          printf("%d   : ", k);
          while(n[k]>0)
            {
                printf("O");
                --n[k];
            }
          printf("\n");
      }
      i=0;
      printf(">=10: ");
      while(n[i]>0)
      {
          printf("O");
          --n[i];
      }
  }

Teraz ten program wygląda dużo lepiej jak na początku, dzięki! :)
I skumałem dzięki twojemu programowi jak działa tablica.

0

Ach - jeszcze jedno pytanie;
czemu zapis:

int c,k,i=0,n[10]={0};

jest poprawny, a zapis:

int c,k,i,n[10];
c=k=i=0;
n[10]={0};

już nie?

0

Bo przypisania wartości w ten sposób do tablicy można dokonać tylko w trakcie jej tworzenia.

0

Takiej odpowiedzi się spodziewałem. Jak chcę przypisać wartość tablicy po utworzeniu to musiałbym wpisać np. n[x]={0}?

0

Nie, po prostu nie możesz i już. Po utworzeniu możesz przypisywać wartości tylko konkretnym elementom tablicy.

0

Odradzam użycia takiego formatowania:

if (i>=10)
            i=0;

używaj to:

if (i>=10) i=0;

lub to:

if (i>=10)
  {
   i=0;
  }
while(n[k]--) ...

Oznacza: Sprawdź czy n[k] jest zerem. Jeżeli jest zerem to koniec pętli jeżeli nie to wykonaj kolejny krok. Ale po sprawdzeniu niezależnie od wyniku zmniejsz n[k]. Czyli właściwie jest tym samym co while((n[k]--)!=0) ...


```c
while(n[k]-->0)

oznacza:while((n[k]--)>0)

Pisałem na szybko wprost na forum bez sprawdzenia więc jakoś mi się nie usunęło to >0 trochę niżej usunęło się. (już poprawiłem).

zamiana:
```c
while(n[0]>0) ...

na:i=0;
while(n[i]>0)

jest pozbawiona sensu ponieważ w pierwszym przypadku kompilator wyliczy co sprawdzamy zaś w drugim adres będzie wyliczany w trakcie wykonania.
0

Z tym i=0 to po prostu nie chciałem zostawiać pustego, nic nie znaczącego zera. Tak na przyszłość, gdy kod będzie miał kilkaset linijek, a nie trzydzieści i będę do tego programu chciał wrócić po pół roku. Takie sugestie są poczynione w "Język ANSI C", chyba że przez te ~20lat zmieniły się nieco standardy ;).

Formatowanie da się załatwić. Zauważyłem po prostu, że w książce tak robią to "przytuliłem". A jak to jest z tym formatowaniem w ogóle? Podobno standard to K&R, co na moje oko jest nieestetyczne i nieczytelne. Dużo lepiej w/g mnie wygląda Allaman i jeśli nie będzie można potraktować tego jako błąd... Chyba wolałbym używać właśnie tego stylu. Nadmienię tylko, że w tym roku rozpoczynam studia, na drugim roku będę miał dużo programowania w C i wolałbym pisać standardowe programy - w miarę możliwości oczywiście.

0

Ok, po przerwie (rekrutacja na studia, sporo obowiązków domowych etc.) wróciłem do książki, wróciły też do mnie problemy...

Zadanie brzmi "Napisz program, który wypisze wszystkie wiersze wejściowe dłuższe niż 80 znaków".

No kurcze - wszystko fajnie, podeszłem na lajcie z myślą "ale banał"... I doszedłem do tego, że ma wypisać WSZYSTKIE wprowadzone na raz.
Nasunęło się pytanie - jak zapisać tabelę do tabeli?

Pomysł był taki - utworzyć tabelę wyrazową lineFN[a], gdzie dla każdego a przypiszę wartość tabeli line (czyli cały wiersz).
wykonanie (podczas robienia zorientowałem się już, że to głupie):

#include <stdio.h>

#define MAXLINE 500
#define PROG 19

int getline(char line[], int maxline);

int getline(char s[], int lim)
{
    int c,i;

    for (i = 0; i<lim-1 && (c=getchar())!=EOF && c !='\n'; ++i)
        {
            s[i] = c;
        }
    if (c=='\n')
        {
            s[i]=c;
            ++i;
        }
    s[i]='\0';
    return i;
}

main()
{
    int a;
    int b;
    int i;

    char line[MAXLINE];
    char lineFN[a];

    a=0;
    while((i=getline(line,MAXLINE))> 0)
        {
            if (i>PROG)
            {
                lineFN[a]=line;
                ++a;
            }
        }
    for (b=0; b<a; ++b)
        printf("%c", lineFN[b]);
    return 0;
}

Zamieszczam, żeby właściwie nie było, że nie próbowałem... Próbowałem.
Więc - pomysł jest zły, czy wykonanie jest złe? Znalazłem odpowiedzi do tego zadania na pebie, ale były pisane albo w C++, albo w sposób dla mnie nie zrozumiały (użyte polecenia, których nie było w piaskownicy książki Język ANSI C).

Proszę, jeszcze raz, o pomoc :)
Dzięki

0
#include <stdio.h>



int main() {
	char ch,tab[100][500] = {{0}};
	unsigned int i=0,j=0,k,h;
	unsigned int loop = 1;
	
	while(loop){
		for(j=0;(ch = getchar())!='\n' && (ch != EOF);++j)
			tab[i][j] = ch;
               // tab[i][j] = '\0';
		if(j > 50) i++;
		if(ch == EOF) loop = 0;

	}
	for(k=0;k<i;++k){
           if(k > 0) putchar('\n');
	   for(h=0;tab[k][h]!='\0';++h)
	     putchar(tab[k][h]);
        }

	return 0;
} 
0

Ehh nie skomentowałem, podejście jest proste, czyli tablica tablic, mniej więcej coś takiego:

tab[i][j] = {
[i0,j0][i0,j1][][][][][][][][][]
[i1,j0][i1,j1][][][][][][][][][]
[i2,j0][i2,j1][][][][][][][][][]
[i3,j0][i3,j1][][][][][][][][][]
[i4,j0][i4,j1][][][][][][][][][]
}

Wczytujesz najpierw do pierwszego wiersza i sprawdzasz czy było > 50 znaków jeżeli tak to zwiększasz i i w kolejnej iteracji wpisujesz do następnego wiersza, jeżeli nie to nadpisujesz znaki.

dodanie znacznika <code class="c"> - fp

0

A bez takich podwójnych tablic i unsigned? Jestem na poziomie 1.9 z książki Język ANSI C i tam takie rzeczy się nie dzieją :/

Kurcze - serio mam problem z mniej więcej co drugim/trzecim zadaniem, uczę się programować od zera, może lepiej przerzucić się na książkę, gdzie to wszystko jest ładnie wytłumaczone od podstaw? Może "Język C. Szkoła programowania"? A może przy nauce z każdej książki napotkam taki problem?

Dzięki za odpowiedzi, pozdrawiam

0
_13th_Dragon napisał(a):
if (i>=10)
  {
   i=0;
  }

Wygląda rodem jak z DevC++. Dwie spacje po klamerkach i jedna wewnątrz? Gdzie konsekwencja? Z jakiej paki klamerki są w ogóle wyciągnięte w prawo? Żeby zrobić dwa poziomy wcięć dla jednej konstrukcji?

0

Witam,
Oryginalne rozwiązanie zadania 1.8 z ćwiczeniówki
Czy nawiasy są postawione poprawnie?

#include <stdio.h>

int main(){

    int c, nb, nt, nl;
    nb=0;
    nt=0;
    nl=0;
    printf("space tab newline");
    while((c=getchar())!=EOF){
        if(c==' ')
            ++nb;
        if(c=='\t')
            ++nt;
        if(c=='\n')
            ++nl;
    }
    printf("    %d   %d       %d\n",nb, nt, nl);


}
 

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