Wiersz danych wejsciowych odczytywany wspak

0

Witam ,mam problem z programem :Napisz program który odczytuje jeden wiersz danych wejściowych a następnie wyświetla wspak
Mam początek programu potrafię wpisać do tablicy znaki ale nie potrafię tej tablicy wyświetlić jej w odwrotnej kolejności oto co napisałem ja ,dzięki wielkie za pomoc

//Program pobiera dane wejsciowe
#include <stdio.h>
#include <conio.h>
#include <string.h>
char tab[100];
char znak;
int k;
int i=0;
main()
{
printf("Program pobiera znaki i wyswietla je wspak\n");
printf("Podaj ciag znakow i nacisnij enter\n");
printf("\n");
for(scanf("%c",&znak);znak!='\n';)
{
tab[i]=znak;
scanf("%c",&znak);
i=i++;

}

for(i=strlen(tab);i>=0;i--)
printf("%c",tab[i]);
getch();
return 0;
}
0

Nie programuję w c, tu masz w C++ starałem się jak najbliższy kod Twemu zrobic :

 
#include <iostream>
using namespace std;


int main()
{
	char tab[100];
	cout << "Podaj lancuch znakow: ";
	cin >> tab;

	for(int i = strlen(tab) - 1; i >= 0; i --)
		cout << tab[i];


	cin.sync();
	cin.get();
	return 0;

}

Wydaje mi się, że Twoim problemem jest pętla ' for(i=strlen(tab);i>=0;i--)'
po prostu musi to wyglądać tak :

for(int i = strlen(tab) - 1; i >=0; i--)
 

Zabrakło Ci '-1' przy strlen(tab). Wyjaśnienie :
masz ciąg znaków np built:
strlen(tab) zwróci Ci 5.
teraz weź sobie wypisz tab[5] - > wyskoczy ERROR ponieważ ostatni Twój element w tablicy ma indeks 4 (w c/c++ tablice numerowane są od 0)
Widzisz już błąd ?

0

Rzeczywiście masz rację co do pętli natomiast i tak nie działa do końca gdyż ,wypisuje tylko ostatnia literę ostatni znak ,a ma wypisywać wszystkie pobrane znaki ,ale dzięki za pomoc

0
  1. A bajt zerowy to Ci Wróżka Stringuszka do tablicy wstawi?
  2. Co się stanie jak z programu skorzysta niespełniony poeta, lubujący się w pisaniu dwudziestozwrotkowców jednym ciągiem?
  3. Po co Ci tam to borlandowe conio, skoro go nie potrzebujesz?
  4. Formatowanie to chyba do linijki robiłeś. Czytać się tego nie da.
0

Witam ,wiec jak powinien wyglądać ten program ,gdybym był zawodowym programista to bym nie prosił o pomoc ,ja dopiero stawiam pierwsze kroki ,krytykować jest dużo łatwiej i może każdy ,natomiast poprawić i wytłumaczyć dlaczego ma tak być a nie inaczej potrafi niewielu.Mimo to dzięki za szczere chęci.

0

Ależ to są podstawy absolutne, tylko albo niedoczytałeś, albo Ci się nie chciało. Wskazówki Ci dałem, ale widzę że muszę rozjaśnić:

  1. Długość danych w tablicy sprawdzasz funkcją strlen(). Co ta funkcja według Ciebie robi? Zwraca długość c-stringa. Czym się charakteryzują c-stringi? Między innymi tym, że zakończone są bajtem zerowym, dzięki czemu funkcje typu strlen() wiedzą gdzie ten łańcuch znaków się kończy. Co się zatem stanie jeśli tego bajtu zerowego nie będzie? Funkcji strlen() (i innym tego typu) to zwisa i powiewa. Będzie sobie szła po pamięci do czasu, aż napotka bajt zerowy. Widzisz teraz problem? Odczytujesz znaki i wkładasz je do tablicy znaków, a później ją traktujesz jak c-string, pomimo że c-stringiem nie jest, bo nie zakończyłeś danych bajtem zerowym. BTW, dodaj sobie licznik w pętli odczytującej i będziesz wiedział ile znaków wczytałeś. Nie będziesz musiał się bawić w bajty zerowe, ani strlen().
  2. Wielokrotnie było już mówione wszędzie (włączając to Forum), że runtime C nie pilnuje zakresów tablic. Co to znaczy? Alokujesz sobie miejsce na stuelementową tablicę, wrzucasz do niej dane, ale nie sprawdzasz czy przypadkiem nie wyjechałeś poza jej zakres. Twój program po wpisaniu 100 znaków do tablicy nie przejdzie sobie do następnej instrukcji bo mu się miejsce skończyło. Będzie zapisywał dane dalej, pisząc po pamięci znajdującej za tablicą. Albo sobie w ten sposób nadpiszesz kolejne zmienne, albo Ci się program wykrzaczy z groźnym komunikatem. To jest tak podstawowy błąd, że dziwię się iż na zajęciach z programowania nie mówią o tym na pierwszej godzinie.
  3. Nie wykorzystujesz funkcji z biblioteki conio, więc na grzyba ją includujesz? Tam są jakieś borlandowe rzeczy do obsługi konsoli, typu przemieszczanie kursora itp.
  4. Formatowanie kodu JEST ISTOTNE jeśli oczekujesz pomocy od kogoś. My nie pisaliśmy tego, nie wiemy co tam jest. Musimy kod przeanalizować, a jeśli jest nieczytelny z powodu braku formatowania, to utrudnia nam to udzielenie Ci pomocy. To chyba nie jest jakaś wielka filozofia żeby porobić wcięcia, a niektóre IDE mogą to nawet zrobić za Ciebie. Nie zmuszaj nas jednak do tego żebyśmy ślęczeli nad Twoim kodem i próbowali go w myślach przeformatować. Jeśli masz to gdzieś, to licz się z tym, że albo pomocy nie uzyskasz (nikomu nie będzie się chciało analizować kodu), albo na Ciebie nakrzyczą (w nadziei, że to poprawisz i więcej nie będziesz wklejał na Forum śmietnika).
0

Moje uwagi oprócz tego co powiedziane:
Nie używaj tak for! Jest to nieczytelne i błędogenne. Lepiej zrób tak:

char znak;
do
{
  scanf(...)
}
while(znak != '\n');

Operator postinkrementacji (++) sam zwiększa wartość zmiennej i nie trzeba tutaj przypisania. Wystarczy samo i++;

0

Nie tyle nie trzeba przypisania przy i++ co nawet nie wolno go stosować. Konstrukcja i=i++ to jest undefined behaviour i nie wiadomo co to zrobi.

0
byku_guzio napisał(a):

Nie tyle nie trzeba przypisania przy i++ co nawet nie wolno go stosować. Konstrukcja i=i++ to jest undefined behaviour i nie wiadomo co to zrobi.

Nooo moim zdaniem przypisze i do i po czym zwiększy i. Jestem bardziej optymistycznie do tego nastawiony, ale podzielam Twoje zdanie, że nie należy tego stosować ;P

0

Dzięki za wszelkie uwagi ,udało mi się już program działa ,natomiast nasuwa mi się pytanie ,jak słusznie zauważył Kumashiro, że tablica ma ograniczenie 100 znaków ,jak miało by to wyglądać by tablica nie była ograniczona.

0

Jeśli tablica ma być tablicą (ciągłym blokiem pamięci) to możesz zaalokować ją sobie dynamicznie (malloc, calloc), a potem realokować o kolejny blok jeśli zabraknie miejsca (realloc), ale to jest kosztowne. Innym rozwiązaniem jest lista powiązana, którą później składasz do kupy, lecz dla wartości typu char linked list to lekki overkill.
Jeśli nie potrzebujesz ciągłego bloku pamięci, możesz spróbować zrobić listę powiązaną, ale jako elementów użyć bloków pamięci (minitablic). Nie da się tego obsłużyć jako tablicy (brak ciągłości), ale możesz opakować to funkcjami-generatorami, które zadbają o przeliczanie indeksu i skakanie do właściwego węzła, lub możesz po zakończeniu wczytywania danych zaalokować przestrzeń na wszystko (jak sobie będziesz zapisywał ile tam wleciało, to będziesz wiedział ile zaalokować) i po kolei przekopiować dane do ciągłej tablicy (memcpy), ale to ostatnie rozwiązanie jest pamięciożerne (zajmujesz ponad dwa razy tyle pamięci). Pytanie tylko czy taki stopień skomplikowania ma sens. W przypadku Twojego programu prościej jest dodać sobie licznik do pętli i kiedy wpiszesz wartość numer 100 (w przypadku tablicy znaków) lub 99 (w przypadku c-stringa), przerywasz zczytywanie wejścia i kontynuujesz wykonanie programu. W ten sposób nie odwrócisz całego ciągu, który chciał podać user, ale nie wyjedziesz poza tablicę i jakiś tam wynik zwrócisz, a o to chyba chodzi tutaj.
W C nie ma czegoś takiego jak kontener samorozszerzający się. Jak zaalokujesz jakieś miejsce w pamięci (statycznie lub dynamicznie), to finito, tylko tyle będziesz miał i koniec. Są różne biblioteki np. do stringów, które rozwiązują ten problem na różne sposoby (np. kiedyś była taka biblioteka bstring, która miała rozszerzający się kontener na c-stringi), ale wtedy nie masz zbytnio wyboru co do metody zwiększania takiego ciągu. Nie ma niestety najlepszego rozwiązania. Albo zużywasz dużo pamięci, albo marnujesz cykle na przerzucanie danych między blokami. Wybór właściwego powinien zależeć od tego, co dla Ciebie ma większe znaczenie (w jakim środowisku będzie działał program, ile tych danych będzie, jak często będą napływać, w jakich porcjach itp. itd.)
Najprostszym rozwiązaniem jest alokacja odpowiednio dużej tablicy/bufora i pilnowanie żeby nie trafiło tam za dużo. Czasami maksymalną ilość danych da się przewidzieć. Weźmy taki przykład: powiedzmy, że budujesz ścieżkę do pliku stat aktualnego procesu w systemie plików /proc (taki wynalazek na uniksach). Pełna ścieżka ma postać /proc/<identyfikator procesu>/stat. Identyfikator procesu jest liczbą szesnastobitową bez znaku, czyli może mieć maksymalnie 5 cyfr (65535). Wiadomo zatem, że aby przechować całą ścieżkę potrzebujemy tablicy o długości 17 znaków (6 na /proc/ + 5 na identyfikator procesu + 5 na /stat + 1 na bajt zerowy terminujący ciąg). Ponieważ PID pobierasz funkcją systemową, masz pewność że 17 znaków będzie wystarczyło, chyba że w przyszłości standard POSIX wprowadzi 32-bitowe identyfikatory procesów (marne szanse), ale to można załatwić makrem.
Jeśli pobierasz dane od użytkownika, nie masz pewności ile user wklepie na wejściu. A może się pomyli i na wejście przekieruje zawartość jakiegoś pliku, albo kot mu będzie łaził po klawiaturze, albo będzie złośliwy (user, nie kot)? Generalna zasada jest taka: nigdy nie ufamy danym z zewnątrz. Dotyczy to nie tylko długości, ale także np. formatu (jeśli ma to znaczenie). Oczekujesz, że user na wejście poda liczbę? A co jeśli wpisze dupa? Zawsze powinieneś traktować użytkowników jak pawiany walące bez ładu młotkami w klawiaturę i sprawdzać czy dane wejściowe są zgodne z oczekiwaniami programu. Inaczej może się to źle skończyć. Wyjechanie poza tablicę może np. zmienić działanie, bo nadmiarowe dane nadpisały licznik używany w pętli. Program wtedy działa, ale nieprawidłowo i nie wiesz zbytnio dlaczego bez ślęczenia nad debuggerem.
Ja mam świadomość, że wiele osób uważa to za głupotę w programach na zajęcia. No bo po co się bawić w sprawdzanie zakresów, skoro do testów będą zawsze podawane prawidłowe dane, a wykładowcy zależy na rozwiązaniu problemu, a nie na obsłudze wszystkich możliwych błędów. Jest w tym trochę racji, ale z drugiej strony trzeba sobie wcześnie wyrabiać nawyki, żeby później (kiedy będzie to miało znaczenie np. dla bezpieczeństwa) zwracać na to uwagę "naturalnie".

EDIT: Autokorekta. Jest kontener samorozszerzający się. Jest nim plik, ale to zazwyczaj jest wolniejsze od realokacji i nie wszędzie możliwe do zrealizowania (środowisko bez pamięci stałej lub odpowiednika, chroot/jail bez praw do zapisu itp.)

0

Dzięki za zainteresowanie tematem i wyjaśnienia, pewno nie raz jeszcze będę miał pytania mam nadzieje że pomożecie.

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