Panel użytkownika
Nazwa użytkownika:
Hasło:
Nie masz jeszcze konta?

[C++] Operacje na wskaźniku - kiedy standard określa niezdefiniowane zachowanie?

Ostatnio zmodyfikowano 2015-01-05 21:52
Autor Wiadomość
SocrateZ
» 2015-01-03 22:12:27
Przeczytaj jeszcze raz.
the behavior is undefined, even if the pointer is not dereferenced
P-124021
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.
P-124022
antonio12
Temat założony przez niniejszego użytkownika
» 2015-01-03 22:59:15
pominąłem jeden znak w kodzie:
C/C++
#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 =.
P-124029
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 + d0


A 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.
P-124030
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:
C/C++
#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.
P-124031
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.
P-124032
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.
P-124033
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.
P-124034
1 2 3 4 « 5 » 6 7 8
Poprzednia strona Strona 5 z 8 Następna strona