#include <stdio.h> #include <iostream> #include <i...
Создано: 28 мая 2025 г.
Создано: 28 мая 2025 г.
#include <stdio.h>
#include <iostream>
#include <iomanip>
using namespace std;
#define N 16
char A1[N] = {10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 65, 70, 75, 80, 85};
char B1[N] = {5, 10, 15, 20, 12, 17, 22, 27, 32, 37, 42, 47, 52, 57, 62, 67};
char A2[N] = {1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 21, 23, 25, 27, 29, 31};
char B2[N] = {0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30};
char A3[N] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16};
char B3[N] = {0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30};
char A4[N] = {10, 13, 16, 18, 21, 24, 30, 35, 40, 45, 50, 55, 60, 65, 70, 75};
char B4[N] = {5, 8, 11, 13, 16, 19, 25, 30, 35, 40, 45, 50, 55, 60, 65, 70};
char A5[N] = {-50, -45, -40, -35, -30, -25, -20, -15, -10, -5, 0, 5, 10, 15, 20, 25};
char B5[N] = {-60, -55, -50, -45, -40, -35, -30, -25, -20, -15, -10, -5, 0, 5, 10, 15};
char A6[N] = {-128, -120, -100, -80, -60, -40, -20, 0, 20, 40, 60, 80, 100, 120, 125, 127};
char B6[N] = {-127, -119, -99, -79, -59, -39, -19, 1, 21, 41, 61, 81, 101, 121, 126, 126};
char A7[N] = {100, 103, 106, 109, 112, 115, 118, 121, 124, 127, 125, 120, 110, 95, 75, 50};
char B7[N] = {50, 53, 56, 59, 62, 65, 68, 71, 74, 77, 75, 70, 60, 45, 25, 0};
char A8[N] = {5, 10, 5, 15, 10, 20, 15, 25, 20, 30, 25, 35, 30, 40, 35, 45};
char B8[N] = {0, 5, 0, 10, 5, 15, 10, 20, 15, 25, 20, 30, 25, 35, 30, 40};
short int C[30];
char pairs_count;
void processArrays(char* A, char* B, const char* testName) {
cout << "\n=== " << testName << " ===" << endl;
textcout << "A = "; for (int i = 0; i < N; i++) { cout << (int)A[i] << (i < N - 1 ? ", " : ""); } cout << endl; cout << "B = "; for (int i = 0; i < N; i++) { cout << (int)B[i] << (i < N - 1 ? ", " : ""); } cout << endl; __asm { xor ecx, ecx xor ebx, ebx mov esi, A mov edi, B main_loop: cmp ecx, N-1 jae finish mov al, [esi + ecx + 1] sub al, [esi + ecx] mov ah, al mov al, [edi + ecx + 1] sub al, [edi + ecx] cmp ah, al jne continue_loop lea eax, [esi + ecx + 1] mov C[ebx*2], ax inc ebx lea eax, [edi + ecx + 1] mov C[ebx*2], ax inc ebx continue_loop: inc ecx jmp main_loop finish: mov pairs_count, bl } cout << "Количество найденных пар: " << (int)(pairs_count/2) << endl; cout << "Адреса элементов в парах:" << endl; for (int i = 0; i < pairs_count; i += 2) { cout << "Пара " << (i/2 + 1) << ": a(" << (C[i] - (short int)A - 1) << "+1) = 0x" << hex << uppercase << C[i] << ", b(" << (C[i+1] - (short int)B - 1) << "+1) = 0x" << C[i+1] << dec << endl; }
}
int main() {
cout << "Задание A4.1 - Вариант 140" << endl;
cout << "Поиск пар где a(i+1)-a(i) = b(i+1)-b(i)" << endl;
textprocessArrays(A1, B1, "Тест 1: Произвольное количество пар (14 пар)"); processArrays(A2, B2, "Тест 2: Максимальное количество пар (15 пар)"); processArrays(A3, B3, "Тест 3: Нулевое количество пар (0 пар)"); processArrays(A4, B4, "Тест 4: Малое количество пар (1 пара)"); processArrays(A5, B5, "Тест 5: Отрицательные числа (15 пар)"); processArrays(A6, B6, "Тест 6: Граничные значения [-128;+127] (8 пар)"); processArrays(A7, B7, "Тест 7: Смешанные разности (5 пар)"); processArrays(A8, B8, "Тест 8: Переменные разности (8 пар)"); return 0;
}
Cool piece of code! 👀
Here’s a quick walk-through and a few things you might want to double-check or modernise.
For each test array pair (A?
, B?
) it compares the difference between consecutive elements:
Whenever the two differences match, it records two 16-bit values in C
:
A[i+1]
B[i+1]
After the scan it prints how many pairs it found and the stored addresses.
Area | What’s happening | Why it might bite |
---|---|---|
char signedness | Differences are computed in 8-bit registers (al , ah ). On most x86 compilers char is signed, so e.g. -128-(-127) works, but on others it’s unsigned (wrap-around). | Results change silently when you move compilers/flags/architectures. |
Storing addresses in short | C is short int C[30] . You write a 32-bit pointer (EAX ) into a 16-bit slot (mov C[ebx*2], ax ). | Fine on 16-bit real mode, but on 32-/64-bit hosts you’re truncating the upper word; the printed “address” is meaningless (and UB if it crosses a segment). |
pairs_count is a char | You count addresses not pairs in BL , then store it in pairs_count . | Works while you find ≤127 addresses, but will wrap past that. With bigger N you’d overflow. |
Hard-wired x86 inline asm | Uses MASM-style Intel syntax in MSVC. | Not portable: won’t compile with Clang/GCC on Windows, and not at all on Linux/macOS without rewriting. |
Pointer arithmetic in the print | (C[i] - (short)A - 1) assumes the shortened address is still comparable to the base of A . | Undefined once the upper bits are lost. |
cppvector<pair<int,int>> equal_diff_positions; for (int i = 0; i < N - 1; ++i) { int da = static_cast<int>(A[i+1]) - static_cast<int>(A[i]); int db = static_cast<int>(B[i+1]) - static_cast<int>(B[i]); if (da == db) equal_diff_positions.emplace_back(i + 1, i + 1); // store indices } cout << "Found " << equal_diff_positions.size() << " pair(s)\n"; for (size_t k = 0; k < equal_diff_positions.size(); ++k) cout << "Pair " << k + 1 << ": A[" << equal_diff_positions[k].first << "], B[" << equal_diff_positions[k].second << "]\n";
If you really need raw addresses, store them in uintptr_t
or size_t
, not short
.
Change
cppshort int C[30]; char pairs_count;
to
cppuintptr_t C[30]; // or size_t, std::uintptr_t unsigned int pairs_count = 0;
Write the full pointer:
asmmov [C + ebx*PTRSIZE], eax ; where PTRSIZE is 4 or 8
Use cmp ecx, N-2
if you later extend N
and want every last diff.
Just let me know what you’d like to dive into!
#include <stdio.h>
#include <iostream>
#include <iomanip>
using namespace std;
#define N 16
char A1[N] = {10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 65, 70, 75, 80, 85};
char B1[N] = {5, 10, 15, 20, 12, 17, 22, 27, 32, 37, 42, 47, 52, 57, 62, 67};
char A2[N] = {1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 21, 23, 25, 27, 29, 31};
char B2[N] = {0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30};
char A3[N] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16};
char B3[N] = {0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30};
char A4[N] = {10, 13, 16, 18, 21, 24, 30, 35, 40, 45, 50, 55, 60, 65, 70, 75};
char B4[N] = {5, 8, 11, 13, 16, 19, 25, 30, 35, 40, 45, 50, 55, 60, 65, 70};
char A5[N] = {-50, -45, -40, -35, -30, -25, -20, -15, -10, -5, 0, 5, 10, 15, 20, 25};
char B5[N] = {-60, -55, -50, -45, -40, -35, -30, -25, -20, -15, -10, -5, 0, 5, 10, 15};
char A6[N] = {-128, -120, -100, -80, -60, -40, -20, 0, 20, 40, 60, 80, 100, 120, 125, 127};
char B6[N] = {-127, -119, -99, -79, -59, -39, -19, 1, 21, 41, 61, 81, 101, 121, 126, 126};
char A7[N] = {100, 103, 106, 109, 112, 115, 118, 121, 124, 127, 125, 120, 110, 95, 75, 50};
char B7[N] = {50, 53, 56, 59, 62, 65, 68, 71, 74, 77, 75, 70, 60, 45, 25, 0};
char A8[N] = {5, 10, 5, 15, 10, 20, 15, 25, 20, 30, 25, 35, 30, 40, 35, 45};
char B8[N] = {0, 5, 0, 10, 5, 15, 10, 20, 15, 25, 20, 30, 25, 35, 30, 40};
short int C[30];
char pairs_count;
void processArrays(char* A, char* B, const char* testName) {
cout << "\n=== " << testName << " ===" << endl;
textcout << "A = "; for (int i = 0; i < N; i++) { cout << (int)A[i] << (i < N - 1 ? ", " : ""); } cout << endl; cout << "B = "; for (int i = 0; i < N; i++) { cout << (int)B[i] << (i < N - 1 ? ", " : ""); } cout << endl; __asm { xor ecx, ecx xor ebx, ebx mov esi, A mov edi, B main_loop: cmp ecx, N-1 jae finish mov al, [esi + ecx + 1] sub al, [esi + ecx] mov ah, al mov al, [edi + ecx + 1] sub al, [edi + ecx] cmp ah, al jne continue_loop lea eax, [esi + ecx + 1] mov C[ebx*2], ax inc ebx lea eax, [edi + ecx + 1] mov C[ebx*2], ax inc ebx continue_loop: inc ecx jmp main_loop finish: mov pairs_count, bl } cout << "Количество найденных пар: " << (int)(pairs_count/2) << endl; cout << "Адреса элементов в парах:" << endl; for (int i = 0; i < pairs_count; i += 2) { cout << "Пара " << (i/2 + 1) << ": a(" << (C[i] - (short int)A - 1) << "+1) = 0x" << hex << uppercase << C[i] << ", b(" << (C[i+1] - (short int)B - 1) << "+1) = 0x" << C[i+1] << dec << endl; }
}
int main() {
cout << "Задание A4.1 - Вариант 140" << endl;
cout << "Поиск пар где a(i+1)-a(i) = b(i+1)-b(i)" << endl;
textprocessArrays(A1, B1, "Тест 1: Произвольное количество пар (14 пар)"); processArrays(A2, B2, "Тест 2: Максимальное количество пар (15 пар)"); processArrays(A3, B3, "Тест 3: Нулевое количество пар (0 пар)"); processArrays(A4, B4, "Тест 4: Малое количество пар (1 пара)"); processArrays(A5, B5, "Тест 5: Отрицательные числа (15 пар)"); processArrays(A6, B6, "Тест 6: Граничные значения [-128;+127] (8 пар)"); processArrays(A7, B7, "Тест 7: Смешанные разности (5 пар)"); processArrays(A8, B8, "Тест 8: Переменные разности (8 пар)"); return 0;
}обьясни как работает программа
Программа — это учебный пример на C++ с встроенным ассемблером (x86, синтаксис MASM).
Она ищет позиции i
, для которых разности соседних элементов двух массивов совпадают:
A[i+1]
и B[i+1]
заносятся в массив C
.Для проверки программа запускает восемь «тестов» — разных пар массивов A/B.
Элемент | Назначение |
---|---|
#define N 16 | размер всех тестовых массивов |
A1…A8 , B1…B8 | тестовые данные |
short int C[30] | буфер для адресов (каждый адрес обрезается до 16 бит) |
char pairs_count | фактическое количество записанных адресов (а не пар!) |
processArrays() | функция-обёртка: печатает входные данные, выполняет ассемблер, выводит результат |
main() | последовательно вызывает processArrays для 8 тестов |
processArrays
cppvoid processArrays(char* A, char* B, const char* testName)
Печать входных массивов
Обычный C++-цикл по i = 0…15
.
Встроенный ассемблер
Регистр | Смысл |
---|---|
ECX | индекс i |
EBX | счётчик записанных адресов |
ESI | база массива A |
EDI | база массива B |
asmxor ecx, ecx ; i = 0 xor ebx, ebx ; count = 0 mov esi, A ; ESI = &A[0] mov edi, B ; EDI = &B[0]
main_loop
asmcmp ecx, N-1 ; пока i <= 14 (последняя допустимая разница) jae finish
ΔA = A[i+1] − A[i]
asmmov al, [esi + ecx + 1] sub al, [esi + ecx] mov ah, al ; AH = ΔA
ΔB = B[i+1] − B[i]
asmmov al, [edi + ecx + 1] sub al, [edi + ecx] ; AL = ΔB
Сравнение разностей
asmcmp ah, al jne continue_loop
Если равны, выполняем «запись»:
asmlea eax, [esi + ecx + 1] ; адрес A[i+1] mov C[ebx*2], ax ; кладём младшие 16 бит inc ebx ; ++count lea eax, [edi + ecx + 1] ; адрес B[i+1] mov C[ebx*2], ax inc ebx
Инкремент индекса
asmcontinue_loop: inc ecx jmp main_loop
asmfinish: mov pairs_count, bl ; BL = count (число адресов)
Печать результата
cppcout << "Количество найденных пар: " << (int)(pairs_count / 2) << endl;
Затем цикл по двум адресам на каждую пару:
cppfor (int i = 0; i < pairs_count; i += 2) { // вычисляем индекс как (адрес — &A[0]) и печатаем в hex }
Адрес обрезается до 16 бит (mov C[ebx*2], ax
).
На 32-/64-битной ОС верхние биты теряются → «адреса» в выводе условны.
pairs_count
хранит адреса, а не пары. Поэтому в C++-части делят на 2.
char
по умолчанию подписанный → при выходе за диапазон -128…127 возможны переполнения в AL/AH
.
Программа собирается только MSVC (Intel-синтаксис). Для GCC/Clang потребовался бы AT&T-синтаксис или asm volatile
.
A
— арифм. прогрессия +1
, B
— +2
⇒ разности никогда не равны ⇒ 0 пар.uintptr_t
?Дайте знать, и разберём!
обьясни как в предыдущей программе адреса найденных пар попадают в массив С
asmxor ecx, ecx ; ECX = i (индекс текущей разности) xor ebx, ebx ; EBX = k (сколько 16‑битных слов уже занесли в C) mov esi, A ; ESI = &A[0] (база массива A) mov edi, B ; EDI = &B[0] (база массива B)
EBX
будет использоваться как сдвиг в байтах внутри C
: каждое слово занимает 2 байта, значит адрес ячейки C[k]
задаётся как C[ebx*2]
.
Когда условие
asmcmp ah, al ; ΔA == ΔB ? jne continue_loop
выполняется (разности равны), нужно сохранить адреса элементов A[i+1]
и B[i+1]
.
asmlea eax, [esi + ecx + 1] ; EAX = &A[i+1]
ESI
— база A
ECX
— текущий индекс i
+ 1
» — смещение к следующему элементуLEA
помещает полный 32‑битный указатель в EAX
.
asmmov C[ebx*2], ax ; AX → C[k] inc ebx ; k++
C
объявлен как short int C[30]
, то есть массив 16‑битных слов.mov …, ax
берёт только младшие 16 бит регистра EAX
; старшие обрезаются.C + (EBX*2)
— автоматическое x86‑масштабирование.asmlea eax, [edi + ecx + 1] ; EAX = &B[i+1] mov C[ebx*2], ax ; AX → C[k] inc ebx ; k++
Теперь у нас в C
подряд лежат:
textC[k-2] = low16(&A[i+1]) C[k-1] = low16(&B[i+1])
asmmov pairs_count, bl ; BL содержит k — количество записанных слов
Каждая пара адресов занимает два слова, поэтому в C++‑части выводится pairs_count / 2
.
LEA
формирует 32/64‑битный указатель на нужный элемент.MOV …, ax
кладёт младшие 16 бит этого указателя в очередную ячейку C
.EBX
после каждой записи увеличивается, поэтому следующая запись идёт в следующий слот.⚠️ На современных 32‑/64‑битных системах это приводит к усечению адресов: в
C
хранится лишь половина реального указателя. Чтобы получить полноценные адреса, нужно объявитьC
какuintptr_t C[30]
и писать целикомmov [C + ebx*PTR], eax
, гдеPTR
— 4 или 8.
inc ebx что делает функция
inc ebx
— это одно машинное-инструкция, а не функция.
Она просто увеличивает содержимое регистра EBX на единицу.
EBX = EBX + 1
.INC
.В контексте вашей программы EBX служит счётчиком «сколько 16-битных слов уже занесли в массив C».
Каждый раз, когда адрес очередного элемента записан:
asmmov C[ebx*2], ax ; кладём адрес в C[k] inc ebx ; k++ → следующая запись пойдёт в C[k+1]
Поскольку адрес ячейки вычисляется как C + ebx*2
, инкремент EBX переносит указатель вперёд ровно на один short
(2 байта), позволяя последовательно заполнять массив.
как адрес перемещается в массив С
C
Ниже речь идёт о двух инструкциях, которые выполняются для каждого совпадения
A[i + 1] – A[i] == B[i + 1] – B[i]
.
asmlea eax, [esi + ecx + 1] ; ❶ вычислить адрес A[i+1] mov C[ebx*2], ax ; ❷ сохранить младшие 16 бит адреса в C[k] inc ebx ; ❸ перейти к следующей ячейке C
№ | Что делает‑ся | Подробно |
---|---|---|
❶ lea eax, [esi + ecx + 1] | Формирует полный 32‑битный указатель на A[i+1] и кладёт его в EAX . <br/>* ESI — база массива A (&A[0] ) <br/>* ECX — текущий индекс i | |
❷ mov C[ebx*2], ax | Кладёт только младшее 16‑битное слово регистра EAX (то есть AX ) в память по адресу &C[0] + EBX*2 . <br/>Почему *2 ? Потому что C — массив short ‑ов (2 байта каждый). | |
❸ inc ebx | Увеличивает счётчик EBX (число 16‑битных слов, уже записанных). Следующая запись пойдёт в следующую ячейку C . |
После этого блок повторяется для массива B
(вторая пара инструкций lea / mov / inc
), поэтому за один проход цикла в C
подряд кладутся два слова:
textC[k] = low16(&A[i+1]) C[k+1] = low16(&B[i+1])
где k
— значение EBX
до первого mov
.
Предположим, что:
C
находится по адресу 0x1000
EBX = 4
(уже записано 4 слова = адреса двух предыдущих пар)Тогда:
C[ebx*2]
адресуется как 0x1000 + 4*2 = 0x1008
&A[i+1]
.inc ebx
→ EBX = 5
C[ebx*2]
теперь 0x1000 + 5*2 = 0x100A
&B[i+1]
.Так как в C
сохраняется только половина указателя (AX
, 16 бит), на 32‑/64‑разрядных системах эта информация пригодна лишь для учебных целей (она не содержит старших разрядов реального адреса). Чтобы хранить полноценные адреса, нужно объявить
cppuintptr_t C[30];
и записывать целиком EAX
/RAX
.
сколько байт в памяти занимает один элемент массива С
C
объявлен так:
cppshort int C[30];
Тип short int
— это знаковое целое короткой длины.
Стандарт C/C++ гарантирует лишь, что его размер не меньше 16 бит, но на всех «обычных» x86-32 и x86-64 компиляторах (gcc
, clang
, MSVC) он фиксирован и равен 2 байтам.
Это подтверждается и кодом на ассемблере:
asmmov C[ebx*2], ax ; *2 — каждое значение занимает 2 байта
Таким образом один элемент массива C
занимает 2 байта памяти
(в редких экзотических ABI он мог бы быть 3 или 4 байта, но на практических современных платформах — ровно 2).
сколько байт занимает переменная типа int
В C и C++ размер int
не фиксирован стандартом — он определяется реализацией компилятора и целевой архитектурой. Стандарт лишь требует, чтобы:
sizeof(char) == 1
sizeof(short) ≤ sizeof(int) ≤ sizeof(long)
и чтобы диапазон int
охватывал как минимум –32 767…+32 767 (т.е. минимум 16 бит).
Платформа / ABI | Модель данных | sizeof(int) |
---|---|---|
x86‑32 Windows, Linux, macOS | ILP32 (int/long/ptr — 32 бит) | 4 байта |
x86‑64 Windows | LLP64 (int — 32 бит, long — 32, ptr — 64) | 4 байта |
x86‑64 Linux, macOS | LP64 (int — 32 бит, long/ptr — 64) | 4 байта |
8‑/16‑битные микроконтроллеры (AVR, MSP430 …) | часто 16 бит | 2 байта |
Некоторые DSP / старые DOS‑компиляторы | 16 бит | 2 байта |
Поэтому на «обычных» настольных и серверных системах последних десятилетий int
практически всегда занимает 4 байта. На встраиваемых 8‑/16‑битных платформах может быть 2 байта; на экспериментальных 128‑битных ABI‑вариантах теоретически мог бы быть и 8 байт.
В собственном коде всегда проверяйте:
cppstd::cout << sizeof(int) << " bytes\n";
или
cprintf("%zu\n", sizeof(int));
Так вы получите реальный размер для конкретного компилятора и цели сборки.
#include <stdio.h>
#include <iostream>
#include <iomanip>
using namespace std;
#define N 16
short int A1[N] = {10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 65, 70, 75, 80, 85};
short int B1[N] = {5, 10, 15, 20, 12, 17, 22, 27, 32, 37, 42, 47, 52, 57, 62, 67};
short int A2[N] = {1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 21, 23, 25, 27, 29, 31};
short int B2[N] = {0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30};
short int A3[N] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16};
short int B3[N] = {0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30};
short int A4[N] = {100, 103, 106, 108, 111, 114, 120, 125, 130, 135, 140, 145, 150, 155, 160, 165};
short int B4[N] = {50, 53, 56, 58, 61, 64, 70, 75, 80, 85, 90, 95, 100, 105, 110, 115};
short int A5[N] = {-500, -450, -400, -350, -300, -250, -200, -150, -100, -50, 0, 50, 100, 150, 200, 250};
short int B5[N] = {-600, -550, -500, -450, -400, -350, -300, -250, -200, -150, -100, -50, 0, 50, 100, 150};
short int A6[N] = {-1000, -800, -600, -400, -200, 0, 200, 400, 600, 800, 1000, 1200, 1400, 1600, 1800, 2000};
short int B6[N] = {-999, -799, -599, -399, -199, 1, 201, 401, 601, 801, 1001, 1201, 1401, 1601, 1801, 2001};
short int A7[N] = {1000, 1030, 1060, 1090, 1120, 1150, 1180, 1210, 1240, 1270, 1250, 1200, 1100, 950, 750, 500};
short int B7[N] = {500, 530, 560, 590, 620, 650, 680, 710, 740, 770, 750, 700, 600, 450, 250, 0};
short int A8[N] = {50, 100, 50, 150, 100, 200, 150, 250, 200, 300, 250, 350, 300, 400, 350, 450};
short int B8[N] = {0, 50, 0, 100, 50, 150, 100, 200, 150, 250, 200, 300, 250, 350, 300, 400};
int ResultArray[30];
short int totalPairs;
void processArrays(short int* A, short int* B, const char* testName) {
cout << "\n=== " << testName << " ===" << endl;
textcout << "A = "; for (int i = 0; i < N; i++) { cout << A[i] << (i < N - 1 ? ", " : ""); } cout << endl; cout << "B = "; for (int i = 0; i < N; i++) { cout << B[i] << (i < N - 1 ? ", " : ""); } cout << endl; _asm { xor ecx, ecx xor ebx, ebx mov esi, A mov edi, B processing_loop: cmp ecx, N-1 jae end_processing mov eax, ecx shl eax, 1 mov dx, word ptr [esi + eax + 2] sub dx, word ptr [esi + eax] mov ax, word ptr [edi + ecx*2 + 2] sub ax, word ptr [edi + ecx*2] cmp dx, ax jne skip_this_pair mov eax, ecx shl eax, 1 add eax, esi add eax, 2 mov dword ptr ResultArray[ebx*4], eax inc ebx mov eax, ecx shl eax, 1 add eax, edi add eax, 2 mov dword ptr ResultArray[ebx*4], eax inc ebx skip_this_pair: inc ecx jmp processing_loop end_processing: mov totalPairs, bx } cout << "Количество найденных пар: " << (int)(totalPairs/2) << endl; cout << "Адреса элементов в парах:" << endl; for (int i = 0; i < totalPairs; i += 2) { cout << "Пара " << (i/2 + 1) << ": a(" << ((ResultArray[i] - (int)A)/2 - 1) << "+1) = 0x" << hex << uppercase << ResultArray[i] << ", b(" << ((ResultArray[i+1] - (int)B)/2 - 1) << "+1) = 0x" << ResultArray[i+1] << dec << endl; }
}
int main() {
cout << "Задание A4.2 - Вариант 140 (расширенные массивы)" << endl;
cout << "Поиск пар где a(i+1)-a(i) = b(i+1)-b(i)" << endl;
cout << "Байтовые массивы преобразованы в массивы слов (short int)" << endl;
textprocessArrays(A1, B1, "Тест 1: Произвольное количество пар (14 пар)"); processArrays(A2, B2, "Тест 2: Максимальное количество пар (15 пар)"); processArrays(A3, B3, "Тест 3: Нулевое количество пар (0 пар)"); processArrays(A4, B4, "Тест 4: Малое количество пар (1 пара)"); processArrays(A5, B5, "Тест 5: Отрицательные числа (15 пар)"); processArrays(A6, B6, "Тест 6: Большие значения (8 пар)"); processArrays(A7, B7, "Тест 7: Смешанные разности (5 пар)"); processArrays(A8, B8, "Тест 8: Переменные разности (8 пар)"); return как в программе адреса найденных пар перемещаются в массив С
ResultArray
(бывший C
)Ниже рассмотрен фрагмент, который выполняется каждый раз, когда разности совпадают
A[i+1] − A[i] == B[i+1] − B[i]
.
asm; ---- сохранить адрес A[i+1] --------------------------------- mov eax, ecx ; EAX = i shl eax, 1 ; *2 → байтовое смещение i*2 add eax, esi ; + база A → &A[i] add eax, 2 ; +2 → &A[i+1] mov dword ptr ResultArray[ebx*4], eax ; записать адрес inc ebx ; счётчик ++ ; ---- сохранить адрес B[i+1] --------------------------------- mov eax, ecx shl eax, 1 add eax, edi ; + база B → &B[i] add eax, 2 ; +2 → &B[i+1] mov dword ptr ResultArray[ebx*4], eax inc ebx
Шаг | Команды | Результат |
---|---|---|
1 | mov eax, ecx <br>shl eax, 1 | Преобразуем индекс i в байтовое смещение. Элементы short занимают 2 байта, поэтому i * 2 = i << 1 . |
2 | add eax, esi | Прибавляем базу массива A (ESI = &A[0] ). Получаем адрес &A[i] . |
3 | add eax, 2 | Смещаемся ещё на один элемент → &A[i+1] . |
4 | mov dword ptr ResultArray[ebx*4], eax | Кладём полный 32-битный адрес в ResultArray[k] , где k = EBX . <br>Множитель *4 используется потому, что ResultArray — массив int (4 байта). |
5 | inc ebx | Увеличиваем счётчик записанных адресов, чтобы следующая запись попала в следующую ячейку. |
6 | Повторяем шаги 1–5 для массива B , но вместо ESI берём EDI (базу B ). |
textResultArray[k] = &A[i+1] (k == EBX до первой записи) ResultArray[k+1] = &B[i+1] (k+1 == EBX до второй записи)
EBX
увеличивается на 2, поэтому при следующем совпадении данные запишутся далее по массиву.
asmmov totalPairs, bx ; BX = количество ЗАПИСАННЫХ адресов
Каждая пара даёт два адреса, поэтому при выводе в C++ коде используется totalPairs / 2
.
Было (байтовые массивы) | Стало (массивы short ) |
---|---|
mov C[ebx*2], ax — хранили 16 бит адреса | mov ResultArray[ebx*4], eax — храним полные 32 бита |
Смещение считали как i (1 байт на элемент) | Смещение считаем как i << 1 (2 байта на элемент) |
Адрес A[i+1] = ESI + ECX + 1 | Адрес A[i+1] = ESI + (ECX<<1) + 2 |
Теперь адреса не усекаются, и массив-результат годится для реального употребления на 32-битной платформе (на x86-64 можно заменить int
на intptr_t
, чтобы сохранить 64-битный указатель целиком).
бобьясни понятнее с помощью какой функции адрес найденного элемента перемещается в массив С
mov
asmmov dword ptr ResultArray[ebx*4], eax
eax
– здесь уже лежит вычисленный указатель &A[i+1]
или &B[i+1]
.ResultArray[ebx*4]
– ячейка результата, куда нужно положить адрес.ebx
— счётчик «сколько значений уже записали»;*4
берётся автоматически, потому что ResultArray
состоит из int
‑ов (4 байта).Инструкция mov
просто копирует 4‑байтовое содержимое регистра в память:
память[ ResultArray + ebx*4 ] ← eax
lea / add
формируют нужный адрес в eax
&A[i+1] = esi + (ecx<<1) + 2
).mov
кладёт этот адрес в ResultArray
.inc ebx
увеличивает счётчик, чтобы следующая запись пошла в следующую ячейку.Никакой отдельной «C‑функции» здесь нет — всё делает одна инструкция mov
, а «инкремент» ebx
обеспечивает последовательное заполнение массива.
почему происходит умножение на 4 если массив задан из слов( 2 байта) а массив указателей состоит из двойных слов 4 байта
ResultArray[ebx*4]
Массив | Тип элемента | Размер одного элемента |
---|---|---|
A , B | short int | 2 байта |
ResultArray | int (сюда кладём адреса) | 4 байта на 32‑битной сборке |
Для данных
asmmov eax, ecx ; eax = i shl eax, 1 ; i * 2 -> каждое A[i] занимает 2 байта add eax, esi ; адрес &A[i]
Потому что A
/B
состоят из short
‑ов (word, 2 байта).
Для массива‑результата
asmmov dword ptr ResultArray[ebx*4], eax
Здесь ebx
— это логический индекс k в ResultArray
, а контроллер адресации x86 автоматически умножает его на 4 (двоичное смещение scale = 4
), потому что каждый int
занимает 4 байта.
ResultArray
= 4 байта, а не 2Мы решили хранить полные 32‑битные указатели &A[i+1]
и &B[i+1]
.
Указатель в 32‑разрядной программе занимает 4 байта, поэтому:
cppint ResultArray[30]; // по 4 байта каждый
Раньше, в примере с
char
, мы клали только младшие 16 бит адреса, и массив былshort C[30]
, тогда масштаб был*2
.
intptr_t ResultArray[30];
ResultArray[rbx*8]
(масштаб 8).Коротко: множитель 4 нужен не потому, что исходные массивы содержат «слова», а потому, что массив, куда мы пишем адреса, состоит из двойных слов (dword, 4 байта) — одного на каждый 32‑битный указатель.
почему происходит умножение на 4 если массив задан из слов( 2 байта) а массив указателей состоит из двойных слов 4 байта при 32-битной сборке
* 4
, если в исходных массивах элементы по 2 байта?Разберёмся, какие два разных массивa фигурируют в программе:
Массив | Тип элемента | Размер элемента | Что в нём лежит |
---|---|---|---|
A , B | short int | 2 байта | сами данные, по условию задачи |
ResultArray | int | 4 байта (в 32-битной программе) | адреса найденных элементов |
A[i]
и B[i]
Чтобы обратиться к элементу № i в массиве short int
, нужно перевести индекс в байтовое смещение:
смещение = i * sizeof(short) = i * 2
В ассемблере это сделано так:
asmmov eax, ecx ; eax = i shl eax, 1 ; i * 2 ← т.к. sizeof(short) = 2 add eax, esi ; &A[i]
ResultArray[k]
ResultArray
хранит полные 32-битные указатели, то есть каждый его элемент занимает sizeof(int) = 4
байта.
Поэтому, чтобы положить очередной адрес в элемент № k, надо взять базу массива и прибавить k * 4
:
asmmov dword ptr ResultArray[ebx*4], eax
ebx
— счётчик уже записанных адресов (0, 1, 2 …);*4
— автоматическое умножение индекса на размер элемента (4 байта).Если бы мы оставили
*2
, пришлось бы инкрементироватьebx
не на 1, а на 2, чтобы каждый новый адрес уходил на 4 байта вперёд. Использовать масштаб 4 просто удобнее — счётчик растёт на единицу за каждую запись.
В 32-битной среде любой указатель (&A[i]
, &B[i]
) — это 32-битное целое, то есть 4 байта. Поэтому логично объявить
cppint ResultArray[30]; // или uintptr_t ResultArray[30];
и обращаться к нему со смещением 4.
Итого
A
и B
умножаем индекс на 2, потому что элемент 2 байта.ResultArray
умножаем индекс на 4, потому что элемент 4 байта.*1
, *2
, *4
, *8
) задаёт ассемблеру, во сколько байт превращать индекс, — это всё, что означает «умножение» в квадратных скобках.