Возможно вы искали: 'World of Kung Fu'

June 04 2025 21:53:51
  • Как сделать 8Gamers.Ru домашней страницей?
  • Игры
    • База данных по играх
    • Игровые новости
    • Игровая индустрия
    • Обзоры на игры
    • Прохождения игр
    • Гайды к играм
    • Превью о играх
    • Игровые тизеры
    • Игровые арты
    • Игровые обои
    • Игровые скриншоты
    • Игровые обложки
    • Игровые трейлеры
    • Игровое видео
    • Вышедшие игры
    • Ближайшие релизы игр
  • Кино и ТВ
    • База данных по кино
    • Статьи о кино
    • Постеры
    • Кадры из кино
    • Кино трейлеры
    • Сегодня в кино
    • Скоро в кино
  • Комиксы и манга
    • Манга по алфавиту
    • База данных по комиксах
    • Читать онлайн комиксы
    • Читать онлайн манга
    • База персонажей
  • Читы и коды
    • Чит-коды для PC игр
    • Чит-коды для консольных игр
    • Трейнеры
    • Коды Game Genie
  • Моддинг
    • Модификации
    • Карты к играм
    • Программы для моддинга
    • Статьи о моддинге
  • Геймдев
    • Всё о создании игр
    • Список движков
    • Утилиты в помощь игроделу
    • Конструкторы игр
    • Игровые движки
    • Библиотеки разработки
    • 3D-модели
    • Спрайты и тайлы
    • Музыка и звуки
    • Текстуры и фоны
  • Рецензии
    • Игры
    • Кино
    • Аниме
    • Комиксы
    • Мангу
    • Саундтреки
  • Саундтреки
    • Лирика
  • Файлы
    • Патчи к играм
    • Русификаторы к играм
    • Сохранения к играм
    • Субтитры к кино
  • Медиа
    • Видео
    • Фото
    • Аудио
    • Фан-арты
    • Косплей
    • Фото с виставок
    • Девушки из игр
    • Рисунки
    • Рисуем онлайн
    • Фотохостинг
  • Юмор
    • Анекдоты
    • Афоризмы
    • Истории
    • Стишки и эпиграммы
    • Тосты
    • Цитаты
  • Флеш
    • Азартные
    • Аркады
    • Бродилки
    • Гонки
    • Для девочек
    • Для мальчиков
    • Драки
    • Квесты
    • Леталки
    • Логические
    • Мультфильмы
    • Открытки
    • Приколы
    • Разное
    • Спорт
    • Стратегии
    • Стрелялки
Статистика

Статей: 87772
Просмотров: 96551654
Игры
Injustice:  Gods Among Us
Injustice: Gods Among Us
...
Dark Souls 2
Dark Souls 2
Dark Souls II - вторая часть самой хардкорной ролевой игры 2011-2012 года, с новым героем, сюжето...
Battlefield 4
Battlefield 4
Battlefield 4 - продолжение венценосного мультиплеер-ориентированного шутера от первого ли...
Кино
Steins;Gate
Steins;Gate
Любители японской анимации уже давно поняли ,что аниме сериалы могут дать порой гораздо больше пи...
Ку! Кин-дза-дза
Ку! Кин-дза-дза
Начинающий диджей Толик и всемирно известный виолончелист Владимир Чижов встречают на шумной моск...
Обзоры на игры
• Обзор Ibara [PCB/PS2] 18421
• Обзор The Walking ... 18867
• Обзор DMC: Devil M... 19942
• Обзор на игру Valk... 15936
• Обзор на игру Stars! 17827
• Обзор на Far Cry 3 18018
• Обзор на Resident ... 16082
• Обзор на Chivalry:... 17577
• Обзор на игру Kerb... 18036
• Обзор игры 007: Fr... 16680
Превью о играх
• Превью к игре Comp... 18018
• Превью о игре Mage... 14523
• Превью Incredible ... 14778
• Превью Firefall 13542
• Превью Dead Space 3 16396
• Превью о игре SimC... 14789
• Превью к игре Fuse 15492
• Превью Red Orche... 15602
• Превью Gothic 3 16403
• Превью Black & W... 17420
Главная » Статьи » Разное » От C к Ассемблеру (gcc assembler)

От C к Ассемблеру (gcc assembler)

Ключевые слова: gcc, assembler, (найти похожие документы)

From: Андрей Киселев <kis_an at mail.ru>
Newsgroups: http://gazette.linux.ru.net
Date: Mon, 12 Jun 2005 18:21:07 +0000 (UTC)
Subject: От C к Ассемблеру

Оригинал: http://gazette.linux.ru.net/lg94/ramankutty.html

От C к Ассемблеру

Автор: Hiran Ramankutty
Перевод: Андрей Киселев


1. Краткий обзор

Что входит в состав микрокомпьютерных систем? Типичный ответ на этот
вопрос: "микропроцессор, шина, подсистема памяти, подсистема
ввода-вывода и интерфейсная часть, объединяющая все компоненты
воедино".

Но это справедливо только для аппаратной составляющей. Любая
микрокомпьютерная система требует наличия программного обеспечения
(ПО), которое будет управлять аппаратурой. Программное обеспечение, в
свою очередь, можно разделить на системное программное обеспечение и
прикладное.

В состав прикладного ПО могут входить разного рода библиотеки в виде
наборов подпрограмм, необходимых для работы программ.

К числу системного ПО можно отнести трансляторы с языков
программирования высокого уровня, ассемблеры, текстовые редакторы и
разного рода программы, которые несут вспомогательные функции при
создании других программ. Нам уже известны три уровня языков
программирования -- это машинный язык, Ассемблер и высокоуровневые
языки программирования.

Программы на машинном языке -- это такие программы, которые
микропроцессор "понимает" без дополнительной обработки. Язык
ассемблера состоит из набора инструкций, каждая из которых практически
один-в-один соответствует своей машинной команде. Ассемблерные
инструкции записываются в виде мнемоник, которые ближе к человеческому
языку, чем машинные коды и потому более просты для понимания
человеком. Языки высокого уровня очень близки к естественному
английскому языку, а их структурированность позволяет программисту
легко и просто излагать свои мысли. Однако, независимо от того на
каком языке была написана программа, будь то ассемблер или
высокоуровневый язык, ее необходимо преобразовать в машинный код с
помощью программы, которая называется транслятором. Чаще их называют
ассемблер и компилятор, или интерпретатор соответственно.

Компиляторы высокоуровневых языков, подобных C/C++, имеют возможность
трансляции программы с языка высокого уровня на язык ассемблера. GNU
C/C++ Compiler, вызванный с ключом -S, сгенерирует ассемблерный код,
эквивалентный тексту исходной программы на языке C/C++. Знакомство с
тем, как элементарные конструкции, такие как циклы, вызовы функций,
объявления переменных выглядят на языке ассемблера позволит вам
достичь высочайших вершин мастерства. Прежде чем я продолжу, хочу
заметить, что знание архитектуры Intel x86, позволит вам лучше понять
материал, излагаемый ниже.


2. Начало

Для начала напишите небольшую программу на языке C, которая выводит на
экран сообщение hello world и скомпилируйте ее с ключом -S. В
результате вы получите файл с ассемблерным кодом, соответствующим
исходной программе. По-умолчанию GCC создает файл с ассемблерным кодом
с тем же именем, что и исходный файл, заменяя расширение `.c' на `.s'.
Попробуйте интерпретировать несколько строк в полученном файле.

Микропроцессоры Intel 80386 и выше имеют огромное число регистровых
инструкций и режимов адресации. Тем не менее, начальных сведений о
нескольких основных инструкциях будет вполне достаточно, чтобы
разобраться в коде, генерируемом компилятором.

Как правило, инструкция на языке ассемблера включает в себя метку,
мнемонику и операнды. Способ записи операндов однозначно определяет
способ их адресации. Мнемоника определяет операцию, выполняемую над
операндами. Фактически все ассемблерные инструкции оперируют с
регистрами и ячейками памяти. Процессоры Intel 80386 и выше имеют ряд
(32 битных) регистров общего назначения: eax, ebx, ecx и пр.. Два
регистра для работы со стеком: ebp и esp. Типичная инструкция,
записанная с использованием синтаксиса GNU Assembler Syntax (GAS),
выглядит так:

movl $10, %eax


Эта инструкция записывает число 10 в регистр eax. Префиксы `%', перед
именем регистра, и `$', перед числом, обязательны ибо того требует
синтаксис ассемблера. Следует отметить, что не все ассемблеры
придерживаются одинакового синтаксиса.

Напишем нашу первую программу на языке ассемблера и сохраним ее в файл
first.s. Текст программы приводится ниже.

#Листинг 1
.globl main
main:
movl $20, %eax
ret


Эту программу можно ассемблировать и слинковать в исполняемый модуль
a.out, если дать команду cc first.s. Расширение имени файла `.s'
помогает компилятору идентифицировать язык, на котором написана
программа, в результате cc вызывает ассемблер и линковщик, пропуская
стадию компиляции.

Первая строка программы -- это комментарий. .globl -- это директива
ассемблера, она объявляет имя main глобальным и делает его доступным
для линковщика. Это совершенно необходимо, поскольку ваша ассемблерная
программа будет линковаться с библиотекой языка C, которая в свою
очередь вызывает функцию main. Если эту строку убрать, то линковщик
выразит свое неудовольствие сообщением 'undefined reference to symbol
main' (ссылка на неопределенный символ main). Эта программа просто
записывает число 20 в регистр eax и возвращает управление операционной
системе.


3. Арифметические операции, операции сравнения и циклы

Следующая наша программа вычисляет факториал числа, находящегося в
регистре eax. Результат вычислений сохраняется в регистре ebx.

#Листинг 2
.globl main
main:
movl $5, %eax
movl $1, %ebx
L1: cmpl $0, %eax // сравнить содержимое регистра eax с 0
je L2 // переход на L2 если 0==eax (je - jump if equa
l, перейти если равно)
imull %eax, %ebx // ebx = ebx*eax
decl %eax // decrement eax (уменьшить на 1)
jmp L1 // безусловный переход на L1
L2: ret


Здесь L1 и L2 -- это метки. Когда программа приходит в точку L2, ebx
содержит факториал числа, находящегося в регистре eax.


4. Подпрограммы

При создании сложной программы мы обычно разбиваем ее на простые
подзадачи. Для каждой из таких подзадач пишется своя подпрограмма или
функция. После этого мы можем вызывать ту или иную функцию по мере
необходимости. В Листинге 3 приводится пример создания и вызова
подпрограмм на языке ассемблера.

#Листинг 3
.globl main
main:
movl $10, %eax
call foo
ret
foo:
addl $5, %eax
ret


Здесь инструкция call передает управление подпрограмме foo. Инструкция
ret, в подпрограмме foo, передает управление обратно, следующей
инструкции, расположенной за инструкцией call.

Как правило, функции определяют свои наборы локальных переменных и
входных аргументов, используемые при каждом вызове. Для этих
переменных необходимо выделять области памяти, обычно для этих целей
используется стек. Очень важно понимать основные принципы выделения
места на стеке под локальные переменные и аргументы, как эти
переменные инициализируются и как они используются при повторном,
рекурсивном или ином способе вызова функции, в ходе выполнения
программы. Работа с регистрами esp/ebp и использование инструкций
работы со стеком push/pop, является центральным аспектом, знание
которого обязательно для понимания механизма вызова подпрограмм и
возврата в точку вызова.


5. Работа со стеком

Часть памяти, выделяемой программе системой, резервируется под стек.
Процессоры Intel 80386 и выше имеют в своем распоряжении регистр esp
(от англ. stack pointer -- указатель вершины стека), в котором
хранится адрес текущей вершины стека. На рисунке 1 показано содержимое
стека, на который положено три целых числа 49,30 и 72 (каждое целое
число занимает 4 байта), и значение регистра esp, который хранит адрес
текущей вершины стека.
Рисунок 1 (http://gazette.linux.ru.net/lg94/misc/ramankutty/stack1.bmp)

Один необычный момент, связанный со стеком для архитектуры Intel,
заключается в том, что стек растет "вниз", т.е. от старших адресов --
к младшим. На рисунке 2 показан стек после выполнения инструкции pushl
$15.
Рисунок 2 (http://gazette.linux.ru.net/lg94/misc/ramankutty/stack2.bmp)

Содержимое регистра esp уменьшилось на 4, а на вершину стека было
помещено число 15, которое разместилось по четырем ячейкам памяти с
адресами 1988, 1989, 1990 и 1991.

Инструкция popl %eax скопирует число с вершины стека (четыре байта) в
регистр eax и увеличит содержимое регистра esp на четыре. А что если
вам нужно просто "выбросить" число с вершины стека, никуда его не
копируя? Для этого можно просто выполнить инструкцию addl $4, %esp,
которая просто увеличит содержимое регистра указателя стека на четыре.

В Листинге 3, инструкция call foo помещает адрес возврата из
подпрограммы на стек и передает управление на метку foo. Подпрограмма
завершается инструкцией ret, которая снимает с вершины стека адрес
возврата и передает управление по этому адресу. Совершенно очевидно,
что на вершине стека должен лежать корректный адрес возврата.


6. Выделение места на стеке под локальные переменные

Программы на языке C могут включать в себя сотни и тысячи переменных.
Ассемблерный листинг, эквивалентный программе на языке C, поможет вам
понять как эти переменные размещаются и как используются регистры
процессора, при работе с переменными.

Количества регистров в процессоре не так много и они не могут быть
использованы для хранения всех переменных, имеющихся в программе.
Локальные переменные размещаются на стеке. Листинг 4 демонстрирует --
как это делается.

#Листинг 4
.globl main
main:
call foo
ret
foo:
pushl %ebp
movl %esp, %ebp
subl $4, %esp
movl $10, -4(%ebp)
movl %ebp, %esp
popl %ebp
ret


Прежде всего содержимое указателя стека копируется в регистр ebp, ( от
англ. base pointer -- указатель основания, или базы). Этот регистр
служит своего рода реперной точкой, относительно которой вычисляются
адреса локальных переменных, размещенных на стеке. Регистр ebp может
использоваться вызывающей программой, поэтому функция foo прежде всего
сохраняет его содержимое на стеке. Инструкция subl $4, %esp
резервирует место на стеке (четыре байта) для хранения целого числа. В
следующей строке выполняется копирование числа 10 (четыре байта) по
адресу на 4 меньшем, чем содержимое базового регистра ebp. Инструкция
movl %ebp, %esp восстанавливает содержимое указателя стека, которое
имелось после выполнения первой инструкции подпрограммы foo и
инструкцией popl %ebp восстанавливает содержимое регистра ebp. Теперь
указатель стека имеет то же значение, что и перед исполнением первой
инструкции подпрограммы foo. В таблице ниже показано как изменялось
содержимое стека и регистров ebp и esp, начиная с точки входа в
функцию main и после выполнения каждой инструкции в программе из
Листинга 4 (исключая момент выхода из функции main). Из таблицы видно,
что в точке входа в функцию main, в регистрах ebp и esp находились
значения 7000 и 4000, а на стеке, по адресам с 3988 по 3999,
находились некоторые значения -- 219986, 1265789 и 86. Так же видно,
что инструкция call foo положила на стек адрес возврата из
подпрограммы -- 30000.
Таблица 1 (http://gazette.linux.ru.net/lg94/misc/ramankutty/table.bmp)


7. Входные аргументы и возвращаемые значения

Стек может использоваться для передачи в подпрограмму значений входных
аргументов. Будем придерживаться соглашений, принятых в языке C, при
передаче входных параметров в подпрограмму. В соответствии с которыми,
регистр eax служит для возврата результата в вызывающую программу, а
входные аргументы передаются вызываемой подпрограмме через стек.
Листинг 5 демонстрирует вызов простой функции sqr, которая принимает
один входной аргумент.

#Listing 5
.globl main
main:
movl $12, %ebx
pushl %ebx
call sqr
addl $4, %esp // вернуть содержимое esp в состояние, предшествующ
// ее выполнению инструкции push
ret
sqr:
movl 4(%esp), %eax
imull %eax, %eax // найти произведение eax * eax, результат остается
в eax
ret


Внимательно прочитайте первую строку подпрограммы sqr. Вызывающая
программа поместила на стек содержимое регистра ebx и затем выполнила
инструкцию call. Инструкция call положила на стек адрес возврата.
Таким образом, на входе в подпрограмму, входной аргумент оказался на
стеке, по адресу, на четыре байта выше текущей вершины стека.


8. Комбинирование программ на языке C и ассемблере

В Листинге 6 представлены программа на языке C и модуль функции,
написанной на языке ассемблера. Программа на языке C находится в файле
main.c, а ассемблерный модуль с текстом функции -- в файле sqr.s.
Чтобы скомпилировать и слинковать такую программу дайте команду cc
main.c sqr.s.

#Listing 6
//main.c
main()
{
int i = sqr(11);
printf("%dn",i);
}

//sqr.s
.globl sqr
sqr:
movl 4(%esp), %eax
imull %eax, %eax
ret


Обратный вариант (вызов C-шной функции из ассемблера) не менее прост и
понятен. В Листинге 7 демонстрируется возможность вызова функции,
написанной на языке C, из программы, написанной на языке ассемблера.

#Listing 7
//print.c
print(int i)
{
printf("%dn",i);
}

//main.s
.globl main
main:
movl $123, %eax
pushl %eax
call print
addl $4, %esp
ret


9. Ассемблерный код, генерируемый компилятором GNU C

Я полагаю, что прочитанного вами в этой статье уже достаточно для
понимания содержимого ассемблерных листингов, которые создает gcc. В
Листинге 8 приведено содержимое файла add.s, полученного в результате
выполнения команды gcc -S add.c. Обратите внимание: в демонстрационных
целях я удалил из файла add.s некоторые директивы ассемблера.

#Листинг 8
//add.c
int add(int i,int j)
{
int p = i + j;
return p;
}

//add.s
.globl add
add:
pushl %ebp
movl %esp, %ebp
subl $4, %esp // выделяется место для переменной p
movl 8(%ebp),%edx // 8(%ebp) -- ссылка на переменную i
addl 12(%ebp), %edx // 12(%ebp) -- ссылка на переменную j
movl %edx, -4(%ebp) // -4(%ebp) -- ссылка на переменную p
movl -4(%ebp), %eax // возвращаемое значение помещается в регистр eax
leave // аналог комбинации инструкций movl %ebp, %esp
; popl %ebp
ret


Вызов функции add с аргументами 10 и 20, будет оттранслирован в
следующий ассемблерный код:

pushl $20
pushl $10
call add


Обратите внимание: последний аргумент помещается на стек первым.


10. Глобальные переменные

Мы уже знаем, что пространство под локальные переменные выделяется на
стеке простым уменьшением содержимого регистра esp. А как выделяется
пространство под глобальные переменные? Ответ на этот вопрос вы
найдете в Листинге 9.

#Listing 9
//glob.c
int foo = 10;
main()
{
int p = foo;
}

//glob.s
.globl foo
foo:
.long 10
.globl main
main:
pushl %ebp
movl %esp,%ebp
subl $4,%esp
movl foo,%eax
movl %eax,-4(%ebp)
leave
ret


Строка foo: .long 10 определяет блок памяти, под именем foo, размером
в 4 байта и инициализирует его начальным значением. Директива .globl
foo объявляет имя foo глобальным, что позволяет ссылаться на него из
других модулей. Теперь попробуйте заменить объявление int foo на
static int foo. Посмотрите -- как теперь будет выглядеть ассемблерный
код. Вы наверняка заметите, что исчезла директива .globl. Попробуйте
изменить тип переменной (double, long, short, const и т.п.).


11. Системные вызовы

Если программа не просто производит какие-либо арифметические
вычисления на языке ассемблера, а так же организует ввод/вывод данных
и т.п., то ей не обойтись без вызова служб операционной системы. В
действительности, если не касаться системных вызовов, то
программирование на ассемблере практически не зависит от типа
операционной системы.

Существует два общепринятых способа выполнения системных вызовов в
Linux: через библиотеку libc и напрямую.

Libc выполняет роль защитной прослойки, предохраняя приложение от
возможных ошибок на тот случай, если в ядре изменится синтаксис того
или иного системного вызова и предоставляет POSIX-совместимый
интерфейс с ядром. Однако, ядро Linux само по себе является более или
менее POSIX-совместимым, это означает, что синтаксис вызова
библиотечных функций-оберток из libc в точности совпадает с
синтаксисом реальных системных вызовов ядра (и наоборот).

Системные вызовы в Linux выполняются через прерывание int 0x80.
Соглашение о системных вызовах в Linux отличается от общепринятого в
Unix и соответствует соглашению "fastcall". Согласно ему, программа
помещает в регистр eax номер системного вызова, входные аргументы
размещаются в других регистрах процессора (таким образом, системному
вызову может быть передано до 6 аргументов через регистры ebx, ecx,
edx, esi, edi и ebp), после чего вызывается инструкция int 0x80. Если
системному вызову необходимо передать большее количество аргументов,
то они размещаются в структуре, адрес на которую передается в качестве
первого аргумента. Результат возвращается в регистре eax, а стек
вообще не используется.

Рассмотрим листинг, представленный ниже.

#Листинг 10
#fork.c
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>

int main()
{
fork();
printf("Hellon");
return 0;
}


Скомпилируйте программу командой cc -g fork.c -static. Запустите gdb и
дайте команды

file fork
disassemble fork.


(Прим.ред. -- во-первых, для cc необходимо добавить ключ -o и в этом
случае команда сборки fork.c будет выглядеть следующим образом: cc -o
fork -g fork.c -static. Либо, запустив gdb, придётся указывать не file
fork, а file a.out, т.к. при отсутсвии ключа -o линкуется программа с
именем a.out. Это относится и к остальным примерам.)
Перед вами появится ассемблерный листинг программы, откуда вы увидите
как выполняется системный вызов fork. Ключ -static -- это ключ
статической линковки GCC (см. страницы справочного руководства man
gcc). Попробуйте проделать то же самое с другими системными вызовами.


11. Программирование на встроенном ассемблере

Компилятор GNU C предоставляет возможность вставлять ассемблерный код
прямо в текст программы на языке C. Само собой разумеется, что
используемые ассемблерные инструкции зависят от архитектуры.

Для вставки ассемблерного кода используется инструкция asm, например:

asm ("fsin" : "=t" (answer) : "0" (angle));


что для процессоров семейства x86 соответствует выражению на языке C:

answer = sin(angle);


Вы можете заметить, что в отличие от обычного ассемблера, инструкция
asm допускает указание входных и выходных аргументов с использованием
синтаксиса языка C. Не следует бездумно пользоваться инструкцией asm.
Но тогда зачем ею пользоваться вообще?
* Инструкция asm позволит программе получить прямой доступ к
аппаратуре компьютера. Это может повысить скорость выполнения
программ. Ее можно использовать для написания кода, который войдет
в состав операционной системы и который будет взаимодействовать с
аппаратурой компьютера. Например, /usr/include/asm/io.h содержит
ассемблерный код для прямого доступа к портам ввода/вывода.
* Ассемблерные вставки помогут значительно поднять скорость
прохождения глубоких вложенных циклов в программе. Например, sine
и cosine для одного и того же значения угла можно заменить одной
ассемблерной инструкцией fsincos. Возможно два листинга,
приведенных ниже, помогут вам лучше понять важность фактора
времени.

#Листинг 11
#Name : bit-pos-loop.c
#Description : Отыскивает позицию бита в цикле

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

int main (int argc, char *argv[])
{
long max = atoi (argv[1]);
long number;
long i;
unsigned position;
volatile unsigned result;

for (number = 1; number <= max; ++number) {
for (i=(number>>1), position=0; i!=0; ++position)
i >>= 1;
result = position;
}
return 0;
}
#Listing 12
#Name : bit-pos-asm.c
#Description : Отыскивает позицию бита с помощью bsrl

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

int main(int argc, char *argv[])
{
long max = atoi(argv[1]);
long number;
unsigned position;
volatile unsigned result;

for (number = 1; number <= max; ++number) {
asm("bsrl %1, %0" : "=r" (position) : "r" (number));
result = position;
}
return 0;
}

Скомпилируйте эти два примера с ключами оптимизации, как это показано
ниже:

$ cc -O2 -o bit-pos-loop bit-pos-loop.c
$ cc -O2 -o bit-pos-asm bit-pos-asm.c


Для измерения скорости исполнения каждой версии воспользуйтесь
командой time и задайте в командной строке достаточно большое число,
чтобы каждая версия отработала по крайней мере несколько секунд..

$ time ./bit-pos-loop 250000000


и

$ time ./bit-pos-asm 250000000


Результаты будут несколько отличаться для разных машин, однако вы
наверняка заметите, что версия программы, которая использует
ассемблерную вставку, работает намного быстрее.
(Прим.ред. -- наверное компьютеру (P4-1.7ГГц), где я проверял примеры,
предложенные автором нужно что-то посолиднее -- результаты тестов
одинаковые.)

Оптимизатор GCC пытается переупорядочить и переписать код программы с
целью минимизации времени исполнения, даже в том случае, когда в
программе имеются ассемблерные вставки. Когда оптимизатор
обнаруживает, что результат исполнения asm-инструкции нигде не
используется, то он может просто исключить ее из текста программы,
если между инструкцией asm и ее операндами отсутствует ключевое слово
volatile (как частный случай, gcc не перемещает ассемблерные вставки,
не возвращающие результат выполнения, за пределы цикла). Любая,
отдельно взятая, ассемблерная вставка может быть перемещена со своего
места и крайне трудно угадать заранее как ею распорядится оптимизатор.
Единственная возможность сохранить порядок следования ассемблерных
инструкций -- это вставить весь блок ассемблерного кода в одну
инструкцию asm.

Использование инструкций asm может снизить эффективность оптимизатора,
поскольку компилятор ничего не знает о семантике asm. В этом случае
GCC вынужден перейти к более консервативному режиму прогнозирования,
что может привести к отказу от некоторых видов оптимизации.


12. Упражнения

1. Разберитесь с ассемблерным кодом для программы на языке C из
Листинга 6. Измените его таким образом, чтобы больше не появлялись
предупреждающие сообщения, возникающие при генерации ассемблерного
кода с ключом -Wall. Сравните два полученных ассемблерных
листинга. Какие изменения вы заметили?
2. Скомпилируйте какую нибудь небольшую программку на языке C с
оптимизацией (например -O2) и без нее. Посмотрите получившийся
ассемблерный код и найдите основные отличия, выполненные
компилятором.
3. Разберите ассемблерный код, который генерируется для оператора
выбора switch.
4. Скомпилируйте небольшую программку на языке C, которая имеет
ассемблерную вставку. Какие различия вы заметили в ассемблерном
коде такой программы?
5. Вложенные функции -- это такие функции, которые определяются в
теле другой ("объемлющей") функции, таким образом:
+ вложенная функция имеет возможность доступа к локальным
переменным "объемлющей" функции и
+ вложенные функции являются локальными по отношению к
"объемлющим" функциям и не могут быть вызваны за пределами
"объемлющих" функций если они не передадут вам указатель на
вложенную функцию.
Разберите программу, приведенную ниже:


#Listing 13
/* myprint.c */
#include <stdio.h>
#include <stdlib.h>

int main()
{
int i;
void my_print(int k)
{
printf("%dn",k);
}
scanf("%d",&i);
my_print(i);
return 0;
}


Скомпилируйте эту программу командой cc -S myprint.c и просмотрите
ассемблерный код. Попробуйте скомпилировать эту программу командой
cc -pedantic myprint.c. Что вы заметили?

Copyright (C) 2003, Hiran Ramankutty. Copying license
http://www.linuxgazette.com/copying.html
Published in Issue 94 of Linux Gazette, September 2003
782 Прочтений •  [От C к Ассемблеру (gcc assembler)] [08.05.2012] [Комментариев: 0]
Добавил: Ukraine Vova
Ссылки
HTML: 
[BB Url]: 
Похожие статьи
Название Добавил Добавлено
• От C к Ассемблеру (gcc assembler) Ukraine Vova 08.05.2012
Ни одного комментария? Будешь первым :).
Пожалуйста, авторизуйтесь для добавления комментария.

Проект входит в сеть сайтов «8Gamers Network»

Все права сохранены. 8Gamers.NET © 2011 - 2025

Статьи
Рецензия на Pressure
Рецензия на Pressure
Чтобы обратить на себя внимание, начинающие маленькие разработчики, как правило, уходят в жанры, ...
Рецензия на Lost Chronicles of Zerzura
Рецензия на Lost Chron...
Игры, сделанные без любви и старания, похожи на воздушный шар – оболочка есть, а внутри пусто. Lo...
Рецензия на The Bridge
Рецензия на The Bridge
«Верх» и «низ» в The Bridge — понятия относительные. Прогуливаясь под аркой, можно запросто перей...
Рецензия на SimCity
Рецензия на SimCity
Когда месяц назад состоялся релиз SimCity, по Сети прокатилось цунами народного гнева – глупые ош...
Рецензия на Strategy & Tactics: World War 2
Рецензия на Strategy &...
Название Strategy & Tactics: World War II вряд ли кому-то знакомо. Зато одного взгляда на ее скри...
Рецензия на игру Scribblenauts Unlimited
Рецензия на игру Scrib...
По сложившейся традиции в информационной карточке игры мы приводим в пример несколько похожих игр...
Рецензия на игру Walking Dead: Survival Instinct, The
Рецензия на игру Walki...
Зомби и продукция-по-лицензии — которые и сами по себе не лучшие представители игровой биосферы —...
Обратная связь | RSS | Донейт | Статистика | Команда | Техническая поддержка