2011-01-10 16 views
5

Mam mały problem z poniższym kodem. Jest to prosty program, który czyta się w 2 tablicach char i int. Następnie przechowuje całą zawartość w innym ciągu i drukuje ją.Błąd: Stos wokół zmiennej "ciąg" został uszkodzony

#include <stdio.h> 
#include <string.h> 

int main() 

{ 
    char string [50]; 
    char first [11]; 
    char last [16]; 
    int age = 0; 


    printf("Please type in your first name: "); 
     scanf("%s", first); 

    printf("Please type in your last name: "); 
     scanf("%s", last); 

    printf("Please type in your age: "); 
     scanf("%d", &age); 

    sprintf(string, "Your name is %s %s and you are %d years old.", first, last, age); 
     puts(string); 

    getchar(); 
    getchar(); 

    return 0; 
} 

Teraz program działa dobrze, ale kiedy go zamknąć, pojawia się następujący błąd: Run-Time Check Failure # 2 - Stack wokół zmiennej 'string' został uszkodzony. To trochę zagmatwane i nie wiem, gdzie jest problem. Byłbym wdzięczny za radę .

+1

Niepowiązane, ale jeśli masz C99 (lub gwarancję niektórych jego części), powinieneś używać 'snprintf' zamiast tego, aby zapobiec temu problemowi. –

Odpowiedz

13

Piszesz więcej znaków na „string”, niż miało to pomieszczenie przeznaczone na (czyli więcej niż 50)

Istnieje 37 znaków w "Your name is %s %s and you are %d years old." przed dodaniem wartości pierwszy, ostatni i wiek. Pozostawia tylko 13 znaków dla wszystkich trzech zmiennych. Rozlewa się więc na inne vary zadeklarowane po twoim zmiennym "ciągu" na stosie.

Zgodnie z tym, co wymienił Jon, najlepiej jest używać funkcji, które ograniczają pisanie (warianty "n"), w przeciwnym razie mogą być źródłem exploitów bufferoverru.

BTW "ciąg" jest bardzo kiepską nazwą zmiennej.

1

Przypuszczam, że to ma coś wspólnego z faktem, że długość tablicy string jest 50 znaków, co masz w sprintf 37 (jeśli Liczyłem po prawej) oraz następnie do 11 dla first i kolejne 16 na last plus może 2 lub 3 dla wieku. To daje więcej niż 50. Wszystko działa poprawnie, ale najprawdopodobniej nadpisujesz po zakończeniu 50 przydzielonych znaków. To będzie "działało", ale zniszczyło stos, jak zauważyłeś.

4

Oprócz czegokolwiek innego, dozwolone jest podawanie imienia składającego się maksymalnie z 10 znaków i nazwiska do 15 znaków. Jeśli te limity zostaną osiągnięte (ale nie przekroczone), a wiek jest dwucyfrową liczbą, która zajmie 66 znaków - musisz zadeklarować, że string będzie tablicą zawierającą 67 znaków (aby uwzględnić terminator z wartością zerową).

Poza tym powinieneś używać funkcji lub ciągów formatujących, które pozwalają ci ograniczyć rozmiar danych wejściowych - obecnie jeśli ktoś wprowadzi imię o długości dłuższej niż 10 znaków (itp.), Zdepczesz inne fragmenty pamięci . Minęło trochę czasu, odkąd napisałem C, ale używanie ciągów formatów "% 10s" i "% 15s" może pomóc w tym względzie - lub użyć fgets.

Podobnie, proponuję użyć snprintf (lub snprintf_s, jeśli jest dostępna) zamiast , aby uniknąć problemu przekroczenia wyjścia. Użyj wartości zwrotu wszystkich tych metod do wykrywania błędów, zbyt :)

+0

Możesz ograniczyć rozmiar za pomocą 'scanf', ale pełne wdzięku odzyskiwanie po osiągnięciu tego limitu jest niepotrzebnie trudne, więc +1 za polecanie innych funkcji. –

2

Można ograniczyć ilość znaków scanf czyta z

scanf("%9s", foo) 

który odczyta co najwyżej 9 znaków, a następnie dołączania NUL, który jest odpowiedni dla bufora o rozmiarze 10.