Что такое код спагетти?
Можете ли вы опубликовать короткий пример реального, переполненного кода спагетти, возможно, говоря, что он делает? Можете ли вы показать мне небольшой кошмар отладчика?
Я не имею в виду IOCCC код, то есть научную фантастику. Я имею в виду примеры реальной жизни, которые произошли с вами...
Update
Фокус изменился с "поместить некоторый код спагетти" на "что такое код спагетти?". С исторической точки зрения, нынешний выбор выглядит следующим образом:
- старый код Fortran с использованием вычисляемых gotos массово
- старый код Cobol с использованием оператора ALTER
Ответы
Ответ 1
Для меня более современный пример кода спагетти - это когда у вас есть 20 dll, и каждая DLL ссылается друг на друга так или иначе. Граф зависимости выглядит как огромный капля, и ваш код переполняется повсюду без реального заказа. Все взаимозависимо.
Ответ 2
Там также Ravioli Code, который является обратным. Хорошие маленькие кусочки функциональности, чистый интерфейс, аккуратно обернутый вокруг мясистой доброты, все сидели в хорошем соусе каркаса.
Ответ 3
Из драйвера SCSI Linux (который должен оставаться безымянным для защиты виновных):
wait_nomsg:
if ((inb(tmport) & 0x04) != 0) {
goto wait_nomsg;
}
outb(1, 0x80);
udelay(100);
for (n = 0; n < 0x30000; n++) {
if ((inb(tmport) & 0x80) != 0) { /* bsy ? */
goto wait_io;
}
}
goto TCM_SYNC;
wait_io:
for (n = 0; n < 0x30000; n++) {
if ((inb(tmport) & 0x81) == 0x0081) {
goto wait_io1;
}
}
goto TCM_SYNC;
wait_io1:
inb(0x80);
val |= 0x8003; /* io,cd,db7 */
outw(val, tmport);
inb(0x80);
val &= 0x00bf; /* no sel */
outw(val, tmport);
outb(2, 0x80);
TCM_SYNC:
/* ... */
small_id:
m = 1;
m <<= k;
if ((m & assignid_map) == 0) {
goto G2Q_QUIN;
}
if (k > 0) {
k--;
goto small_id;
}
G2Q5: /* srch from max acceptable ID# */
k = i; /* max acceptable ID# */
G2Q_LP:
m = 1;
m <<= k;
if ((m & assignid_map) == 0) {
goto G2Q_QUIN;
}
if (k > 0) {
k--;
goto G2Q_LP;
}
G2Q_QUIN: /* k=binID#, */
Как я нашел этот камень?
find /usr/src/linux -type f -name \*.c |
while read f
do
echo -n "$f "
sed -n 's/^.*goto *\([^;]*\);.*/\1/p' $f | sort -u | wc -l
done |
sort +1rn |
head
Вывод представляет собой ряд строк, перечисляющих файлы, упорядоченные по количеству gotos, на различные метки, например:
kernel/fork.c 31
fs/namei.c 35
drivers/infiniband/hw/mthca/mthca_main.c 36
fs/cifs/cifssmb.c 45
fs/ntfs/super.c 47
Ответ 4
Реальный код спагетти был выполнен в COBOL и использовался оператор ALTER.
Здесь пример, в то время как в списке "юмор" я видел такие вещи. Почти уволили один раз за то, что любая программа с выражением Альтера явно находится в состоянии греха. Я отказался "поддерживать" эту программу, ее быстрее заменить, чем ее понять.
Ответ 5
Я не вытаскиваю это из головы. Это то, с чем мне пришлось работать, хотя и упрощен. Скажем, что в основном у вас есть программа, которая нуждается в перечислении:
enum {
a, b, c;
} myenum;
Но вместо этого мы имеем
HashTable t;
t["a"] = 0;
t["b"] = 1;
t["c"] = 2;
Но, конечно, реализация хэш-таблицы не является достаточной, поэтому существует локальная реализация хеш-таблиц, которая содержит примерно в 10 раз больше кода, чем средняя реализация с открытым исходным кодом, с половиной функций и удвоение количества ошибок. HashTable фактически определен виртуальным и существует factory HashTableFactory для создания экземпляров HashTables, но true для шаблона HashTableFactory также является виртуальным. Чтобы предотвратить инфинитный каскад виртуальных классов, существует функция
HashTableFactory *makeHashTableFactor();
Таким образом, везде, где код нуждается в myenum, он ссылается на экземпляр HashTable и HashTableFactory, если вы хотите сделать больше HashTable. Но подождите, это не все! Это не так, как инициализируется хэш-таблица, но это делается путем написания кода, который читает XML:
<enum>
<item name="a" value="0"/>
<item name="b" value="1"/>
<item name="c" value="2"/>
</enum>
и вставляет в хеш-таблицу. Но код "оптимизирован", так что он не читает файл ascii myenum.xml, но вместо этого существует время компиляции script, которое генерирует:
const char* myenumXML = [13, 32, 53 ....];
из myenum.xml, а хэш-таблица инициализируется функцией:
void xmlToHashTable(char *xml, HashTable *h, HashTableFactory *f);
который называется:
HashTableFactory *factory = makeHashTableFactory();
HashTable *t = facotry.make();
xmlToHashTable(myenumXML, t, f);
Хорошо, поэтому у нас есть много кода, чтобы получить структуру перечисления. Он в основном используется в функции:
void printStuff(int c) {
switch (c) {
case a: print("a");
case b: print("b");
case c: print("c");
}
}
и это вызывается в контексте, где:
void stuff(char* str) {
int c = charToEnum(str);
printStuff(c);
}
Итак, у нас есть вместо
void stuff(char *str) {
printf(str);
}
нам удалось создать тысячи строк кода (private new, buggy, complex, реализация хэш-таблиц и xml-ридеров и писателей) вместо указанных выше.
Ответ 6
Не забывайте упоминать объектно-ориентированные спагетти.
Это когда вы пытаетесь использовать все шаблоны дизайна в книге, даже если они не имеют смысла. Это приводит к коду спагетти на концептуальном уровне, что гораздо более вредно для качества, чем классический код спагетти на основе goto.
Ответ 7
Вы просили об этом, вы получите его:
Это источник файла DOS.com, который играет вальс Blue Danube. Исполняемый файл имеет размер 176 байт. Код повторно используется как данные и наоборот.
.286
.model tiny
g4 equ 55-48 ; removed note-decoding !
a4 equ 57-48 ; now: storing midi-notes for octaves 0..2 and convert
h4 equ 59-48 ; to 4..6 with a simple add 48.
c5 equ 60-48
d5 equ 62-48
e5 equ 64-48
g5 equ 67-48
h5 equ 71-48
c6 equ 72-48
d6 equ 74-48
e6 equ 76-48
g6 equ 79-48 ; = 00011111b
pp equ 0 ; c4 is not used in the walz, using it as play-pause.
EOM equ 1 ; c#4 is also available... End Of Music
; warning: experts only beyond this point !
pau1 equ 00100000b ; bitfield definitions for note-compression
pau2 equ 01000000b ; you can or a pau to each note!
pau3 equ 01100000b
;rep1 equ 01000000b ; rep1 is history (only used once).
;rep3 equ 11000000b ; rep3 was never used.
rep2 equ 10000000b ; or a rep2 to a note to play it 3 times.
drumsize equ 5
.code
org 100h
start:
mov ah,9
mov dx,offset msg
int 21h ; print our headerstring
mov dx,0330h ; gus midi megaem -port
mov si,offset music_code ; start of music data
mainloop:
; get new note (melody)
xor bp,bp ; bp= repeat-counter
lodsb ; get a new note
cmp al, EOM ; check for end
jne continue
ret
continue:
jns no_rep2 ; check for rep2-Bit
inc bp
inc bp ; "build" repeat-counter
no_rep2:
push ax ; save the note for pause
; "convert" to midi-note
and al,00011111b
jz skip_pp ; check pp, keep it 0
add al,48 ; fix-up oktave
skip_pp:
xchg ax,bx ; bl= midi-note
play_again:
mov cl,3
push cx ; patch program (3= piano)
push 0c8h ; program change, channel 9
; wait (cx:dx) times
mov ah,86h ; wait a little bit
int 15h
; prepare drums
dec di ; get the current drum
jns no_drum_underflow
mov di,drumsize
no_drum_underflow:
; play drum
push dx ; volume drum
push [word ptr drumtrk+di] ; note drum
mov al,99h
push ax ; play channel 10
; play melody
push dx ; volume melody
push bx ; note melody
dec ax ; replaces dec al :)
push ax ; play channel 9
; send data to midi-port
mov cl,8 ; we have to send 8 bytes
play_loop:
pop ax ; get the midi event
out dx,al ; and send it
loop play_loop
; repeat "bp" times
dec bp ; repeat the note
jns play_again
; check and "play" pause
xor bx,bx ; clear the note, so we can hear
; a pause
; decode pause value
pop ax
test al,01100000b
jz mainloop ; no pause, get next note
; decrement pause value and save on stack
sub al,20h
push ax
jmp play_again ; and play next drum
; don't change the order of the following data, it is heavily crosslinked !
music_code db pp or rep2
db g4 or rep2 or pau1
db h4 or pau1, d5 or pau1, d5 or pau3
db d6 or pau1, d6 or pau3, h5 or pau1, h5 or pau3
db g4 or rep2 or pau1
db h4 or pau1, d5 or pau1, d5 or pau3
db d6 or pau1, d6 or pau3, c6 or pau1, c6 or pau3
db a4 or rep2 or pau1
db c5 or pau1, e5 or pau1, e5 or pau3
db e6 or pau1, e6 or pau3, c6 or pau1, c6 or pau3
db a4 or rep2 or pau1
db c5 or pau1, e5 or pau1, e5 or pau3
db e6 or pau1, e6 or pau3, h5 or pau1, h5 or pau3
db g4 or rep2 or pau1
db h4 or pau1, g5 or pau1, g5 or pau3
db g6 or pau1, g6 or pau3, d6 or pau1, d6 or pau3
db g4 or rep2 or pau1
db h4 or pau1, g5 or pau1, g5 or pau3
db g6 or pau1, g6 or pau3, e6 or pau1, e6 or pau3
db a4 or rep2 or pau1
db c5 or pau1, e5 or pau1, e5 or pau3, pp or pau3
db c5 or pau1, e5 or pau1, h5 or pau3, pp or pau3, d5 or pau1
db h4 or pau1, h4 or pau3
db a4 or pau1, e5 or pau3
db d5 or pau1, g4 or pau2
; db g4 or rep1 or pau1
; replace this last "rep1"-note with two (equal-sounding) notes
db g4
db g4 or pau1
msg db EOM, 'Docking Station',10,'doj&sub'
drumtrk db 36, 42, 38, 42, 38, 59 ; reversed order to save some bytes !
end start
Ответ 8
Реальный спагетти-код требует множества нелокальных gotos. К сожалению, это невозможно с использованием большинства современных языков.
Изменить: некоторые предлагают исключения и longjmp в качестве замены для GOTO. Но они далеки от ограничений и структурированы, поскольку они позволяют вам возвращать стоп-колл. Real GOTO позволяет вам прыгать на любую строку в любом месте программы, что необходимо для создания реальных спагетти.
Ответ 9
Вот Duff Device, от Matt ответьте на этот вопрос:
int n = (count + 7) / 8;
switch (count % 8) {
case 0: do { *to = *from++;
case 7: *to = *from++;
case 6: *to = *from++;
case 5: *to = *from++;
case 4: *to = *from++;
case 3: *to = *from++;
case 2: *to = *from++;
case 1: *to = *from++;
} while (--n > 0);
}
Ответ 10
Проще говоря, код спагетти - это любой код на любом языке программирования, в котором невозможно проследить следующий пост исполнения или, по крайней мере, трудно определить, куда будет идти следующая точка в ответ на одно действие.
Ответ 11
Это из парсера MIDI, который я написал некоторое время назад. Это было быстрое и грязное доказательство концепции, но, тем не менее, я возьму на себя вину за его уродство: 4 уровня вложенных условностей плюс страшные множественные возвращения. Этот код предназначен для сравнения двух MIDI-событий, чтобы сортировать их по приоритету при записи в файл. Хотя это было ужасно, но все-таки это работало прилично.
internal class EventContainerComparer : IComparer {
int IComparer.Compare(object a, object b) {
MIDIEventContainer evt1 = (MIDIEventContainer) a;
MIDIEventContainer evt2 = (MIDIEventContainer) b;
ChannelEvent chanEvt1;
ChannelEvent chanEvt2;
if (evt1.AbsoluteTime < evt2.AbsoluteTime) {
return -1;
} else if (evt1.AbsoluteTime > evt2.AbsoluteTime) {
return 1;
} else {
// a iguar valor de AbsoluteTime, los channelEvent tienen prioridad
if(evt1.MidiEvent is ChannelEvent && evt2.MidiEvent is MetaEvent) {
return -1;
} else if(evt1.MidiEvent is MetaEvent && evt2.MidiEvent is ChannelEvent){
return 1;
// si ambos son channelEvent, dar prioridad a NoteOn == 0 sobre NoteOn > 0
} else if(evt1.MidiEvent is ChannelEvent && evt2.MidiEvent is ChannelEvent) {
chanEvt1 = (ChannelEvent) evt1.MidiEvent;
chanEvt2 = (ChannelEvent) evt2.MidiEvent;
// si ambos son NoteOn
if( chanEvt1.EventType == ChannelEventType.NoteOn
&& chanEvt2.EventType == ChannelEventType.NoteOn){
// chanEvt1 en NoteOn(0) y el 2 es NoteOn(>0)
if(chanEvt1.Arg1 == 0 && chanEvt2.Arg1 > 0) {
return -1;
// chanEvt1 en NoteOn(0) y el 2 es NoteOn(>0)
} else if(chanEvt2.Arg1 == 0 && chanEvt1.Arg1 > 0) {
return 1;
} else {
return 0;
}
// son 2 ChannelEvent, pero no son los 2 NoteOn, el orden es indistinto
} else {
return 0;
}
// son 2 MetaEvent, el orden es indistinto
} else {
return 0;
}
}
}
}
Ответ 12
Код спагетти: возникший в начале 60-х годов в Италии в качестве альтернативного рецепта для некоторых блюд из макарон, код спагетти был приготовлен одним предпринимателем из ресторана, который попытался автоматизировать создание дурацкого входа. Из-за нехватки времени, чтобы завершить дизайн, инженер/шеф-повар отрезали углы, которые в раннем возрасте вносили проблемы в рецепт. В безумной попытке исправить неплохую идею, специи были быстро добавлены в смесь, поскольку рецепт вырос из-под контроля. Результатом стала жесткая, извилистая, но потенциально вкусная куча текста, которая впоследствии станет практикой, заветной для разработчиков во всем мире.
Ответ 13
Вы когда-нибудь смотрели код, созданный сканером Flex/Bison и генератором? Множество надписей и предпроцессорных директив.
Абсолютно невозможно понять, что внутри.. и абсолютно невозможно следить за потоком программы.
Это определенно код спагетти.