x86-64 Машинний код, 30 байт
31 C0 99 8B 4C B7 FC F6 C1 01 74 04 01 CA EB 02 01 C8 FF CE 75 ED 29 D0 99 31 D0 29 D0 C3
Вищевказаний код визначає функцію, яка приймає список / масив цілих цифр і повертає абсолютну різницю між сумою його парних цифр і сумою його непарних цифр.
Як у С , мова складання не реалізує списки або масиви як першокласні типи, а представляє їх як комбінацію вказівника та довжини. Тому я домоглася, щоб ця функція приймала два параметри: перший - вказівник на початок списку цифр, а другий - ціле число, що визначає загальну довжину списку (загальна кількість цифр, одноіндексований) .
Ця функція відповідає умові виклику System V AMD64 , який є стандартним для систем Gnu / UNIX. Зокрема, перший параметр (вказівник на початок списку) передається RDI
(оскільки це 64-бітний код, це 64-бітний покажчик), а другий параметр (довжина списку) передається у ESI
( це лише 32-бітове значення, тому що це більш ніж достатньо цифр, з якими можна грати, і, природно, передбачається, що це не нульове значення). Результат повертається в EAX
реєстр.
Якщо це ясніше, це буде прототип C (і ви можете використовувати це для виклику функції з C):
int OddsAndEvens(int *ptrDigits, int length);
Невикольована збірна мнемоніка:
; parameter 1 (RDI) == pointer to list of integer digits
; parameter 2 (ESI) == number of integer digits in list (assumes non-zero, of course)
OddsAndEvens:
xor eax, eax ; EAX = 0 (accumulator for evens)
cdq ; EDX = 0 (accumulator for odds)
.IterateDigits:
mov ecx, [rdi+rsi*4-4] ; load next digit from list
test cl, 1 ; test last bit to see if even or odd
jz .IsEven ; jump if last bit == 0 (even)
.IsOdd: ; fall through if last bit != 0 (odd)
add edx, ecx ; add value to odds accumulator
jmp .Continue ; keep looping
.IsEven:
add eax, ecx ; add value to evens accumulator
.Continue: ; fall through
dec esi ; decrement count of digits in list
jnz .IterateDigits ; keep looping as long as there are digits left
sub eax, edx ; subtract odds accumulator from evens accumulator
; abs
cdq ; sign-extend EAX into EDX
xor eax, edx ; XOR sign bit in with the number
sub eax, edx ; subtract sign bit
ret ; return with final result in EAX
Ось короткий опис коду:
- По-перше, ми нулюємо
EAX
і EDX
регістри, які будуть використані для проведення сумарних підсумків парних і непарних цифр. Очищається EAX
реєстр, XOR
вводячи його в себе (2 байти), а потім EDX
очищає регістр шляхом розширення знака EAX до нього ( CDQ
, 1 байт).
Потім ми переходимо до циклу, який повторюється через усі цифри, передані в масиві. Він отримує цифру, перевіряє, чи є парним або непарним (випробувавши найменш значущий біт, який буде 0, якщо значення парне або 1, якщо воно непарне), а потім перестрибне або провалиться відповідно, додавши, що значення для відповідного акумулятора. У нижній частині циклу декрементуємо лічильник цифр ( ESI
) і продовжуємо циклічно до тих пір, поки він не дорівнює нулю (тобто, поки в списку залишиться більше цифр для отримання).
Тут складне лише початкова інструкція MOV, яка використовує найскладніший режим адресації, можливий на x86. * Він бере RDI
як базовий регістр (вказівник на початок списку), масштабує RSI
(лічильник довжини, який служить індексом) на 4 (розмір цілого числа, в байтах) і додає це до бази, і потім віднімаємо 4 від загальної суми (оскільки лічильник довжини одноосновний, і нам потрібно, щоб зміщення було нульовим). Це дає адресу цифри в масиві, який потім завантажується в ECX
регістр.
Після закінчення циклу робимо віднімання коефіцієнтів з рівнів ( EAX -= EDX
).
Нарешті, ми обчислюємо абсолютне значення, використовуючи загальний трюк - той самий, який використовується для більшості компіляторів С для abs
функції. Я не буду вникати в деталі про те, як працює цей трюк; перегляньте коментарі до коду для підказок або виконайте пошук в Інтернеті.
__
* Код можна переписати для використання більш простих режимів адресації, але це не робить його коротшим. Мені вдалося придумати альтернативну реалізацію, яка дереферендувала RDI
та збільшувала її до 8 кожного разу через цикл, але оскільки вам все одно доведеться зменшити лічильник ESI
, це виявилося тим самим 30 байтів. Спочатку сподівався на мене, що add eax, DWORD PTR [rdi]
це лише 2 байти, те саме, що додавання двох зареєстрованих значень. Ось така реалізація, якщо тільки врятувати когось, хто намагається перевершити мене деякими зусиллями :-)
OddsAndEvens_Alt:
31 C0 xor eax, eax
99 cdq
.IterateDigits:
F6 07 01 test BYTE PTR [rdi], 1
74 04 je .IsEven
.IsOdd:
03 17 add edx, DWORD PTR [rdi]
EB 02 jmp .Continue
.IsEven:
03 07 add eax, DWORD PTR [rdi]
.Continue:
48 83 C7 08 add rdi, 8
FF CE dec esi
75 ED jne .IterateDigits
29 D0 sub eax, edx
99 cdq
31 D0 xor eax, edx
29 D0 sub eax, edx
C3 ret