SocrateZ |
» 2015-01-03 22:12:27 Przeczytaj jeszcze raz. the behavior is undefined, even if the pointer is not dereferenced |
|
|
DejaVu |
» 2015-01-03 22:15:06 Jeśli by tak robił to wiele innych zapisów, z musu zgodnych ze standardem było by nieprawidłowych.
|
Przytocz tą 'mnogość' innych nieprawidłowych zapisów, a nie rzucasz pustymi hasłami. My Ci rzuciliśmy fragmentami standardu oraz napisaliśmy przykłady dlaczego ma taki zapis sens tj. zachowanie będzie niezdefiniowane w omawianym przykładzie. |
|
antonio12 Temat założony przez niniejszego użytkownika |
» 2015-01-03 22:59:15 pominąłem jeden znak w kodzie: #include <stdio.h>
int main() { char c1, c2; char tekst[] = "kolorowo"; for( c1 =*( tekst - 1 + strlen( tekst ) ), c2 =* tekst; c1-->= c2; ) { switch( c1 ) { case 'o': printf( "^ " ); c2++; case 'l': printf( "* " ); break; case 'k': printf( "& " ); c2++; break; case 'm': printf( "% " ); case 'r': printf( "# " ); break; default: printf( "%c %c ", c1, c2 ); c2++; } } return 0; } Jednak to nie zmienia wyniku działania programu, do c1-->=c2 dodałem jeszcze znak =. |
|
Elaine |
» 2015-01-03 23:10:52 RejestrX = numerek; RejestrY = 0; RejestrY = RejestrY - 1; RejestrY = RejestrY + strlen(zmienna); Adres = RejestrX + RejestrY; Wyjście poza wartość 0 dla RejestrY jest sprzętowym naruszeniem pamięci |
Dlaczego miałoby być? Ten ciąg instrukcji jest równoważny adres = numerek + (-1 + strlen(zmienna)), najpierw wykonywane są operacje na liczbach — liczbach całkowitych bez znaku, gdzie przepełnienie i niedopełnienie mają zdefiniowane zachowanie: wynik się po prostu "przekręci" — przez co efekt jest taki sam, jak w przypadku adres = numberek + strlen(zmienna) - 1: poprawny kod, który robi to, czego można się spodziewać. Przynajmniej jeśli strlen(zmienna) nie jest zerem. Lepszym przykładem byłoby: a0 = tekst a1 = zmienna a0 = a0 - 1 ; tu nastąpi wyjątek d0 = strlen(a1) a0 = a0 + d0A widziałeś kiedyś, aby procesor potrafił sumować n-liczb naraz? |
Tak, na x86 z = x + y - 1 najprawdopodobniej skompiluje się do czegoś takiego: lea ecx, [eax + ebx - 1]Nie rozumiem tylko, dlaczego tak bardzo skupiacie się tym, co zrobi procesor. Zadaniem implementacji jest jedynie zapewnienie, że obserwowalne zachowanie działającego kodu jest takie samo, jak obserwowalne zachowanie podanego kodu działającego na opisanej przez standard abstrakcyjnej maszyny (1.9/1). To, w jaki sposób się to dzieje, nie ma większego znaczenia, kod może równie dobrze być interpretowany z uzwględnieniem szczegółowych sprawdzeń, czy w którymś miejscu nie występuje niezdefiniowane zachowanie; ba, dzisiejsze kompilatory zwykle można poprosić o generowanie podobnych sprawdzeń. W takiej sytuacji nawet na systemach, gdzie procesor nie ma żadnych form ochrony pamięci kod, którego dotyczy temat, zostałby zatrzymany. |
|
Kaikso |
» 2015-01-03 23:12:06 RejestrX = numerek; RejestrY = 0; RejestrY = RejestrY - 1; RejestrY = RejestrY + strlen(zmienna); Adres = RejestrX + RejestrY;
|
A więc o to dowód: #include <stdio.h> #include <string.h>
void * test( void * ptr, size_t size ) { register void * RejestrX = ptr; register size_t RejestrY = 0; RejestrY = RejestrY - 1; RejestrY = RejestrY + size; return RejestrX + RejestrY; }
int main( void ) { char str[] = "tekst"; size_t len = strlen( str ); void * res = test( str, len ); printf( "str: %p\n", str ); printf( "res: %p\n", res ); return 0; }
Słowem kluczowym register wymusiłem kod taki jak ty zaprezentowałeś: push ebp mov ebp, esp push esi push ebx ; tu początek mov esi, DWORD PTR [ebp+8] mov ebx, 0 sub ebx, 1 add ebx, DWORD PTR [ebp+12] lea eax, [esi+ebx] ; eax to wartość zwracana pop ebx pop esi pop ebp ret
A wynik: str: 0xbfdca956 res: 0xbfdca95a
0xbfdca956 - 0xbfdca95a = 4 (czyli index w tablicy) Aj wygląda na to że procesor sobie poradził i obliczył prawdziwy adres :P @DejaVu: To nic nie zmienia, zmienne w kodzie nie są dokładnym odwzorowaniem danych na których operuje procesor. Dlatego język C jest językiem niskopoziomowym ale abstrakcyjnym. |
|
DejaVu |
» 2015-01-03 23:20:52 @up: to nie jest dowód. To przykład działania jednego, specyficznego kompilatora. Zrozum to w końcu... To, że architektura x86 postawi flagę underflow przy odejmowaniu, a następnie overflow przy dodawaniu bez 'zauważalnych' skutków w ostatecznym wyniku nie oznacza, że inna architektura nie wywali w kosmos Twojej aplikacji z powodu wystąpienia zarówno underflow jak i overflow. |
|
Kaikso |
» 2015-01-03 23:27:49 To nie przykład działania kompilatora, ale procesora. Nie skompilowałem kodu w c tylko kod w asemblerze. Ten kod wyglądał dokładnie tak jak napisałeś (co zacytowałem), a działał mimo to poprawnie :P
Co mam jeszcze załączyć wynik disassemblera z pliku binarnego. |
|
Elaine |
» 2015-01-03 23:29:12 To, że architektura x86 postawi flagę underflow przy odejmowaniu, a następnie overflow przy dodawaniu bez 'zauważalnych' skutków w ostatecznym wyniku nie oznacza, że inna architektura nie wywali w kosmos Twojej aplikacji z powodu wystąpienia zarówno underflow jak i overflow. |
Nie ma prawa, liczby całkowite bez znaku po prostu się "przekręcą". 3.9.1/4: Unsigned integers shall obey the laws of arithmetic modulo 2^n where n is the number of bits in the value representation of that particular size of integer. |
|
|
1 2 3 4 « 5 » 6 7 8 |