c++ dziwne "krzaczki"

0

Witam! Mam za zadanie zaimplementować własną klasę String zawierającą pola napis jako char* i długość tego napisu jako int.
Jednak w poniższym kodzie dzieje się coś złego ;( gdy String ma długośc 8-10 znaków pojawiają się "krzaki". Macie jakiś pomysł? Podejrzewam memcpy.

#include <cstdlib>
#include <iostream>
 
using namespace std;
class String{
                char * napis;
                int dlugosc;
 
        public :
                String(const char* nap){
                                        dlugosc=strlen(nap);
                                        napis = new char[dlugosc];
                (char*)memcpy(napis,nap,dlugosc);
 
                }
                String(int dlugosc){
                                        dlugosc=dlugosc;
                                        napis=new char[dlugosc];
                }
                ~String(){
                                        cerr<<"usuwam "<<this->napis <<endl;
                                        delete [] napis;
                }
                int strlen(const char* source) {  //metoda obliczająca długość napisu char*
                           const char* s = source;
                           while ( *s++ );
                           return s-source-1;
                }
                //konstruktor kopiujący
                String(const String &s){
                                        dlugosc=s.dlugosc;
                                        napis=new char[s.dlugosc];
                                        (char*)memcpy(napis,s.napis,s.dlugosc);
                }
 
 
            int length(){      
                                        return (*this).dlugosc;
                }
 
        friend ostream& operator<<(ostream&, const String&);
        };
 
 
 
        ostream& operator<<(ostream& s, const String& str) {                  
               return s <<str.napis;
    }
 
int main(int argc, char *argv[])
{
 
        String* p1=new String("1234567");
        String* p2=new String("12345678");
        String* p3=new String("123456789");
        String* p4=new String("123456789ab");
 
        cout<<*p1<<endl;
        cout<<"dlugosc "<<p1->length()<<endl;
        cout<<*p2<<endl;
        cout<<"dlugosc "<<p2->length()<<endl;
        cout<<*p3<<endl;
        cout<<"dlugosc "<<p3->length()<<endl;
        cout<<*p4<<endl;
        cout<<"dlugosc "<<p4->length()<<endl;
 
 
 
 
        system("pause");
        return 0;
}
1

String(const char* nap){
dlugosc=strlen(nap);
napis = new char[dlugosc+1]; // na znak końca
memcpy(napis,nap,dlugosc+1);

            }

explicit String(int dlugosc){
dlugosc=dlugosc;
napis=new char[dlugosc+1];
}

String(const String &s){
dlugosc=s.dlugosc;
napis=new char[dlugosc+1];
memcpy(napis,s.napis,dlugosc+1);
}
int strlen(const char* source) // wywalić całą
int length()const {

typ skladowej dlugosc zamień na unsigned, no chyba że spodziewasz się ujemnej długości napisów.

1

No widze, że mnie już uprzedził ;D Słowem wyjaśnienia - oprócz znaków, które kopiujesz, na końcu dodawany jest znak końca bitu '\0', którego length nie uwzględnia, więc go nie kopiujesz. Tym samym przy wypisywaniu wyrzuca trochę za dużo.

2
  • Do kopiowania c-stringów lepiej użyć strncpy (Komentarz - racja).
  • Po co pisać swoje własne strlen?
  • return (*this).dlugosc; wtf? Wystarczy return dlugosc;.
  • Nie ma operatora przypisania.
0

To była tylko "esencja" mojego kodu ;) pozostałe operatory mam przeciążone. Dzięki za pomoc. Gdybyście mieli jeszcze jakieś sugestie to proszę się nie wstydzić i śmiało pisać

2
Endrju napisał(a):
  • Do kopiowania c-stringów lepiej użyć strcpy.

Po kiego, jak już ma zmierzoną wcześniej długość?
strcpy - sprawdza każdy bajt czy nie jest równy '\0'
memcpy - 25% szybsza (o ile i tak już znamy długość).
memcpy(dst,src,strlen(src)+1) - już wolniejsze niż strcpy

"Premature optimization is the root of all evil"
Donald Knuth.

Właśnie widać czym się skończyło używanie rzeczy, których się nie rozumie. strcpy ma jasno napisane - kopiuje c-stringi.

0
Endrju napisał(a):

Właśnie widać czym się skończyło używanie rzeczy, których się nie rozumie. strcpy ma jasno napisane - kopiuje c-stringi.
strcpy w tym przypadku dało by gorsze efekty. Przez to memcpy było czytanie poza przydzieloną pamięcią (przy wyświetlaniu, przez brak końca napisu), strcpy zrobił by pisanie poza przydzieloną pamięcią (pamięci przydzielono tyle co zwrócił strlen() a strcpy kopiuje strlen()+1 znaków).

W tym przypadku można tak powiedzieć, ale w innym taki wyciek informacji może być dużo gorszy niż wywalenie lub dziwne zachowanie sie programu.

0

Od razu mówię że to przykład i nikt nie powinien tak trzymać danych security ;p chociaż patrząc na obecnych informatykó jest to wielce prawdopodobne...

struct User {
public:
char login[20];
char password[20];

User(char *login_, char *password_) {
    memcpy(login, login_, strlen(login_));
    memcpy(pasword, password_, strlen(password_));
}

};

i gdzieś w kodzie:
User user("<długi login>", "S3cr3t");
cout<<"podaj haslo dla "<<user.login<<endl;

W tym przypadku nie skopiujesz znaku końca linii po login i możesz wypisać dalszą część struktury(czyli password) i jakieś śmieci. Jeżeli zastąpisz memcpy poprzez strcpy to na koniec dopiszesz znak końca zaraz za login[20] czyli prawdopodobnie w password[20]. Wiec powyższy kod juz nie ujawni hasła... ale moze pojawić sie inny problem .. pierwszym znakiem dla password[20] stanie sie znak końca wiec hasło stanie sie puste... no ale przecież w funkcji logowania powinno być sprawdzone czy length(wprowadzone hasło) >= jakiejś minimalnej długości.

0
lolos napisał(a):

memcpy(login, login_, strlen(login_));
Chyba napisałem wyraźnie:
strcpy - sprawdza każdy bajt czy nie jest równy '\0'
memcpy - 25% szybsza (o ile i tak już znamy długość).
memcpy(dst,src,strlen(src)+1) - już wolniejsze niż strcpy
W tym przykładzie:

  • kompletnie bezsensowne użycie memcpy;
  • kompletnie bezsensowne stworzona struktura;
    Podaj mi przykład kiedy potrzebowałeś długość do czegoś sensownego i już ją zmierzyłeś a potem dopiero użyty memcpy da gorsze wyniki niż strcpy.
    Bo właśnie o takim przypadku mówimy.
0

Ty to naprawdę masz jakieś trudności z myśleniem :

"W tym przypadku można tak powiedzieć, ale w innym taki wyciek informacji może być dużo gorszy niż wywalenie lub dziwne zachowanie sie programu." - hexol
"O jakim tym przypadku mowa? O jakim wycieku mówisz? Mówisz o strcpy/memcpy?" - _13th_Dragon
"Od razu mówię że to przykład i nikt nie powinien tak trzymać danych security ;p chociaż patrząc na obecnych informatyków jest to wielce prawdopodobne..." - lolos DO TEEEEEGOOOO: "W tym przypadku można tak powiedzieć, ale w innym taki wyciek informacji może być dużo gorszy niż wywalenie lub dziwne zachowanie sie programu." - hexol

" - kompletnie bezsensowne użycie memcpy;

  • kompletnie bezsensowne stworzona struktura;" - _13th_Dragon
    **Do którego kodu? bo jak dla mnie do obu, i wcale tego nie kryje w końcu napisałem : "Od razu mówię że to przykład i nikt nie powinien tak trzymać danych security ;p chociaż patrząc na obecnych informatyków jest to wielce prawdopodobne..." - lolos **

Teraz podsumowując mam nadzieje ze jeszcze sie nie pogubiłeś ....
To jest PRZYKŁAD nie koniecznie życiowy bo tylko ma pokazać co jest możliwe w przypadku pomyłki z użyciem memcpy, a pomyłki strcpy w odniesieniu do "O jakim tym przypadku mowa? O jakim wycieku mówisz? Mówisz o strcpy/memcpy?" - _13th_Dragon
I jak byś już zapomniał to przypomnę to jest PRZYKŁAD ....

Kiedy taki szajs sie moze zdarzyć? Kiedy pracujesz w dużym zespole i ktoś źle zrobi merge. np edytujecie tę samą funkcję zapiszesz długość cstring i później z niej korzystasz, ktoś niestety dopisze kod modyfikujący tego cstringa i pech chciał że miedzy pobraniem długości cstringa, a jego kopiowaniem przez memcpy. Albo prościej sam za jakiś czas wprowadzisz zmiany i tego nie zauważysz.
I tak to też jest PRZYKŁAD

0
lolos napisał(a):

Kiedy taki szajs sie moze zdarzyć? Kiedy pracujesz w dużym zespole i ktoś źle zrobi merge. np edytujecie tę samą funkcję zapiszesz długość cstring i później z niej korzystasz, ktoś niestety dopisze kod modyfikujący tego cstringa i pech chciał że miedzy pobraniem długości cstringa, a jego kopiowaniem przez memcpy. Albo prościej sam za jakiś czas wprowadzisz zmiany i tego nie zauważysz.
Super, to właśnie nazywam sensownym życiowym przykładem. Tylko że w tej sytuacji znowu memcpy jest bezpieczniejszy ;P

0

Zgadza sie pod warunkiem ze ktoś dba o ten znak końca linii, ja jednak odnosze sie do przypadku, w którym ktoś źle użył memcpy co też wczesniej napisałem. Mam nadzieje ze juz wszystko jasne.

0

Ja stwierdziłem że nie istnieje żaden sensowny przypadek kiedy to memcpy spowoduje większe szkody niż strcpy. Ty próbując obalić tą tezę tylko ją potwierdziłeś. Zastanów się czemu w visualu strcpy jest depricated zaś memcpy - nie.

0

przypominam żę to było tylko w odniesieniu do tego
"strcpy w tym przypadku dało by gorsze efekty. Przez to memcpy było czytanie poza przydzieloną pamięcią (przy wyświetlaniu, przez brak końca napisu), strcpy zrobił by pisanie poza przydzieloną pamięcią (pamięci przydzielono tyle co zwrócił strlen() a strcpy kopiuje strlen()+1 znaków)"
Czyli typowe strzały. Jezeli chcesz bardziej realny to wracając do tamtej struktury z hasłami. Jeżeli ktoś źle użyje memcpy o ten jeden znak to w przypadku gdy nowo tworzona struktura będzie wpisywana w wyzerowany obszar pamięci, lub ktoś będzie zerować pamięć np w ten sposób:
User *user = ...
memset(user, 0, sizeof(User));

albo korzysta z bardziej low level np // mapowania na jakieś bufory itd.
char buf = (char)malloc(sizeof(char) * 4096);
memset(buf, 0, sizeof(char)4096);
...
User * user = (User
)(buf + 1300);

to sprawi że login i password będą wyzerowane na starcie. Jeżeli ktoś nieumiejętnie użyje memcpy to ten błąd w tym przypadku wyjdzie dopiero gdy ktoś zrobi set loginu większego niz 19 znaków i wtedy wypluje nam na ekran hasło.
Tutaj gorąco pozdrawiam hinduskich programistów...

co do oznaczenia strcpy jako przestarzała... masz racje, tylko że została tak oznaczona dla tego że wprowadzono nową wersje strcpy_s, która w tym przypadku jest najbezpieczniejszym rozwiązaniem ponieważ dba o to zeby nie przekroczyć rozmiaru bufora, a nawet w przypadku gdy źródło jest większe niż cel to przytnie dane i wstawi 0.

natomiast memcpy nikt raczej nie oznaczy jako przestarzała gdyż stosuje się ją w celu szybkiego kopiowania pamięci bez sprawdzania poprawności buforów, w końcu po to jest żeby przyśpieszyć operacje bez dodatkowych obciążeń, całość walidacji zostawiając programiście.

stringi zwykle nie wymagają szybkich operacji i w takim przypadku lepiej użyć strcpy lub własnie strcpy_n do zadbają one o ten "specjalne" operacje na string. Jezeli zalezy nam na wydajności za wszelką cene i możemy sobie pozwolić na ewentualny błąd to oczywiście memcpy będzie lepsze.

0

@_13th_Dragon ahh masz racje jestem debilem, bo zakładam że mogę zrobić głupi błąd, baaaa mało tego... pisze unit testy(!) taki ze mnie nieudolny programista ... wstyd... zapomniałem że są tacy bogowie i haxiory co nigdy nie robią błędów... a im większa presja i tępo kodowania tym lepszej jakości kod piszą ... Mało tego każdy zespół w firmie składa sie tylko z takich haxiorów i ewentualne błędy powstałe przy pracy z svn i merge nie wchodzą w grę.

0

Widać mało widziałeś, ja na produkcyjnym kodzie znalazłem tego typu krzaczki:

char *tab = new char[50];
...
memcpy(buf, tab, sizeof(tab));

Ktoś źle zrozumiał jak oblicza sie długość string i na jego fart to przez długi czas działało, aż trafi się odpowiedni przypadek. Ty zakładasz bezbłędność kodowania swoją i ludzi którzy z tobą pracują, ja jednak uważam że moge sie pomylić, sory nie jestem tak zajebisty jak ty =/ i jeżeli pisze kod dla banku/public safety itd. to wole korzystać z bezpiecznych metod a nie wynajdować koło od nowa. Naprawdę uważasz że memcpy da ci jakieś wymierne korzyści przy kopiowaniu kilku stringów????

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