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

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