Miałem napisać taki program, który wczytuje string z tablicy napis1 i wyrazy parzyste mają znaleźć się w tablicy (znakowej) parzyste , a nieparzyste w tablicy nieparzyste. String ma 5 wyrazów więc ostatni jest nieparzysty stąd taki dość dziwny warunek przy while. (Zaznaczyłem to miejsce wykrzyknikiem w komentarzu)…
Gdy go kompiluje wyświetla się taki komunikat:
#include
using namespace std;
void strcpy(char zrodlo[], char parzyste[], char nieparz[]);
int main()
{
char napis1[23] = { "It is very stupid test" }; // 22 elementów + NULL (0)
char parzyste[23];
char nieparzyste[23];
strcpy(napis1, parzyste, nieparzyste);
cout "Mamy ciag wyrazow w tablicy 'napis1': " napis1 "\n"
"\tWyrazy parzyste to: " parzyste
"\tWyrazy nieparzyste to: " nieparzyste;
cout "\n\n\n\n\n\n---------------------------------------------------------------\n";
system("pause");
}
void strcpy(char zrodlo[], char parzyste[], char nieparz[])
{
int i = 0;
int x = 0;
int bbb = 0;
while(nieparz[i] != 0) // --- to ten dość dziwny warunek, no ale lepszego nie wymyśliłem...
{
for( ; ; i++)
{
nieparz[i] = zrodlo[bbb];
b++;
if(nieparz[i] == ' ')
{
break;
}
}
for( ; ; x++)
{
parzyste[x] = zrodlo[bbb];
bbb++;
if(parzyste[x] == ' ')
{
break;
}
}
}
}
nie umiem dobrze c++ ale wydaje mi się, że po pierwsze wywołujesz funkcję samą funkcją (sic), która z resztą jest (jw) źle napisana, mhh
po drugie jeśli nawet użyjesz prawidłowo strcpy na początku, (co wydaje mi się jest błędem), to już na początku będziesz miał w tablicach parzyste i nieparzyste skopiowane znaki.
wydaje mi się, że trzeba by użyć jedną pętlę
for(; i sprawdzać indeksy czy są parzyste czy nie, i wtedy wrzucać znaki do poszczególnych tablic
First-chance exception at 0x00000000 in Zamiana_tablicy_znakowej_na_parzyste_i_nieparzyste_wyrazy.exe: 0xC0000005: Access violation reading location 0x00000000.
I tak ze 100 linijek tego…
Dwa parametry będzie miała, jak jej nie napiszę ja, tylko wywołam z jakiejś biblioteki, chyba … Zresztą i tak nawet po zmianie nazwy jest źle… Takie komunikaty na moje oko znaczą, że coś jest na pewno z pętlą while, pewnie pamięć zapełniona albo cuś…
EDIT:
Ale co podejrzewacie, że jest źle z tą pętlą :?: Może jakiś inny warunek :?:
Nie chcę wnikać w Twój kod, bo kompletnie nie mam pomysłu co chciałeś w ten sposób osiągnąć. Widzę tylko dwa nieskończone fory w jednym while’u i straszny galimatias w indeksach. Mieszasz coś w pamięci i dziwisz się, że błędy są. Jeśli chcesz się dowiedzieć dlaczego, to najprościej będzie Ci chyba użyć debbugera.
A najlepiej będzie NAJPIERW wymyślić algorytm, a POTEM go zapisać w języku.
Mamy string “It is very stupid test”. W funkcji jest przypisany do tablicy zrodlo[bbb]. bbb zawsze na koniec pętli się inkrementuje. Przykładowo pierwszy wyraz będzie na pewno nie parzysty bo można powiedzieć, że jest pierwszy, a 1 jest nie parzysta. Dlatego jest on w pierwszej pętli for:
Pierwszy element tablicy zrodlo jest przypisywany do pierwszego elementu tablicy nieparz. Jest to literka “I”, następnie jest “t” i spacja " ". Jeżeli jest spacja to oznacza to, że to koniec wyrazu i wyskakujemy z pętli. Zgodnie z logiką po wyrazie nieparzystym przychodzi kolej na parzysty Za to odpowiada 2 pętla for. W tym przypadku jest dokładnie tak samo jak w pierwszym, tyle, że zamiast tablicy nieparz jest tablica parzyste. Po “przebiegnięciu” wyrazu “is” zaczyna się ponownie wykonywać pętla while, ponieważ aktualny element tablicy parzyste jest różny od zera - warunek jest związku z tym prawdziwy. C-string kończy się po wyrazie “test”. Znajduje się tam znak 0, a więc w końcu nastąpi przypisanie NULL’a do tablicy nieparz. (Potem już nie ma żadnych wyrazów, zapomniałem więc o takiej modyfikacji 2 pętli for):
Owszem, twój kod jest bardzo zawiły i do tego błędny. Radzę napisać wszystko od początku, może lepiej najpierw pomyśleć nad sposobem z kartką i ołówkiem?
Wiesz - można komentować kod w trakcie (używając // i /* */), wtedy nie trzeba pisać oddzielnych wyjaśnień. Masz dziwnie ułożone pętle, na dodatek dwa fory bez ograniczenia przebiegu pętli, a jedynie z break, który nie musi nigdy nastąpić. Obstawiam, że dlatego wyjeżdżasz w pętli poza pamięć tablicy i masz błąd pamięci.
Nie wiem, czemu używasz trzech pętli, gdy wystarczy jedna, a do zmienna logiczna przechowująca informację o tym, czy wyraz jest parzysty czy nie. W pętli wystarczy w konstrukcji if-else przypisać znaki ze źródła do odpowiedniej tablicy w zależności od wartości zmiennej logicznej.
Ehh, chodzi tu o zmienną typu bool, tak :?: Nie mogę poradzić sobie z jedną rzeczą - przecież gdy nastąpi spacja musi oznacza to, że następuje drugi wyraz - parzysty lub nie. No ale na końcu nigdy nie ma spacji…
char napis1[23] = { "It is very stupid test" };
A przecież nie pisze się tak:
char napis1[23] = { "It is very stupid test " };
Mógłbym to zrobić jakoś poprzez przesłanie arg. o rozmiarze stringu, ale funkcja niestety musi mieć tylko te 3 argumenty…
Przechodzisz po wszystkich znakach w pętli, dopóki wartość znaku = 0, czyli nie trafisz koniec łańcucha.
while(zrodlo[i] != 0)
{
...
i++;
}
Masz zmienną typu bool, która określa, czy wyraz jest parzysty, czy nieparzysty - pierwszy jest oczywiście nieparzysty. W pętli sprawdzasz ten warunek i na tej podstawie kopiujesz znak ze zrodlo do odpowiedniej tablicy. A następnie sprawdzasz, czy znak ze źródła nie jest spacją. Jeśli jest, to po prostu zmieniasz wartość zmiennej logicznej. I nie ma znaczenia, czy na końcu jest spacja, czy nie.
Nie wiem jak Ci dziękować Tyle, że tam jest mały błąd, inkrementacja i powinna być na początku, bo do tych jednej z tablic nie dopisze się 0 i będzie niepoprawny C-string…
#include
using namespace std;
void zamiana(char zrodlo[], char parzyste[], char nieparz[]);
int main()
{
char napis1[23] = { "it is very stupid test" }; // 22 elementów + NULL (0)
char parzyste[100];
char nieparzyste[100];
zamiana(napis1, parzyste, nieparzyste);
cout << "Mamy ciag wyrazow w tablicy 'napis1': " << napis1 << "\n"
<< "\tWyrazy parzyste to: " << parzyste << "\n"
<< "\tWyrazy nieparzyste to: " << nieparzyste;
cout << "\n\n\n\n\n\n---------------------------------------------------------------\n";
system("pause");
}
void zamiana(char zrodlo[], char parzyste[], char nieparz[])
{
int x = 0; // specjalnie dla naszej tablicy 'nieparz'
int b = 0; // specjalnie dla naszej tablicy 'parzyste'
int i = -1; // specjalnie dla oryginalnego zrodla
bool wyraz = false;
while(zrodlo[i] != 0)
{
i++;
if(wyraz)
{
parzyste[b] = zrodlo[i];
b++; // po parzyste[0] następuje element [1] więc za każdym
if(zrodlo[i] == ' ') // razem inkrementujemy 'b'
wyraz = false;
}
else
{
nieparz[x] = zrodlo[i];
x++; // po nieparz[0] następuje element [1] więc za każdym
if(zrodlo[i] == ' ') // razem inkrementujemy 'b'
wyraz = true;
}
}
}
W DevC++ to działa poprawnie, w Visual 2008 Express niestety nie…
#include
using namespace std;
//void strcpy(char zrodlo[], char parzyste[], char nieparz[]);
void strcpy(const char *zrodlo,char *parzyste,char *nieparz);
int main()
{
char napis1[100] =" It is very stupid test "; // BEZ KLAMER
char parzyste[100];
char nieparzyste[100];
//strcpy(napis1,parzyste,nieparzyste);
strcpy(napis1,parzyste,nieparzyste);
cout
<<"Mamy ciag wyrazow w tablicy 'napis1': \""<
<< "\tWyrazy parzyste to: \""<
<< "\tWyrazy nieparzyste to: \"" << nieparzyste<<'"'<
;
cin.get();
return 0;
}
/* //rozwiązanie primitiwne
void strcpy(char zrodlo[], char parzyste[], char nieparz[])
{
int ip=0,in=0; // poczatkowe indeksy;
bool wyr=false,par=true; // wyr - czy jesteśmy w słowie (false=pomiędzy słowami), par - czy parzysty
for(int i=0;zrodlo[i];++i) // pętla znak po znaku ze źródła
{
if(zrodlo[i]!=' ') // jeżeli nie spacja
{
if(!wyr) // jeżeli to pierwsza litera słowa
{
par=!par; // przełącz
if(par) { if(ip) parzyste[ip++]=' '; } // jeżeli w parzystym i parzyste[] puste to dopisz spacje do parzyste[]
else { if(in) nieparz[in++]=' '; } // jeżeli w nieparzystym i nieparz[] puste to dopisz spacje do nieparz[]
wyr=true; // już jesteśmy w słowie
}
if(par) parzyste[ip++]=zrodlo[i]; // jeżeli w parzystym to dopisz znak do parzyste[]
else nieparz[in++]=zrodlo[i]; // jeżeli w nieparzystym to dopisz znak do nieparz[]
}
else wyr=false; // już jesteśmy pomiędzy słowami
}
parzyste[ip]=0; // znak końca napisu dla słów parzystych
nieparz[in]=0; // znak końca napisu dla słów nieparzystych
}
*/
/* //rozwiązanie normalne
void strcpy(const char *zrodlo,char *parzyste,char *nieparz)
{
bool wyr=false,par=true,fp=false,fn=false; // wyr - czy jesteśmy w słowie (false=pomiędzy słowami), par - czy parzysty
// fp/fn - czy trzeba dodać spacje do nieparzystych/parzystych odpowiednio
// fp/fn - jeżeli true to przed kolejnym słowem dopisz spacje
for(;*zrodlo;++zrodlo) // pętla znak po znaku ze źródła
{
if(*zrodlo!=' ') // jeżeli nie spacja
{
if(!wyr) // jeżeli to pierwsza litera słowa
{
par=!par; // przełącz
if(par?fp:fn) *((par?parzyste:nieparz)++)=' '; // do odpowiedniej listy (w zależności od par) dopisz spacje jeżeli potrzebna
wyr=true; // już jesteśmy w słowie
}
*((par?parzyste:nieparz)++)=*zrodlo; // do odpowiedniej listy (w zależności od par) dopisz znak
(par?fp:fn)=true; // ustaw odpowiednią flagę (w zależności od par) - przed następnym słowem dopisz spację
}
else wyr=false; // już jesteśmy pomiędzy słowami
}
*parzyste=0; // znak końca napisu dla słów parzystych
*nieparz=0; // znak końca napisu dla słów nieparzystych
}
*/
// rozwiązanie ambitne
void strcpy(const char *zrodlo,char *parzyste,char *nieparz)
{
char **w=0; // wskaźnik na parzyste/nieparz będzie zależało od zmiennej par
bool wyr=false,par=true,fp=false,fn=false,*f=0; // wyr - czy jesteśmy w słowie (false=pomiędzy słowami), par - czy parzysty
// fp/fn - czy trzeba dodać spacje do nieparzystych/parzystych odpowiednio
// fp/fn - jeżeli true to przed kolejnym słowem dopisz spacje
// f wskaźnik na fp/fn będzie zależało od zmiennej par
for(;*zrodlo;++zrodlo) // pętla znak po znaku ze źródła
{
if(*zrodlo!=' ') // jeżeli nie spacja
{
if(!wyr) // jeżeli to pierwsza litera słowa
{
if(par) { f=&fn; w=&nieparz; par=false; } // jeżeli poprzednie słowo parzyste, ustaw w/f na niepaz/fn odpowiednio oraz przełącz
else { f=&fp; w=&parzyste; par=true; } // jeżeli poprzednie słowo nieparzyste, ustaw w/f na parzyste/fp odpowiednio oraz przełącz
if(*f) *((*w)++)=' '; // jeżeli potrzebna spacja (wg fp/fn) to dopisz spacje do parzyste/niepaz
wyr=true; // już jesteśmy w słowie
}
*((*w)++)=*zrodlo; // dopisz znak do parzyste/niepaz
(*f)=true; // staw flagę fp/fn - przed następnym słowem dopisz spację
}
else wyr=false; // już jesteśmy pomiędzy słowami
}
*parzyste=0; // znak końca napisu dla słów parzystych
*nieparz=0; // znak końca napisu dla słów nieparzystych
}
EDIT: Dopisałem komentarzy, specjalnie dla osób którzy wolą programy “przejrzyste” od działających
Quentin - Twój kod chodzi w VS poprawnie, tzn. tak jak jest napisany.
Parę uwag:
zmienne i, b, x - ich nazwy niewiele mówią. Ja bym je nazwał np iz, in, ip od “iterator źródła”, “iterator nieparzystych”, “iterator parzystych” - tak będzie się łatwiej zorientować później. Można definiować wiele zmiennych w jednej linii, wiesz o tym?
int iz = 0, in = 0, ip = 0;
zmienna “wyraz” - gorzej jej nazwać się nie dało, ta nazwa kompletnie nic nie mówi. Zmienne logiczne najlepiej nazywać np. “czyCośTam”, wtedy łatwo zrozumieć do czego służą, ja proponuję:
bool czyParzysty = false;
Jeśli i++ (w mojej wersji iz++) dasz na koniec pętli, to nie będziesz musiał zaczynać od -1. Ja bym tak zrobił, bo dla mnie logiczna wydaje się iteracja na koniec pętli 4) Dwa razy sprawdzasz, czy znak jest spacją - ale po co tak? Przecież możesz to zrobić po if - else kopiującym znaki, np tak:
if(zrodlo[iz] == 32) //jeśli to spacja
czyParzysty = !czyParzysty; //to wyraz nie jest taki, jaki był do tej pory
Prawda, że prościej? 5) I najważniejsze - zapomniałeś o “zakończeniu” skopiowanych łańcuchów, przez co mogą być one niepoprawnie wyświetlane, tzn. mogą być wyświetlane śmieci znajdujące się w tablicy, za tekstem, który wpisałeś. I pewno o ten problem z VS Ci chodziło. Po prostu pod koniec pętli trzeba dać:
parzyste[ip] = 0;
nieparz[in] = 0;
[alex] - moim zdaniem każde Twoje rozwiązanie, które jest siecią zagnieżdżonych if-else jest prymitywne. Ale za to jest świetnym przykładem jak nie programować i jak sobie utrudnić życie motając prosty problem w stopniu niesamowitym.
Na dodatek nie sądzę, żeby Quentin był na etapie wskaźników, więc mieszanie mu tutaj nimi jest co najmniej nie na miejscu.
No i wklejanie gotowego kodu (zwłaszcza takiego) ma zerowy skutek dydaktyczny i wcale nikomu nie pomaga, a wręcz odwrotnie. Tak przy okazji, to chyba coś o tym regulamin nawet mówi.
Wiem, ale ja po prostu do tego nie przywykłem jeszcze, tak samo jak nie przywykłem do wyrażeń typu i *= 2
Dzięki za rade, rzeczywiście z tym ‘czy’ dobry pomysł
Myślałem, że nie będzie działać, bo jak i pod koniec pętli dojdzie do zera to następnej pętli już nie będzie i nie przypisze się tego null’a do końcowego wyrazu w tablicy parzyste lub nieparz - w zależności na jakim wyrazie się kończy to. Rozwinę ten temat bardziej żeby upewnić się czy dobrze to rozumiem. Przykładowo jak zapomniałem o przypisaniu tych NULLi jak to piszesz w 5) i i++ było na “starym” miejscu to jest coś takiego w cmd:
Pomyliłem się pisząc poprzedniego posta - ja to zrobiłem na koniec funkcji, już za pętlą. Ale nawet pod koniec ciała pętli też działa (tylko jest to bez sensu). Dlaczego? Bo w tym momencie ip i in wskazują na następne niezapisane jeszcze elementy tablic docelowych, więc zerami nadpisywane jest to, czego w zasadzie jeszcze nie używałeś wcześniej.
No chyba właśnie jest tak, jak piszesz, że inicjalizowane są zerami. Bo Twój kod jest przecież równoważny takiemu:
Wartości w G++ są inicjowane zerami, w VC++ jest to maksymalna wartość dla danego typu(dla int - INT_MAX, dla short - SHORT_MAX itd.), więc w tym wypadku reszta będzie inicjowana w zależności od kompilatora(AFAIK inicjalizacji zbiorowa, jak to nazwałeś, występuje tylko wtedy, gdy podamy jeden element).
EDIT:
W C++ NULL to zwykłe 0, czyli int. W C NULL to zero rzutowane na void*, czyli ((void*)0)
[alex] - przestań wreszcie wciskać swój chłamowaty kod. Co z tego, że szpanujesz użyciem wskaźników, które powinny być wydajniejsze, niż dostęp po indeksach, skoro po Twojej gmatwaninie instrukcji warunkowych procesor skacze jak konik polny? Kod powinien być przede wszystkim przejrzysty i łatwy do zrozumienia.
Jeśli uważasz, że Twoje dzieła są lepsze niż to, do czego Quentin doszedł tutaj z naszą pomocą, to podaj jakiś argument - jestem pewien, że nie tylko ja chętnie go poznam.
Dla przypomnienia:
void strcpy(char zrodlo[], char parzyste[], char nieparz[])
{
int iz = 0, in = 0, ip = 0; //iteratory poszczególnych tablic
bool czyParzysty = false; //bo pierwszy wyraz musi być nieparzysty
while(zrodlo[iz] != 0) //dopóki nie natrafiliśmy na koniec łańcucha
{
if(czyParzysty) //jeśli wyraz jest parzysty
{
parzyste[ip] = zrodlo[iz]; //to jego znaki dopisujemy do tablicy parzystych
ip++; //iteracja odpowiedniego iteratora
}
else //a jeśli nieparzysty
{
nieparz[in] = zrodlo[iz]; //to jego znaki dopisujemy do tablicy nieparzystych
in++; //iteracja odpowiedniego iteratora
}
if(zrodlo[iz] == 32) //jeśli trafiliśmy na spację
czyParzysty = !czyParzysty; //to przełączamy flagę wyrazu parzystego/nieparzystego
iz++; //iteracja iteratora znaków źródłowych
};
parzyste[ip] = 0; //tu koniec parzystych
nieparz[in] = 0; //a tu nieparzystych (żeby nie wyświetlało śmieci)
}
A jeśli już konieczna wersja na wskaźnikach, to np. taka:
somekind - przestań wreszcie wciskać swój chłamowaty kod. Kod powinien być przede wszystkim działający zaś przejrzysty to już na drugim miejscu wg ważności. Co do “łatwy do zrozumienia” - wytłumacz mi proszę czym to się różni od “przejrzysty” ?
Każde z “moich dzieł”, poprawnie podzieli na parzyste i nieparzyste napis:
char napis[]="Ala ma kota" // UWAGA! po dwie spacji pomiędzy słowami
no i jest oczywiste że to co podałeś w “Dla przypomnienia:” nie podzieli tego napisu poprawnie.