Zaloguj się, aby obserwować  
Bartuc

Język C / C++ / C# / Java - pytania, problemy...

1979 postów w tym temacie

/.../

To forum się kiepsko nadaje do kodu bo zamieni ci niektóre symbole w znaczniki np. kursywy bądź pogrubienia.
Zresztą spójrz na swój własny kod na tym forum, pojawiają się jakieś bzdury jak tab=x;
Radzę wstawiać kod na pastebin ( http://pastebin.com/) to całość będzie czytelniejsza i miała nawet podkreśloną składnię. To tak na wstępie.

To teraz przejdźmy do sedna problemu.
Jak sądzę to chcesz swoje funkcje wywołać w mainie. Robisz to w następujący sposób:


funkcja(argument);

Czyli dla
int znak(char tab[]){}
wywołaniem jest
znak(tablica_char);

Konkretniej:

int main()
{
char slowo[30];
tekst(slowo);
int ilosc_wystapien=znak(slowo);
printf("%d", ilosc_wystapien);
}

I tak, to jest w stanie wyświetlić ilość wyświetleń małego a. Choć na przyszłość wygodniej jest zamiast konstrukcji:

if (tab[pozycja]==97) używać if (tab[pozycja]==''a'') - wynik ten sam a nie trzeba zerkać do tablicy kodów ascii żeby sprawdzić kod cyfrowy danej litery (który swoją drogą NIE MUSI zawsze wynosić 97 ;))

Aha, jeszcze coś:
for(i=0;i<1000;i++)

To coś mi się bardzo nie podoba. Potencjalnie wykonujesz aż 1000 obiegów pętli. I liczysz na to że wstawione przez ciebie zdanie/słowo skończy się znakiem nowej linii. A jeśli z jakichś przyczyn nie to zaczniesz losowo jeździć po pamięci komputera.
Co prawdopodobnie skończy się zwisem programu.

Lepiej jest to zrobić w ten sposób:
int znak(chat tab[], unsigned int rozmiar)
{
for (i=0; i<rozmiar; i++){}
}

Z kolei w mainie piszesz sobie np. takie coś:

char zdanie[50];
znak(slowo, 50);
Masz wtedy większą kontrolę nad ilością wykonań pętli i nie boisz się że przypadkiem ci nie wetnie gdzieś tego znaku nowej linii (zresztą zamiast znaku nowej linii może się pojawić np. znak End of File).


PS Za samo nazywanie zmiennych i, a, b, x to mój profesor programowania stawiał 0 punktów. Staraj się zawsze pisać je w formie która jest zrozumiała dla postronnego czytelnika, np. ilosc_wykonan_petli, pozycja_tablicy itp. Na pierwszy rzut oka więcej pisania ale naprawdę, jeśli kiedykolwiek będziesz chciał sprawdzić np. swój stary program czy chciał żeby zrobił to ktoś inny to pomaga to niesamowicie. I przy okazji popełniasz mniej błędów - przypadkiem zainicjujesz dwa razy np. int i co doprowadzi kod do cokolwiek ciekawego stanu. Z kolei pisząc nazwy które coś znaczą jest dużo mniejsza szansa na taką pomyłkę.

Udostępnij ten post


Link to postu
Udostępnij na innych stronach
Dnia 29.11.2013 o 20:33, Matti299 napisał:

http://pastebin.com/fD0Me5L7 mam teraz taki kod poprawiony ale wywołanie nice chce się
skomilować bo woła o jakieś wyrażenie przed char tab


Nie sprawdziłem czy sam program działa, ale błąd był tutaj
tekst(tab);
x=znak(tab);

W złym wywołaniu funkcji. "Przekazywanie tablic jako argumentu" jeśli bys szukał jeszcze kiedyś innych przykładów.
Wywolując funkcję wysyłasz coś, to zadaniem funkcji jest dopasować to do tego, co ma zadeklarowane jako to, co może być zmienną. (Sorry za ilość zaimków :P) Wywołanie nie musi tego wiedzieć, bo i po co. Ma tylko przekazać coś dalej.

Edit.Nie mam niestety czasu, by przeczytać wcześniejszej części wątku, za co przepraszam, ale mam wrażenie, że podobnie w funkcji tekst nie potrzebujesz wysyłania tablicy. Jes zmienną globalną i w miejscu jej wywołania w programie nic nowego nie dosyłasz. W samej funkcji gdybyś użył tego, program prawdopodobnie by wiedział o co chodzi. Problemy wychodzą jeśli bawisz się w zmienne lokalne i globalne itp. ale tutaj nie widzę takiej potrzeby.
Gdybyś tablicę deklarował w mainie to co innego, teraz globalną tablicę wysyłasz do maina by wysłał jądalej, choć widzieć ją powinnien bez tego. Chyba. Samo sobie przypominam C :)

Udostępnij ten post


Link to postu
Udostępnij na innych stronach
Dnia 29.11.2013 o 20:33, Matti299 napisał:

http://pastebin.com/fD0Me5L7 mam teraz taki kod poprawiony ale wywołanie nice chce się
skomilować bo woła o jakieś wyrażenie przed char tab


Ech, dostajesz gotowy kod a i tak ci nie wyszło ;P

char tab[1000]={0}; - masz takie coś.

I funkcję int znak(char tab[])


Więc sposobem wywołania tej funkcji jest:
znak(tab). Bez żadnych nawiasów kwadratowych itd. Po prostu:
znak(tab);

Ale widzę że przyda ci się nieco dłuższy wykład co i jak jeśli chodzi o funkcje i zakres zmiennych, może pomoże ci zrozumieć jak one tak naprawdę działają i co w nich robisz bo coś czuję że lada moment zrobisz niezła wtopę bo twoja funkcja co prawda działa ale... działa bardziej przypadkiem niż celowo. Z góry przepraszam za ścianę tekstu :D

W obecnej postaci to ty nawet nie musisz przyjmować bowiem argumentów.
char tab[1000]={0};
void tekst()
{
int i=0;
y=0;
printf("Wpisz dowolny tekst: \n");

do
{
x=getchar();
tab[___i]=x; // musialem wstawic te _ w celu uniknięcia forum psującego kod
i++;
y++;


} while(x!=10 && i<1000);
}

Które potem w mainie uruchamiamy wpisując po prostu:
tekst();

Dlaczego tak się dzieje? Bo zadeklarowałeś że te tab ma być globalne. Globalne = widoczne z każdego miejsca. Nie musisz tego inicjować nigdzie po raz drugi. Ba, jest to wręcz niewskazane. Dlaczego? Wyjaśnię na przykładzie, skompiluj to u siebie:

int argument=5;

void funkcja1(int argument)
{
argument=argument+5;
}

void funkcja1(int argument)
{
argument=argument+10;
}

int main()
{
printf("%d", argument);
funkcja1(argument);
printf("%d", argument);
funkcja2(argument);
printf("%d", argument);
system("PAUSE");
return 0;
}

Jakie twoim zdaniem powinny być wyświetlone liczby z printfa? Jeśli sądzisz iż będą to odpowiednio 5, 10 i 20 to się mylisz. Wyświetli się 5 5 5.
Dlaczego tak jest? Z powodu twojej podwójnej deklaracji:

int argument = 5;

void funkcja1(int argument){}

Te dwa argumenty mimo identycznych nazw to dwie zupełnie różne rzeczy. Tak naprawdę w funkcji tworzona jest KOPIA obiektu jaki do niej wysyłasz, NIE pracujesz na oryginale. Którą to kopię możesz sobie nazwać jak tylko zechcesz, nawet tak jak jakaś inna w mainie czy zakresie globalnym. Co zresztą stało się w tym przypadku. Z kolei:

void funkcja1()
{
argument=argument+5;
}

Nagle sprawi iż całość zacznie działać tak jakbyś oczekiwał bo zamiast używać kopii przesłanego argumentu funkcja nagle spojrzy i zauważy:
- Hmmm... nie mam zadeklarowanej zmiennej o takiej nazwie u siebie. Pewnie chodzi mu o tą globalną!
Ewentualnie robisz tak:
int pieknazmienna=10;

int funkcja(int pieknazmienna)
{
return ::pieknazmienna+pieknazmienna;
{

int main()
{
int costam=10;
int wynik;
wynik=funkcja(costam);
}
Da ci wynik=20;

Te dwa dwukropki obok nazwy zmiennej mówią funkcji:
- Ej, słuchaj! Wiem że masz jedną własną zmienną o nazwie pieknazmienna ale ja chcę żebyś użyła tej globalnej!

Taka zmienna globalna rzeczywiście jest widoczna z poziomu całego programu i może być zmieniona w każdym jego punkcie, nieważne czy to main czy jakaś funkcja.

Gwoli ścisłości, co tak naprawdę znaczą te pogrubione części?
void misio(int argument){}

int main()
{
int zmienna=5;
misio(zmienna);
}

Odpowiedź brzmi:
- To jest funkcja o nazwie misio. Ma ona jedną zmienną o nazwie argument.
- A teraz w mainie ją wywołujemy.
Tak naprawdę cżęść
misio(zmienna); jest równoznaczny:

int argument=zmienna;

int funkcja(int a)
Tak naprawdę to co jest w nawiasie jest niejako "łącznikiem" pomiędzy mainem a samą funkcją i jedynym miejscem gdzie wartość z maina może zostać przekazana funkcji. Potem to funkcja pracuje na tym co ma w swoim brzuszku (swoich nawiasach kwadratowych).

Podsumowując:
int alpha=5;
int oblicz_costam(int alpha)
{
<tu wstaw jakis kod>
}

int main()
{
int alpha=7;
oblicz_costam(alpha);
oblicz_costam(::alpha);
oblicz_costam(3);
}
To 3 całkowicie osobne funkcje z własnymi danymi. W pierwszym przypadku mówimy funkcji oblicz_costam że w miejscu swojego int alpha ma wstawić wartość z alpha z maina. Czyli w sumie jest to oblicz_costam(7) a niejako pierwszą linijką tej funkcji jest int alpha=7; W drugim mówimy jej że ma wziąć wartość alfy z zakresu globalnego. Czyli oblicz_costam(5). W trzecim mówimy jej aby wykonała się gdy jej prywatna alpha=3;

Tak naprawdę więc mimo identycznej nazwy za każdym razem mówimy o zupełnie innych obiektach.

I teraz dochodzimy do sedna problemu - dlaczego twój kod przypadkiem działa chociaż normalnie tworzona jest kopia danego argumentu czyli teoretycznie NIEWAŻNE co byś wpisał w funkcji tekst to funkcja znak nie powinna tego widzieć bo mimo identycznych nazw pracujesz na zupełnie innych danych?

Odpowiedź (niepełna) brzmi - bo osoby tworzące C i C++ słusznie uznały że niekoniecznie najlepszym pomysłem jest aby funkcja tworzyła sobie własną kopię np. tablicy mającej 500.000 elementów typu double. I przesyłając nazwę tablicy (i TYLKO tablicy) pracujesz zawsze na oryginale. Co przy okazji sprawia iż w ogóle niepotrzebnie inicjowałeś swoje tab jako globalne. Wywal je stamtąd, wstaw do maina. Kod będzie działać tak samo. Ba, można też zrobić tak:

void slowo(char tab[])
{
<reszta funkcji>
}

int main()
{
char zdanie[40];
slowo(zdanie);
}

W tym przypadku zdanie=tab a tab=zdanie. Mimo dwóch różnych nazw jest to jeden obiekt dla komputera. Jakakolwiek zmiana w jednym z nich tak naprawdę jest też identyczną zmianą dla drugiego. Dlatego nie musisz mieć identycznych nazw zmiennych w funkcjach, zakresie globalnym i mainie. Wręcz przeciwnie, staraj się tego unikać. Albo przesłonisz sobie nazwę (zamiast globalnej użyjesz jakiejś innej) albo i tak nie będzie to miało znaczenia (jak w przypadku tablic).

PS Odpowiedź jest niepełna bo prawdziwym stwierdzeniem jest iż "nazwa tablicy jest wskaźnikiem do jej pierwszego elementu" ale wolę nie używać tu takich zwrotów, niewiele z niego zrozumiesz na razie ;)

PS2 Oczywiście da się też pracować tak na kopii tablicy jak i oryginałach pojedynczych zmiennych w funkcjach. Ale wymaga to pewnego obycia w języku C (jest to nieco prostsze w C++) z zakresu wskaźników i alokacji pamięci.

Udostępnij ten post


Link to postu
Udostępnij na innych stronach
Dnia 30.11.2013 o 12:19, Matti299 napisał:

tablica została zadeklarowana globalne bo tak było w poleceniu i dziękuje za pomoc ale
już sobie poradziłem


Tak z ciekawości, o jakim poziomie nauczania mówimy? Uniwersytet, polibuda, liceum czy technikum? ;P
Bo autentycznie zdumiewa mnie inicjalizacja obiektów takich jak tablica globalnie bez wyraźnej potrzeby, naprawdę niezbyt dobra praktyka programistyczna.

Udostępnij ten post


Link to postu
Udostępnij na innych stronach
Dnia 02.12.2013 o 02:03, Daronn_Darker napisał:

Z jakich powodów konsola nie zatrzymuje mi się? Środowisko VS 2013, nawet już zniżyłem
się do system("pause") i dalej nie zatrzymuje.


Zrób tak:
- Skompiluj sobie normalnie plik.
- Kliknij na start. Teraz uruchom->cmd
- Używając komendy cd wejdź do katalogu gdzie masz skompilowany plik
- Uruchom go z wiersza poleceń.

W ten sposób okno nie zamknie się po wykonaniu programu co by się nie działo.
A powodów takiego zachowania u ciebie może być kilka - jednym choćby jakiś fatal error w pliku wykonywalnym a całość wylatuje w powietrze wyświetlając na moment komunikat o błędzie i znika bez śladu nie dochodząc nawet do tej linijki system("pause").

Zresztą możesz też spróbować przed zakończeniem programu zamiast system("pause") zrobić coś takiego:
int a;
cin >> a;
cin.ignore();

W sumie też będzie oczekiwało od ciebie wciśnięcia klawisza.

Poza tym jest też szansa że nie wiesz jak działa system("pause"). W końcu jedynym co to robi jest wywołanie funkcji systemu windows która z c++ nie ma nic wspólnego. Tam może sobie być i system("jestem_krową") czy system("format C:") i to się ładnie skompiluje. Istnieje możliwość że z jakiegoś powodu system("pause") działa u ciebie inaczej niż powinno. Bądź nawet że to działa ale msvc sam zamyka okno po upływie jakiegoś tam czasu bądź po prostu ignoruje komendy system(); (jak pisałem - spróbuj ręcznie uruchomić skompilowany program tak bezpośrednio jak i poprzez dodatkowe cmd).

Udostępnij ten post


Link to postu
Udostępnij na innych stronach

Dlaczego wynik tego programu:
#include<stdio.h>
main()
{
int num[] = {1,4,8,12,16};
int *p,*q;
int i;
p = num;
q = num+2;
i = *p++;
printf("%d, %d, %d\n",i, *p, *q);
}
to 1,4,8? chodzi dokładnie o tą linijkę z przypisaniem pointera do i, nie rozumiem...

Udostępnij ten post


Link to postu
Udostępnij na innych stronach
Dnia 02.12.2013 o 20:09, Daronn_Darker napisał:

i = *p++;


przed tym p jest ustawione na num, czyli dokładnie num[0], więc *p == 1. Postinkrementacja przesuwa p na num[1] dopiero po zwróceniu tej jedynki do zmiennej i.

Udostępnij ten post


Link to postu
Udostępnij na innych stronach

Robiąc program wczytujący słowa do tablicy charów w C (takich z GW 2, nie no żartuje) napotykam dziwny błąd. Mimo że kod zasadniczo się kompiluje z jakiegoś oczywistego powodu, który przeoczam nie działa.
Oto kod:
FILE *plik;
plik=fopen("import.txt","r");

while(fscanf(plik,"%s",tabpom)!=EOF)
{
strcpy(X,tabpom);
i=i+1;
}
fclose(plik);


tabpom to tablica długości domyślnie 7. Powinnenem pisać tabpom w pętli wczytującej?

Udostępnij ten post


Link to postu
Udostępnij na innych stronach
Dnia 02.12.2013 o 20:09, Daronn_Darker napisał:

Dlaczego wynik tego programu:
/.../
}
to 1,4,8? chodzi dokładnie o tą linijkę z przypisaniem pointera do i, nie rozumiem...


No więc, na początku p ma wartości tablicy num, to znaczy jakby wskaźnika na nią, czyli pierwszej liczby.
q to wskaźnik przesunięty o 2
i to wartość p, po czym po przypisaniu zostaje p powiększone o jeden- różnica pomiędzy ++p i p++.

Udostępnij ten post


Link to postu
Udostępnij na innych stronach

pytanie jak napisać pętle która będzie eliminować co k element tablicy tak aby pozostał w niej jeden element ale żeby nie przekraczało wymiarów tablicy

Udostępnij ten post


Link to postu
Udostępnij na innych stronach
Dnia 04.12.2013 o 11:04, Matti298 napisał:

pytanie jak napisać pętle która będzie eliminować co k element tablicy tak aby pozostał
w niej jeden element ale żeby nie przekraczało wymiarów tablicy


Jak rozumiesz eliminacje elementu? Pozostawienie pustej wartość czy coś innego? Jest to zdecydowanie łatwiejsze w listach, to też powód po co powstały (między innymi).

Udostępnij ten post


Link to postu
Udostępnij na innych stronach

chodzi o to żeby element stał się pusty bez wartości i żeby później takie elementy pomijało i niesety musi to zostać zrobione na tabeli bo tak zarządał wykładowca

Udostępnij ten post


Link to postu
Udostępnij na innych stronach
Dnia 04.12.2013 o 15:22, Matti299 napisał:

/.../


Bez wartości czyli NULL?
Możesz również wstawić tam umowną wartośc "bral" np. dla napisów/liter jest to "".
I jeśli program natrafi na (nie)taką wartość, to wtedy coś robisz.
np for(i=0;i<10;i++)
if (tab=="") printf("%s",tab);

Nie pamiętam już tego zbytnio, sam sobie powoli to przypominam, co widać kilka postów wcześniej- gdzie nikt mi jeszcze nie odpowiedział :P

Udostępnij ten post


Link to postu
Udostępnij na innych stronach
Dnia 04.12.2013 o 15:22, Matti299 napisał:

chodzi o to żeby element sta się pusty bez wartości i żeby później takie elementy pomijało

Ignorowanie co drugiego elementu:

int tab[100];

for (int j=0; j<100; j++)
{
if (j%2!=0){tab[j]=j;}
}
Tak zbudowana pętla wpisze wartości tylko do nieparzystych pól w pętli (1,3,5,7 itd).
A "pomijanie" elementów o których mówisz i "stanie się pustym bez wartości" nie jest możliwe. Jeśli każesz kompilatorowi wyświetlić np. tab[2] to on to zrobi, zapewne wyświetlając jakieś śmieci. Masz dwie możliwości:

int tab[100];
int licznik=0;
for (int j=0; j<100; j++)
{
if (j%2!=0){tab[j]=j; licznik++;}
}
int *dyn_tab= new int[licznik];
for (int tab1=1, j=0; j<licznik; tab1+=2, j++)
{
dyn_tab[j]=tab[tab1];
cout << dyn_tab[j] << endl; }
(dalsza część kodu która będzie używać tylko dyn_tab.)

W tym przykładzie posłużyłem się zmienną licznik i tablicą dynamiczną o jego rozmiarze. Tworząc w ten sposób tablicę na tylko te elementy które chciałem żeby się znalazły w tablicy, pozostałe są pomijane.
Jeśli chcemy ignorować np. co trzeci element to korzystamy z if (j%3), co czwarty to j%4 itd

Opcją drugą, wymagającą mniejszego zużycia pamięci (mimo wszystko tworzymy dodatkową tablicę) jest coś takiego:
for (int j=0; j<100; j++)
{
if (j%2!=0){tab[j]=j;}
else {tab[j]=-5000;}
}

W tym przypadku -5000 zastąp dowolną liczbą która wiesz że nie pojawi się w twoim programie (jeśli jesteś przekonany że w zasadzie każda z nich może wystąpić to dołącz nagłówek climits lub limits.h jeśli programujesz w C i zamiast -5000 podstawiaj INT_MIN, szansa na to że ktoś użyje tej liczby jest dość niewielka.)

W dalszej części kodu po prostu dostawiasz w pętlach coś takiego:
if (tab[j]==-5000){}
else
{
normalny kod
}
Takie rozwiązanie z jednej strony zachowuje pamięć, z drugiej wymaga od nas korzystania z dodatkowych instrukcji warunkowych w dalszej części programu.

Opcja 3 - zamiast tablic użyj wektorów (tylko C++).

http://pastebin.com/bdM7FB7N
Wektor, w przeciwieństwie do tablic, może zmieniać swoje rozmiary i można usuwać z niego elementy w sposób względnie prosty. Teoretycznie więc da się wykorzystać jego funkcję erase do usunięcia "nadmiarowych" elementów. Np. takich które uprzednio przybrały wartość o której wiemy że jest "zła" (np. nasze -5000) bądź po prostu, jak w przykładzie, elementy od 1 do 9;
Uwaga - wydajnościowo może wyjść przez to całkiem sporo operacji (prawdę mówiąc wektor to tylko wysokopoziomowa obudowa na tablicę więc podejrzewam że wykonując tego operację w jego trzewiach pojawi się pełno delete i new jeśli by coś selektywnie w nim karczować). Zalety - ostatecznie brak dodatkowych instrukcji w dalszej części kodu a elementy których nie chcemy po prostu zostają całkowicie usunięte z pamięci.

Udostępnij ten post


Link to postu
Udostępnij na innych stronach

Chodzi o to żeby to ignorowło co k-ty element i wyświetało stan po kroku ignorowania i ta do momentu aż zostanie jeden element w pewnym sensie tak jak byśmy rozłożyli n liczb od 0 do n naokoło okręgu i ignorowali ignorowai co k-tą liczbe i wzytko mui być bez użycia iostream na stdio.h

Udostępnij ten post


Link to postu
Udostępnij na innych stronach
Dnia 04.12.2013 o 16:28, Matti299 napisał:

Chodzi o to żeby to ignorowło co k-ty element i wyświetało stan po kroku ignorowania
i ta do momentu aż zostanie jeden element

Już dostałeś odpowiedź na to pytanie, gotowego kodu ci nie będę pisać. Skorzystaj z symbolu % (reszty z dzielenia). To wszystko czego potrzebujesz by wyświetlać (bądź ignorować) co k-ty element.

Udostępnij ten post


Link to postu
Udostępnij na innych stronach

Utwórz konto lub zaloguj się, aby skomentować

Musisz być użytkownikiem, aby dodać komentarz

Utwórz konto

Zarejestruj nowe konto na forum. To jest łatwe!


Zarejestruj nowe konto

Zaloguj się

Masz już konto? Zaloguj się.


Zaloguj się
Zaloguj się, aby obserwować