Discussion:
ujemne indeksy do tablicy
(Wiadomość utworzona zbyt dawno temu. Odpowiedź niemożliwa.)
qfel
19 lat temu
Permalink
Czy cos takiego jest legalne?
int _t[21],*t=_t+1;
t[-1]=10;
Sektor van Skijlen
19 lat temu
Permalink
Post by qfel
Czy cos takiego jest legalne?
int _t[21],*t=_t+1;
t[-1]=10;
Oczywiście. Indeks może być całkowity, a legalność jest tylko kwestią runtime.
--
// _ ___ Michal "Sektor" Malecki <sektor(whirl)kis.p.lodz.pl>
\\ L_ |/ `| /^\ ,() <ethourhs(O)wp.pl>
// \_ |\ \/ \_/ /\ C++ bez cholesterolu: http://www.intercon.pl/~sektor/cbx
"I am allergic to Java because programming in Java reminds me casting spells"
Tomek
19 lat temu
Permalink
Post by Sektor van Skijlen
Post by qfel
Czy cos takiego jest legalne?
int _t[21],*t=_t+1;
t[-1]=10;
Oczywiście. Indeks może być całkowity, a legalność jest tylko kwestią runtime.
co rozumiesz przez "legalne".
Piotr Sietnik
19 lat temu
Permalink
Post by Sektor van Skijlen
Oczywiście. Indeks może być całkowity, a legalność jest tylko kwestią runtime.
Co ma do tego runtime?
W runtimie z takim samym prawdopodobieństwem wywalają się "legalne"
programy, jak i poprawnie działają "nielegalne".
--
Piotr Sietnik
***@pk.mofnet.gov.pl
Piotr Sietnik
19 lat temu
Permalink
Post by Piotr Sietnik
W runtimie z takim samym prawdopodobieństwem wywalają się "legalne"
programy, jak i poprawnie działają "nielegalne".
Dodam jeszcze, że doświadczonym programistom znacznie częściej
wywalają się te "legalne".
--
Piotr Sietnik
***@pk.mofnet.gov.pl
Maciej Sobczak
19 lat temu
Permalink
Post by Piotr Sietnik
Post by Piotr Sietnik
W runtimie z takim samym prawdopodobieństwem wywalają się "legalne"
programy, jak i poprawnie działają "nielegalne".
Dodam jeszcze, że doświadczonym programistom znacznie częściej
wywalają się te "legalne".
Opowiedz coś więcej na ten temat.
--
Maciej Sobczak : http://www.msobczak.com/
Programming : http://www.msobczak.com/prog/
Piotr Sietnik
19 lat temu
Permalink
Post by Maciej Sobczak
Post by Piotr Sietnik
Post by Piotr Sietnik
W runtimie z takim samym prawdopodobieństwem wywalają się "legalne"
programy, jak i poprawnie działają "nielegalne".
Dodam jeszcze, że doświadczonym programistom znacznie częściej
wywalają się te "legalne".
Opowiedz coś więcej na ten temat.
Owszem.
Doświadczony programista prawie nie pisze "nielegalnych" programów,
ale to nie znaczy, że te "legalne" nie są wolne od bugów.
Co więcej doświadczenie uczy, że nigdy nie trzeba nawet zakładać,
że to co napisze jest "czyste".
--
Piotr Sietnik
***@pk.mofnet.gov.pl
Sektor van Skijlen
19 lat temu
Permalink
Post by Piotr Sietnik
Post by Maciej Sobczak
Post by Piotr Sietnik
Post by Piotr Sietnik
W runtimie z takim samym prawdopodobieństwem wywalają się "legalne"
programy, jak i poprawnie działają "nielegalne".
Dodam jeszcze, że doświadczonym programistom znacznie częściej
wywalają się te "legalne".
Opowiedz coś więcej na ten temat.
Owszem.
Doświadczony programista prawie nie pisze "nielegalnych" programów,
ale to nie znaczy, że te "legalne" nie są wolne od bugów.
Że "legalne" nie są wolne od bugów?

Czyli inaczej, doświadczony programista pisze głównie legalne programy i są
one wolne od bugów. Tak przynajmniej można zrozumieć twoją wypowiedź.
Post by Piotr Sietnik
Co więcej doświadczenie uczy, że nigdy nie trzeba nawet zakładać,
że to co napisze jest "czyste".
Piotrku, ale to jest standardowe "wszystko huj".

Jeśli twierdzisz, że mogą istnieć programy napisane "legalnie" i mimo to
zawierać bugi, to:
1. Zdefiniuj pojęcie "legalny"
2. O który rodzaj bugów ci chodzi, bo bugi logiczno/algorytmiczne robi się
niezależnie od języka programowania.
--
// _ ___ Michal "Sektor" Malecki <sektor(whirl)kis.p.lodz.pl>
\\ L_ |/ `| /^\ ,() <ethourhs(O)wp.pl>
// \_ |\ \/ \_/ /\ C++ bez cholesterolu: http://www.intercon.pl/~sektor/cbx
"I am allergic to Java because programming in Java reminds me casting spells"
Piotr Sietnik
19 lat temu
Permalink
Post by Sektor van Skijlen
Post by Piotr Sietnik
Owszem.
Doświadczony programista prawie nie pisze "nielegalnych" programów,
ale to nie znaczy, że te "legalne" nie są wolne od bugów.
Że "legalne" nie są wolne od bugów?
Mogą zawierać bugi.
Post by Sektor van Skijlen
Czyli inaczej, doświadczony programista pisze głównie legalne programy i
są one wolne od bugów. Tak przynajmniej można zrozumieć twoją wypowiedź.
No to ją źle zrozumiałęś. Wyraźnie napisałem, że
"doświadczony programista pisze głównie legalne programy", (OK)

ale mogą one zawierać od bugi:
"ale to nie znaczy, że te "legalne" nie są wolne od bugów".
Post by Sektor van Skijlen
Post by Piotr Sietnik
Co więcej doświadczenie uczy, że nigdy nie trzeba nawet zakładać,
że to co napisze jest "czyste".
Piotrku, ale to jest standardowe "wszystko huj".
Jeśli twierdzisz, że mogą istnieć programy napisane "legalnie" i mimo to
1. Zdefiniuj pojęcie "legalny"
Dla mnie legalny oznacza jedno - zgodny z definicją języka.
Oczywiści nie oznacza to jednocześnie, że każdy "legalny" program
jest programem "poprawnym".
To jest tak jak z kosą - legalne jest jej posiadanie, ale nie każde jej
użycie jest legalne.
--
Piotr Sietnik
***@pk.mofnet.gov.pl
Sektor van Skijlen
19 lat temu
Permalink
Post by Piotr Sietnik
Post by Sektor van Skijlen
Post by Piotr Sietnik
Owszem.
Doświadczony programista prawie nie pisze "nielegalnych" programów,
ale to nie znaczy, że te "legalne" nie są wolne od bugów.
Że "legalne" nie są wolne od bugów?
Mogą zawierać bugi.
Czyli podmieniając na to, co napisałeś:

Doświadczony programista prawie nie pisze "nielegalnych" programów, ale to nie
znaczy, że te "legalne" mogą zawierać bugi.
Post by Piotr Sietnik
Post by Sektor van Skijlen
Czyli inaczej, doświadczony programista pisze głównie legalne programy i
są one wolne od bugów. Tak przynajmniej można zrozumieć twoją wypowiedź.
No to ją źle zrozumiałęś. Wyraźnie napisałem, że
"doświadczony programista pisze głównie legalne programy", (OK)
To ty chyba sam nie rozumiesz tego, co sam napisałeś.
Post by Piotr Sietnik
"ale to nie znaczy, że te "legalne" nie są wolne od bugów".
Jeśli przyjmiemy, że "nie są wolne od bugów" == "mogą zawierać bugi", to twoje
zdanie brzmi:

"ale to nie znaczy, że te "legalne" mogą zawierać bugi".

Proste jak budowa cepa.

Piotrku, wiem, że jesteś nieskłonny do pokory i nie chcę się tu na tobie
złośliwie wyżywać (chociaż z tego właśnie względu skądinąd aż trudno mi się
powstrzymać), więc pozwól że ci podpowiem, że w oryginalnym zdaniu użyłeś po
prostu o jedno "nie" za dużo. W szczególności, to ostatnie "nie" jest
nadmiarowe.
Post by Piotr Sietnik
Post by Sektor van Skijlen
Post by Piotr Sietnik
Co więcej doświadczenie uczy, że nigdy nie trzeba nawet zakładać,
że to co napisze jest "czyste".
Piotrku, ale to jest standardowe "wszystko huj".
Jeśli twierdzisz, że mogą istnieć programy napisane "legalnie" i mimo to
1. Zdefiniuj pojęcie "legalny"
Dla mnie legalny oznacza jedno - zgodny z definicją języka.
No to indeksowanie poza tablicą jest niezgodne z definicją języka. Czy to
znaczy jednak, że poniższy fragment jest legalny, czy jest nielegalny?

int indeksuj( int* t, size_t i )
{ return t[i]; }
Post by Piotr Sietnik
Oczywiści nie oznacza to jednocześnie, że każdy "legalny" program
jest programem "poprawnym".
Oczywiście, ale tylko z tego względu, że mimo że program jest napisany
poprawnie, to realizowany przezeń algorytm może nie mieć sensu.
Post by Piotr Sietnik
To jest tak jak z kosą - legalne jest jej posiadanie, ale nie każde jej
użycie jest legalne.
Oto kolejny mistrz trudnej sztuki porównań!

Prawie dobrze ci wyszło, popełniłeś tylko jeden drobny błąd: nie mówiłeś o
porównaniu legalnych do nielegalnych, tylko o tym, że istnieją programy
legalne poprawne i legalne niepoprawne. Chyba o tym zapomniałeś pisząc swoje
porównanie z kosą.

Rany, co ty masz za robotę, że za piętnaście trzecia masz już całkowicie
wyprany umysł ;)?
--
// _ ___ Michal "Sektor" Malecki <sektor(whirl)kis.p.lodz.pl>
\\ L_ |/ `| /^\ ,() <ethourhs(O)wp.pl>
// \_ |\ \/ \_/ /\ C++ bez cholesterolu: http://www.intercon.pl/~sektor/cbx
"I am allergic to Java because programming in Java reminds me casting spells"
Piotr Sietnik
19 lat temu
Permalink
Post by Sektor van Skijlen
Piotrku, wiem, że jesteś nieskłonny do pokory i nie chcę się tu na tobie
złośliwie wyżywać (chociaż z tego właśnie względu skądinąd aż trudno mi
się powstrzymać), więc pozwól że ci podpowiem, że w oryginalnym zdaniu
użyłeś po prostu o jedno "nie" za dużo. W szczególności, to ostatnie "nie"
jest nadmiarowe.
Przyznaję masz rację, pokręciłem nieco to zdanie.
Post by Sektor van Skijlen
Post by Piotr Sietnik
Dla mnie legalny oznacza jedno - zgodny z definicją języka.
No to indeksowanie poza tablicą jest niezgodne z definicją języka.
Czy to znaczy jednak, że poniższy fragment jest legalny, czy jest
nielegalny?
int indeksuj( int* t, size_t i )
{ return t[i]; }
Oczywiście, że jest legalne, ale może być przyczyną wielu kłopotów.
Podobnie zresztą jak całkiem legalne standardowe funkcje: sprintf,
gets i wiele innych.
Post by Sektor van Skijlen
Post by Piotr Sietnik
Oczywiści nie oznacza to jednocześnie, że każdy "legalny" program
jest programem "poprawnym".
Oczywiście, ale tylko z tego względu, że mimo że program jest napisany
poprawnie, to realizowany przezeń algorytm może nie mieć sensu.
To to już inne trampki.
Post by Sektor van Skijlen
Post by Piotr Sietnik
To jest tak jak z kosą - legalne jest jej posiadanie, ale nie każde jej
użycie jest legalne.
Oto kolejny mistrz trudnej sztuki porównań!
Prawie dobrze ci wyszło, popełniłeś tylko jeden drobny błąd: nie mówiłeś o
porównaniu legalnych do nielegalnych, tylko o tym, że istnieją programy
legalne poprawne i legalne niepoprawne. Chyba o tym zapomniałeś pisząc
swoje porównanie z kosą.
Pisząc o kosie miałem na myśli ujemne indeksy, one są jak kosa.
No ale fakt pisałem na chybcika i czasu mi zabrakło na przeczytanie tego,
co napisałem.
Post by Sektor van Skijlen
Rany, co ty masz za robotę, że za piętnaście trzecia masz już całkowicie
wyprany umysł ;)?
Dłubie w HTML-u, w plikach kolegi, który leży w łóżku z dziurą po wyrostku.
Czasem wolałbym te pliki od początku sam...
--
Piotr Sietnik
***@pk.mofnet.gov.pl
Sektor van Skijlen
19 lat temu
Permalink
Post by Piotr Sietnik
Post by Sektor van Skijlen
int indeksuj( int* t, size_t i )
{ return t[i]; }
Oczywiście, że jest legalne, ale może być przyczyną wielu kłopotów.
Właśnie nie do końca. Tzn. ten fragment jest legalny, ale jeśli tego fragmentu
gdzieś użyjemy, to wtedy legalność używającego go kawałka jest zdeterminowana
szczególnymi warunkami, w jakich to jest użyte.

Taki niestety jest C++.
Post by Piotr Sietnik
Podobnie zresztą jak całkiem legalne standardowe funkcje: sprintf,
gets i wiele innych.
Oj, nie! To jest zupełnie inna sprawa. Poprawności użycia tej funkcji nie
jesteś w stanie skontrolować w żaden sposób. Natomiast 'indeksuj' jesteś w
stanie skontrolować przed sprawdzeniem poprawności indeksu przed podaniem go
do tej funkcji.
Post by Piotr Sietnik
Post by Sektor van Skijlen
Rany, co ty masz za robotę, że za piętnaście trzecia masz już całkowicie
wyprany umysł ;)?
Dłubie w HTML-u, w plikach kolegi, który leży w łóżku z dziurą po wyrostku.
Czasem wolałbym te pliki od początku sam...
Ba. Każdy by wolał.
--
// _ ___ Michal "Sektor" Malecki <sektor(whirl)kis.p.lodz.pl>
\\ L_ |/ `| /^\ ,() <ethourhs(O)wp.pl>
// \_ |\ \/ \_/ /\ C++ bez cholesterolu: http://www.intercon.pl/~sektor/cbx
"I am allergic to Java because programming in Java reminds me casting spells"
Piotr Sietnik
19 lat temu
Permalink
Post by Sektor van Skijlen
Post by Piotr Sietnik
Post by Sektor van Skijlen
int indeksuj( int* t, size_t i )
{ return t[i]; }
Oczywiście, że jest legalne, ale może być przyczyną wielu kłopotów.
Właśnie nie do końca. Tzn. ten fragment jest legalny, ale jeśli tego
fragmentu gdzieś użyjemy, to wtedy legalność używającego go kawałka jest
zdeterminowana szczególnymi warunkami, w jakich to jest użyte.
Podobnie jest z ujemnymi indeksami - są legalne, ale musi się
ich używać rozważnie.
...
Mój kolega (ten od wyrostka) lubi dłubać w HTML-ach, nawet wyplutych
w MSWorda. ;)
--
Piotr Sietnik
***@pk.mofnet.gov.pl
Sektor van Skijlen
19 lat temu
Permalink
Post by Piotr Sietnik
Post by Piotr Sietnik
W runtimie z takim samym prawdopodobieństwem wywalają się "legalne"
programy, jak i poprawnie działają "nielegalne".
Dodam jeszcze, że doświadczonym programistom znacznie częściej
wywalają się te "legalne".
Zakładam, że mówisz to na podstawie własnego doświadczenia.

lol ;)
--
// _ ___ Michal "Sektor" Malecki <sektor(whirl)kis.p.lodz.pl>
\\ L_ |/ `| /^\ ,() <ethourhs(O)wp.pl>
// \_ |\ \/ \_/ /\ C++ bez cholesterolu: http://www.intercon.pl/~sektor/cbx
"I am allergic to Java because programming in Java reminds me casting spells"
Piotr Sietnik
19 lat temu
Permalink
Post by Sektor van Skijlen
Post by Piotr Sietnik
Dodam jeszcze, że doświadczonym programistom znacznie częściej
wywalają się te "legalne".
Zakładam, że mówisz to na podstawie własnego doświadczenia.
lol ;)
A prawda, zapomniałem dodać, że jesteś wyjątkiem od tej reguły. ;-)
No ale, zobacz co miałem na myśli odpowiadając Maćkowi (nie chce drugi
raz pisać tego samego).
--
Piotr Sietnik
***@pk.mofnet.gov.pl
Maciej Sobczak
19 lat temu
Permalink
Post by Piotr Sietnik
Post by Sektor van Skijlen
Oczywiście. Indeks może być całkowity, a legalność jest tylko kwestią runtime.
Co ma do tego runtime?
To ma, że w ogólności dopiero w runtime można to sprawdzić albo się na
tym wywalić. Ze statycznego punktu widzenia ujemny indeks w tablicy jest
legalny. Może się to objawić jako undefined BEHAVIOR, ale jak sama nazwa
wskazuje objawi się to dopiero w czasie wykonania. Czyli w run-time, w
przeciwieństwie do compile-time.

(nie mylić z "runtime" jako zbiorem bibliotek i innego badziewia, który
jest potrzebny programowi do działania)
Post by Piotr Sietnik
W runtimie z takim samym prawdopodobieństwem wywalają się "legalne"
programy, jak i poprawnie działają "nielegalne".
?

Można prosić o rozwinięcie tematu?
--
Maciej Sobczak : http://www.msobczak.com/
Programming : http://www.msobczak.com/prog/
Piotr Sietnik
19 lat temu
Permalink
Post by Maciej Sobczak
Post by Piotr Sietnik
Co ma do tego runtime?
To ma, że w ogólności dopiero w runtime można to sprawdzić albo się na
tym wywalić. Ze statycznego punktu widzenia ujemny indeks w tablicy jest
legalny.
Ujemny indeks tablicy jest legalny z definicji, która mówi,
że indeks ma być całkowity i

X[N] jest tożsame z *((X)+(N))

Arytmetyki na liczbach ujemnych, chyba nikogo nie trzeba uczyć.
Post by Maciej Sobczak
Post by Piotr Sietnik
W runtimie z takim samym prawdopodobieństwem wywalają się "legalne"
programy, jak i poprawnie działają "nielegalne".
?
Można prosić o rozwinięcie tematu?
Trzymając się tematu wątku powiem krótko:
stosując dodatnie indeksy nie mamy żadnych gwarancji, że nie wyjedziemy
poza tablicę.
--
Piotr Sietnik
***@pk.mofnet.gov.pl
Sektor van Skijlen
19 lat temu
Permalink
Post by Piotr Sietnik
Post by Maciej Sobczak
Post by Piotr Sietnik
W runtimie z takim samym prawdopodobieństwem wywalają się "legalne"
programy, jak i poprawnie działają "nielegalne".
?
Można prosić o rozwinięcie tematu?
stosując dodatnie indeksy nie mamy żadnych gwarancji, że nie wyjedziemy
poza tablicę.
Tak samo, jak użycie ujemnych indeksów wcale nie musi oznaczać wyjechania poza
tablicę. lol ;)

Wykręcasz się sianem.

To stwierdzenie powyżej nie ma się nijak do tego ogólnego stwierdzenia, które
podałeś wcześniej.
--
// _ ___ Michal "Sektor" Malecki <sektor(whirl)kis.p.lodz.pl>
\\ L_ |/ `| /^\ ,() <ethourhs(O)wp.pl>
// \_ |\ \/ \_/ /\ C++ bez cholesterolu: http://www.intercon.pl/~sektor/cbx
"I am allergic to Java because programming in Java reminds me casting spells"
Maciej Sobczak
19 lat temu
Permalink
Post by Piotr Sietnik
Post by Maciej Sobczak
Post by Piotr Sietnik
W runtimie z takim samym prawdopodobieństwem wywalają się "legalne"
programy, jak i poprawnie działają "nielegalne".
Można prosić o rozwinięcie tematu?
stosując dodatnie indeksy nie mamy żadnych gwarancji, że nie wyjedziemy
poza tablicę.
Bzdura. Stosuję dodatnie indeksy i zwykle jestem pewny, że nie wyjeżdżam
poza tablicę, więc nie mów mi, że nie mam żadnych gwarancji. Natomiast
kod używający ujemnych indeksów byłby pierwszym podejrzanym.

To, czy wyjedziesz poza tablicę czy nie, zależy od tego, jaką wartość ma
indeks i na co wskazuje nazwa użyta z tym indeksem. To oczywiste. Zwykle
(tzn. biorąc statystyczny kod źródłowy) taka nazwa wskazuje na początek
tablicy, więc statystycznie dodatni indeks ma większe prawdopodobieństwo
zadziałać poprawnie, niż ujemny. Czyli kod (statystyczny) używający
ujemnego indeksu prawie na pewno jest zły, podczas gdy zdecydowana
większość poprawnego kodu używa indeksów dodatnich.
Na podstawie czyjego kodu wyznaczyłeś to równe prawdopodobieństwo? :)
--
Maciej Sobczak : http://www.msobczak.com/
Programming : http://www.msobczak.com/prog/
z***@wp.pl
19 lat temu
Permalink
...
Hmmm :)
To ciekawe jak banalny przykład podany przez qfela stał się zarzewiem
tak zażartej dyskusji.
Już nie wiem czy Wy tak powżnie, czy sobie jaja robicie ? :)

pzdr
warspite
--
Wysłano z serwisu OnetNiusy: http://niusy.onet.pl
Maciej Sobczak
19 lat temu
Permalink
Post by z***@wp.pl
Hmmm :)
To ciekawe jak banalny przykład podany przez qfela stał się zarzewiem
tak zażartej dyskusji.
A Ty jakiś nowy tutaj? :) To nie wiesz, że żeby zrobić zażartą dyskusję,
to *trzeba* podać banalny przykład? I najlepiej dorzucić "dlatego
właśnie C++ to kicha, Java ma to lepiej" albo coś porównywalnego. Wtedy,
przy odpowiednim splocie wydarzeń jest nawet szansa ujrzeć kolejny
rozdział epopei ornitologicznej ("Kogut w przykładach") albo chociaż
zebrać do kolekcji następny fragment jakiegoś chronionego prawem
autorskim standardu, lub po prostu uaktualnić swoją wiedzę nt.
lansowanych dzisiaj teorii.
Post by z***@wp.pl
Już nie wiem czy Wy tak powżnie, czy sobie jaja robicie ? :)
My? My to jesteśmy amatorzy. Obejrzyj sobie obrady sejmu.

;-) ;-) ;-)
--
Maciej Sobczak : http://www.msobczak.com/
Programming : http://www.msobczak.com/prog/
Mateusz Łoskot
19 lat temu
Permalink
Post by Maciej Sobczak
Post by z***@wp.pl
Hmmm :)
To ciekawe jak banalny przykład podany przez qfela stał się zarzewiem
tak zażartej dyskusji.
A Ty jakiś nowy tutaj? :) To nie wiesz, że żeby zrobić zażartą dyskusję,
to *trzeba* podać banalny przykład? I najlepiej dorzucić "dlatego
właśnie C++ to kicha, Java ma to lepiej" albo coś porównywalnego. Wtedy,
przy odpowiednim splocie wydarzeń jest nawet szansa ujrzeć kolejny
rozdział epopei ornitologicznej ("Kogut w przykładach")
Hehe, no to mam patcha do dokumentacji dla Koguta.
W rozdziale (jak będzie) Prerequisites jako jeden z punktów dobrze jest
wymienić lornetkę ;-)

Pozdrawiam
--
Mateusz Łoskot
http://mateusz.loskot.net
Sc0rpi0
19 lat temu
Permalink
Post by Maciej Sobczak
Post by z***@wp.pl
Hmmm :)
To ciekawe jak banalny przykład podany przez qfela stał się zarzewiem
tak zażartej dyskusji.
A Ty jakiś nowy tutaj? :) To nie wiesz, że żeby zrobić zażartą dyskusję,
to *trzeba* podać banalny przykład? I najlepiej dorzucić "dlatego
właśnie C++ to kicha, Java ma to lepiej" albo coś porównywalnego.
C++ to kicha ;) BASIC ma to lepiej, a Balcerowicz... :P
--
Sc0rpi0
I hated going to weddings.
All the grandmas would poke me saying "You're next".
They stopped that when I started doing it to them at funerals.
Piotr Sietnik
19 lat temu
Permalink
Post by Maciej Sobczak
Bzdura. Stosuję dodatnie indeksy i zwykle jestem pewny, że nie wyjeżdżam
poza tablicę, więc nie mów mi, że nie mam żadnych gwarancji.
To dlaczego użyłeś określenia "zwykle"?
Post by Maciej Sobczak
Natomiast
kod używający ujemnych indeksów byłby pierwszym podejrzanym.
Co nie znaczy, że nie byłby poprawny.

Dla mnie, i nie tylko dla minie, co widać po lekturze tej grupy,
używanie indeksów zawsze jest podejrzane. Moją "tezę" potwierdzają
odpowiedzi na pytania właśnie na tej grupie zadawane. Jeśli komuś
się coś wykrzaczy i zada tutaj pytanie, większość odpowiedzi będzie
typu: "nie chce mi się analizować kodu, ale pewnie wyjeżdżasz za tablicę"
i z reguły taka odpowiedź bywa trafna.
Dlaczego piszesz więc, że nikt dodatnich indeksów nie podejrzewa?

Nikt nie podejrzewa ujemnych, bo ich mało kto stosuje, ale jeśli
już zastosuje, to z większa rozwagą i przemyśleniem.

Poza tym odpowiedz sobie na pytanie:
Czy Masz problemy z arytmetyką liczb ujemnych?
Ja myślę, że nie, dlatego uważam, iż nie sprawiałoby Ci problemu
używanie ujemnych indeksów na równi z dodatnimi.
...
Prawdopodobieństwo wyznacza się na podstawie teorii nie kodu.
Na podstawie istniejącego kodu wylicza się statystyki.
--
Piotr Sietnik
***@pk.mofnet.gov.pl
Maciej Sobczak
19 lat temu
Permalink
Post by Piotr Sietnik
Post by Maciej Sobczak
Bzdura. Stosuję dodatnie indeksy i zwykle jestem pewny, że nie wyjeżdżam
poza tablicę, więc nie mów mi, że nie mam żadnych gwarancji.
To dlaczego użyłeś określenia "zwykle"?
Bo dwa razy w roku wyjeżdżam. :)

I ostatnie dwa były w kodzie, który używa funkcji z jakiegoś interfejsu,
gdzie wszystko przekazuje się przez wskaźnik. Fuj.
Post by Piotr Sietnik
Post by Maciej Sobczak
Natomiast
kod używający ujemnych indeksów byłby pierwszym podejrzanym.
Co nie znaczy, że nie byłby poprawny.
Bijesz pianę i tyle. Kod, który używa ujemnych indeksów wysłałbym do
/dev/null a programiście pokazałbym żółtą kartkę. Albo od razu czerwoną.

Samo użycie ujemnego indeksu w sytuacji, gdy wszystkie
deklaracje/new/malloc/łotewer dają nazwy, które trzymają tablice za
początek (czyli nieujemne indeksy są jedynymi legalnymi dla tych nazw),
oznacza, że ktoś postarał się zdobyć wskaźnik do środka lub końca
tablicy i używa tego wskaźnika tak, jakby to była jakaś nowa tablica.
I to już jest powód, żeby taki kod zaorać.
Post by Piotr Sietnik
Dla mnie, i nie tylko dla minie, co widać po lekturze tej grupy,
używanie indeksów zawsze jest podejrzane.
Więc w ogóle należy ich nie używać? Nie rozumiem.

Co chcesz udowodnić?
--
Maciej Sobczak : http://www.msobczak.com/
Programming : http://www.msobczak.com/prog/
Sektor van Skijlen
19 lat temu
Permalink
Post by Maciej Sobczak
Post by Piotr Sietnik
Post by Maciej Sobczak
Bzdura. Stosuję dodatnie indeksy i zwykle jestem pewny, że nie wyjeżdżam
poza tablicę, więc nie mów mi, że nie mam żadnych gwarancji.
To dlaczego użyłeś określenia "zwykle"?
Bo dwa razy w roku wyjeżdżam. :)
Taaa... w tym raz w Alpy i raz do Warszawy lol ;)
Post by Maciej Sobczak
I ostatnie dwa były w kodzie, który używa funkcji z jakiegoś interfejsu,
gdzie wszystko przekazuje się przez wskaźnik. Fuj.
Ano fuj. Ale jak inaczej chciałbyś przekazać tablicę?

Zwłaszcza jak to masz w kodzie legacy lol ;)
Post by Maciej Sobczak
Post by Piotr Sietnik
Post by Maciej Sobczak
Natomiast
kod używający ujemnych indeksów byłby pierwszym podejrzanym.
Co nie znaczy, że nie byłby poprawny.
Bijesz pianę i tyle. Kod, który używa ujemnych indeksów wysłałbym do
/dev/null a programiście pokazałbym żółtą kartkę. Albo od razu czerwoną.
Nie, no nie bądź taki radykalny :)

Ale to wymaga odpowiedniego sformalizowania. Można w ostateczności mieć coś
takiego, że wskaźnik wskazuje nie na pierwszy element tablicy, tylko gdzieś w
środku (ja to nazywam: obiekt nie jest trzymany za łeb, tylko za jaja). Ale
można tylko wtedy, gdy jest ściśle ograniczony zakres miejsc, w którym taki
wskaźnik jest używany, a dokładnie to to powinna być jedna klasa, która ten
wskaźnik ma w polach prywatnych (nie chronionych!). Tak robi przecież
std::string; tam wskaźnik w wewnętrznych danych wskazuje na miejsce, gdzie
zaczynają się znaki, ale struktura z danymi stringa zaczyna się wcześniej. Ale
operowanie takim wskaźnikiem w publicznym interfejsie nigdy nie może mieć
miejsca i powinno być obwarowane czerwoną kartką.
Post by Maciej Sobczak
Samo użycie ujemnego indeksu w sytuacji, gdy wszystkie
deklaracje/new/malloc/łotewer dają nazwy, które trzymają tablice za
początek (czyli nieujemne indeksy są jedynymi legalnymi dla tych nazw),
oznacza, że ktoś postarał się zdobyć wskaźnik do środka lub końca
tablicy i używa tego wskaźnika tak, jakby to była jakaś nowa tablica.
I to już jest powód, żeby taki kod zaorać.
Tu się zgodzę - jeśli zakładamy, że to jest wskaźnik w środek, to trzeba
zadbać o to, żeby nikt nie miał do niego dostępu. Bo konwencja wskaźników jako
nosicieli tablic jest taka, że wskazują na ich początek.

Maciek, z czym ty tutaj wyjeżdżasz? Ja właśnie znalazłem kod, w którym
specjalista w celu wyszukania elementu tablicy spełniającego kryterium stosuje
mniej więcej taką metodę:

for (i = 0; i < MAX; ++i)
{
if ( t[i] < value )
{
if ( t[i+1] < value )
send_value( t[i+2] );
else
send_value( t[i+1] );
}
else
send_value( t[i] );
}

I jeszcze przed pętlą jest taki komentarz mniej więcej: "Ten kod jest
dostosowany do wartości MAX równiej 3; kod trzeba będzie poprawić, jeśli
wartość będzie inna".

I nikt by nie zauważył tego kawałka, gdyby nie Klocwork, który stwierdził, że
"indeksowanie t potencjalnie wyjeżdża za tablicę".

Wydawało mi się, że widziałem już różne skrajne głupoty w programowaniu, ale
ten kawałek przebił wszystko. Nawet ping-pong-while.
--
// _ ___ Michal "Sektor" Malecki <sektor(whirl)kis.p.lodz.pl>
\\ L_ |/ `| /^\ ,() <ethourhs(O)wp.pl>
// \_ |\ \/ \_/ /\ C++ bez cholesterolu: http://www.intercon.pl/~sektor/cbx
"I am allergic to Java because programming in Java reminds me casting spells"
Jedrzej Dudkiewicz
19 lat temu
Permalink
Post by Sektor van Skijlen
Wydawało mi się, że widziałem już różne skrajne głupoty w programowaniu, ale
ten kawałek przebił wszystko. Nawet ping-pong-while.
Cóż to jest "ping-pong-while"?

JD
Maciej Sobczak
19 lat temu
Permalink
Post by Sektor van Skijlen
Post by Maciej Sobczak
Post by Piotr Sietnik
Post by Maciej Sobczak
Bzdura. Stosuję dodatnie indeksy i zwykle jestem pewny, że nie wyjeżdżam
poza tablicę, więc nie mów mi, że nie mam żadnych gwarancji.
To dlaczego użyłeś określenia "zwykle"?
Bo dwa razy w roku wyjeżdżam. :)
Taaa... w tym raz w Alpy i raz do Warszawy lol ;)
Się nie śmiej, bo poważnie mówię. Nie chcę tu się przedstawiać jako
kozak albo jakiś Chuck Norris, który nigdy nie robi bugów.
Ku przestrodze wyglądało to tak:

Jest sobie funkcja z jakiegoś API, która bierze tablicę danych przez
wskaźnik, np.:

void fun(void *p);

A ja mam dane w wektorze v, więc strzelam coś takiego:

fun(&v[0]);

Gra i buczy. Jakiś czas później wpadam na pomysł (ale nie pamiętam
powodu), żeby jednak ten wektor trzymać nie przez referencję (bo v było
referencją), ale przez wskaźnik, więc v stał się wskaźnikiem.
Zapomniałem zmienić wywołanie funkcji fun. Dalej się kompiluje, bo dalej
jest to legalne. Fajne, nie?
Najgorsze jest to, że program się nawet nie wywalił. Funkcja fun to było
coś z API Oracla, co zapisywało dane do bazy. Owszem, zapisało, bo
Oraclowi wisi co bierze z pamięci i po prostu w bazie znalazły się dane
z czapki.

Dlatego uważam, że w ogóle wskaźniki nie powinny pozwalać na
indeksowanie. Wskaźniki mają *wskazywać* a nie udawać tablice.
To samo z iteratorami.
...
Jeśli to sobie zaenkapsulujesz, to możesz nadać temu dowolną semantykę.
Weź np. std::map - ma operator indeksowania i łyka nie tylko indeksy
ujemne, ale nawet zespolone. ;)
Ale ja mówię o *tablicach*.

Chociaż - z drugiej strony - w Adzie zakres indeksów tablicy określa się
w czasie jej definiowania. Ujemne? No problem. Chociaż to też słaby
argument, bo w C/C++ zakres indeksów *też* określa się w czasie
definiowania tablicy - ale dolnym jest zawsze 0.
--
Maciej Sobczak : http://www.msobczak.com/
Programming : http://www.msobczak.com/prog/
Sektor van Skijlen
19 lat temu
Permalink
Post by Maciej Sobczak
Jest sobie funkcja z jakiegoś API, która bierze tablicę danych przez
void fun(void *p);
fun(&v[0]);
Nie no... teoretycznie można, ale to jest hardkor. Zresztą to jest chyba
nielegalne. Nie dlatego, żeby ktoś miał inną implementację wektora (bo wektor
z założenia musi być rozłożony w jednym kawałku), tylko dlatego, że referencja
zwracana przez [] w założeniu określa jeden obiekt, zatem po & jest z tego
wskaźnik, ale z założenia wskazujący na JEDEN obiekt. Fakt, że możesz go
awansować i indeksować, wynika tylko stąd, że operujesz wtedy na gołej
pamięci (szczególny przypadek wektora).
Post by Maciej Sobczak
Gra i buczy. Jakiś czas później wpadam na pomysł (ale nie pamiętam
powodu), żeby jednak ten wektor trzymać nie przez referencję (bo v było
referencją), ale przez wskaźnik, więc v stał się wskaźnikiem.
Zapomniałem zmienić wywołanie funkcji fun. Dalej się kompiluje, bo dalej
jest to legalne. Fajne, nie?
No ba... ;)
Post by Maciej Sobczak
Najgorsze jest to, że program się nawet nie wywalił. Funkcja fun to było
coś z API Oracla, co zapisywało dane do bazy. Owszem, zapisało, bo
Oraclowi wisi co bierze z pamięci i po prostu w bazie znalazły się dane
z czapki.
:)
Post by Maciej Sobczak
Dlatego uważam, że w ogóle wskaźniki nie powinny pozwalać na
indeksowanie. Wskaźniki mają *wskazywać* a nie udawać tablice.
To samo z iteratorami.
Z tego, co wiem, iteratory (nawet swobodnego dostępu) nie mają operatora [],
aczkolwiek mają swoją funkcję advance.

W ogóle uważam, że najsensowniej byłoby mieć opakowanie do gołego wskaźnika
(np. ptr<T>), które by udostępniało wszystko, tylko nie advance/distance. A do
poruszania się po tablicy mieć osobny typ, podobnie opakowujący.

Ale to i tak nie stosowałoby się do wskaźników w języku (szczególnie operatora
& na typie referencyjnym), więc nie za bardzo ma sens.

Fajnie, jakby ktoś wymyślił np. takie cuś:

int __iterator* t;

przy czym np. gdyby na zwykłym wskaźniku robić arytmetykę lub inicjalizować go
tablicą, kompilator rzucałby ostrzeżenia. Trzeba by było stosować to powyżej,
to wtedy ostrzeżeń nie będzie. Również int __iterator* może się konwertować na
int*, ale w drugą stronę dostajesz ostrzeżenie. Tak samo jeśli próbujesz
inicjalizować to powyżej czymś innym niż tablicą (np. wskaźnikiem pobranym z
&).
Post by Maciej Sobczak
Jeśli to sobie zaenkapsulujesz, to możesz nadać temu dowolną semantykę.
No właśnie. Ale tylko wtedy!
Post by Maciej Sobczak
Weź np. std::map - ma operator indeksowania i łyka nie tylko indeksy
ujemne, ale nawet zespolone. ;)
Ta. Można to indeksować nawet drzewem, pod warunkiem, że uważamy go za typ
wartościowy lol ;)
Post by Maciej Sobczak
Ale ja mówię o *tablicach*.
Chociaż - z drugiej strony - w Adzie zakres indeksów tablicy określa się
w czasie jej definiowania. Ujemne? No problem. Chociaż to też słaby
argument, bo w C/C++ zakres indeksów *też* określa się w czasie
definiowania tablicy - ale dolnym jest zawsze 0.
W C i C++ też się da. Właśnie na tej zasadzie. Tylko że nikt cię nie
przypilnuje, czy nie wyjeżdżasz ;)

Problem nie tyle polega na tym, że ujemne indeksy są złe, tylko że tradycją w
C się stało (i C++ to niestety też przeniósł), że przez wskaźniki przekazuje
się tablice (w tym również stringi jako NTS-y). I w związku z tym najczęściej
wskaźnik oznacza początek tablicy.

W ujemne indeksy można by się bawić, pod warunkiem, że stworzymy sobie typ
tablicowy, w którym jawnie można określać zakres indeksów. Wtedy nie burzymy
tej tradycji.
--
// _ ___ Michal "Sektor" Malecki <sektor(whirl)kis.p.lodz.pl>
\\ L_ |/ `| /^\ ,() <ethourhs(O)wp.pl>
// \_ |\ \/ \_/ /\ C++ bez cholesterolu: http://www.intercon.pl/~sektor/cbx
"I am allergic to Java because programming in Java reminds me casting spells"
Maciej Sobczak
19 lat temu
Permalink
...
Sektor, co Ty bredzisz? :)
Zapis &v[0] jest idiomatycznym sposobem na uzyskanie wskaźnika na
pierwszy element wektora (albo zwykłej tablicy). Jeżeli jakaś funkcja
oczekuje wskaźnika na tablicę, to &v[0] jest najprostszą rzeczą, jaką
można zrobić. I jak najbardziej jest to legalne.
Post by Sektor van Skijlen
Post by Maciej Sobczak
Dlatego uważam, że w ogóle wskaźniki nie powinny pozwalać na
indeksowanie. Wskaźniki mają *wskazywać* a nie udawać tablice.
To samo z iteratorami.
Z tego, co wiem, iteratory (nawet swobodnego dostępu) nie mają operatora [],
aczkolwiek mają swoją funkcję advance.
Niestety mają operator[], i jest to wymagane dla iteratorów swobodnego
dostępu (dla prawników: 24.1.5).
I to jest niestety powielanie bzdury. Parafrazując jednego szanowanego pana:

"If your iterators behave like dumb pointers, they are dumb pointers."
Post by Sektor van Skijlen
W ogóle uważam, że najsensowniej byłoby mieć opakowanie do gołego wskaźnika
(np. ptr<T>), które by udostępniało wszystko, tylko nie advance/distance. A do
poruszania się po tablicy mieć osobny typ, podobnie opakowujący.
Ma to sens.
Post by Sektor van Skijlen
Ale to i tak nie stosowałoby się do wskaźników w języku (szczególnie operatora
& na typie referencyjnym), więc nie za bardzo ma sens.
Ma. Trzeba jeszcze tylko napisać skrypt (w Tcl, oczywiście ;) ), który
po znalezieniu w kodzie zwykłego nieopakowanego wskaźnika, wywala kod na
śmietnik.
Post by Sektor van Skijlen
int __iterator* t;
Nie podoba mi się zwyczaj wymyślania nowych słów kluczowych z __ na
początku. Ale idźmy dalej.
Post by Sektor van Skijlen
przy czym np. gdyby na zwykłym wskaźniku robić arytmetykę lub inicjalizować go
tablicą, kompilator rzucałby ostrzeżenia. Trzeba by było stosować to powyżej,
to wtedy ostrzeżeń nie będzie.
Czemu ostrzeżenia a nie błędy?
Post by Sektor van Skijlen
Również int __iterator* może się konwertować na
int*, ale w drugą stronę dostajesz ostrzeżenie.
Po co konwersje?
Post by Sektor van Skijlen
Tak samo jeśli próbujesz
inicjalizować to powyżej czymś innym niż tablicą (np. wskaźnikiem pobranym z
&).
OK.
Post by Sektor van Skijlen
Problem nie tyle polega na tym, że ujemne indeksy są złe, tylko że tradycją w
C się stało (i C++ to niestety też przeniósł), że przez wskaźniki przekazuje
się tablice (w tym również stringi jako NTS-y). I w związku z tym najczęściej
wskaźnik oznacza początek tablicy.
W ujemne indeksy można by się bawić, pod warunkiem, że stworzymy sobie typ
tablicowy, w którym jawnie można określać zakres indeksów. Wtedy nie burzymy
tej tradycji.
Jedyną tradycją jest to, że wskaźniki udają tablice. I to jest złe.
Wskaźniki powinny wskazywać. Nic więcej.
--
Maciej Sobczak : http://www.msobczak.com/
Programming : http://www.msobczak.com/prog/
Tomasz Kaczanowski
19 lat temu
Permalink
...
Sektor poniekąd ma rację wystarczy, ze wektor będzie inaczej
zaimplementowany i lezymy. To, ze teoretycznie jest w jednym kawalku i
powinna byc to pamiec ciagla, nie oznacza, ze zawsze tak bedzie i lepiej
by sie do tego nie przyzwyczajac... Kiedys sie przyzwyczailem, ze majac
funkcje fun(int a,...) zczytywalem adres pierwszego elementu, i
wychodzilem z zalozenia, ze nastepne beda mialy kolejne adresy... Jakze
sie zdziwilem, gdy pisalem cos na system, gdzie dane te niekoniecznie sa
przekazywane w ciaglym fragmecie pamieci... Tak wiec wydaje mi sie, ze
fakt iz dzis takie cos spelni nasze oczekiwania, nie oznacza, ze bedzie
tak jutro...
--
Kaczus/Pegasos User
http://kaczus.republika.pl
Tomek Nurkiewicz
19 lat temu
Permalink
Post by Tomasz Kaczanowski
Post by Maciej Sobczak
Post by Sektor van Skijlen
Post by Maciej Sobczak
fun(&v[0]);
Nie no... teoretycznie można, ale to jest hardkor. Zresztą to jest chyba
Sektor, co Ty bredzisz? :)
Zapis &v[0] jest idiomatycznym sposobem na uzyskanie wskaźnika na
pierwszy element wektora
Sektor poniekąd ma rację wystarczy, ze wektor będzie inaczej
zaimplementowany i lezymy.
Według mnie taki zapis jest jak najbardziej poprawny, mało tego, ja dałbym
w implementacji wektora albo specjalną funkcję np. raw(), która zwraca T*,
albo wręcz operator konwersji na T* (gdzie T to typ danych w wektorze).
Weź pod uwagę, że wektor coraz częściej jest polecany jako zamiennik
zwykłej tablicy alokowanej new/delete.

Jedyny hardcore polega na tym, że zmieniając w jakikolwiek sposób rozmiar
wektora wskaźnik na pierwszy element może stracić ważność. Może więc warto
opakować wektor zabierając z niego wszystkie funkcje zmieniające rozmiar
po jego konstrukcji? Taka klasa (vide wyżej - dynamiczne tablice w stylu
C) i tak miałaby dużą funkcjonalność, a pozbawiona by była
niebezpieczeństw dealokacji wewnętrznej pamięci wektora.


pozdrowienia
--
Tomek Nurkiewicz
"Kat oszalał brodząc we krwi" - http://hunter.art.pl
In order to send me an e-mail remove the dot between "N" and "u"
Maciej Sobczak
19 lat temu
Permalink
Post by Tomasz Kaczanowski
Post by Maciej Sobczak
Zapis &v[0] jest idiomatycznym sposobem na uzyskanie wskaźnika na
pierwszy element wektora (albo zwykłej tablicy). Jeżeli jakaś funkcja
oczekuje wskaźnika na tablicę, to &v[0] jest najprostszą rzeczą, jaką
można zrobić. I jak najbardziej jest to legalne.
Sektor poniekąd ma rację wystarczy, ze wektor będzie inaczej
zaimplementowany i lezymy.
std::vector nie może być zaimplementowany inaczej. To jest
zagwarantowane przez standard (uwaga dla kolekcjonerów: to jest
zagwarantowane w standardzie z roku 2003 a nie w drafcie z zeszłego
tysiąclecia, gdzie brak takiej gwarancji został uznany za defekt).
Post by Tomasz Kaczanowski
To, ze teoretycznie jest w jednym kawalku i
powinna byc to pamiec ciagla, nie oznacza, ze zawsze tak bedzie i lepiej
by sie do tego nie przyzwyczajac...
Można na tym polegać, bo właśnie taka była idea (tylko ktoś zapomniał to
napisać czarno na białym, ale na szczęście nigdy nie powstała
implementacja, która robiłaby to inaczej, niż zamierzano). std::vector
miał być alternatywą dla tablic dynamicznych i skoro ma być alternatywą,
to musi dać się zastosować wszędzie tam, gdzie da się zastosować
tablicę. W szczególności z funkcjami z C, które oczekują tablicy podanej
przez wskaźnik.
--
Maciej Sobczak : http://www.msobczak.com/
Programming : http://www.msobczak.com/prog/
Tomasz Kaczanowski
19 lat temu
Permalink
...
No to przepraszam, moj blad... Nie interesowalem sie od dawna biblioteka
STL, bo uwazam ja za najmniej wartowsiowy fragmet c++, niestety api i
implementacja jest koszmarna, tzn brakuje wielu moim zdaniem
podstawowych metod, dlatego wole korzystac ze specyficznych rzeczy dla
danej platformy, ew z wlasnych rozwiazan...
--
Kaczus/Pegasos User
http://kaczus.republika.pl
Maciej Sobczak
19 lat temu
Permalink
Post by Tomasz Kaczanowski
No to przepraszam, moj blad... Nie interesowalem sie od dawna biblioteka
STL, bo uwazam ja za najmniej wartowsiowy fragmet c++, niestety api i
implementacja jest koszmarna
API jest jedno a implementacji może być wiele. Która jest koszmarna?
Post by Tomasz Kaczanowski
tzn brakuje wielu moim zdaniem
podstawowych metod
Jakich?
Post by Tomasz Kaczanowski
dlatego wole korzystac ze specyficznych rzeczy dla
danej platformy
Np. jakich?
Post by Tomasz Kaczanowski
ew z wlasnych rozwiazan...
Np. jakich?
--
Maciej Sobczak : http://www.msobczak.com/
Programming : http://www.msobczak.com/prog/
Tomasz Kaczanowski
19 lat temu
Permalink
Post by Maciej Sobczak
Post by Tomasz Kaczanowski
No to przepraszam, moj blad... Nie interesowalem sie od dawna
biblioteka STL, bo uwazam ja za najmniej wartowsiowy fragmet c++,
niestety api i implementacja jest koszmarna
API jest jedno a implementacji może być wiele. Która jest koszmarna?
Post by Tomasz Kaczanowski
tzn brakuje wielu moim zdaniem podstawowych metod
Jakich?
Dlugo by pisac, popatrz na inne rozwiazania... Ktos by powiedzial, ze
jest wszystko, tylko po co robic templata, do ktorego polowe funkcji
trzeba dopisywac, prosty przyklad, masz std::map, az sie prosi, by moc
sprawdzic, czy element o zadanym indeksie jest w kontenerze... O
zamianie w stringu wielkosci liter przez grzecznosc nie wspominam.
Oczywiscie wszystko sobie mozna napisac, ale nie po to sie chyba tworzy
uniwersalne rozwiazania, by tak na prawde wszystko pisac samemu... sorry
ale allokacje, reallokacje i kasowanie obiektow robi sie na prawde
szybko, wiec jak juz byc hardcorowcem, to po co wogole uzywac gotowych
rozwiazan, skoro one sa niedopracowane...
Post by Maciej Sobczak
Post by Tomasz Kaczanowski
dlatego wole korzystac ze specyficznych rzeczy dla danej platformy
Np. jakich?
Zalezy od platformy na ktorej pracuje... Wlasne kontenery ma MFC
(najslabsze chyba), sa tez w .Net, czy wxWidget. W sumie to chyba kazda
platforma obecnie ma jakies kontenery i chyba wszystkie sa lepiej
opracowane od tych w C++...
Post by Maciej Sobczak
Post by Tomasz Kaczanowski
ew z wlasnych rozwiazan...
Np. jakich?
Tych, ktore sobie kiedys napisalem...
--
Kaczus/Pegasos User
http://kaczus.republika.pl
Witold Kuzminski
19 lat temu
Permalink
...
Moze sprobuj tak: map m; if (index <[<=] m.size()) { jest } else { nie ma };
Czy chodzi ci moze o zadany klucz, bo index w mapie takiej jak std::map
raczej malo znaczy?
Post by Tomasz Kaczanowski
O
zamianie w stringu wielkosci liter przez grzecznosc nie wspominam.
Ja tez.
Post by Tomasz Kaczanowski
Oczywiscie wszystko sobie mozna napisac, ale nie po to sie chyba tworzy
uniwersalne rozwiazania, by tak na prawde wszystko pisac samemu... sorry
ale allokacje, reallokacje i kasowanie obiektow robi sie na prawde
szybko,
Naprawde szybko to wg. ciebie jak szybko?
Post by Tomasz Kaczanowski
wiec jak juz byc hardcorowcem, to po co wogole uzywac gotowych
rozwiazan, skoro one sa niedopracowane...
Sorry ale allokacje, reallokacje i kasowanie to dzis chyba najagorszy problem.
Problem rosnie wykladniczo ze wzrostem ilosci procesorow firmy Intel, ktore
te alokacje i reallokacje wykonuja w jednej obudowie.
Post by Tomasz Kaczanowski
Post by Maciej Sobczak
Post by Tomasz Kaczanowski
dlatego wole korzystac ze specyficznych rzeczy dla danej platformy
Np. jakich?
Zalezy od platformy na ktorej pracuje... Wlasne kontenery ma MFC
(najslabsze chyba), sa tez w .Net, czy wxWidget. W sumie to chyba kazda
platforma obecnie ma jakies kontenery i chyba wszystkie sa lepiej
opracowane od tych w C++...
Patrzac na przyklad std::map, wedlug ciebie dobre opracowanie kontenerow
wydaje sie tozsame z twoim stopniem ich zrozumienia :)
--
Wysłano z serwisu Usenet w portalu Gazeta.pl -> http://www.gazeta.pl/usenet/
Piotr Wyderski
19 lat temu
Permalink
Post by Witold Kuzminski
Sorry ale allokacje, reallokacje i kasowanie to dzis chyba najagorszy
problem. Problem rosnie wykladniczo ze wzrostem ilosci procesorow
firmy Intel, ktore te alokacje i reallokacje wykonuja w jednej
obudowie.
Problem jest istotnie dotkliwy, ale powoduje go nie jest marka procesora,
lecz implementacja alokatorów, które w znanych mi przypadkach bardzo
słabo radzą sobie ze współbieżnością, a i wydajność bez kolizji mają
niewielką.
Mnie ten problem wyszedł na Sparcu -- na 4 procesorach alokator systemowy
działał zaledwie 2,3 raza szybciej, więc go zastąpiłem własnym, który nawet
na
8 procesorach działa 8-epsilon raza szybciej. Wydajność też ma nieco
większą:
ostatnio dla eksperymentu podstawiłem go w części w programu, a resztę
zastąpiłem malloc(). Wyniki Quantify były takie:

malloc(); # of calls 188xxx; % of root: 7.83 (f+d)
memory::acquire(); # of calls 788yyy; % of root: 0,19 (f+d)

gdzie xxx, yyy = nie pamiętam dokładnie. ;-) Działa on więc około
(7,83/0,19)*(788/188) = 172 raza szybciej od malloc z MSVC7.1.

Niestety prawo Amdahla "zabrania" wzrostu wydajności całego
programu o aż taki czynnik, bo alokacje są jedynie jego fragmentem,
ale w zależności od kontenera przyspieszenie jest typowo 1,5..10-krotne
(std::string 2,5x) i w przypadku całego programu wynosi 4,94 (Sparc
wykonujący jede wątek). Na 4 procesorach i 4 wątkach system działa
3,92x szybciej, podczas gdy z malloc() ponad 2 razy _wolniej_ niż na 1.

Pozdrawiam
Piotr Wyderski

--
"If you were plowing a field, which would you rather use?
Two strong oxen or 1024 chickens?" -- Seymour Cray
Witold Kuzminski
19 lat temu
Permalink
Post by Piotr Wyderski
Post by Witold Kuzminski
Sorry ale allokacje, reallokacje i kasowanie to dzis chyba najagorszy
problem. Problem rosnie wykladniczo ze wzrostem ilosci procesorow
firmy Intel, ktore te alokacje i reallokacje wykonuja w jednej
obudowie.
Problem jest istotnie dotkliwy,
Bardzo dotkliwy, bardzo zlozony i chyba ma bardzo male
zrozumienie w "spolecznosci" w ogole. Szkoda, bo tego typu problemy
to moim zdaniem najwieksze "wyzwanie" dla ludzi piszacych oprogramowanie.
Post by Piotr Wyderski
ale powoduje go nie jest marka procesora,
lecz implementacja alokatorów,
Oczywiscie, ze nie marka procesorow sama z siebie, ale tez nie tylko implementacja
alokatorow. To raczej wszystko do kupy, zaczynajac od zelaztwa, przez
OS (z jego alokatorami) po "wlasne" twory w konkretnych jezykach.
Post by Piotr Wyderski
które w znanych mi przypadkach bardzo
słabo radzą sobie ze współbieżnością, a i wydajność bez kolizji mają
niewielką.
Mnie ten problem wyszedł na Sparcu -- na 4 procesorach alokator systemowy
działał zaledwie 2,3 raza szybciej, więc go zastąpiłem własnym, który nawet
na
8 procesorach działa 8-epsilon raza szybciej.
Zauwaz, ze problem nie jest taki prosty; to 2.3 raza to dla szczegolnej aplikacji
o szczegolnym patternie alokacji i dealokacji. Hmmm, 8 razy szybciej niz malloc
na jednym procesorze?
Wogole to piszesz o porownaniu alokatorow czy wydajnosci systemu, uzywajacego
jednego albo innego alokatora?
...
Tak, bardzo ciekawe. Napisz cos wiecej o tym swoim alokatorze, jak znajdziesz
chwile, bo wyglada ciekawie i na papierze za ladnie..:)

Ja pisze o czym innym. System uzytkowy/produkcyjny.
Na dwu-Xeon'owym blade chodzi wielokrotnie szybciej niz na 4-Xeonowym.
Mozna przy tym zaobserwowac ciekawe zjawisko: jesli system "wyrabia" to
i tak ma baaaardzo niezdrowy stosunek user do system time, zblizony do 1.
W pewnym momencie (gdy load rosnie) cos "peka" i box pokazuje 30%
iddle 50% system i 20% user. To na pierwszy rzut oka oznacza, ze OS nie
robi nic innego tylko budzi i usypia watki.
Ten sam kod binarny na 2-Xeonie zachowuje sie znacznie lepiej
user/system ~4 co tez wyglada na niezdrowe, ale przynajmniej wyrabia
i potrafi box wykrecic do 0% iddle. Trudno powiedziec jak wolno chodzi
4-Xeon, bo to kawalek, ktory jak sie sypnie, to idzie pod wode blyskawicznie...
Taka systuacja jak wyzej to byc moze alokator, ale napewno polaczony z zelaztwem.
Na moje oko, w takim zastosowaniu Intele sie sobie na wzajem tylko placza pod
nogami...
--
Wysłano z serwisu Usenet w portalu Gazeta.pl -> http://www.gazeta.pl/usenet/
Piotr Wyderski
19 lat temu
Permalink
Post by Witold Kuzminski
Bardzo dotkliwy, bardzo zlozony i chyba ma bardzo male
zrozumienie w "spolecznosci" w ogole.
Jasne, ale to jest dość trudny temat, więc jego dobre zrozumienie nie
jest zjawiskiem powszechnym, do tego pokutuje wiara w ekstremalną
fachowość twórców bibliotek, przez co mało komu przy zrównoleglaniu
przychodzi na myśl, że to biblioteka bruździ.
Post by Witold Kuzminski
Oczywiscie, ze nie marka procesorow sama z siebie, ale tez nie tylko
implementacja alokatorow. To raczej wszystko do kupy, zaczynajac od
zelaztwa, przez
OS (z jego alokatorami) po "wlasne" twory w konkretnych jezykach.
Owszem, ale to nie jest zasługa procesorów firmy Intel jako takich, lecz
marnej jakości chipsetu zapewniającego komunikację międzyprocesorową
i komunikację procesorów z pamięcią. Dla przykładu zobacz moduł XD1
Craya:

http://www.cray.com/products/xd1/
http://www.fpgajournal.com/articles_2005/20050405_cray.htm

Jest zbudowany na zwykłych Opteronach (a więc IA-32 + pochodna magistrali
EV-7), tylko switch fabric ma na Virtexie 4 (w pełni rekonfigurowalny
hardware!),
co daje mu maksymalną przepustowość szyny pamięci 77 GiB/s. Jak to porównać
do Pentium 4 oferującego w zwykłym pececie biedne 3,2 GiB/s...?
Post by Witold Kuzminski
Zauwaz, ze problem nie jest taki prosty; to 2.3 raza to dla
szczegolnej aplikacji o szczegolnym patternie alokacji i dealokacji.
A bo ja wiem? To był najprostszy opracowany przykład ilustrujący problem
i składał się z pętli biorącej dane std::string, sklejającej je, tnącej,
obliczającej
hashe, wycinającej substringi itp. Miało to symulować przetwarzanie
strumienia
z bazy danych. Nic specjalnego w nim nie widzę -- ani to szczególna
aplikacja,
ani szczególny wzorzec alokacji -- po prostu miało to spowodować intensywne
użycie kontenerów alokujących pamięć w warunkach zbliżonych do
rzeczywistych,
bo testy typu "wykonaj malloc a następnie free 10.000.000 razy" są
niemiarodajne.
Post by Witold Kuzminski
Hmmm, 8 razy szybciej niz malloc na jednym procesorze?
Nie, 8 razy szybciej niż _ten sam_ alokator (malloc albo std::allocator albo
mój)
na 1 procesorze.
Post by Witold Kuzminski
Wogole to piszesz o porownaniu alokatorow czy wydajnosci systemu,
uzywajacego jednego albo innego alokatora?
O wydajności aplikacji z podstawionym konkretnym alokatorem w stosunku
do tej samej aplikacji wykonującej identyczny test, ale z innym alokatorem.
Post by Witold Kuzminski
Tak, bardzo ciekawe. Napisz cos wiecej o tym swoim alokatorze, jak
znajdziesz chwile, bo wyglada ciekawie i na papierze za ladnie..:)
Raczej świadczy o niskiej jakości alokatorów dostarczanych w bibliotekach
z kompilatorami (GCC, MSVC) i leżących pod nimi alokatorów systemowych.
Co do przedstawionych wyników: są to pomiary, a nie oszacowania teoretyczne.
:-)
Po prostu kod na głównej ścieżce przepływu sterowania jest bardzo krótki
(listing asemblerowy to kilkanaście linijek) i w ogóle nie synchronizuje się
z niczym.
Tylko co z tego, skoro prawo Amdahla nieubłaganie przycina wzrost wydaności
ogólnej kontenerów do co najwyżej rzędu wielkości? Jeśli kod procedury
składa
się z dwóch części, z których jedna to alokacja, a druga operacje na
przydzielonym
bloczku i obie zajmują 50% czasu wykonania, to nawet nieskończone
przyspieszenie
alokatora da w efekcie zaledwie dwukrotny wzrost wydajności procedury...

Ale nie powinno to specjalnie dziwić, biorąc pod uwagę, co napisano w pracy
opisującej alokator Hoard: "For 14 processors, the Hoard version runs 18
times
faster than the next best allocator." A ponieważ malloc() z Solaris ani MSVC
to zdecydowanie nie jest "next best allocator", to te wyniki zbliżają się do
moich.
Uprzedzając pytanie: nie wiem, jak ma się mój alokator do Hoarda, bo nie
miałem
ani czasu, ani okazji wykonać odpowiednich testów, a zgadnąć też trudno, bo

one oparte na innych pomysłach, choć inny wniosek z cytowanej pracy brzmi
"The Solaris allocator performs poorly overall because serial single heap
allocators
do not scale", co się w pełni pokrywa z moimi pomiarami.
Post by Witold Kuzminski
Ja pisze o czym innym. System uzytkowy/produkcyjny.
Na dwu-Xeon'owym blade chodzi wielokrotnie szybciej niz na 4-Xeonowym.
Mozna przy tym zaobserwowac ciekawe zjawisko: jesli system "wyrabia" to
i tak ma baaaardzo niezdrowy stosunek user do system time, zblizony do 1.
Dlaczego niezdrowy? Tak wysoki stosunek wskazuje, że system jest
CPU-bounded -- toż to raj dla optymalizatora (ludzkiego, nie maszynki). :-)
Post by Witold Kuzminski
W pewnym momencie (gdy load rosnie) cos "peka" i box pokazuje 30%
iddle 50% system i 20% user. To na pierwszy rzut oka oznacza, ze OS nie
robi nic innego tylko budzi i usypia watki.
Moim zdaniem to bardzo dobry wniosek -- najprawdopodobniej zaczęły
się kolizje w dostępie do zasobu chronionego muteksem i wątki, które
przegrały rywalizację o jego zajęcie zaczęły się usypiać, by po krótkim
czasie
zostać obudzone przez wątek zwalniający ten muteks. Niestety w takim
przypadku każda (za wyjątkiem pierwszej) operacja zajęcia/zwolnienia muteksu
oznacza bardzo drogą wycieczkę do jądra, stąd przyrost system time. Na 2
procesorach prawdopodobieństwo kolizji było na tyle małe, że narzut wnoszony
przez ich obsługę ginął w szumie. Namierz ten muteks i go zlikwiduj. :-)

Druga możliwość: przetwarzanie strumieniowe, gdzie watki częściowo
przetwarzają dostarczone dane i przekazują je innemu wątkowi do dalszej
obróbki. Wówczas jeśli czas przetwarzania pakietu będzie znacznie krótszy
od time slice przydzielonego wątkowi, a synchronizacja między wątkami
będzie odbywała się w oparciu o muteksy (tj. śpij do chwili, aż przyjdą
dane,
przetwórz je i obudź wątek czekający na wynik), to wystąpi podobne zjawisko,
mimo całkowitego braku kolizji.
Post by Witold Kuzminski
Taka systuacja jak wyzej to byc moze alokator, ale napewno polaczony z zelaztwem.
Na moje oko, w takim zastosowaniu Intele sie sobie na wzajem tylko placza
pod nogami...

Nie, wówczas nastąpiłoby nasycenie pasma szyny pamięci i wszystko działałoby
wolniej
-- zaobserwowałbyś spadek skalowalności aplikacji, ale bez istotnej zmiany
współczynnika
user_time/system_time. Miałem tak na Fire 880, gdzie jakiś geniusz z GNU
wpadł na
pomysł synchronizowania operacji atomowej inkrementacji/dekrementacji w
libstdc++
pojedynczym spinlockiem (gałąź po #else), a więc jednym ruchem spowodował
false sharing
w cache, zamulając szynę, oraz zwykłe kolizje wątków:

http://www.opensource.apple.com/darwinsource/10.3/gccfast-1614/libstdc++-v3/
config/cpu/sparc/atomicity.h

Sporo czasu mi zajęło namierzenie drania, ale po spatchowaniu wydajność
skaluje
mi się liniowo wraz z liczbą procesorów. Tymczasem u Ciebie zaczynają się
odwołania
do jądra systemu, których nasycenie pasma nie ma prawa spowodować. Masz
muteks
albo nawet kilka, niekoniecznie w swoim kodzie. :-)

Pozdrawiam
Piotr Wyderski

--
"If you were plowing a field, which would you rather use?
Two strong oxen or 1024 chickens?" -- Seymour Cray
Witold Kuzminski
19 lat temu
Permalink
...
Moje zainteresowanie tematem nie schodzi az tak gleboko... Ciagle
Opterony to AMD a to wedlug powszechnego mniemania twor gosci
z DEC (tych od Alphy)...
Nie tylko Cray sprzedaje boxy IA32; np. Sun ma tez z... Opteronami.
Ciekawe, ze Cray ma na dobra sprawe 2-way SMP. Robi sie z tego
4-way gdy uzyc dual-core Opteronow.
...
Napewno nie.
...
Oczywiscie rozumiem, ze pomiary. Usiluje zrozumiec co mierzyles.
Post by Piotr Wyderski
Po prostu kod na głównej ścieżce przepływu sterowania jest bardzo krótki
(listing asemblerowy to kilkanaście linijek) i w ogóle nie synchronizuje się
z niczym.
OK... to jest tylko mozliwe gdy masz pool pamieci na dany watek.
Post by Piotr Wyderski
Tylko co z tego, skoro prawo Amdahla nieubłaganie przycina wzrost wydaności
ogólnej kontenerów do co najwyżej rzędu wielkości? Jeśli kod procedury
składa
się z dwóch części, z których jedna to alokacja, a druga operacje na
przydzielonym
bloczku i obie zajmują 50% czasu wykonania, to nawet nieskończone
przyspieszenie
alokatora da w efekcie zaledwie dwukrotny wzrost wydajności procedury...
Ja akurat robie w zastosowaniach, gdzie nie jest wazne jak wydajny jest
alokator. Moze sobie zajmowac 90% gdy malloc zajmuje 50%.
Wazne jest czy/jak system jako calosc skaluje sie na procesory/watki.
...
Nie rozumiem dlaczego tak myslisz. Ten sam system, w tych samych warunkach
na 2-Xeonie ma ten stosunek 4:1.
...
Bardzo dobry pomysl :)
Post by Piotr Wyderski
i go zlikwiduj. :-)
Nie jest moj. Nie jest tez bibliotek, ktorych uzywam.
Zostaje jeden podejrzany... zwlaszcza, ze moje watki tez sa na nim
suspendowane (gettimeofday nie wchodzi w gre.)
Post by Piotr Wyderski
Druga możliwość: przetwarzanie strumieniowe, gdzie watki częściowo
przetwarzają dostarczone dane i przekazują je innemu wątkowi do dalszej
obróbki. Wówczas jeśli czas przetwarzania pakietu będzie znacznie krótszy
od time slice przydzielonego wątkowi, a synchronizacja między wątkami
będzie odbywała się w oparciu o muteksy (tj. śpij do chwili, aż przyjdą
dane,
przetwórz je i obudź wątek czekający na wynik), to wystąpi podobne zjawisko,
mimo całkowitego braku kolizji.
Nie bardzo rozumiem jak to moze sie stac, gdy system idzie "under".
Nie dajesz rady obsluzyc tego co przychodzi a box jest 30% iddle.
Post by Piotr Wyderski
Post by Witold Kuzminski
Taka systuacja jak wyzej to byc moze alokator, ale napewno polaczony z
zelaztwem.
Post by Witold Kuzminski
Na moje oko, w takim zastosowaniu Intele sie sobie na wzajem tylko placza
pod nogami...
Nie, wówczas nastąpiłoby nasycenie pasma szyny pamięci i wszystko działałoby
wolniej
Nie. Gdy kazdy procesor chce robic to samo - zlapac ten sam mutex, to nie
bedzie nasycenia szyny, bo nie ma co nia przesylac, bo wszyscy czekaja na
mutex.
--
Wysłano z serwisu Usenet w portalu Gazeta.pl -> http://www.gazeta.pl/usenet/
Witold Kuzminski
19 lat temu
Permalink
[...]
Post by Piotr Wyderski
Ale nie powinno to specjalnie dziwić, biorąc pod uwagę, co napisano w pracy
opisującej alokator Hoard: "For 14 processors, the Hoard version runs 18
times
faster than the next best allocator."
Ciekawe. Ni kota nie moglem powtorzyc tych swietnych wynikow (w miedzyczasie
znalazlem chwile na "zabawe" Hoardem). PRzyznaje, ze nie analizowalem kodu,
ani zadnych tam takich. Po prostu przeczytalem manual (bo mial 1 linijke) i
LD_PRELOADnalem Hoarda.
W sumie kicha, albo ptmalloc (czy co tam jest w libc) jest taki swietny.
Box to SuSE9.2 big SMP, mierzylem z 4 Xeonami.
Post by Piotr Wyderski
A ponieważ malloc() z Solaris ani MSVC
to zdecydowanie nie jest "next best allocator", to te wyniki zbliżają się do
moich.
Nie bylbym taki pewien. Ze strony Hoarda jest link do testow Sun'a. Pomiary Suna
na Sparc zblizone sa do moich na SuSE/Xeon.
Czyli w zasadzie masz racje: alokator z Solaris nie jest "second best" (jest "better" :)
Post by Piotr Wyderski
Uprzedzając pytanie: nie wiem, jak ma się mój alokator do Hoarda, bo nie
miałem
ani czasu, ani okazji wykonać odpowiednich testów, a zgadnąć też trudno, bo

one oparte na innych pomysłach, choć inny wniosek z cytowanej pracy brzmi
"The Solaris allocator performs poorly overall because serial single heap
allocators
do not scale", co się w pełni pokrywa z moimi pomiarami.
Oczywiscie, ale to jest wykrzywianie rzeczywistosci. Dzis ani Solaris ani Linux _nie_
maja seryjnego alokatora. Zdaje sie, ze na Solaris 9 wystarczylo zlinkowax -lmtmalloc,
i tez smiem twierdzic ze wyniki byly podobne do tych Suna, a ma sie wtedy alokator systemowy.
Trudno to przecenic, bo np. Hoard na "moich" 2-Xeon'owych blades _nie_chodzi_.
Ot i zagwozdka...

Moze kiedys znajdziesz chwile na porownania Hoarda ze swoim?
--
Wysłano z serwisu Usenet w portalu Gazeta.pl -> http://www.gazeta.pl/usenet/
Piotr Wyderski
19 lat temu
Permalink
Post by Maciej Sobczak
API jest jedno a implementacji może być wiele. Która jest koszmarna?
Np. to z GCC 3.3. Ale popatrz np. na std::list -- co tam robi sort, merge
itp.?!
Przecież to mają być implementacje podstawowych struktur danych, a nie
wynik konkursu pt. "co by tu jeszcze dołożyć?".
Post by Maciej Sobczak
Post by Tomasz Kaczanowski
ew z wlasnych rozwiazan...
Np. jakich?
M.in. nielokującego shared pointera (ale to boost, a nie STL), intrusive
lists,
unrolled lists, list jednokierunkowych, nieblokujących międzyprocesowych
strumieni pamięciowych o olbrzymiej przepustowości, wydajnych singletonów
odpornych na spekulatywny dostęp do pamięci, sensownie przenośnej (tj.
wydajnie implementowalnej na każdej interesującej z ekonomicznego punktu
widzenia platformie) biblioteki operacji atomowych, sensownie przenośnej
biblioteki do profilowania i wielu innych rzeczy.

Pozdrawiam
Piotr Wyderski

--
"If you were plowing a field, which would you rather use?
Two strong oxen or 1024 chickens?" -- Seymour Cray
Witold Kuzminski
19 lat temu
Permalink
Post by Piotr Wyderski
Post by Maciej Sobczak
API jest jedno a implementacji może być wiele. Która jest koszmarna?
Np. to z GCC 3.3. Ale popatrz np. na std::list -- co tam robi sort,
Sort w liscie to sygnal, ze std::sort nie najlepiej sie nadaje do sortowania listy.
--
Wysłano z serwisu Usenet w portalu Gazeta.pl -> http://www.gazeta.pl/usenet/
Sektor van Skijlen
19 lat temu
Permalink
Post by Witold Kuzminski
Post by Piotr Wyderski
Post by Maciej Sobczak
API jest jedno a implementacji może być wiele. Która jest koszmarna?
Np. to z GCC 3.3. Ale popatrz np. na std::list -- co tam robi sort,
Sort w liscie to sygnal, ze std::sort nie najlepiej sie nadaje do sortowania listy.
Przecież to oczywiste, że się nie nadaje, z przyczyn zasadniczych. std::sort
korzysta z algorytmu introsort, który wymaga nagminnego liczenia długości
zakresu, więc z azałożenia nie ma sensu w liście, gdzie liczenie rozmiaru
zakresu elementów jest liniowego czasu.

Lista ma własny sort, głównie jako podpowiedź, że tego należy używać i jest on
tożsamy ze stable sort. Jak wiemy, stable sort korzysta z algorytmu mergesort,
który jest w istocie przeznaczony własnie dla list.
--
// _ ___ Michal "Sektor" Malecki <sektor(whirl)kis.p.lodz.pl>
\\ L_ |/ `| /^\ ,() <ethourhs(O)wp.pl>
// \_ |\ \/ \_/ /\ C++ bez cholesterolu: http://www.intercon.pl/~sektor/cbx
"I am allergic to Java because programming in Java reminds me casting spells"
Piotr Wyderski
19 lat temu
Permalink
Post by Witold Kuzminski
Sort w liscie to sygnal, ze std::sort nie najlepiej sie nadaje do sortowania listy.
To niech go specjalizują. Tylko skąd w ogóle pomysł, że
elementy listy są comparable?

Pozdrawiam
Piotr Wyderski


--
"If you were plowing a field, which would you rather use?
Two strong oxen or 1024 chickens?" -- Seymour Cray
Maciej Sobczak
19 lat temu
Permalink
Post by Piotr Wyderski
Post by Witold Kuzminski
Sort w liscie to sygnal, ze std::sort nie najlepiej sie nadaje do sortowania listy.
To niech go specjalizują. Tylko skąd w ogóle pomysł, że
elementy listy są comparable?
Nie muszą być. I jeśli nie są, to po prostu metody sort nie wołaj.
To taki ficzer w szablonach, nawet bardzo przydatny.
--
Maciej Sobczak : http://www.msobczak.com/
Programming : http://www.msobczak.com/prog/
Maciej Sobczak
19 lat temu
Permalink
Post by Piotr Wyderski
Post by Maciej Sobczak
API jest jedno a implementacji może być wiele. Która jest koszmarna?
Np. to z GCC 3.3.
Nie znam, ta wersja mnie ominęła. :)
Post by Piotr Wyderski
Ale popatrz np. na std::list -- co tam robi sort, merge
itp.?!
Sortuje, łączy, itp.

Różnica pomiędzy std::sort (funkcją) a std::list::sort (metodą) jest
taka, że std::sort operuje na *wartościach elementów* i robi to bez
wiedzy kontenera (może to zrobić nawet na zwykłej tablicy), podczas gdy
std::list::sort operuje na *wewnętrznych strukturach listy* i z tego
tytułu musi mieć dostęp do prywatnych danych listy. Metoda się do tego
nadaje lepiej, niż zewnętrzna funkcja.
Z tej różnicy wynika też np. to, że std::list::sort utrzymuje ważność
iteratorów wskazujących na sortowane elementy. Tzn. jeśli miałeś
iterator wskazujący na "Piotr Wyderski" w liście, to po posortowaniu
metodą std::list::sort ten sam iterator nada wskazuje na ten sam element
(natomiast mogły się zmienić relacje pomiędzy iteratorami). Z drugiej
strony, std::sort wymaga iteratorów swobodnego dostępu, których
std::list nie ma, więc jednego z drugim i tak nie można zastosować. I te
dwie rzeczy razem pokazują, że std::sort i std::list::sort to dwie
kompletnie różne rzeczy. Czego innego oczekują i co innego gwarantują
(poza banalnym stwierdzeniem, że obie coś sortują).
Post by Piotr Wyderski
Przecież to mają być implementacje podstawowych struktur danych, a nie
wynik konkursu pt. "co by tu jeszcze dołożyć?".
Ten konkurs i tak wygrywa std::string. Natomiast std::list jest OK i
bardzo dobrze, że ma metodę sort. I merge. I splice.
Post by Piotr Wyderski
Post by Maciej Sobczak
Post by Tomasz Kaczanowski
ew z wlasnych rozwiazan...
Np. jakich?
M.in. [...]
Piotr, ja się Ciebie nie pytam, bo wiem, że Ty akurat wiesz *kiedy* jest
sens to robić. :)

I uwierz mi, ja wcale nie twierdzę, że STL jest uniwersalny (i np. w
jednym z obecnych projektów nie używam). Mnie po prostu trzęsie, jak
widzę ludzi, którzy odkrywają koło na nowo nie dlatego, że faktycznie
potrafią zrobić bardziej okrągłe, ale dlatego, że nawet nie obejrzeli
dokładnie tego standardowego.
--
Maciej Sobczak : http://www.msobczak.com/
Programming : http://www.msobczak.com/prog/
Kyle
19 lat temu
Permalink
[snip]
Post by Tomasz Kaczanowski
No to przepraszam, moj blad... Nie interesowalem sie od dawna biblioteka
STL, bo uwazam ja za najmniej wartowsiowy fragmet c++, niestety api i
implementacja jest koszmarna, tzn brakuje wielu moim zdaniem
podstawowych metod, dlatego wole korzystac ze specyficznych rzeczy dla
danej platformy, ew z wlasnych rozwiazan...
O:
Jedrzej Dudkiewicz
19 lat temu
Permalink
Post by Tomasz Kaczanowski
Sektor poniekąd ma rację wystarczy, ze wektor będzie inaczej
zaimplementowany i lezymy.
To będzie implementacja "non-conforming". Standard mówi jasno, że to ma być
ciągły kawał(ek) pamięci. Dodatkowo tylko funkcje oznaczone jako "modifiers"
mogą zmienić jego wewnętrzny stan. Więc jeżeli ich nie wykorzystujemy w
czasie, kiedy wskaźnik na 1 element wektora, tj. &v[0], jest "zajęty", to
taka konstrukcja jest tak samo poprawna jak:

T v[N : N > 0];
T* ptr = &v[0];
Post by Tomasz Kaczanowski
Kiedys sie przyzwyczailem, ze majac
funkcje fun(int a,...) zczytywalem adres pierwszego elementu, i
wychodzilem z zalozenia, ze nastepne beda mialy kolejne adresy...
Pokaż mi standard ISO albo inny, który gwarantuje Ci, że tak będzie.
Przyzwyczaiłeś się do konkretnej implementacji i jej zachowania, to nie jest
ustandaryzowane (przynajmniej nic mi o takim standardzie nie wiadomo).

JD
Piotr Sietnik
19 lat temu
Permalink
Post by Maciej Sobczak
Jedyną tradycją jest to, że wskaźniki udają tablice. I to jest złe.
Wskaźniki powinny wskazywać. Nic więcej.
Czy w przypadku wskaźnika:

int ***tab;

też mam pisać

*(*(*(tab+N)+N)+N);

zamiast

tab[N][N][N];

hę?
--
Piotr Sietnik
***@pk.mofnet.gov.pl
Maciej Sobczak
19 lat temu
Permalink
Post by Piotr Sietnik
Post by Maciej Sobczak
Jedyną tradycją jest to, że wskaźniki udają tablice. I to jest złe.
Wskaźniki powinny wskazywać. Nic więcej.
int ***tab;
też mam pisać
*(*(*(tab+N)+N)+N);
zamiast
tab[N][N][N];
hę?
Oczywiście, że tak.

Nie powinno być nigdy takiej sytuacji, żeby wskaźnik na wskaźnik na
wskaźnik był utoższamiany z tablicą 3D, albo z tablicą tablic tablic.

(I pamiętaj, że jak dojdziesz do 4 gwiazdek, to powinieneś pójść do
wojska i tam być kapitanem.)

Tablica tablic tablic wygląda tak:

int tab[a][b][c];

i potem możesz sobie pisać:

tab[x][y][z];

Nie widzę problemu.
--
Maciej Sobczak : http://www.msobczak.com/
Programming : http://www.msobczak.com/prog/
Piotr Sietnik
19 lat temu
Permalink
Post by Maciej Sobczak
Nie powinno być nigdy takiej sytuacji, żeby wskaźnik na wskaźnik na
wskaźnik był utoższamiany z tablicą 3D, albo z tablicą tablic tablic.
(I pamiętaj, że jak dojdziesz do 4 gwiazdek, to powinieneś pójść do
wojska i tam być kapitanem.)
Łoł!
Post by Maciej Sobczak
int tab[a][b][c];
A jak wygląda tablica tablic tablic, albo wystarczy tablica tablic,
ale taka bardziej dynamiczna (chcę mieć macierz na stercie)?
No, i jak mam dostać się do jej elementów, ma się rozumieć.
--
Piotr Sietnik
***@pk.mofnet.gov.pl
Maciej Sobczak
19 lat temu
Permalink
Post by Piotr Sietnik
Post by Maciej Sobczak
int tab[a][b][c];
A jak wygląda tablica tablic tablic, albo wystarczy tablica tablic,
ale taka bardziej dynamiczna (chcę mieć macierz na stercie)?
Jak dla mnie, vector<vector<vector<int> > > działa.
Jeżeli rozmiary tablicy są znane w czasie kompilacji, to dobrze działa
też array<array<array<int> > >.
Post by Piotr Sietnik
No, i jak mam dostać się do jej elementów, ma się rozumieć.
Skoro to już jest tablica a nie wskaźnik, to oczywiście t[x][y][z].
--
Maciej Sobczak : http://www.msobczak.com/
Programming : http://www.msobczak.com/prog/
Piotr Sietnik
19 lat temu
Permalink
Post by Maciej Sobczak
Jak dla mnie, vector<vector<vector<int> > > działa.
Jeżeli rozmiary tablicy są znane w czasie kompilacji, to dobrze działa
też array<array<array<int> > >.
No w C sie tak nie da.
Post by Maciej Sobczak
Post by Piotr Sietnik
No, i jak mam dostać się do jej elementów, ma się rozumieć.
Skoro to już jest tablica a nie wskaźnik, to oczywiście t[x][y][z].
To teraz weźmy na to przykład:
Mam tablicę 131 elementową, która dajmy na to, zawiera
parametry opisujące zachowanie się hipotetycznego układu
przy wskazaniach miernika mającego skalę od -30 do 80 jednostek.

Mamy zatem tablicę:

double param[111];

Mógłbym ją używać tak:

param[5]; // dla wskazania -25 [30 - 25]
param[15]; // dla wskazania -15 [30 - 15]
param[105]; // dla wskazania +75 [30 + 75]

Ułatwiam więc sobie życie i zaczepiam wskaźnik za tę tablicę
w punkcie zero.

double * const pparam = &param[30];

I teraz dla uzyskania tych samych danych mogę go używać tak:

*(pparam - 25);
*(pparam - 15);
*(pparam + 75);

Z właściwości wskaźnika wynika również, że mogę i tak:

pparam[-25];
pparam[-15];
pparam[75];

Jak dla mnie, tak jest bardziej czytelnie i trudniej o pomyłkę.
Tym bardziej w przypadku, gdy granice skali będą mniej okrągłe,
np. -1342 i +1759.

Poza tym, dając const wskaźnikowi niejako zamieniam go w nową,
dość użyteczną (jak widać) tablicę, bo tak naprawdę to, to już
nie jest wskaźnik, gdyż z logicznego punktu widzenia traci
właściwość wskaźnika.
--
Piotr Sietnik
***@pk.mofnet.gov.pl
Maciej Sobczak
19 lat temu
Permalink
Post by Piotr Sietnik
Post by Maciej Sobczak
Jak dla mnie, vector<vector<vector<int> > > działa.
Jeżeli rozmiary tablicy są znane w czasie kompilacji, to dobrze działa
też array<array<array<int> > >.
No w C sie tak nie da.
A to pech. Tym gorzej dla C. :)
Post by Piotr Sietnik
Post by Maciej Sobczak
Post by Piotr Sietnik
No, i jak mam dostać się do jej elementów, ma się rozumieć.
Skoro to już jest tablica a nie wskaźnik, to oczywiście t[x][y][z].
Mam tablicę 131 elementową, która dajmy na to, zawiera
parametry opisujące zachowanie się hipotetycznego układu
przy wskazaniach miernika mającego skalę od -30 do 80 jednostek.
Przykład ciekawy, ale krótkowzroczny i rozwiązanie, które pokazałeś też
jest krótkowzroczne. W praktyce ten miernik będzie dawał wartości
dyskretne, ale niecałkowite, np. -27.63.
Wtedy Twoja metoda z "przesuniętą tablicą" nadaje się do /dev/kiepskie.

To co chcesz zrobić wymaga albo mapy jeśli wartości są dyskretne i
dziedzina ma sensowną ilość różnych wartości (to nawet nie muszą być
liczby, mogą być też np. kolory, litery, kody państw, itd.), albo
ciągłej transformacji w przeciwnym wypadku (fizyczne parametry, czyli
właśnie mierniki!), ewentualnie z rozpoznawaniem przedziałów lub progów.
--
Maciej Sobczak : http://www.msobczak.com/
Programming : http://www.msobczak.com/prog/
Piotr Sietnik
19 lat temu
Permalink
Post by Maciej Sobczak
Przykład ciekawy, ale krótkowzroczny i rozwiązanie, które pokazałeś też
jest krótkowzroczne. W praktyce ten miernik będzie dawał wartości
dyskretne, ale niecałkowite, np. -27.63.
Wtedy Twoja metoda z "przesuniętą tablicą" nadaje się do /dev/kiepskie.
Niekoniecznie, gdyż wtedy można wprowadzić jednostkę "kwantową",
na zasadzie "jedna działka" = "jedna jednostka". Wtedy wartość
dla -27.63 mogłaby być w komórce [-2763], a to i tak jest bardziej
czytelne niż jakakolwiek inna możliwość na indeksach dodatnich.
--
Piotr Sietnik
***@pk.mofnet.gov.pl
Maciej Sobczak
19 lat temu
Permalink
...
Jaja sobie chyba robisz. Albo nie widziałeś nigdy na oczy miernika.
Jeżeli miernik zwraca wartości dyskretne (a jak jest cyfrowy to takie
właśnie zwraca), to pomiędzy jego dokładnością i kwantem nie musi być
żadnego związku. W szczególności może mieć większą dokładność, niż kwant
(czyli dwie "sąsiednie" wartości nie muszą być dwoma "sąsiednimi" liczbami).

Wyobraź sobie, że miernik zwraca 256 różnych wartości, z dokładnością do
4 miejsc po przecinku, w zakresie -30 do +80. Tylko mi nie mów, że
tablica na 1100000 elementów, z których tylko 256 jest wykorzystanych
jest czytelniejsza, niż mapa albo transformacja z programi.

W ogólnym przypadku takie coś i tak będzie zawinięte w co najmniej
funkcję (np. T getValueForTemperature(double d)) i to właśnie wywołanie
tej funkcji ma być czytelne a nie to, co ma w środku. Jeżeli już ma w
środku jakąś tranformację, to równie dobrze może mieć jedno dodawanie
więcej (offset), bez straty na czytelności wywołań.
--
Maciej Sobczak : http://www.msobczak.com/
Programming : http://www.msobczak.com/prog/
Piotr Sietnik
19 lat temu
Permalink
Post by Maciej Sobczak
Jaja sobie chyba robisz. Albo nie widziałeś nigdy na oczy miernika.
Jeżeli miernik zwraca wartości dyskretne (a jak jest cyfrowy to takie
właśnie zwraca), to pomiędzy jego dokładnością i kwantem nie musi być
żadnego związku. W szczególności może mieć większą dokładność, niż kwant
(czyli dwie "sąsiednie" wartości nie muszą być dwoma "sąsiednimi" liczbami).
Oj napisałem, że to tylko hipotetyczny miernik. Równie dobrze mogłem
jako przykład podać skalę na wykresie. Ot załóżmy że chcemy mieć
wykres jakiejś funkcji i dla potrzeb "wyświetlacza" mamy "zebrane"
w tablicy wcześniej obliczone wartości funkcji w punktach.
Przykładów można by mnożyć i byłoby ich tak wiele, jak wiele
zastosowań mają liczby ujemne.
Post by Maciej Sobczak
Wyobraź sobie, że miernik zwraca 256 różnych wartości, z dokładnością do
4 miejsc po przecinku, w zakresie -30 do +80. Tylko mi nie mów, że
tablica na 1100000 elementów, z których tylko 256 jest wykorzystanych
jest czytelniejsza, niż mapa albo transformacja z programi.
Każdy przypadek wymaga innego podejścia. Ja założyłem że miernik,
dla którego podaję przykłady jest miernikiem zwracającym wartości
"sąsiednie".
Poza tym podałem, że nie chodzi o czytanie z miernika, tylko o korzystanie
ze stablicowanych parametrów opisujących zachowanie się określonego układu.
Sam miernik podałem dla celów określenia, że tablica zawiera wartości
z danego przedziału.
--
Piotr Sietnik
***@pk.mofnet.gov.pl
Sektor van Skijlen
19 lat temu
Permalink
Post by Piotr Sietnik
Post by Maciej Sobczak
Jedyną tradycją jest to, że wskaźniki udają tablice. I to jest złe.
Wskaźniki powinny wskazywać. Nic więcej.
int ***tab;
też mam pisać
*(*(*(tab+N)+N)+N);
zamiast
tab[N][N][N];
Tak.

Natomiast gdybyś miał:

int iterative* iterative* iterative* tab;

to wtedy możesz pisać:

tab[N][N][N];
--
// _ ___ Michal "Sektor" Malecki <sektor(whirl)kis.p.lodz.pl>
\\ L_ |/ `| /^\ ,() <ethourhs(O)wp.pl>
// \_ |\ \/ \_/ /\ C++ bez cholesterolu: http://www.intercon.pl/~sektor/cbx
"I am allergic to Java because programming in Java reminds me casting spells"
Sektor van Skijlen
19 lat temu
Permalink
...
Chciałeś powiedzieć chyba "idiotycznym".
Post by Maciej Sobczak
sposobem na uzyskanie wskaźnika na
pierwszy element wektora (albo zwykłej tablicy).
Maćku, co dokumentacja mówi nt. tego, co ci zwraca operator []?

Zwraca ci referencję na POJEDYNCZY obiekt.

A co dokumentacja (w szczególności standard) mówi nt. wyniku operatora &
zastosowanego dla typu referencyjnego? Czy istnieje coś takiego, jak
referencja DO TABLICY (tzn. w ogólności istnieje, ale pytam o taką, gdzie
pierwszy element uzyskujesz pisząc po prostu nazwę wartości referencyjnej bez
dodatkowych oznaczeń)? W szczególności:

int x[10];

int* p = x; // w porządku
int& r = x[0]; // w porządku
int* p = &r; // w porządku
int i = p[2]; // w porządku, bo równoważne &r[2], czyli (&x[0])[2], czyli x[2]


my_ra_container<int> x;

int& p = x[21]; // w porządku
int* s = &p; // ok
int i = s[2]; // popierdoliło???

Fakt, że dla wektora akurat konstrukcja (&v[0])[2] jest bezpieczna, wynika
tylko i wyłącznie ze szczególnych warunków polegających na tym, że wektor jest
wewnętrznie implementowany przez dynamiczną tablicę, alokowaną w jednym
kawałku.

Czy masz gdzieś napisane w dokumentacji do wektora, że wyrażenie &v[2] zwraca
ci wskaźnik, który możesz traktowac jak iterator do tablicy? Gdyby to było
takie proste, vector<T>::iterator robiono by jako T* (z jakiegoś powodu jednak
od wersji libstdc++ v3 jest już odpowiednie opakowanie, podobnie jak string).

Po prostu wektor nie ma takich gwarancji, że można nim operować jak surową
tablicą. Gdyby to było dozwolone, to być może wektor miałby metodę data(),
tktóra zwracałaby wskaźnik na pierwszy element rozumiany jako wskaźnik do
tablicy. Ale &v[0] to jest wskaźnik do POJEDYNCZEGO zerowego elementu i fakt,
że jest przypadkiem równy wewnętrznemu wskaźnikowi wektora oznaczającemu
początek tablicy, nie oznacza bynajmniej, że tak po prostu wolno ci go
"arytmetykować".
Post by Maciej Sobczak
Jeżeli jakaś funkcja
oczekuje wskaźnika na tablicę, to &v[0] jest najprostszą rzeczą, jaką
można zrobić.
W szczególności jest JEDYNĄ rzeczą, jaką można zrobić. Nie wiem, dlaczego
wektor nie posiada żadnych urządzeń pozwalających operować jego wewnętrzną
tablicą. Na zdrowy rozum nie ma żadnych przeszkód, skoro gwarantuje się, że
wektor ma miec pamięć w jednym kawałku.
Post by Maciej Sobczak
I jak najbardziej jest to legalne.
Wiem, że to brzmi nielogicznie i bezsensownie, ale niestety legalne nie jest.
Wskaźnik pobrany z referencji jest ZAWSZE WSKAŹNIKIEM DO ELEMTNU, NIGDY DO
TABLICY. Wskaźnik do tablicy może powstać tylko poprzez zainicjalizowanie go
tablicą, albo dynamicznie przydzieloną pamięcią. I fakt, że dany wskaźnik
wskazuje na miejsce w pamięci, a ta pamięć jest akurat zajęta przez liniowo
rozłożone elementy, czyli tablicę, nie powoduje bynajmniej, że możesz robić na
nim legalnie arytmetykę.

Już to kiedyś przerabialiśmy z Qrczakiem (po czym oczywiście, rozeszliśmy się,
jak zwykle, bez porozumienia :). Arytmetykę możesz robić tylko na takiej
wartości wskaźnika, której źródło bezpośrednio wskazuje, że został pobrany z
tablicy, a nie z pojedynczego obiektu. Użycie arytmetyki w tym drugim
przypadku jest tak samo "legalne", jak stosowanie reinterpret_cast.

Pomyśl sobie, co by było, gdyby ktoś gdzieś wymienił vector na deque. Gdybym
ja potrzebował taką zmianę, to gościa, który mi takie coś wkręcił do kodu...
wezwałbym na poważną rozmowę lol ;)

Gdyby istniało np. coś takiego jak T* vector<T>::data(), które zwracałoby
praktycznie to samo co &v[0], to jednak byłoby wtedy legalne, bo dokumentacja
jasno by stwierdzała, że jest to wskaźnik DO TABLICY, a nie DO ELEMENTU.
Gdybyś wtedy użył data(), to byłoby wiadomo, że chodzi ci o wewnętrzną
tablicę. I wtedy, gdyby ktoś zmienił na deque, to by mu się nie skompilowało i
byłoby jasno i wyraźnie wiadomo, co ktoś chciał tutaj zrobić.

A jak stosujesz &v[0], no to się nie dziw, że wyjeżdżasz.

Ja na twoim miejscu, gdybym miał robić &v[0], to prędzej rozważyłbym możliwość
zrezygnowania ze stosowania wektora w ogóle w takim wypadku.
Post by Maciej Sobczak
Post by Sektor van Skijlen
Post by Maciej Sobczak
Dlatego uważam, że w ogóle wskaźniki nie powinny pozwalać na
indeksowanie. Wskaźniki mają *wskazywać* a nie udawać tablice.
To samo z iteratorami.
Z tego, co wiem, iteratory (nawet swobodnego dostępu) nie mają operatora [],
aczkolwiek mają swoją funkcję advance.
Niestety mają operator[], i jest to wymagane dla iteratorów swobodnego
dostępu (dla prawników: 24.1.5).
No właśnie kurde miałem spojrzeć jeszcze w standard, zanim wyślę (lub nawet
prościej na SGI), ale ktoś mnie tu zagadał i zapomniałem :)
Post by Maciej Sobczak
"If your iterators behave like dumb pointers, they are dumb pointers."
Dokładnie się zgadzam. Aczkolwiek z drugiej strony, iterator ma jednak w
założeniu móc "biegać po zbiorniku", jak goły konduktor po pociągu (naked
conductor runs along the train), więc wykonanie operacji awansu operatorem []
nie powinno być groźne.

Ale z drugiej strony przydałoby się też coś takiego, jak wskaźnik na
pojedynczy obiekt, którego nie można "arytmetykować".
...
No, to już ma więcej sensu, szczególnie ten skrypt ;)

Wiesz, ja w ogólności to miałem już nawet pomysł na różne wzbogacanie składni
C++, a przerobienie całego kodu tak, żeby zamienić & na adrof(), który wymusi
zwrócenie ptr<T> (a implementacja odwoła się po prostu do tego przez &, tylko
że przekabaci typ wskaźnika), nie powinno być problematyczne. Odmiennie, niż
inne moje pomysły, które zarzuciłem w chwili postania, zdając sobie sprawę, że
taki mój "preprocesorek" musiałby rozumieć składnię standardowego C++, a
jedynie DODATKOWO jeszcze moje rozszerzenia.
Post by Maciej Sobczak
Post by Sektor van Skijlen
int __iterator* t;
Nie podoba mi się zwyczaj wymyślania nowych słów kluczowych z __ na
początku. Ale idźmy dalej.
To była lekko karkołomna propozycja, ale tylko to mi przyszło do głowy (w
szczególności, nie pasowało mi nic bardziej odpowiedniego, niż iterator, a
słowo iterator jest już zbyt często używane, żeby mogło być słowem kluczowym).
Post by Maciej Sobczak
Post by Sektor van Skijlen
przy czym np. gdyby na zwykłym wskaźniku robić arytmetykę lub inicjalizować go
tablicą, kompilator rzucałby ostrzeżenia. Trzeba by było stosować to powyżej,
to wtedy ostrzeżeń nie będzie.
Czemu ostrzeżenia a nie błędy?
Bo się większość istniejącego kodu nie skompiluje! lol ;)
Oczywiście zawsze można włączyć -Wglupiwskaznikerror i wtedy będą błędy. ;)
Post by Maciej Sobczak
Post by Sektor van Skijlen
Również int __iterator* może się konwertować na
int*, ale w drugą stronę dostajesz ostrzeżenie.
Po co konwersje?
Bo będą oddzielnymi typami. Tak samo jak int* i int const* są oddzielnymi
typami i pomiędzy nimi tez zachodzą konwersje.

O, może być np. 'iterative', jako nowy cv-qualifier ;)

Ale nie, nie bardzo. Bo nie mógłby być aplikowany do referencji (w
szczególności, wskaźnik pobrany z referencji nigdy nie może być int
iterative*.
Post by Maciej Sobczak
Post by Sektor van Skijlen
Tak samo jeśli próbujesz
inicjalizować to powyżej czymś innym niż tablicą (np. wskaźnikiem pobranym z
&).
OK.
No. To teraz tylko wykonać odpowiednią analizę standardu dla Bronka i można
opijać.
...
No właśnie. I po to to jest.
--
// _ ___ Michal "Sektor" Malecki <sektor(whirl)kis.p.lodz.pl>
\\ L_ |/ `| /^\ ,() <ethourhs(O)wp.pl>
// \_ |\ \/ \_/ /\ C++ bez cholesterolu: http://www.intercon.pl/~sektor/cbx
"I am allergic to Java because programming in Java reminds me casting spells"
Jedrzej Dudkiewicz
19 lat temu
Permalink
Post by Sektor van Skijlen
Czy masz gdzieś napisane w dokumentacji do wektora, że wyrażenie &v[2] zwraca
ci wskaźnik, który możesz traktowac jak iterator do tablicy?
No nie ma, natomiast w 23.2.4.1 jest napisane: "if v is a vector<T,
Allocator> where T is some type other than bool, then it obeys the identity
&v[n] == &v[0] + n for all 0 <= n < v.size()". Jak napisał Maciek,
konstrukcji &v[0] używa się do przekazywania zawartości wektora (traktowanej
jak kolejno ułożone elementy) do API korzystającego ze wskaźników. To samo
API spodziewa się, że jeżeli otrzyma T* ptr i size_t len, to, parafrazując,
"ptr obeys identity &ptr[n] = &ptr[0] + n for all 0 <= n < len". Czy czegoś
nie widzę?

JD
Maciej Sobczak
19 lat temu
Permalink
Post by Sektor van Skijlen
Post by Maciej Sobczak
Sektor, co Ty bredzisz? :)
Zapis &v[0] jest idiomatycznym
Chciałeś powiedzieć chyba "idiotycznym".
Maćku, co dokumentacja mówi nt. tego, co ci zwraca operator []?
Zwraca ci referencję na POJEDYNCZY obiekt.
Czyli co - specjalnie w tym celu stworzy go na boczku?
Zwraca referencję na obiekt, który już tam jest. I w przypadku wektora
te elementy są w tzw. kupie.
Post by Sektor van Skijlen
A co dokumentacja (w szczególności standard) mówi nt. wyniku operatora &
zastosowanego dla typu referencyjnego?
Sektor, ja tej Twojej teorii i tak nie rozumiem, więc nie używaj jej po
to, żeby mi coś wyjaśnić. :D

Operator & nigdy nie jest stosowany dla typu referencyjnego. Może być co
najwyżej zastosowany dla lvalue typu T. I wtedy zwraca wskaźnik na dany
element.
Post by Sektor van Skijlen
Czy masz gdzieś napisane w dokumentacji do wektora, że wyrażenie &v[2] zwraca
ci wskaźnik, który możesz traktowac jak iterator do tablicy? Gdyby to było
takie proste, vector<T>::iterator robiono by jako T* (z jakiegoś powodu jednak
od wersji libstdc++ v3 jest już odpowiednie opakowanie, podobnie jak string).
vector<T>::iterator może być zrobiony jako T*, nie ma z tym
najmniejszego problemu. A to, że ostatnio częściej robi się to inaczej
wynika chociażby z faktu, że w klasę łatwiej się wcisnąć np. z kodem
diagnostycznym.
Post by Sektor van Skijlen
Po prostu wektor nie ma takich gwarancji, że można nim operować jak surową
tablicą. Gdyby to było dozwolone, to być może wektor miałby metodę data(),
tktóra zwracałaby wskaźnik na pierwszy element rozumiany jako wskaźnik do
tablicy. Ale &v[0] to jest wskaźnik do POJEDYNCZEGO zerowego elementu i fakt,
że jest przypadkiem równy wewnętrznemu wskaźnikowi wektora oznaczającemu
początek tablicy, nie oznacza bynajmniej, że tak po prostu wolno ci go
"arytmetykować".
Ogólnie diagnoza jest taka, że z jakiegoś powodu masz chwilowe zaćmienie.

Scott Meyers, "Effective STL", Item 16: Know how to pass vector and
string data to legacy APIs.

<cytat>
Given

vector<int> v;

the expression v[0] yields a reference to the first elements in the
vector, so &v[0] is a pointer to that first element. The elements in a
vector are constrained by the C++ Standard to be stored in contiguous
memory, just like an array, so if we wish to pass v to a C API that
looks something like this,

void doSomething(const int* pInts, size_t numInts);

we can do it like this:

doSomethind(&v[0], v.size());

</cytat>
Post by Sektor van Skijlen
W szczególności jest JEDYNĄ rzeczą, jaką można zrobić. Nie wiem, dlaczego
wektor nie posiada żadnych urządzeń pozwalających operować jego wewnętrzną
tablicą. Na zdrowy rozum nie ma żadnych przeszkód, skoro gwarantuje się, że
wektor ma miec pamięć w jednym kawałku.
No i skoro gwarantuje, to nie musi już niczego innego mieć. Po co?
Post by Sektor van Skijlen
Pomyśl sobie, co by było, gdyby ktoś gdzieś wymienił vector na deque.
Źle. Zastosowałem std::vector nie dlatego, że chciałem mieć "byle jaką
kolekcję", ale dlatego, że chciałem mieć automatycznie zarządzany
zamiennik dla zwykłej tablicy i to było dokładnie w tym celu, żeby móc
pracować z API w C, któro oczekiwało tablicy podanej przez wskaźnik.
Poważnie. Zwłaszcza w tym przypadku - to było w bibliotece SOCI, która
służy do obsługi baz danych. Jak myślisz, dlaczego ze wszystkich
kolekcji obsługuje *tylko* wektory? Już ktoś marudził, że fajnie by było
obsługiwać wszystkie kolekcje, ale właśnie wybór wektora wziął się stąd,
że niektóre bazy danych (w szczególności Oracle) potrafią odczytać cały
szereg wartości z ciągłego obszaru pamięci jednym strzałem, co
niesamowicie przyśpiesza niektóre operacje.
W tym kontekście nie ma mowy o wymianie vector na deque, bo vector już
jest konkretnym kontenerem, który został wybrany właśnie ze względu na
swoje szczególne gwarancje. I właśnie te szczególne gwarancje
wykorzystuję pisząc &v[0].
Post by Sektor van Skijlen
Gdyby istniało np. coś takiego jak T* vector<T>::data(), które zwracałoby
praktycznie to samo co &v[0], to jednak byłoby wtedy legalne, bo dokumentacja
jasno by stwierdzała, że jest to wskaźnik DO TABLICY, a nie DO ELEMENTU.
Gdybyś wtedy użył data(), to byłoby wiadomo, że chodzi ci o wewnętrzną
tablicę. I wtedy, gdyby ktoś zmienił na deque, to by mu się nie skompilowało i
byłoby jasno i wyraźnie wiadomo, co ktoś chciał tutaj zrobić.
Tu masz rację, taka metoda sprawiłaby, że intencje byłyby jaśniej
wyrażone. Z braku metody data() używam idiomu &v[0].
Post by Sektor van Skijlen
A jak stosujesz &v[0], no to się nie dziw, że wyjeżdżasz.
Wyjechałem dlatego, że wskaźniki potrafią udawać tablice (przypomnę, że
zamieniłem v na wskaźnik). Czyli tak naprawdę nie wyjechałem poza zakres
wewnętrznej tablicy wektora - wyjechałem poza *obiekt* wektora, czyli
coś zupełnie bez sensu.
Wskaźniki nie powinny udawać tablic - wtedy kod by się nawet nie
skompilował.
Post by Sektor van Skijlen
Ja na twoim miejscu, gdybym miał robić &v[0], to prędzej rozważyłbym możliwość
zrezygnowania ze stosowania wektora w ogóle w takim wypadku.
To z czego być wtedy skorzystał? Z new T[N]? Chyba sobie jaja robisz.
Post by Sektor van Skijlen
Ale z drugiej strony przydałoby się też coś takiego, jak wskaźnik na
pojedynczy obiekt, którego nie można "arytmetykować".
To też by się przydało.

[...]
--
Maciej Sobczak : http://www.msobczak.com/
Programming : http://www.msobczak.com/prog/
Sektor van Skijlen
19 lat temu
Permalink
Post by Maciej Sobczak
Post by Sektor van Skijlen
Maćku, co dokumentacja mówi nt. tego, co ci zwraca operator []?
Zwraca ci referencję na POJEDYNCZY obiekt.
Czyli co - specjalnie w tym celu stworzy go na boczku?
Przecież nie może, bo musi zachować nie tylko wartości, ale i zmiany.
Post by Maciej Sobczak
Zwraca referencję na obiekt, który już tam jest. I w przypadku wektora
te elementy są w tzw. kupie.
Tak. Ale fakt, że możesz go "arytmetykować" wynika z faktu, że wskaźnik możesz
arytmetykować w ogóle. A nie, że ktoś dał jakiekolwiek gwarancje, że
arytmetykowanie tego wskaźnika jest dozwolone.
Post by Maciej Sobczak
Post by Sektor van Skijlen
A co dokumentacja (w szczególności standard) mówi nt. wyniku operatora &
zastosowanego dla typu referencyjnego?
Sektor, ja tej Twojej teorii i tak nie rozumiem, więc nie używaj jej po
to, żeby mi coś wyjaśnić. :D
No dobra, niech będzie lvalue.
Post by Maciej Sobczak
Operator & nigdy nie jest stosowany dla typu referencyjnego. Może być co
najwyżej zastosowany dla lvalue typu T. I wtedy zwraca wskaźnik na dany
element.
No właśnie.
...
Ech... ale to wciąż opiera się na tym samym: bo wskaźnik jest odpowiedniego
typu i jest numerycznie równy wewnętrznemu wskaźnikowi na dynamiczną tablicę.
O jawne gwarancje, że &v[0] to jest wskaźnikiem do tablicy, nikt sięnie
postarał. I ja wcale nie oczekuję, że je ktoś da, ale przynajmniej przydałaby
się metoda data(), która by taki wskaźnik zwracała. I wtedy mając kombinację
data() i size(), można takim wektorem operować jak tablicą.
Post by Maciej Sobczak
Post by Sektor van Skijlen
W szczególności jest JEDYNĄ rzeczą, jaką można zrobić. Nie wiem, dlaczego
wektor nie posiada żadnych urządzeń pozwalających operować jego wewnętrzną
tablicą. Na zdrowy rozum nie ma żadnych przeszkód, skoro gwarantuje się, że
wektor ma miec pamięć w jednym kawałku.
No i skoro gwarantuje, to nie musi już niczego innego mieć. Po co?
Na przykład dlatego, że dla każdego typu zbiornika w STL, &v[0] oznacza
wskaźnik na pojedynczy element. Tak jest z deque, mapą i całą resztą,
jedynie vector ma taki "szczególny myk". Dlaczego ten myk powoduje, że nagle
akurat w takim zbiorniku możesz zastosować szczególne gwarancje dla tego, co
zwraca &v[0], zamiast usankcjonować to odpowiednimi szczególnościami danego
typu, niedostępnymi dla innych?
Post by Maciej Sobczak
Post by Sektor van Skijlen
Pomyśl sobie, co by było, gdyby ktoś gdzieś wymienił vector na deque.
Źle. Zastosowałem std::vector nie dlatego, że chciałem mieć "byle jaką
kolekcję",
[...]

Ale ja nie mówię odnośnie tego, co pisałeś, tylko w ogólności.

Ponieważ &v[0] jest wyrażeniem generycznym, więc i kod, który z takiego
wyrażenia korzysta, powinien się opierać na generycznych regułach. Jeśli więc,
powiedzmy, można coś zrobić z wynikiem takiego wyrażenia, to powinno się to
samo móc zrobić z wynikiem tego wyrażenia niezależnie od tego, jaki zbiornik
został tam użyty.
...
W sumie chyba masz rację.
Post by Maciej Sobczak
Post by Sektor van Skijlen
Ja na twoim miejscu, gdybym miał robić &v[0], to prędzej rozważyłbym możliwość
zrezygnowania ze stosowania wektora w ogóle w takim wypadku.
To z czego być wtedy skorzystał? Z new T[N]? Chyba sobie jaja robisz.
W ostateczności tak. Choć raczej starałbym się do tego dorobić jakieś
opakowanie.

Uważam, że skoro nie postarano sięo data(), to znaczy, że nie zadbano
wystarczająco, żeby można było wektorem zastąpić każde wystąpienie tablicy.
--
// _ ___ Michal "Sektor" Malecki <sektor(whirl)kis.p.lodz.pl>
\\ L_ |/ `| /^\ ,() <ethourhs(O)wp.pl>
// \_ |\ \/ \_/ /\ C++ bez cholesterolu: http://www.intercon.pl/~sektor/cbx
"I am allergic to Java because programming in Java reminds me casting spells"
Maciej Sobczak
19 lat temu
Permalink
Post by Sektor van Skijlen
Post by Maciej Sobczak
Post by Sektor van Skijlen
Maćku, co dokumentacja mówi nt. tego, co ci zwraca operator []?
Zwraca ci referencję na POJEDYNCZY obiekt.
Czyli co - specjalnie w tym celu stworzy go na boczku?
Przecież nie może, bo musi zachować nie tylko wartości, ale i zmiany.
I właśnie to upoważnie mnie do traktowania tego wskaźnika jako czegoś,
co wskazuje też na cały dalszy blok.
Post by Sektor van Skijlen
Post by Maciej Sobczak
Zwraca referencję na obiekt, który już tam jest. I w przypadku wektora
te elementy są w tzw. kupie.
Tak. Ale fakt, że możesz go "arytmetykować" wynika z faktu, że wskaźnik możesz
arytmetykować w ogóle. A nie, że ktoś dał jakiekolwiek gwarancje, że
arytmetykowanie tego wskaźnika jest dozwolone.
Przecież ja go nie "arytmekuję". Ja go przekazuję jakiejś funkcji C,
która po pierwsze primo ma argument void* (więc mowy nie ma o
arytmetyce) a po drugie primo ona też nie musi nic robić, bo może po
prostu wywołuje ::write i dane lecą hurtem w świat. Może tak być? Może.
Post by Sektor van Skijlen
Ech... ale to wciąż opiera się na tym samym: bo wskaźnik jest odpowiedniego
typu i jest numerycznie równy wewnętrznemu wskaźnikowi na dynamiczną tablicę.
O jawne gwarancje, że &v[0] to jest wskaźnikiem do tablicy, nikt sięnie
postarał.
Bo takie gwarancje wynikają z tego, że dane są w ciągłym bloku pamięci.
Post by Sektor van Skijlen
I ja wcale nie oczekuję, że je ktoś da, ale przynajmniej przydałaby
się metoda data(), która by taki wskaźnik zwracała. I wtedy mając kombinację
data() i size(), można takim wektorem operować jak tablicą.
data() jest OK. Przydałoby się.
Post by Sektor van Skijlen
Post by Maciej Sobczak
Post by Sektor van Skijlen
Pomyśl sobie, co by było, gdyby ktoś gdzieś wymienił vector na deque.
Źle. Zastosowałem std::vector nie dlatego, że chciałem mieć "byle jaką
kolekcję",
[...]
Ale ja nie mówię odnośnie tego, co pisałeś, tylko w ogólności.
W ogólności nigdy się nie pisze &v[0]. Takie coś ma sens tylko wtedy,
gdy trzeba zrobić wskaźnik do tablicy C. A wtedy nie ma już ogólności,
jest tylko std::vector (albo boost::array).
Post by Sektor van Skijlen
Post by Maciej Sobczak
Post by Sektor van Skijlen
Ja na twoim miejscu, gdybym miał robić &v[0], to prędzej rozważyłbym możliwość
zrezygnowania ze stosowania wektora w ogóle w takim wypadku.
To z czego być wtedy skorzystał? Z new T[N]? Chyba sobie jaja robisz.
W ostateczności tak. Choć raczej starałbym się do tego dorobić jakieś
opakowanie.
Jakie poleciłbyś opakowanie dla new T[N]? Może... std::vector? :D
Post by Sektor van Skijlen
Uważam, że skoro nie postarano sięo data(), to znaczy, że nie zadbano
wystarczająco, żeby można było wektorem zastąpić każde wystąpienie tablicy.
Postarano się, bo tablice też nie mają data(). Lol!
--
Maciej Sobczak : http://www.msobczak.com/
Programming : http://www.msobczak.com/prog/
Sektor van Skijlen
19 lat temu
Permalink
Post by Maciej Sobczak
Post by Sektor van Skijlen
Post by Maciej Sobczak
Post by Sektor van Skijlen
Pomyśl sobie, co by było, gdyby ktoś gdzieś wymienił vector na deque.
Źle. Zastosowałem std::vector nie dlatego, że chciałem mieć "byle jaką
kolekcję",
[...]
Ale ja nie mówię odnośnie tego, co pisałeś, tylko w ogólności.
W ogólności nigdy się nie pisze &v[0]. Takie coś ma sens tylko wtedy,
gdy trzeba zrobić wskaźnik do tablicy C. A wtedy nie ma już ogólności,
jest tylko std::vector (albo boost::array).
No, jeśli wektor stosujesz w szczególności do tego typu rzeczy, to owszem. Ale
wektor jest jak by nie patrzć częścią STL, więc szczególnych przypadków nie
powinno się budować na elementach, które są dostępne w ogólności. Do
szczególnych przypadków powinno się wykorzystywać elementy szczególne, takie,
które nie są dostępne dla innych zbiorników, które takich gwarancji, jak
wektor, nie posiadają.
Post by Maciej Sobczak
Post by Sektor van Skijlen
Post by Maciej Sobczak
Post by Sektor van Skijlen
Ja na twoim miejscu, gdybym miał robić &v[0], to prędzej rozważyłbym możliwość
zrezygnowania ze stosowania wektora w ogóle w takim wypadku.
To z czego być wtedy skorzystał? Z new T[N]? Chyba sobie jaja robisz.
W ostateczności tak. Choć raczej starałbym się do tego dorobić jakieś
opakowanie.
Jakie poleciłbyś opakowanie dla new T[N]? Może... std::vector? :D
Lol.
Już się swego czasu bawiłem w takie opakowanie jak auto_array. Stwierdziłem,
że wektor jest dla tego przypadku "unsuitable". Nie pamiętam już szczegółów.
Post by Maciej Sobczak
Post by Sektor van Skijlen
Uważam, że skoro nie postarano sięo data(), to znaczy, że nie zadbano
wystarczająco, żeby można było wektorem zastąpić każde wystąpienie tablicy.
Postarano się, bo tablice też nie mają data(). Lol!
Ależ data() ma być metodą zbiornika, który ma właśnie zwrócić początek tablicy
C. Tablica już sama czymś takim jest, to po co jej jakieś dodatkowe zabawki?
--
// _ ___ Michal "Sektor" Malecki <sektor(whirl)kis.p.lodz.pl>
\\ L_ |/ `| /^\ ,() <ethourhs(O)wp.pl>
// \_ |\ \/ \_/ /\ C++ bez cholesterolu: http://www.intercon.pl/~sektor/cbx
"I am allergic to Java because programming in Java reminds me casting spells"
Piotr Sietnik
19 lat temu
Permalink
...
No nie przesadzaj.
Post by Maciej Sobczak
Post by Piotr Sietnik
Dla mnie, i nie tylko dla minie, co widać po lekturze tej grupy,
używanie indeksów zawsze jest podejrzane.
Więc w ogóle należy ich nie używać? Nie rozumiem.
Należy kontrolować czy nie przekracza się granic tablicy,
no ala jakie w takim razie ma znaczenie czy indeks, który
sprawdzamy jest dodatni, czy ujemny. Żadne jeśli nie znamy
miejsca, od którego jest liczony. Dalej jest już tylko zwykła
arytmetyka.

Nawet standard C++ zapodaje:

operator[] [lib.reverse.iter.opindex]

Reference operator[](...

Returns:
current[-n-1]

Nie oznacza to, że w samej implementacji tak jest, ale...
Post by Maciej Sobczak
Co chcesz udowodnić?
Że ujemne indeksy nie są takie straszne. Nic poza tym.
--
Piotr Sietnik
***@pk.mofnet.gov.pl
Sektor van Skijlen
19 lat temu
Permalink
Post by Piotr Sietnik
Post by Sektor van Skijlen
Oczywiście. Indeks może być całkowity, a legalność jest tylko kwestią runtime.
Co ma do tego runtime?
Tyle, że poprawności statycznej takiej konstrukcji nie można zapewnić. Tak
samo, jak z użyciem const_cast, na przykład.
Post by Piotr Sietnik
W runtimie z takim samym prawdopodobieństwem wywalają się "legalne"
programy, jak i poprawnie działają "nielegalne".
Szczególnie to pierwsze mnie ciekawi.
--
// _ ___ Michal "Sektor" Malecki <sektor(whirl)kis.p.lodz.pl>
\\ L_ |/ `| /^\ ,() <ethourhs(O)wp.pl>
// \_ |\ \/ \_/ /\ C++ bez cholesterolu: http://www.intercon.pl/~sektor/cbx
"I am allergic to Java because programming in Java reminds me casting spells"
z***@wp.pl
19 lat temu
Permalink
Post by Sektor van Skijlen
Tyle, że poprawności statycznej takiej konstrukcji nie można zapewnić. Tak
samo, jak z użyciem const_cast, na przykład.
Możesz to dokładniej wyjaśnić ?
--
Wysłano z serwisu OnetNiusy: http://niusy.onet.pl
Marcin 'Qrczak' Kowalczyk
19 lat temu
Permalink
Post by qfel
Czy cos takiego jest legalne?
int _t[21],*t=_t+1;
t[-1]=10;
Tak. Byle tylko nie wyjeżdżać wskaźnikiem poza istniejącą tablicę,
tzn. przed indeks 0 ani za indeks równy jej długości.
--
__("< Marcin Kowalczyk
\__/ ***@knm.org.pl
^^ http://qrnik.knm.org.pl/~qrczak/
Paweł Kierski
19 lat temu
Permalink
Post by qfel
Czy cos takiego jest legalne?
int _t[21],*t=_t+1;
t[-1]=10;
W końcu t[x] <=> *(t+x), wobec tego t[-1] jest tak samo poprawne,
jak *(t-1). Byle tylko pamięć wskazywana przez t-1 była przydzielona.
--
Paweł Kierski
***@pkierski.net
dodaj "[nomorespam]" w temacie jeśli piszesz z domeny innej niż .pl,
albo koniecznie chcesz obejść moje filtry 8-)
artiun
19 lat temu
Permalink
Post by qfel
Czy cos takiego jest legalne?
int _t[21],*t=_t+1;
t[-1]=10;
Czy wam odbija?
Tak czytam i czekam na jakieś jasne określenie.
Przecież to jest poprawne! Jak mi standard napisze, że (-1) jest błędem to
ja mam gdzieś taki standard!
--
Artur
Loading...