Pull to refresh

Баги в Hello World

Reading time3 min
Views9.9K
Original author: sunfishcode

Hello World, должно быть, самая часто создаваемая компьютерная программа. Уже десятилетия это первая программа, которую пишут люди, когда начинают изучение нового языка программирования.

Конечно же эта простая программа не должна иметь баги?

В конце концов, hello world программы делают только одну вещь. Как там может быть баг?

Hello world в C

Есть множество различных способов написать hello world в C. Версия Википедии, hello world в книге K&R и даже самая старая из известных hello world программ из 1974.

Вот ещё одна написанная на "ANSI C":

/* Hello World in C, Ansi-style */

#include <stdio.h>
#include <stdlib.h>

int main(void)
{
  puts("Hello World!");
  return EXIT_SUCCESS;
}

Это самая надёжная версия из представленных. Она использует (void) чтобы гарантировать что main вызывается без аргументов. Она использует EXIT_SUCCESS макрос вместо предположения что платформа использует 0 для индикации успеха, что не обязательно согласно стандарту C, но мы не рискуем здесь. Она использует соответствующие заголовки, чтобы избежать неявного объявления puts. Эта версия пытается сделать правильно всё.

И всё равно, тут есть баг.

Во всех версиях представленных выше есть баг.

Баг?

В Linux есть интересный файл под названием "/dev/full", который похож на его более известного собрата "/dev/null", но когда вы пишете в "/dev/full", вместо того чтобы игнорировать данные, он выдаёт ошибку. Он ведёт себя как файл в файловой системе у которой закончилось место.

$ echo "Hello World!" > /dev/full
bash: echo: write error: No space left on device
$ echo $?
1

Это прекрасный инструмент для тестирования того, что программа правильно обрабатывает ошибки ввода-вывода. Неудобно создавать реальную файловую систему без свободного места или эмулировать диск, который сломан, но очень просто направить вывод программы в "/dev/full" и посмотреть что произойдёт.

Так давайте проверим ту программу на C выше:

$ gcc hello.c -o hello
$ ./hello > /dev/full
$ echo $?
0

В отличие от echo в предыдущем примере, программа ничего не вывела и код возврата был 0. Это значит что программа hello сообщила что завершилась успешно. Однако, это не так. Мы можем убедиться, что ошибка была, с помощью strace:

$ strace -etrace=write ./hello > /dev/full
write(1, "Hello World!\n", 13)          = -1 ENOSPC (No space left on device)
+++ exited with 0 +++

Вот и ошибка о нехватке места, но программа просто проигнорировала её и возвратила 0 - код успеха. Это баг!

Насколько серьёзный это баг? Возможно, hello world - это не самая критичная программа. Однако, hello world делает то же, что и другие программы в реальном мире: печать в стандартный вывод, который может быть перенаправлен в файл, а ведь в реальном мире для файла может закончиться место. Если программа игнорирует такую ошибку и не сообщает о ней через код возврата, её родительский процесс не будет знать что она завершилась с ошибкой и продолжит работать как ни в чём не бывало, хотя вывод, который он ожидал получить, был потерян.

Например, рассмотрим программу, которая печатает yaml файл в стандартный поток вывода. Если у стандартного вывода закончится место, то вывод может быть обрезан в какой-то произвольной точке, хотя это всё ещё может быть валидный yaml. Поэтому нам стоит ожидать, что программы обнаруживают такую ситуацию и сообщают о ней.

Что насчёт других языков?

Ранее мы рассмотрели bash и C, но что насчёт Python, который говорит нам что "Ошибки никогда не должны замалчиваться"? Вот Python 2:

$ python2 hello.py > /dev/full
close failed in file object destructor:
sys.excepthook is missing
lost sys.stderr
$ echo $?
0

Он напечатал сообщение в stderr, хотя оно и сбивает с толку. Однако он также вернул 0, что означает что программа завершилась успешно.

К счастью, Python 3 правильно сообщает об ошибке и печатает понятное сообщение о ней:

$ python3 hello.py > /dev/full
Exception ignored in: <_io.TextIOWrapper name='<stdout>' mode='w' encoding='utf-8'>
OSError: [Errno 28] No space left on device
$ echo $?
120

Вот результаты работы hello world программ на различных языках программирования с различных обучающих сайтов, которые я попробовал запустить:

Язык

Есть ли баг

Тестируемая версия

C

Да

(все)

C++

Да

(все)

Python 2

Да

Python 2.7.18

Ruby

Да

ruby 2.7.2p137 (2020-10-01 revision 5445e04352) [x86_64-linux-gnu]

Java

Да

openjdk 11.0.11 2021-04-20

Node.js

Да

v12.21.0

Haskell

Да

The Glorious Glasgow Haskell Compilation System, version 8.8.4

Rust

Нет

rustc 1.59.0 (9d1b2106e 2022-02-23)

Python 3

Нет

Python 3.9.5

Perl

Нет

perl 5, version 32, subversion 1 (v5.32.1) built for x86_64-linux-gnu-thread-multi (with 46 registered patches...)

Perl 6

Нет

v2020.12

Bash

Нет

GNU bash, version 5.1.4(1)-release (x86_64-pc-linux-gnu)

Awk

Нет

GNU Awk 5.1.0, API: 3.0 (GNU MPFR 4.1.0, GNU MP 6.2.1)

OCaml

Нет

4.08.1

Tcl

Нет

8.6.11

C#

Нет

Mono JIT compiler version 6.8.0.105

Более полный и актуальный список находится тут.

Tags:
Hubs:
Total votes 27: ↑21 and ↓6+19
Comments21

Articles