Samodzielna nauka c++

0

Witam serdecznie wszystkich,
uczę się programowania od paru dni z książki Symfonia C++ standard, kiedyś trochę liznąłem HTML ale to raczej śladowe ilości.
Szukam miejsca gdzie będę mógł wstawić jakiś kodzik i powiecie mi co jest z nim nie tak dlaczego tak a nie inaczej itd.
Oraz na pewno jakieś pytania.
Tak wiec Od razu chce zapytać czy dobrze trafiłem i mogę tu taki temat założyć :)
Zawsze staram się sam rozwiązać problem analizować co zrobiłem zle(pisze w DEV-c++)w końcu mam książke przed nosem :x
Ale czasem mimo wielu poprawek banalnego kodu i tak nie mogę tego compilowac,
wiec postanowiłem znaleźć miejsce gdzie będę mógł się zapytać o swoje głupie początkujące błędy.
Pierwsze pytanie
mam 2 pliki cpp i 1 h
plik.h

#include<iostream>
using namespace std;

extern int liczba1, liczba2;
extern double liczba3, liczba4;
extern char znak1, znak2;

void funkcja1();
void funkcja2();
void funkcja3();

plik main

#include "baza.h"

int liczba2;
char znak2;
double liczba4;
int liczba1;
char znak1;
double liczba3;

int main()
{
	funkcja1();
	
	system("pause");
}

void funkcja1()
{
	liczba1=4;
	liczba2=6;
	liczba3=2.3;
	liczba4=1.1;
	znak1='C';
	znak2='G';
	
	cout<<"liczba:1 "<<liczba1<<"  liczba 2:"<<liczba2<<"  liczba 3:"<<liczba3<<"  liczba 4:"<<liczba4<<"  znak 1:"<<znak1<<"  znak 2:"<<znak2<<endl;
	
	funkcja2();
	return;
}

dlaczego w tym przypadku musze podać? myślałem ze poprzez "extern" nie trzeba, dlaczego wiec funkcji nie muszę definiować w każdym pliku?
int liczba2;
char znak2;
double liczba4;
int liczba1;
char znak1;
double liczba3;
i dlaczego to działa również jak podam je tylko w drugim pliku ?

1
Dariusz Nowosielski napisał(a):

dlaczego w tym przypadku musze podać? myślałem ze poprzez "extern" nie trzeba, dlaczego wiec funkcji nie muszę definiować w każdym pliku?
int liczba2;
char znak2;
double liczba4;
int liczba1;
char znak1;
double liczba3;
i dlaczego to działa również jak podam je tylko w drugim pliku ?

Gdybyś tak sformułował te pytania jakoś tak składniej, to byłoby łatwiej odpowiedzieć, bo nie do końca można zrozumieć co masz na myśli że musisz coś podać, albo co to znaczy że poprzez extern nie trzeba.

Zauważ że coś takiego:

void funkcja1(); // deklaracja

jest równoważne temu:

extern void funkcja1(); // deklaracja

ale już w przypadku zmiennych to:

int liczba1; // definicja

to nie jest to samo co

extern int liczba1; // deklaracja
0

Dzięki za odpowiedz.
Przepraszam za składnie faktycznie może być lekki problem ze zrozumieniem o co mi może chodzić.

Zauważ że coś takiego:

void funkcja1(); // deklaracja
jest równoważne temu:

extern void funkcja1(); // deklaracja .
Tego nie wiedziałem umknęło mi.
Użyłem komendy "extern" w pliku.h i byłem przekonany że dzięki temu już są globalne, a jednak muszę je zadeklarować w chociaż 1 pliku.cpp.
No nic, doczytam i wrócę z innym pytaniem i czytelniejszym mam nadzieje :)

1
Dariusz Nowosielski napisał(a):

Użyłem komendy "extern" w pliku.h i byłem przekonany że dzięki temu już są globalne, a jednak muszę je zadeklarować w chociaż 1 pliku.cpp.
No nic, doczytam i wrócę z innym pytaniem i czytelniejszym mam nadzieje :)

extern int liczba; informuje o istnieniu zmiennej o danej nazwie i o danym typie, ale nie powoduje istnienia tej zmiennej. to robi int liczba;.
Ale z kolei gdybyś wszędzie pisał po prostu int liczba; to w każdym pliku .cpp powstałaby nowa zmienna o tej samej nazwie, a nie o to chyba ci chodziło.

0

Witam ponownie dziś chciałem się zapytać o tablice a mianowicie:
w poniższym przykładzie aby dobrze działał muszę zdefiniować tablice przypisując jej wartość "i" "tabela[i]=i;" moje pytanie brzmi dlaczego.
Dlaczego nie wystarczy samo "tabela[i];" tak wiem że wychodzą śmieci później ale przy samej jej definicji tabela[i] nie powinna się już stworzyć i działać poprawnie dlaczego trzeba jej przypisać wartość "i".

#include <iostream>
using namespace std;

void x_3(int x,long t[]);

int main()
{
	int liczba = 400;
	long tabela[liczba];
	
	for(int i=0;i<liczba;i++)
	{
		tabela[i]=i;
		if(i<6)
		cout<<"liczba ["<<i<<"]="<<tabela[i]<<endl;
	}
	x_3(liczba,tabela);
	{
		cout<<"po wywolaniu"<<endl;
		for(int i=0;i<4;i++)
		{
			cout<<"liczba ["<<i<<"]="<<tabela[i]<<endl;
		}
		
	}
	
}
void x_3(int x,long t[])
{
	for(int i=0;i<4;i++)
	t[i] *=5;
}
0

Tak jak powiedziałeś, wychodzą śmieci. Gdy nie zainicjalizujesz elementów tej tablicy to przechowuje ona losowe wartości. Jest to, więc bezużyteczne, bo po co Ci ta losowa wartość?
Jak potem taką losową wartość przemnożysz przez 5 jak w tym przykładzie to możesz też wyjść poza zakres wartości jaki typ zmiennej jest w stanie przechować i wtedy znowu dostaniesz nie wiadomo co.

Ogólnie dobrą praktyką jest inicjalizowanie zmiennych od razu przy tworzeniu, może to choćby zmniejszyć szansę na jakiś dziwny błąd w programie, gdy np nieświadomie użyjemy niezainicjalizowanej zmiennej.
Przykład, który podałeś byłby pozbawiony sensu, gdybyś usunął tę inicjalizację. No ale ogólnie tak jak powiedziałeś, taka niezainicjalizowana tablica jest gotowa do użycia.

Swoją drogą to tablic statycznych się unika w C++ - zamiast tego lepiej użyć std::vector lub std:array.

0

Do tego, co wyżej, dodam, że deklaracja zmiennej znaczy dokładnie tyle: "grzecznie proszę system, żeby pozwolił mi użyć RAMu". Pamięć przydzielona - zadanie wykonane. System nie ma obowiązku zgadnięcia co użytkownik chce mieć w tablicy. Ustawienie wartości wiąże się z dodatkową pracą. Jeśli chciałbym zadeklarować tablicę o wielkości 200 GB (mam sobie superkomputer, a co!) to ustawienie wartości chwilę potrwa. Nie zawsze użytkownik chce robić to od razu i w jednym kawałku.
Odpowiem na ewentualne pytanie: C++ to nie Java, C# czy Python. Jak komuś pasuje pisanie w innym języku to niech sobie pisze. Od zabaw w automatyczną inicjalizację zmiennych itp. są biblioteki. Można też zrobić to samemu. Jak już napisałem: C++ zostawia tę możliwość użytkownikowi.

0
zdz napisał(a):

Tak jak powiedziałeś, wychodzą śmieci. Gdy nie zainicjalizujesz elementów tej tablicy to przechowuje ona losowe wartości. Jest to, więc bezużyteczne, bo po co Ci ta losowa wartość?
Jak potem taką losową wartość przemnożysz przez 5 jak w tym przykładzie to możesz też wyjść poza zakres wartości jaki typ zmiennej jest w stanie przechować i wtedy znowu dostaniesz nie wiadomo co.

Ogólnie dobrą praktyką jest inicjalizowanie zmiennych od razu przy tworzeniu, może to choćby zmniejszyć szansę na jakiś dziwny błąd w programie, gdy np nieświadomie użyjemy niezainicjalizowanej zmiennej.
Przykład, który podałeś byłby pozbawiony sensu, gdybyś usunął tę inicjalizację. No ale ogólnie tak jak powiedziałeś, taka niezainicjalizowana tablica jest gotowa do użycia.

Swoją drogą to tablic statycznych się unika w C++ - zamiast tego lepiej użyć std::vector lub std:array.
co do
lepiej użyć std::vector lub ```std:array
jeszcze do tego nie doszedłem a wracając do tabeli
po prostu spodziewałem sie ze dodajemy inicjuje w tablicy w [] np. "tablica[i]" i dlatego wydawało mi się to dziwne.

1

jeszcze do tego nie doszedłem a wracając do tabeli
po prostu spodziewałem sie ze jak dodamy inicjuje w tablicy i gdy dodaje ją w [] np. "tablica[i]" i dlatego wydawało mi się to dziwne.

Szczerze mówiąc to nie zrozumiałem co napisałeś.

Odnośnie nauki to Symfonia C++ Standard jest przestarzała i radziłbym uczyć się z czegoś innego. Jeśli zależy Ci na polskiej książce to Opus Magnum C++11 tego samego autora będzie lepsze.

0

anologicznie do tego przykładu który podałem wcześniej zamiast (1)"tablica[i]=i" napisać samo (2)"tablica[i]" byłem po prostu przekonany ze w 2 robi to co 1 wiec dziwiło mi to ze trzeba dodać "=i".
tak wiec czy było by poprawne jak bym napisał "tablica[]=i' co to zmienia? już myślałem że coś wiem a teraz wiem że nic nie wiem ;x
btw. skoro nie zrozumiałeś z czym mam problem to chyba ja mam większy problem niż myślałem idę przeczytać jeszcze raz ten rozdział.

(Książka)Tak myślałem bo wydanie które posiadam jest bodajże z roku 2008 tak wiec pewnie sporo się zmieniło ale planuje przerobić je do końca i zakupić następną( planowałem :Thinking in C++")
Czy zmieniło się na tyle dużo że mogę się przez to nauczyć czegoś nie tak z niej? wydaje mi się że aby zbudować jakieś podstawy na razie wystarczy mi symfonia.

2

Tablice jednowymiarowe to struktury danych, które można sobie wyobrazić jako tabele o jednym wierszu i wielu kolumnach (ewentualnie na odwrót). Ich cecha jest to, że składają się z komórek, do których możesz się odwołać za pomocą indeksów numerowanych zazwyczaj od zera do wartości równej jej rozmiarowi - 1. Dla przykładu, pisząc coś takiego:

long tabela[5];

Sygnalizujesz kompilatorowi, że ma utworzyć tablice o rozmiarze 5 elementów. Taka tablica będzie miała 5 komórek o indeksach od 0 do 4. W tym przypadku nie powiedziano w sposób jawny jakie wartości mają się znaleźć w tych komórkach więc o ile dobrze pamiętam pojawią się tam wartości, których raczej się nie spodziewasz. Taka tablica mogłaby wyglądać np. tak.

Indeks 0 Indeks 1 Indeks 2 Indeks 3 Indeks 4
12 1008 4 12204 12485

Pisząc następujący kod:

tabela[2] = 5;

Sygnalizujesz kompilatorowi, że ma odwołać się do komórki o indeksie 2 i ustawić tam wartość równą 5. Po takim poleceniu powyższa tablica zmieni się w następujący sposób:

Indeks 0 Indeks 1 Indeks 2 Indeks 3 Indeks 4
12 1008 5 12204 12485

Natomiast pisząc taki kod:

tabela[3];

Wydajesz polecenie "odczytaj mi wartość z komórki o indeksie 3". Wartość ta jest równa 12204. Oczywiście zamiast podawać konkretny numer komórki w postaci stałej (w tym przypadku liczby 3) można podać indeks komórki za pomocą zmiennej (np tabela[i] - wtedy będzie to oznaczało "odczytaj mi wartość z komórki o indeksie i", czyli indeks komórki będzie zależeć od wartości i. Jak i będzie równe 2 to odwołasz się do komórki o indeksie 2 a jak i będzie równe 4 to odwołasz się do komórki o indeksie 4. Wartości większej niż 4 nie możesz w tym przypadku podać bo wyjedziesz poza zakres tablicy, która ma 5 elementów czyli indeksy od 0 do 4.).

Istnieje możliwość zasygnalizowania kompilatorowi aby ustawił wartość we wszystkich komórkach. Jeżeli więc zamiast pisać tak:

long tabela[5];

Napiszesz tak:

long tabela[5] = { 0, 1, 2, 3 ,4 };

To dostaniesz taką tablicę:

Indeks 0 Indeks 1 Indeks 2 Indeks 3 Indeks 4
0 1 2 3 4

W efekcie nie będziesz musiał przypisywać wartości do komórek za pomocą wyrażenia:

tabela[i] = i;
Dariusz Nowosielski napisał(a):

Witam ponownie dziś chciałem się zapytać o tablice a mianowicie:
w poniższym przykładzie aby dobrze działał muszę zdefiniować tablice przypisując jej wartość "i" "tabela[i]=i;" moje pytanie brzmi dlaczego.

Ponieważ w tablicy masz wartości losowe gdyż tak ją zadeklarowałeś. Zanim więc odczytasz z niej jakieś wartości należy je najpierw tam umieścić. Zapis:

tabela[i] = i;

Oznacza "umieść wartość i w komórce o indeksie i". Oczywiście nie musisz tam umieszczać w tablicy akurat takich danych. Równie dobrze możesz tam umieścić same piątki czyli:

tabela[i] = 5;

lub sam wzór wyliczania wartości może być inny. Na przykład możesz napisać:

tabela[i] = i * i;

Wtedy w tablicy powinieneś mieć wartości: 0, 1, 4, 9, 16. Dane w komórkach tablicy możesz mieć jakie tylko chcesz pod warunkiem, że będą to dane których rzeczywiście oczekujesz i typ danych będzie kompatybilny. Mimo wszystko zanim coś pobierasz z tablicy należy najpierw to coś tam wsadzić.

Dariusz Nowosielski napisał(a):

Dlaczego nie wystarczy samo "tabela[i];"

Ten zapis jest raczej kompletnie bez sensu. Oznacza "odczytaj mi wartość która jest umieszczona w komórce i a następnie nic z tym nie rób". Odczytuje się wartości w jakimś konkretnym celu. Możesz np. wypisać wartość na ekran konsoli:

cout << tabela[i] << endl;

To już ma sens bo takie polecenie mówi "odczytaj mi wartość która jest umieszczona w komórce i a następnie wyślij ją na strumień cout (spowoduje to wyświetlenie wartości na ekranie)". Sens ma także takie wyrażenie:

tabela[i] = tabela[i] * 2;

Ponieważ oznacza ono "Odczytaj wartość z komórki o indeksie i, pomnóż ją przez dwa a następnie umieść ją w komórce o tym samym indeksie". Oznacza to, że wszystkie komórki tablicy zostaną pomnożone przez dwa (oczywiście dane na których pracujemy powinny być wcześniej umieszczone w tej tablicy). Powyższy zapis powinieneś oczywiście zredukować do postaci:

tabela[i] *= 2;

Nie zmienia to jednak faktu, że oba zapisy są tożsame pod względem efektu działania (co nie oznacza, że zamienne ponieważ drugi zapis może być nieco szybszy pod względem działania).

Reasumując: tablice to dane, które są takim ciągiem komórek i żeby coś z nich odczytywać wypadałoby najpierw coś do nich wpisać.

0

Dziękuję wszystkim za odpowiedzi.
Nie wiem dlaczego od początku sobie źle to wyobraziłem i jakoś nie mogłem tego ogarnąć.
Po przeczytaniu tego co napisał użytkownik tk wszystko stało się jasne, jeszcze raz dzięki.

0
Dariusz Nowosielski napisał(a):

Nie wiem dlaczego od początku sobie źle to wyobraziłem i jakoś nie mogłem tego ogarnąć.

My chyba wiemy. Ważne, że idzie do przodu :) .

0

(Książka)Tak myślałem bo wydanie które posiadam jest bodajże z roku 2008 tak wiec pewnie sporo się zmieniło ale planuje przerobić je do końca i zakupić następną( planowałem :Thinking in C++")

Z książek nie nauczysz się programować. Przerób na razie jedną, naucz się składni, oczywiście rób różne zadania jak przerabiasz poszczególne rozdziały, żeby utrwalić to czego się uczysz.
A potem zrób kilka projektów, żeby ten język stosować w praktyce i rozwiązywać prawdziwe problemy.

Czy zmieniło się na tyle dużo że mogę się przez to nauczyć czegoś nie tak z niej? wydaje mi się że aby zbudować jakieś podstawy na razie wystarczy mi symfonia.

Symfonia nie obejmuje standardu C++11, a ten standard to absolutne minimum, w którym weszło wiele funkcjonalności, które są praktycznie niezbędne do dzisiejszej pracy w C++. Przez to z Symfonii możesz nauczyć się niektórych nawyków, których się aktualnie unika oraz będziesz musiał się dodatkowo douczyć wielu rzeczy. Najlepiej jakbyś od razu zaczął od Opus Magnum (albo jakiejś innej polecanej), no ale to tylko moja rada.

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