gets a "niebezpieczeństwo"

0

Witam,
przy kompilacji programu, w którym użyłem gets'a dostaję taki warning:
"warning: the `gets' function is dangerous and should not be used."

I teraz jakie powikłania mogą wyniknąć z użycia gets'a?

0

gets nie obchodzi jak duży bufor jej podajesz (tablicę na znaki), zakłada że jest na tyle duży aby zapisać wszystkie znaki aż do nowej linii.

0

Poczytaj np. o stack-smashing attack

Pzdr.

0

Gdyby tylko o stos chodziło - po prostu buffer overflow. Równie niebezpieczne jest nadpisanie elementów kontrolnych sterty, sekcji danych, wszelakich struktur kontrolnych, których można 'dosięgnąć' za buforem docelowym. W bardzo głupich przypadkach (jak dzieci próbujące pisać trojany w assemblerze) można nawet po prostu nadpisać kod programu... Generalnie temat-rzeka, o tym się książki pisze.

0

Właściwie to ja nie muszę się bać o to co mówicie - mam sprecyzowaną maksymalną wielkość string'a(jeśli dobrze zrozumiałem o co wam chodzi), a zależy mi w 99% na szybkości tego co używam.

Tylko czy jest to najszybsza funkcja do wczytywania linii?
Wydaję się, że niby tak skoro nawet nie sprawdza czy ma miejsce na to co ma zapisać...

No ale jak Wy myślicie, ew. co byście wybrali?

0

Nie zrozumiałeś.
Funkcji gets podajesz skończonej długości tablicę. Zawsze na standardowym wejściu może pojawić się coś dłuższego.

Tylko czy jest to najszybsza funkcja do wczytywania linii?
Wydaję się, że niby tak skoro nawet nie sprawdza czy ma miejsce na to co ma zapisać...
Przyrost prędkości jest znikomy w porównaniu z całą operacją, nieopłacalny. Zresztą zrób testy, zmierz, porównaj :d

0

No dobra, ale musisz przyznać, że zadaniowo(gdzie masz podane jaki może być maksymalny ciąg znaków, który nie może zostać przekroczony) jest to chyba dość przydatna funkcja, która zdaje się działać szybko ;-).

0

No i Proszę Państwa wyniki pomiarów są już znane:

gets

real    0m3.389s
user    0m0.564s
sys     0m0.936s

getline

real    0m2.919s
user    0m0.460s
sys     0m0.956s

Dla każdego testu wczytywałem milion razy po milion znaków do zmiennej.
Jak widać o dziwo geline jest szybszy, lepszy i nowszy, więc gets'a można już do lamusa odstawić...


0

Fgets pozwala określić rozmiar maksymalny... A skąd wiesz, że limit nie zostanie przekroczony? Uszkodzony plik, durny albo bardzo cwany użytkownik... różne wypadki chodzą po programach.

W praktyce gets ma zastosowanie tylko tam, gdzie rozmiar danych jest absolutnie pewny - na konkursach.

Jeszcze co do tego testu - co z synchrionizacją z stdio? http://www.cplusplus.com/reference/iostream/ios_base/sync_with_stdio.html Ile razy te pomiary wykonywałeś?

0

Gwoli ścisłości jeszcze raz testy - sync_with_stdio(0).

getline

krzys@Krzys:~$ time ./t<t.in

real    0m0.137s
user    0m0.096s
sys     0m0.000s
krzys@Krzys:~$ time ./t<szy.in

real    0m0.119s
user    0m0.100s
sys     0m0.000s
krzys@Krzys:~$ time ./t<szy.in

real    0m0.163s
user    0m0.104s
sys     0m0.000s
krzys@Krzys:~$ time ./t<szy.in

real    0m0.970s
user    0m0.248s
sys     0m0.568s
krzys@Krzys:~$ time ./t<szy.in

real    0m1.011s
user    0m0.312s
sys     0m0.580s
krzys@Krzys:~$ time ./t<szy.in

real    0m1.082s
user    0m0.268s
sys     0m0.644s

Hmm w sumie to teraz robię właściwie takie "konkursowe" zadanie tylko nie widzę, żeby w czymś był lepszy gets(na konkurs również). No ale to tak na boku może komuś się przyda - w moim przypadku nie ma za dużej różnicy czasowej(wczytuję tylko raz ciąg).

Drugie czasy to gets'a zapomniałem dopisać...

0

W Twoich testach większość czasu to inicjalizacja lub\i buforowanie pliku przez system, do tego dochodzą programy działające w tle. Odrobinkę się nudzę więc naklepałem na szybko taki test w Ruby (grrr, oczywiście geshi na 4p nie obsługuje Ruby)...

def prepareFile fileName
  File.open fileName, 'w' do |file|
    1000000.times { file.puts '*'*80 }
  end
end

def getExecutionTime fileName, testfile
  getTime = lambda { `#{fileName} < #{testfile}`; Time.now }
  - (getTime.call - getTime.call)
end

testfile = "testfile.txt"

prepareFile testfile

%w/getline1 getline2 gets fgets/.each do |file|
  `g++ -O3 #{file}.cpp -o #{file}.exe`
  puts "#{file}: #{getExecutionTime file, testfile} s."
end

Czyli do wczytania milion linii zawierających po osiemdziesiąt gwiazdek. Kody w C++, zawartość main:

// gets.cpp
  char str[256];
  for (int i = 0; i < 1000000; i++)
    std::gets(str);
// fgets.cpp
 char str[256];
  for (int i = 0; i < 1000000; i++)
    std::fgets(str, sizeof(str), stdin);
// getline1.cpp
  std::string str;
  for (int i = 0; i < 1000000; i++)
    getline(std::cin, str);
// getline2.cpp
  std::string str;
  std::iostream::sync_with_stdio(false);
  for (int i = 0; i < 1000000; i++)
    getline(std::cin, str);

Pliki są kolejno kompilowane i uruchamiane. Każdy program jest wykonywany dwukrotnie, mierzony jest tylko czas drugiego wykonania - pierwsze jest ignorowane ze względu na cache'owanie plików. Dzięki temu wszystkie testy mają zapewnione bardzo zbliżone warunki wykonywania.

Wyniki z mojego Windows Server 2003 (kompilator G++ 4.2.1) na niemłodym już sprzęcie:

c:/Documents and Settings/baiji $ ruby test.rb
gets: 2.493 s.
fgets: 2.624 s.
getline1: 63.781 s.
getline2: 2.363 s.

Faktycznie getline z synchronizacją jest STRASZNIE wolne. Ciekawe, że gets przegrywa nieco z niesynchronizowanym getline. Drugi test, 25x (+ jeden na rozgrzewkę) te szybsze:

def getExecutionTime times, fileName, testfile
  `#{fileName} < #{testfile}`
  start = Time.now
  times.times { `#{fileName} < #{testfile}` }
  Time.now - start
end

testfile = "testfile.txt"

%w/gets fgets getline2/.each do |file|
  puts "#{file}: #{getExecutionTime 25, file, testfile} s."
end

I wyniki:

c:/Documents and Settings/baiji $ ruby test2.rb
gets: 60.657 s.
fgets: 69.52 s.
getline2: 58.695 s.

Byłoby miło gdybyś ten test wykonał u siebie, ew. inny użytkownik Linuksa...

0
deus napisał(a)

W Twoich testach większość czasu to inicjalizacja lub\i buforowanie pliku przez system
Przecież te czynniki będą występować. Test udowodnił, że różnica jest znikoma w porównaniu z całą resztą. Więc nie warto zaprzątać sobie prędkością głowę, lepiej postawić na bezpieczeństwo.

A jeśli piszesz na konkurs i chcesz wycisnąć każdą milisekundę, to użyj ciut szybszego gets bo problemu z bezpieczeństwem nie ma (dane zawsze poprawne).

0

No dobra, ale jak już zaczęliśmy rozważania to już tylko dokończę jak to wychodzi test na linuxie - Debian, Pentium 4.

gets: 28.976422 s.
fgets: 30.439965 s.
getline2: 6.084864 s.

No i troszkę zaskakuję wynik getlina2 - sprawdzałem 2 razy i tak samo wychodzi.
Zastanawiające czemu tak szybki jest, choćby nawet w porównaniu z Twoimi wynikami...
No, ale zresztą czy to aż tak ważne? ;]

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