|
|
12 |
| |
|
|
13 |
| |
|
|
14 |
| |
|
|
15 |
|
|
|
16 |
|
|
|
17 |
Документация на C--. |
|
|
18 |
| |
|
|
19 |
| |
|
|
20 |
| |
|
|
21 |
| |
|
|
22 |
|
|
|
|
23 |
|
|
|
24 |
|
|
|
25 |
|
|
|
26 |
|
|
|
27 |
Содержание. |
|
|
28 |
|
|
|
29 |
|
|
|
30 |
|
|
|
31 |
|
|
|
32 |
1 Введение. |
|
|
33 |
1.1 История создания и развития. |
|
|
34 |
1.2 Что такое C--? |
|
|
35 |
1.3 Как установить C--. |
|
|
36 |
|
|
|
37 |
2. Управление компиляцией. |
|
|
38 |
2.1 Параметры командной строки компилятора C--. |
|
|
39 |
2.1.1 /ON - Оптимизация числовых выражений. |
|
|
40 |
2.1.2 /DE - Временное расширение разрядности переменной. |
|
|
41 |
2.1.3 /ARGC - Альтернативный обработчик командной строки. |
|
|
42 |
2.1.4 /OST - слияние одинаковых строковых констант. |
|
|
43 |
2.1.5 /D - установка идентификатора в TRUE из командной строки. |
|
|
44 |
2.1.6 /IA - упрощенный ввод ассемблерных инструкций. |
|
|
45 |
2.1.7 /CRI - пропуск повторно включаемого файла. |
|
|
46 |
2.1.8 /IND - импорт имен процедур из DLL. |
|
|
47 |
2.1.9 /WS - задать имя stub файла для программ под windows. |
|
|
48 |
2.1.10 /WBSS - разместить не инициализированные данные в отдельной секции. |
|
|
49 |
2.1.11 /DBG - создание отладочной информации. |
|
|
50 |
2.1.12 /J0 /J1 /J2. |
|
|
51 |
2.1.13 /LST - Создание ассемблерного листинга. |
|
|
52 |
2.1.14 /ENV - Сохранение адреса переменных окружения. |
|
|
53 |
2.1.15 /CPA - Очистка post-области данных. |
|
|
54 |
2.1.16 /W - вывод предупреждений. |
|
|
55 |
2.1.17 /NW - Выборочное отключение типов предупреждений. |
|
|
56 |
2.1.18 /WSI - короткая таблица импорта. |
|
|
57 |
2.2 Директивы транслятора. |
|
|
58 |
2.2.1 ?ifdef/?ifndef |
|
|
59 |
2.2.2 ?initallvar |
|
|
60 |
2.2.3 ?usestartup |
|
|
61 |
2.2.4 ?startusevar |
|
|
62 |
2.2.5 ?atexit |
|
|
63 |
2.2.6 ?startuptomain |
|
|
64 |
2.2.7 ?undef |
|
|
65 |
2.2.8 ?align и ?aligncode |
|
|
66 |
2.2.9 ?pragma |
|
|
67 |
|
|
|
68 |
3. Константы. |
|
|
69 |
3.1 Числовые константы. |
|
|
70 |
3.2 Символьные константы. |
|
|
71 |
3.3 Строковые константы. |
|
|
72 |
3.4 Постоянные выражения. |
|
|
73 |
|
|
|
74 |
4. Выражения. |
|
|
75 |
4.1 Типы выражений. |
|
|
76 |
4.2 Выражения типа EAX/AX/AL. |
|
|
77 |
4.3 Выражения использующие получатель при вычислении выражения. |
|
|
78 |
4.4 Не - EAX/AX/AL выражения. |
|
|
79 |
4.5 Условные выражения. |
|
|
80 |
4.5.1 Простые условные выражения. |
|
|
81 |
4.5.2 Сложные условные выражения. |
|
|
82 |
4.6 Изменение типа выражения при присваивании. |
|
|
83 |
4.7 Вычисление в регистры EAX/AX/AL со знаком. |
|
|
84 |
|
|
|
85 |
5. Идентификаторы. |
|
|
86 |
5.1 Формат идентификатора. |
|
|
87 |
5.2 Зарезервированные идентификаторы. |
|
|
88 |
5.3 Универсальные регистры для 16 и 32-битного режима. |
|
|
89 |
5.4 Предопределенные идентификаторы. |
|
|
90 |
|
|
|
91 |
6. Переменные. |
|
|
92 |
6.1 Типы переменных. |
|
|
93 |
6.2 Объявление переменных. |
|
|
94 |
6.3 Глобальные переменные. |
|
|
95 |
6.4 Локальные переменные. |
|
|
96 |
6.5 Динамические переменные и структуры. |
|
|
97 |
6.6 Присваивание одного значения нескольким переменным. |
|
|
98 |
6.7 Переменные типа float. |
|
|
99 |
6.7.1 Формат переменных типа float. |
|
|
100 |
6.7.2 Константы с плавающей точкой. |
|
|
101 |
6.7.3 Диапазон допустимых значений. |
|
|
102 |
6.7.4 Математические операции. |
|
|
103 |
6.7.5 Преобразования типов. |
|
|
104 |
6.7.6 Операции сравнения. |
|
|
105 |
6.7.7 Сравнение переменных типа float с 32-битным регистром. |
|
|
106 |
6.8 Указатели. |
|
|
107 |
|
|
|
108 |
7. Адресация. |
|
|
109 |
7.1 Относительная адресация. |
|
|
110 |
7.2 Абсолютная адресация. |
|
|
111 |
|
|
|
112 |
8. Работа с блоками данных. |
|
|
113 |
8.1 Структуры. |
|
|
114 |
8.1.1 Что такое структуры. |
|
|
115 |
8.1.2 Синтаксис. |
|
|
116 |
8.1.3 Инициализация структур при объявлении. |
|
|
117 |
8.1.4 Инициализация структуры при выполнении программы. |
|
|
118 |
8.1.5 Операции с элементами структур. |
|
|
119 |
8.1.6 Вложенные структуры. |
|
|
120 |
8.1.7 Отображение тега структуры на блок памяти. |
|
|
121 |
8.1.8 Битовые поля структур. |
|
|
122 |
8.2 Объединения. |
|
|
123 |
8.3 Команды FROM и EXTRACT. |
|
|
124 |
|
|
|
125 |
9. Операторы. |
|
|
126 |
9.1 Условные инструкции. |
|
|
127 |
9.2 Циклы do{} while. |
|
|
128 |
9.3 Циклы loop, LOOPNZ, loopnz. |
|
|
129 |
9.4 Цикл while, WHILE. |
|
|
130 |
9.5 Цикл for, FOR. |
|
|
131 |
9.6 Оператор переключатель switch. |
|
|
132 |
9.7 Оператор перехода goto, GOTO. |
|
|
133 |
9.8 Оператор разрыва break, BREAK. |
|
|
134 |
9.9 Оператор продолжения continue, CONTINUE. |
|
|
135 |
9.10 Логическое объединение условий. |
|
|
136 |
9.11 Переход через циклы. |
|
|
137 |
9.12 Инвертирование флага проверки условий. |
|
|
138 |
9.13 Вычисление выражения, а затем проверка условия. |
|
|
139 |
9.14 Проверка битов при операции сравнения. |
|
|
140 |
9.15 Оператор перестановки. |
|
|
141 |
9.16 Оператор отрицания. |
|
|
142 |
9.17 Оператор инверсии. |
|
|
143 |
9.18 Специальные условные выражения. |
|
|
144 |
9.19 Символ $ - вставляет текущий адрес программы. |
|
|
145 |
9.20 Ключевое слово static и оператор ::. |
|
|
146 |
9.21 Оператор sizeof. |
|
|
147 |
9.22 Метки перехода. |
|
|
148 |
|
|
|
149 |
10. Ассемблер. |
|
|
150 |
10.1 Поддержка команд ассемблера. |
|
|
151 |
10.2 Ключевое слово asm. |
|
|
152 |
10.3 Префикс dup - повторение инструкций DB/DW/DD. |
|
|
153 |
10.4 Инструкции процессора Pentium III. |
|
|
154 |
|
|
|
155 |
11. Процедуры. |
|
|
156 |
11.1 Типы процедур, функций и макрокоманд. |
|
|
157 |
11.2 Стековые процедуры. |
|
|
158 |
11.3 Регистровые процедуры. |
|
|
159 |
11.4 Динамические процедуры. |
|
|
160 |
11.4.1 Установка динамической процедуры в определенное место программы. |
|
|
161 |
11.5 inline-процедуры. |
|
|
162 |
11.5.1 Другое применение inline. |
|
|
163 |
11.6 Процедуры обработки прерываний. |
|
|
164 |
11.7 Замена return на goto. |
|
|
165 |
11.8 Возвращаемые значения. |
|
|
166 |
11.9 Объявление параметров в регистровых процедурах. |
|
|
167 |
11.10 Объявление параметров в стековых процедурах. |
|
|
168 |
11.11 Использование макрокоманд. |
|
|
169 |
11.12 Передача параметров в стековые процедуры через регистры. |
|
|
170 |
11.13 Вызов процедур с адресом в регистре. |
|
|
171 |
11.14 Встоенные в компилятор процедуры. |
|
|
172 |
11.14.1 Процедуры ABORT, ATEXIT и EXIT. |
|
|
173 |
11.14.2 Процедуры inp/inportb, inport, inportd, outp/outportb, outport и |
|
|
174 |
outportd. |
|
|
175 |
11.14.3 Процедуры для работы с вещественными числами. |
|
|
176 |
11.15 Классы. |
|
|
177 |
11.15.1 Объявление процедур в структурах. |
|
|
178 |
11.15.2 Наследование. |
|
|
179 |
11.15.3 Наследование процедур. |
|
|
180 |
|
|
|
181 |
12. Типы выходных файлов. |
|
|
182 |
12.1 Выходные файлы типа COM. |
|
|
183 |
12.2 Выходные файлы типа EXE. |
|
|
184 |
12.3 Выходной файл *.EXE с моделью памяти tiny. |
|
|
185 |
12.4 Объектный выходной файл OBJ. |
|
|
186 |
12.5 COM файл symbiosis. |
|
|
187 |
12.5.1 СИМБИОЗ - что это такое? |
|
|
188 |
12.5.2 Как это делать. |
|
|
189 |
12.5.3 Использование. |
|
|
190 |
12.5.4 Злоупотребления. |
|
|
191 |
12.6 SYS - драйверы устройств. |
|
|
192 |
12.7 Компиляция кода расширителей ROM-BIOS. |
|
|
193 |
12.8 32-битные файлы. |
|
|
194 |
12.8.1 32-битный код под DOS. |
|
|
195 |
12.8.2 32-битный код под Windows. |
|
|
196 |
12.8.3 Вызов API процедур по ординалам. |
|
|
197 |
12.8.4 Создание DLL под Windows. |
|
|
198 |
12.8.5 Инициализация DLL при загрузке. |
|
|
199 |
12.8.6 Компиляция ресурсов. |
|
|
200 |
12.9 Выходные файлы для MeOS. |
|
|
201 |
|
|
|
202 |
13. Приложения. |
|
|
203 |
13.1 Поиск включаемых файлов. |
|
|
204 |
13.2 Регистры, которые должны быть сохранены. |
|
|
205 |
13.3 C--.ini файл. |
|
|
206 |
13.4 startup.h-- файл. |
|
|
207 |
13.5 mainlib.ldp файл. |
|
|
208 |
13.6 C-- символы. |
|
|
209 |
|
|
|
210 |
|
|
|
211 |
|
|
|
212 |
1. Вступление. |
|
|
213 |
|
|
|
214 |
1.1 История создания и развития. |
|
|
215 |
|
|
|
216 |
|
|
|
217 |
Автором языка SPHINX C-- является Peter Cellik (CANADA). Последняя |
|
|
218 |
авторская версия SPHINX C-- v0.203 от 28.Oct.96. К сожалению автор |
|
|
219 |
отказался от дальнейшего развития языка. С 1998 года, уже почти умерший |
|
|
220 |
проект, подхватил Михаил Шекер (Россия). Изначально компилятор был freeware |
|
|
221 |
(и даже greenware, как его называл Peter Cellik). Таким статус компилятора |
|
|
222 |
остался и поныне. |
|
|
223 |
|
|
|
224 |
Первоначально компилятор мог создавать только *.com файлы и был |
|
|
225 |
рассчитан на создание небольших demo-программ и резидентов (TSR). В |
|
|
226 |
дальнейшем возможности компилятора расширялись, так как этого требовало |
|
|
227 |
наше бурное время. |
|
|
228 |
|
|
|
229 |
При развитии компилятора, было стремление придерживаться следующих |
|
|
230 |
принципов: |
|
|
231 |
|
|
|
232 |
1. Максимально возможная совместимость синтаксиса с последней версией |
|
|
233 |
компилятора написанного Peter Cellik. Это давало возможность с минимальными |
|
|
234 |
затратами (а чаще всего без всяких затрат) адаптировать программы, |
|
|
235 |
написанные для 0.203 версии компилятора, к последней на этот момент версии |
|
|
236 |
компилятора. |
|
|
237 |
|
|
|
238 |
2. Сблизить синтаксис компилятора со стандартным языком C. Это могло |
|
|
239 |
значительно облегчить перенос программ написанных на C. |
|
|
240 |
|
|
|
241 |
3. Также прилагались усилия, для того, чтобы человек знающий только |
|
|
242 |
ассемблер мог бы с минимальными затратами освоить C--. |
|
|
243 |
|
|
|
244 |
Вот эти, зачастую противоречащие друг другу принципы, влияли на выбор |
|
|
245 |
реализации возможностей компилятора. Насколько это удалось - судить Вам. |
|
|
246 |
|
|
|
247 |
Если у Вас есть предложения и идеи по улучшению компилятора - пишите. |
|
|
248 |
Мой e-mail sheker@mail.ru . Я с удовольствием выслушаю Ваши предложения, но |
|
|
249 |
не гарантирую, что все они будут реализованы. Если реализовывать все |
|
|
250 |
поступающие предложения, то компилятор превратится в свалку. Но если Ваше |
|
|
251 |
предложение будет ценным (на мой взгляд, так что Вам придется свое |
|
|
252 |
предложение хорошо аргументировать) и его будет возможным реализовать, оно |
|
|
253 |
без сомнения найдет место в компиляторе. |
|
|
254 |
Return to contents. |
|
|
255 |
|
|
|
256 |
|
|
|
257 |
|
|
|
258 |
1.2 Что такое C--? |
|
|
259 |
|
|
|
260 |
|
|
|
261 |
C-- был разработан, для того чтобы строить маленькие и быстрые |
|
|
262 |
программы. Это наиболее подходит для создания резидентных программ (TSR), |
|
|
263 |
программ, требующих обработку прерываний или программ у которых ограничены |
|
|
264 |
ресурсы. |
|
|
265 |
|
|
|
266 |
C-- занимает промежуточное положение между си и ассемблером. В связи с |
|
|
267 |
этим промежуточным положением, Вам, для того чтобы писать программы на C--, |
|
|
268 |
необходимо знать и ассемблер и си. Если Вам надоело возиться с огромными |
|
|
269 |
ассемблерными листингами, а излишняя строгость языка C Вас угнетает, то этот |
|
|
270 |
язык для ВАС. |
|
|
271 |
|
|
|
272 |
Сейчас компилятор C-- может создавать 32-битные программы под Windows |
|
|
273 |
(EXE-файлы формата PE) и 32-битные программы под DOS (LE-формат). Имеет |
|
|
274 |
встроенный компилятор ресурсов и дизассемблер для генерации листинга |
|
|
275 |
откомпилированного файла. Поддерживает ассемблерные инструкции процессора |
|
|
276 |
Pentium III и ассемблерные инструкции FPU. Компилятор может генерировать |
|
|
277 |
отладочную информацию совместимую с отладчиками фирмы Borland. Компилятор |
|
|
278 |
может создавать объектные файлы (obj), но только для DOS программ. |
|
|
279 |
|
|
|
280 |
C-- разработан только для использования на компьютерах с процессорами |
|
|
281 |
совместимыми с семейством 80x86. Компилятор может работать только с |
|
|
282 |
операционными системами DOS и семейством Windows. |
|
|
283 |
Return to contents. |
|
|
284 |
|
|
|
285 |
|
|
|
286 |
|
|
|
287 |
1.3 Как установить C--. |
|
|
288 |
|
|
|
289 |
|
|
|
290 |
Компилятору C-- для работы нужны совсем незначительные ресурсы: |
|
|
291 |
процессор 386 или лучше, чуть более 1 Мб дискового пространства и 4Мб |
|
|
292 |
оперативной памяти. Компилятор может быть установлен на компьютеры с |
|
|
293 |
операционной системой Windows 95 или лучше. Компилятор также может работать |
|
|
294 |
в среде чистого DOS. В основном пакете компилятора находится 32-битная DOS |
|
|
295 |
версия компилятора. На сайте http://sheker.chat.ru или |
|
|
296 |
http://c--sphinx.narod.ru можно найти и консольную версию компилятора. |
|
|
297 |
Консольная версия компилятора может работать только в среде Windows, но |
|
|
298 |
она, в отличие от DOS версии, может работать с длинными именами исходных |
|
|
299 |
файлов. |
|
|
300 |
|
|
|
301 |
Установить компилятор C-- на Ваш компьютер очень просто. Предположим, |
|
|
302 |
что Вы решили установить C-- на диск C. Создайте на диске C директорию |
|
|
303 |
(папку) с именем C-- или с другим, удобным и понятным для Вас именем |
|
|
304 |
(например, ДОСовской командой: MD C-- или другим доступным Вам способом). |
|
|
305 |
Затем с сайта http://sheker.chat.ru или http://c--sphinx.narod.ru скачайте |
|
|
306 |
файлы full_c--.zip и ful_c--2.zip и разархивируйте их в этой директории. |
|
|
307 |
Затем в файле autoexec.bat можно прописать путь к директории с |
|
|
308 |
компилятором. И все. Компилятор готов к работе. Если Вы добавляли путь к |
|
|
309 |
компилятору в файл autoexec.bat, то Вам придется перегрузить операционную |
|
|
310 |
систему. |
|
|
311 |
|
|
|
312 |
Переменная окружения для компилятора C-- задается либо из командной |
|
|
313 |
строки либо из командного файла (лучше всего ее прописать в autoexec.bat). |
|
|
314 |
Эта переменная должна указывать компилятору, где находятся его библиотечные |
|
|
315 |
файлы. Пример: |
|
|
316 |
|
|
|
317 |
set C--=c:\c--\lib |
|
|
318 |
|
|
|
319 |
Большой необходимости в переменной окружения для сегодняшней версии |
|
|
320 |
компилятора нет. Существует несколько других способов, указать компилятору |
|
|
321 |
место расположения библиотек. Поэтому определять или не определять |
|
|
322 |
переменную окружения дело вашего вкуса и привычек. |
|
|
323 |
Return to contents. |
|
|
324 |
|
|
|
325 |
|
|
|
326 |
|
|
|
327 |
2. Управление компиляцией. |
|
|
328 |
|
|
|
329 |
2.1 Параметры командной строки компилятора C--. |
|
|
330 |
|
|
|
331 |
|
|
|
332 |
Формат командной строки вызова компилятора C--: |
|
|
333 |
|
|
|
334 |
C-- [Параметры] [ИМЯ INI ФАЙЛА] [ИМЯ ИСХОДНОГО ФАЙЛА] |
|
|
335 |
|
|
|
336 |
Имя исходного файла можно задавать без расширения. Компилятор ищет |
|
|
337 |
файл с расширением c--, cmm, c. |
|
|
338 |
|
|
|
339 |
Параметры выделяются предшествующим символом / или -. |
|
|
340 |
Инвертировать функцию опции можно завершающим символом -. |
|
|
341 |
|
|
|
342 |
Список поддерживаемых параметров: |
|
|
343 |
|
|
|
344 |
/0 использовать только команды 8086/8088 процессора (установлено |
|
|
345 |
по умолчанию при компиляции 16-битных программ). |
|
|
346 |
/1 использовать команды 80186 процессора. |
|
|
347 |
/2 использовать команды и оптимизацию для 80286 процессора. |
|
|
348 |
/3 использовать команды и оптимизацию для 80386 процессора. |
|
|
349 |
(установлено по умолчанию для 32-битных программ). |
|
|
350 |
/4 использовать команды и оптимизацию для 80486 процессора. |
|
|
351 |
/5 использовать команды и оптимизацию для Pentium процессора. |
|
|
352 |
/6 использовать команды и оптимизацию для Pentium MMX процессора. |
|
|
353 |
/7 использовать команды и оптимизацию для Pentium Pro процессора. |
|
|
354 |
/8 использовать команды и оптимизацию для Pentium II процессора. |
|
|
355 |
/9 использовать команды и оптимизацию для Pentium III процессора |
|
|
356 |
(пока не реализовано из-за отсутствии информации). |
|
|
357 |
/A выравнивание данных на четный адрес |
|
|
358 |
по умолчанию разрешено, поддерживает инверсию |
|
|
359 |
/AC выравнивание адреса начала циклов |
|
|
360 |
по умолчанию отключено, поддерживает инверсию |
|
|
361 |
имеет смысл только на процессорах Pentium+ |
|
|
362 |
/AL=## установить значение байта заполнения при выравнивании данных |
|
|
363 |
по умолчанию 0. |
|
|
364 |
/AP выравнивание адреса начала процедур. |
|
|
365 |
по умолчанию отключено, поддерживает инверсию |
|
|
366 |
имеет смысл только на процессорах Pentium и лучше |
|
|
367 |
/ARGC вставить блок разбора командной строки |
|
|
368 |
по умолчанию отключено, поддерживает инверсию |
|
|
369 |
/AS выравнивание в структурах. |
|
|
370 |
по умолчанию отключено, поддерживает инверсию |
|
|
371 |
/AT вставить блок поддержки ATEXIT процедуры |
|
|
372 |
по умолчанию отключено, поддерживает инверсию |
|
|
373 |
/C вставить блок игнорирования CTRL-C |
|
|
374 |
по умолчанию отключен, поддерживает инверсию |
|
|
375 |
имеет смысл только под DOS программы |
|
|
376 |
/CRI проверять включаемые файлы на повторную загрузку |
|
|
377 |
по умолчанию включено, поддерживает инверсию |
|
|
378 |
/CPA очистка post-области данных |
|
|
379 |
/D32 создать EXE файл (32 битный код под DOS) |
|
|
380 |
по умолчанию COM |
|
|
381 |
/D=idname определить идентификатор для условной компиляции |
|
|
382 |
по умолчанию нет |
|
|
383 |
/DBG генерировать отладочную информацию |
|
|
384 |
по умолчанию нет |
|
|
385 |
/DE временное расширение разрядности после умножения |
|
|
386 |
по умолчанию отключено, поддерживает инверсию |
|
|
387 |
/DLL создать DLL для Windows32 |
|
|
388 |
по умолчанию COM |
|
|
389 |
/ENV сохранение адреса переменных окружения |
|
|
390 |
/EXE создать EXE файл для DOS (модель SMALL) |
|
|
391 |
по умолчанию COM |
|
|
392 |
/HELP /H /? справка, эта информация |
|
|
393 |
/IA имена ассемблерных инструкций являются идентификаторами |
|
|
394 |
по умолчанию отключено, поддерживает инверсию |
|
|
395 |
/IND=name импорт имен из файла name. |
|
|
396 |
/IP=path задать путь поиска включаемых файлов |
|
|
397 |
по умолчанию нет |
|
|
398 |
/IV инициализировать все переменные |
|
|
399 |
по умолчанию отключено, поддерживает инверсию |
|
|
400 |
/J0 не делать начальный jump на main() |
|
|
401 |
по умолчанию отключено, поддерживает инверсию |
|
|
402 |
В COM-файлах не создает jmp на main. В остальных не создается |
|
|
403 |
блок начальной инициализации программы, а управление |
|
|
404 |
передается сразу на main. |
|
|
405 |
/J1 делать короткий jump на main() |
|
|
406 |
по умолчанию нет |
|
|
407 |
имеет смысл только в COM-файлах |
|
|
408 |
/J2 делать jump на main() |
|
|
409 |
по умолчанию да, поддерживает инверсию |
|
|
410 |
имеет смысл только в COM-файлах |
|
|
411 |
/LAI список поддерживаемых ассемблерных инструкций |
|
|
412 |
/LRS загружать числовые константы через стек. |
|
|
413 |
по умолчанию да, поддерживает инверсию |
|
|
414 |
/LST создать ассемблерный листинг |
|
|
415 |
/ME показать мой адрес и имя |
|
|
416 |
/MEOS создать исполняемый файл для MeOS |
|
|
417 |
по умолчанию COM |
|
|
418 |
/MER=## установить максимальное число ошибок |
|
|
419 |
по умолчанию 16 |
|
|
420 |
/MIF=file определить имя главного компилируемого файла |
|
|
421 |
/NS запретить подключать stub файлов |
|
|
422 |
по умолчанию нет, поддерживает инверсию |
|
|
423 |
/NW=## выборочное отключение предупреждений |
|
|
424 |
/OBJ создать OBJ файл |
|
|
425 |
только 16 битный код. |
|
|
426 |
по умолчанию COM |
|
|
427 |
/OC оптимизировать по размеру кода |
|
|
428 |
по умолчанию нет, поддерживает инверсию |
|
|
429 |
/ON оптимизация чисел |
|
|
430 |
по умолчанию нет, поддерживает инверсию |
|
|
431 |
/OS оптимизация по скорости выполнения |
|
|
432 |
по умолчанию да, поддерживает инверсию |
|
|
433 |
/OST оптимизация строковых идентификаторов |
|
|
434 |
по умолчанию отключено, поддерживает инверсию |
|
|
435 |
/P вставить блок разборки командной строки |
|
|
436 |
по умолчанию нет, поддерживает инверсию |
|
|
437 |
/R вставить блок уменьшающий размер доступной памяти. |
|
|
438 |
по умолчанию да, поддерживает инверсию |
|
|
439 |
имеет смысл только в DOS-файлах |
|
|
440 |
/S=##### установить размер стека |
|
|
441 |
по умолчанию 2048 |
|
|
442 |
/SA=#### начальное смещение адреса запуска программы |
|
|
443 |
имеет смысл только в COM-файлах, по умолчанию 0x100 |
|
|
444 |
/SOBJ создать ведомый OBJ файл |
|
|
445 |
по умолчанию COM |
|
|
446 |
/STM перенести блок startup кода в процедуру main |
|
|
447 |
по умолчанию нет, поддерживает инверсию |
|
|
448 |
имеет смысл только в COM-файлах |
|
|
449 |
/SUV=#### начальный адрес не инициализированных переменных, при |
|
|
450 |
использовании ими startup кода. |
|
|
451 |
имеет смысл только в COM-файлах, по умолчанию равен /SA |
|
|
452 |
/SYM надстройка для COM файла |
|
|
453 |
по умолчанию COM |
|
|
454 |
/SYS создать драйвер устройств (SYS) |
|
|
455 |
по умолчанию COM |
|
|
456 |
/TEXE создать EXE файл для DOS (модель TINY) |
|
|
457 |
по умолчанию COM |
|
|
458 |
/UL использовать lea при оптимизации сложения регистров. |
|
|
459 |
по умолчанию да, поддерживает инверсию |
|
|
460 |
/UST использовать startup код для переменных. |
|
|
461 |
имеет смысл только в COM-файлах |
|
|
462 |
по умолчанию нет, поддерживает инверсию |
|
|
463 |
/W разрешить предупреждения |
|
|
464 |
по умолчанию нет, поддерживает инверсию |
|
|
465 |
/W32 создать EXE файл для Windows32 GUI |
|
|
466 |
по умолчанию COM |
|
|
467 |
/W32C создать EXE файл для Windows32 console |
|
|
468 |
по умолчанию COM |
|
|
469 |
/WBSS помещать не инициализированные данные в отдельную секцию. |
|
|
470 |
по умолчанию для /w32 разрешено, для остальных запрещено. |
|
|
471 |
поддерживает инверсию |
|
|
472 |
/WF=file перенаправить вывод предупреждений в файл. |
|
|
473 |
по умолчанию нет |
|
|
474 |
/WFA использовать быстрые вызовы API процедур |
|
|
475 |
по умолчанию да, поддерживает инверсию |
|
|
476 |
только под windows |
|
|
477 |
/WFU создавать таблицу перемещений (для Windows32) |
|
|
478 |
по умолчанию нет, поддерживает инверсию |
|
|
479 |
только под windows |
|
|
480 |
для DLL устанавливается в да |
|
|
481 |
/WIB=##### установить адрес image base |
|
|
482 |
по умолчанию 0x400000 |
|
|
483 |
/WMB создавать Windows-файл с единым блоком |
|
|
484 |
по умолчанию да, поддерживает инверсию |
|
|
485 |
только под windows |
|
|
486 |
для DLL устанавливается в нет |
|
|
487 |
/WORDS выдать список зарезервированных идентификаторов |
|
|
488 |
/WS=name указывает имя файла используемого в качестве stub под windows. |
|
|
489 |
/X запретить вставлять в код SPHINXC-- сигнатуру |
|
|
490 |
по умолчанию разрешено, поддерживает инверсию |
|
|
491 |
отключается если есть J0 |
|
|
492 |
|
|
|
493 |
Примечание: выражение поддерживает инверсию означает, что для данной |
|
|
494 |
опции можно использовать и противоположное значение с помощью символа - |
|
|
495 |
после опции. Пример: |
|
|
496 |
|
|
|
497 |
/WFA- |
|
|
498 |
|
|
|
499 |
Параметры командной строки можно писать как большими, так и |
|
|
500 |
маленькими буквами. |
|
|
501 |
Return to contents. |
|
|
502 |
|
|
|
503 |
|
|
|
504 |
|
|
|
505 |
2.1.1 /ON - Оптимизация числовых выражений. |
|
|
506 |
|
|
|
507 |
|
|
|
508 |
При включении в командную строку опции /ON или в файл C--.INI строчки |
|
|
509 |
ON, компилятор будет анализировать операции над числами и где это |
|
|
510 |
можно, сокращать число операций. Например: |
|
|
511 |
|
|
|
512 |
Строка до оптимизации | После оптимизации |
|
|
513 |
----------------------------------------------- |
|
|
514 |
AX = var + 7 - 3; | AX = var + 4; |
|
|
515 |
AX = var * 2 * 5; | AX = var * 10; |
|
|
516 |
AX = var * 2 / 4; | AX = var / 2; |
|
|
517 |
AX = var * 10 / 2; | AX = var * 5; |
|
|
518 |
AX = var / 2 / 3; | AX = var / 6; |
|
|
519 |
AX = var / 4 * 8; | AX = var * 2; |
|
|
520 |
AX = var / 16 * 16; | AX = var; |
|
|
521 |
|
|
|
522 |
Возможные отрицательные последствия: |
|
|
523 |
Применение этой оптимизации может иметь и негативные последствия. |
|
|
524 |
Например, если Вам нужно выровнять значение переменной на границу |
|
|
525 |
параграфа, Вы напишите строку: |
|
|
526 |
|
|
|
527 |
var = var / 16 * 16; |
|
|
528 |
|
|
|
529 |
но после оптимизации будет |
|
|
530 |
|
|
|
531 |
var = var; |
|
|
532 |
|
|
|
533 |
т.е. выравнивание не будет произведено. Этого можно избежать, если |
|
|
534 |
разбить это выражение на два: |
|
|
535 |
|
|
|
536 |
var = var / 16; |
|
|
537 |
var = var * 16; |
|
|
538 |
|
|
|
539 |
тогда оптимизация не будет произведена. Но для получения более |
|
|
540 |
компактного кода лучше будет записать так: |
|
|
541 |
|
|
|
542 |
AX = var; |
|
|
543 |
AX = AX / 16; |
|
|
544 |
AX = AX * 16; |
|
|
545 |
var = AX; |
|
|
546 |
Return to contents. |
|
|
547 |
|
|
|
548 |
|
|
|
549 |
|
|
|
550 |
2.1.2 /DE - Временное расширение разрядности переменной. |
|
|
551 |
|
|
|
552 |
|
|
|
553 |
Как известно, после умножения может произойти переполнение, т.е |
|
|
554 |
разрядность результата может превысить разрядность исходных операндов и |
|
|
555 |
произойдет искажение результата. Частично решить эту проблему Вам поможет |
|
|
556 |
опция командной строки /DE или строка DE в файле C--.INI. После команды |
|
|
557 |
умножения компилятор будет просматривать остаток строки и если обнаружит, |
|
|
558 |
что расширение разрядности может быть востребовано (востребовать |
|
|
559 |
расширенную разрядность могут операции деления и вычисления остатка), то |
|
|
560 |
будут приняты меры по ее сохранению. Например: |
|
|
561 |
|
|
|
562 |
a = b*c+d/e; //здесь будет включена поддержка расширения разрядности |
|
|
563 |
a = b*c+d*e; //здесь поддержки расширения разрядности не будет. |
|
|
564 |
|
|
|
565 |
Однако применение этой опции может иметь и негативные последствия. |
|
|
566 |
Покажу это на примере: |
|
|
567 |
|
|
|
568 |
пусть имеется выражение |
|
|
569 |
|
|
|
570 |
a = b * c / d; |
|
|
571 |
|
|
|
572 |
если значения переменных b = 0xC000, c = 0x1000, d=0x10, после запуска |
|
|
573 |
такая программа зависнет с сообщением о том, что произошло переполнение |
|
|
574 |
при делении. |
|
|
575 |
Return to contents. |
|
|
576 |
|
|
|
577 |
|
|
|
578 |
|
|
|
579 |
2.1.3 /ARGC - Альтернативный обработчик командной строки. |
|
|
580 |
|
|
|
581 |
|
|
|
582 |
Отличие этого обработчика командной строки от parsecommandline |
|
|
583 |
заключается в том, что при вызове PARAMSTR(0); Вы получите адрес строки в |
|
|
584 |
которой указан путь и имя запущенной программы. Следующие вызовы этой |
|
|
585 |
процедуры с увеличивающимся параметром будут возвращать адреса слов |
|
|
586 |
командной строки. А вызов процедуры PARAMCOUNT вернет Вам число слов в |
|
|
587 |
командной строке плюс один. |
|
|
588 |
|
|
|
589 |
Альтернативный обработчик командной строки включается директивой |
|
|
590 |
?argc TRUE или из командной строки компилятора ключом /argc или |
|
|
591 |
строчкой argc в файле C--.INI. |
|
|
592 |
Return to contents. |
|
|
593 |
|
|
|
594 |
|
|
|
595 |
|
|
|
596 |
2.1.4 /OST - слияние одинаковых строковых констант. |
|
|
597 |
|
|
|
598 |
|
|
|
599 |
Если этот режим оптимизации будет активизирован, то компилятор будет |
|
|
600 |
запоминать все строковые константы и при обнаружении одинаковых в код |
|
|
601 |
файла не будет вставлена повторная строковая константа, а будет сделана |
|
|
602 |
ссылка на первую, обнаруженную ранее строковую константу. В оптимизации |
|
|
603 |
участвуют только неименованные строковые константы. Т.е. если массив или |
|
|
604 |
структура будет инициализированы строкой, то такая строка не будет |
|
|
605 |
участвовать в процессе инициализации, так эта строка может быть изменена |
|
|
606 |
в процессе работы программы. Пример: |
|
|
607 |
|
|
|
608 |
char var="test"; //эта строка не будет участвовать в процессе |
|
|
609 |
//оптимизации. |
|
|
610 |
|
|
|
611 |
void proc(){ |
|
|
612 |
WRITESTR("test"); // эта строка будет участвовать в оптимизации. |
|
|
613 |
AX="test"; // переменной AX будет присвоен адрес строки, |
|
|
614 |
// которая была вставлена в код программы в |
|
|
615 |
// предыдущей строке. |
|
|
616 |
} |
|
|
617 |
|
|
|
618 |
Обо всех случаях обнаружения повторной строки компилятор будет |
|
|
619 |
выдавать предупреждения. |
|
|
620 |
|
|
|
621 |
Включается этот режим оптимизации либо с командной строки /ost, либо |
|
|
622 |
директивой #pragma option ost, либо строкой в файле c--.ini - ost. |
|
|
623 |
Отключить, включенный ранее, этот режим можно директивой #pragma option ost-. |
|
|
624 |
Return to contents. |
|
|
625 |
|
|
|
626 |
|
|
|
627 |
|
|
|
628 |
2.1.5 /D - установка идентификатора в TRUE из командной строки. |
|
|
629 |
|
|
|
630 |
|
|
|
631 |
Если Вы написали программу, которая может компилироваться по разному, |
|
|
632 |
в зависимости от состояния некоторых идентификаторов (используется режим |
|
|
633 |
условной компиляции), то Вам очень может пригодится эта опция. |
|
|
634 |
Устанавливая с командной строки различные идентификаторы, Вы можете |
|
|
635 |
получать различные варианты программы, не редактируя исходный текст |
|
|
636 |
программы. |
|
|
637 |
|
|
|
638 |
Идентификатор вводится с командной строки ключом /d=idname. |
|
|
639 |
Return to contents. |
|
|
640 |
|
|
|
641 |
|
|
|
642 |
|
|
|
643 |
2.1.6 /IA - упрощенный ввод ассемблерных инструкций. |
|
|
644 |
|
|
|
645 |
|
|
|
646 |
Стало возможным использовать ассемблерные инструкции без префикса $ |
|
|
647 |
и вне блока asm. Этот режим включается: с командной строки опцией /ia; |
|
|
648 |
в файле конфигурации строкой ia или директивой #pragma option ia. |
|
|
649 |
|
|
|
650 |
Когда этот режим включен, все имена ассемблерных инструкций становятся |
|
|
651 |
зарезервированными словами, т.е. Вы не сможете эти имена использовать в |
|
|
652 |
качестве имен переменных или процедур. Ассемблерные инструкции компилятор |
|
|
653 |
распознает независимо от того, написаны они маленькими или большими |
|
|
654 |
буквами. |
|
|
655 |
Return to contents. |
|
|
656 |
|
|
|
657 |
|
|
|
658 |
|
|
|
659 |
2.1.7 /CRI - пропуск повторно включаемого файла. |
|
|
660 |
|
|
|
661 |
|
|
|
662 |
Чаще всего, повторно включать файл в компилируемый проект, нет |
|
|
663 |
необходимости, но это иногда происходит из-за того, что некоторые |
|
|
664 |
включаемые файлы сами включают другие файлы. Чтобы этого не происходило |
|
|
665 |
приходится делать проверку на повторную загрузку файла. Теперь эту |
|
|
666 |
функцию берет на себя компилятор и у Вас отпадает необходимость делать |
|
|
667 |
эту проверку. |
|
|
668 |
|
|
|
669 |
Но иногда (очень редко) возникает потребность сделать повторное |
|
|
670 |
включение файла. Для этого в компиляторе есть опция командной строки |
|
|
671 |
/cri-, которая запрещает компилятору делать проверку на повторное |
|
|
672 |
включение. Соответственно, для c--.ini файла, это можно сделать строкой |
|
|
673 |
cri- или директивой в компилируемом файле - #pragma option cri-. |
|
|
674 |
Return to contents. |
|
|
675 |
|
|
|
676 |
|
|
|
677 |
|
|
|
678 |
2.1.8 /IND - импорт имен процедур из DLL. |
|
|
679 |
|
|
|
680 |
|
|
|
681 |
Если Вы хотите в своей программе использовать DLL, для которой нет |
|
|
682 |
заголовочного файла с описанием процедур, то компилятор может |
|
|
683 |
импортировать имена из этой DLL. Для этого Вам надо указать имя этой |
|
|
684 |
библиотеки либо через опцию командной строки /ind=name.dll, либо в |
|
|
685 |
файле INI строкой 'ind=name.dll', либо через директиву '#pragma option |
|
|
686 |
ind=name.dll'. |
|
|
687 |
|
|
|
688 |
К недостатком такого способа получения имен можно отнести то, что при |
|
|
689 |
компиляции программы библиотека, из которой импортируются имена, |
|
|
690 |
обязательно должна присутствовать в компьютере. Также, если имена в |
|
|
691 |
библиотеке написаны без суффикса '@number', компилятор не будет |
|
|
692 |
контролировать число параметров передаваемых процедуре. И, к сожалению, |
|
|
693 |
компилятор умеет импортировать имена из библиотек имеющих только формат |
|
|
694 |
PE-файла. |
|
|
695 |
Return to contents. |
|
|
696 |
|
|
|
697 |
|
|
|
698 |
|
|
|
699 |
2.1.9 /WS - задать имя stub файла для программ под windows. |
|
|
700 |
|
|
|
701 |
|
|
|
702 |
Как известно, в программах под windows есть DOS заглушка, называемая |
|
|
703 |
stub, которой передается управление при запуске такой программы в чистом |
|
|
704 |
DOS-е. Обычно такая заглушка выводит на экран сообщение о том, что эту |
|
|
705 |
программу надо запускать в среде windows. |
|
|
706 |
|
|
|
707 |
Вы можете вместо стандартного stub использовать свой. Для этого Вам |
|
|
708 |
необходимо указать имя 16-битного EXE-файла либо через опцию командной |
|
|
709 |
строки /ws=filename, либо строкой в INI-файле ws=filename, либо |
|
|
710 |
директивой #pragma option ws=filename. |
|
|
711 |
|
|
|
712 |
Таким образом, у Вас появилась возможность создавать программы, |
|
|
713 |
работающие и под DOS и под windows. |
|
|
714 |
Return to contents. |
|
|
715 |
|
|
|
716 |
|
|
|
717 |
|
|
|
718 |
2.1.10 /WBSS - разместить не инициализированные данные в отдельной секции. |
|
|
719 |
|
|
|
720 |
|
|
|
721 |
Секция .bss создается автоматически при компиляции программ с ключом |
|
|
722 |
/w32. Если Вы хотите иметь эту секцию и при компиляции программ с |
|
|
723 |
ключами /w32c или /dll Вам необходимо добавить либо в командной |
|
|
724 |
строке опцию /wbss, либо строку wbss в INI-файле, либо директиву |
|
|
725 |
#pragma option wbss. |
|
|
726 |
|
|
|
727 |
Использование секции .bss практически не влияет на размер получаемого |
|
|
728 |
файла. Теоретически, для процессоров, у которых есть отдельный кэш для |
|
|
729 |
данных, использование секции .bss, должно повышать скорость работы |
|
|
730 |
программы. |
|
|
731 |
Return to contents. |
|
|
732 |
|
|
|
733 |
|
|
|
734 |
|
|
|
735 |
2.1.11 /DBG - создание отладочной информации. |
|
|
736 |
|
|
|
737 |
|
|
|
738 |
Если при компиляции программы в командную строку добавить ключ /dbg, |
|
|
739 |
или в файл конфигурации c--.ini добавить строку dbg, то компилятор после |
|
|
740 |
окончания компиляции создаст файл с отладочной информацией. Этот файл |
|
|
741 |
имеет имя главного модуля и имеет расширение *.tds. |
|
|
742 |
|
|
|
743 |
Отладочная информация создаваемая компилятором C-- совместима с |
|
|
744 |
отладочной информацией создаваемой компиляторами фирмы Borland. Но, пока, |
|
|
745 |
эта информация реализована еще не в полном объеме. Создаваемой сейчас |
|
|
746 |
отладочной информации достаточно для проведения простейшей отладки |
|
|
747 |
программы. |
|
|
748 |
|
|
|
749 |
Для 16-битных программ под DOS для отладки надо использовать Turbo |
|
|
750 |
Debugger из пакета Borland C v4.5 или лучше (файл td.exe). |
|
|
751 |
|
|
|
752 |
Для программ под Windows надо использовать 32-битный отладчик из этого |
|
|
753 |
же пакета (файл td32.exe). |
|
|
754 |
|
|
|
755 |
Для 32-битных программ, использующих расширитель DOS применять для |
|
|
756 |
отладки Turbo Debugger невозможно. Но, может быть я не знаю, как это |
|
|
757 |
делать. Если Вы знаете, как создавать 32-битные программы с |
|
|
758 |
DOS-расширителем компиляторами фирмы Borland с включением в них отладочной |
|
|
759 |
информации, то расскажите мне. А я попробую применить это для C--. |
|
|
760 |
Return to contents. |
|
|
761 |
|
|
|
762 |
|
|
|
763 |
|
|
|
764 |
2.1.12 /J0 /J1 /J2 |
|
|
765 |
|
|
|
766 |
|
|
|
767 |
Синонимом ключей /J0 /J1 /J2 является директива #jumptomain с |
|
|
768 |
параметрами NONE, SHORT и NEAR соответственно. |
|
|
769 |
|
|
|
770 |
Директива #jumptomain выполняет немного различные функции в |
|
|
771 |
зависимости от типа выходного файла. |
|
|
772 |
|
|
|
773 |
Компиляция файла типа *.com и *.exe модель памяти tiny: |
|
|
774 |
|
|
|
775 |
#jumptomain NONE (-j0) - в этом случае по окончании кода начальной |
|
|
776 |
инициализации программы не генерируется jmp на процедуру main. Эту |
|
|
777 |
директиву следует использовать в случае, если до процедуры main нет других |
|
|
778 |
не динамических процедур и инициализированных переменных. |
|
|
779 |
|
|
|
780 |
#jumptomain SHORT (-j1) - в этом случае по окончании кода начальной |
|
|
781 |
инициализации генерируется короткий jmp на процедуру main. Эту директиву |
|
|
782 |
следует использовать, если до процедуры main находится не более 128 байт |
|
|
783 |
кода и данных. |
|
|
784 |
|
|
|
785 |
#jumptomain NEAR (-j2) - это состояние устанавливается по умолчанию. При |
|
|
786 |
этом генерируется близкий jmp на процедуру main. |
|
|
787 |
|
|
|
788 |
Компиляция файлов *.exe (ключи -exe -d32 -w32 -w32c): |
|
|
789 |
|
|
|
790 |
#jumptomain NONE (-j0) - в этом случае код начальной инициализации |
|
|
791 |
программы не генерируется и управление при запуске передается сразу на |
|
|
792 |
процедуру main. |
|
|
793 |
|
|
|
794 |
Во всех остальных случаях генерируется код начальной инициализации и |
|
|
795 |
управление на процедуру main передается инструкцией call. |
|
|
796 |
|
|
|
797 |
Компиляция файлов *.dll: |
|
|
798 |
|
|
|
799 |
#jumptomain NONE (-j0) - в этом случае код начальной инициализации |
|
|
800 |
программы не генерируется и управление при запуске передается сразу на |
|
|
801 |
процедуру main. |
|
|
802 |
|
|
|
803 |
Во всех остальных случаях генерируется код заглушки и управление на |
|
|
804 |
процедуру main не передается. Фактически процедура main в этом случае не |
|
|
805 |
нужна. |
|
|
806 |
|
|
|
807 |
Процедура main при создании файлов DLL должна выглядеть немного иначе, |
|
|
808 |
чем в других случаях: |
|
|
809 |
|
|
|
810 |
dword main ( dword hInstDLL, reason, reserv ) |
|
|
811 |
{ |
|
|
812 |
... |
|
|
813 |
} |
|
|
814 |
Return to contents. |
|
|
815 |
|
|
|
816 |
|
|
|
817 |
|
|
|
818 |
2.1.13 /LST - Создание ассемблерного листинга. |
|
|
819 |
|
|
|
820 |
|
|
|
821 |
С помощью дополнительной опции командной строки -lst Вы можете |
|
|
822 |
получить вместе с исполнительным файлом и его ассемблерный листинг. |
|
|
823 |
Листинг будет помещен в файл одноименный с исполнительным файлом и |
|
|
824 |
имеющим расширение *.lst. |
|
|
825 |
|
|
|
826 |
Ассемблерный листинг создается независимой от компилятора частью кода |
|
|
827 |
с использованием информации накапливаемой при компиляции программы. |
|
|
828 |
Return to contents. |
|
|
829 |
|
|
|
830 |
|
|
|
831 |
|
|
|
832 |
2.1.14 /ENV - Сохранение адреса переменных окружения. |
|
|
833 |
|
|
|
834 |
|
|
|
835 |
Если при компиляции программы Вы в командную строку добавите опцию |
|
|
836 |
-ENV или в файл c--.ini строка ENV, то компилятор добавит в вашу |
|
|
837 |
программу переменную environ, в которой при загрузке будет сохранятся |
|
|
838 |
адрес переменных окружения запускаемой программы. Для программ под |
|
|
839 |
Windows это будет полный адрес, а для остальных в этой переменной будет |
|
|
840 |
сохраняться только адрес сегмента. |
|
|
841 |
Return to contents. |
|
|
842 |
|
|
|
843 |
|
|
|
844 |
|
|
|
845 |
2.1.15 /CPA - Очистка post-области данных. |
|
|
846 |
|
|
|
847 |
|
|
|
848 |
Переменные, которым в теле программы не было присвоено никакое |
|
|
849 |
значение, не включаются в тело скомпилированной программы. Для них |
|
|
850 |
резервируется память за пределами программы. Но эта память может быть |
|
|
851 |
заполнена произвольной информацией. |
|
|
852 |
|
|
|
853 |
Если Вам необходимо, чтобы неинициализированные переменные при |
|
|
854 |
загрузке программы всегда содержали одно и тоже значение (ноль) - |
|
|
855 |
включите в командную строку опцию -CPA. |
|
|
856 |
Return to contents. |
|
|
857 |
|
|
|
858 |
|
|
|
859 |
|
|
|
860 |
2.1.16 /W - вывод предупреждений. |
|
|
861 |
|
|
|
862 |
|
|
|
863 |
По умолчанию компилятор не выводит предупреждения и многие даже не |
|
|
864 |
подозревают о существовании такой полезной опции. В C-- предупреждения |
|
|
865 |
фактически являются подсказками для создания оптимальных программ и |
|
|
866 |
зачастую облегчают отладку программ. В предупреждениях компилятор может |
|
|
867 |
сообщить Вам о том, в каком месте можно использовать короткие формы |
|
|
868 |
операторов IF, WHILE, FOR... О том, какие процедуры, переменные и |
|
|
869 |
структуры определенные в вашей программе не были использованы. О том |
|
|
870 |
какие регистры компилятор использовал без вашего ведома и много другой |
|
|
871 |
полезной информации. |
|
|
872 |
|
|
|
873 |
По умолчанию предупреждения выводятся на экран. Но их бывает так |
|
|
874 |
много, что они могут не поместиться на экране. Поэтому в компиляторе есть |
|
|
875 |
опция, по которой все предупреждения выводятся в файл. Имя этого файла |
|
|
876 |
задается в той же опции. Поместив в свой c--.ini файл пару вот этих строк: |
|
|
877 |
|
|
|
878 |
w |
|
|
879 |
wf=warning |
|
|
880 |
|
|
|
881 |
Вы будете получать в файле warning предупреждения. |
|
|
882 |
Return to contents. |
|
|
883 |
|
|
|
884 |
|
|
|
885 |
|
|
|
886 |
2.1.17 /NW - Выборочное отключение типов предупреждений. |
|
|
887 |
|
|
|
888 |
|
|
|
889 |
Сейчас компилятор может выдавать 12 типов предупреждений и, иногда их |
|
|
890 |
бывает так много, что становится трудно в них ориентироваться. Теперь |
|
|
891 |
можно выборочно запрещать выдачу предупреждений. Для этого в командной |
|
|
892 |
строке (или в файле C--.INI) можно установить опцию /nw=number, где |
|
|
893 |
number - число от 1 до 12. Этим цифрам соответствуют следующие типы |
|
|
894 |
предупреждений: |
|
|
895 |
|
|
|
896 |
1 - Optimize numerical expressions |
|
|
897 |
2 - Compiler used register ..." |
|
|
898 |
3 - Short operator '...' may be used |
|
|
899 |
4 - String '...' repeated |
|
|
900 |
5 - Expansion variable |
|
|
901 |
6 - Signed value returned |
|
|
902 |
7 - '...' defined above, therefore skipped. |
|
|
903 |
8 - Variable/structure/procedure '...' possible not used |
|
|
904 |
9 - Non-initialized variable may have been used |
|
|
905 |
10 - Return flag was destroyed |
|
|
906 |
11 - Code may not be executable |
|
|
907 |
12 - Don't use local/parametric values in inline procedures |
|
|
908 |
Return to contents. |
|
|
909 |
|
|
|
910 |
|
|
|
911 |
|
|
|
912 |
2.1.18 /WSI - короткая таблица импорта. |
|
|
913 |
|
|
|
914 |
|
|
|
915 |
Таблица импорта обычно состоит в свою очередь из четырех таблиц. Две |
|
|
916 |
таблицы LookUp Table и Import Address Table абсолютно одинаковы. |
|
|
917 |
|
|
|
918 |
Опцией командной строки /WSI Вы можете заставить компилятор |
|
|
919 |
генерировать только одну из этих двух одинаковых таблиц (генерируется |
|
|
920 |
только Import Address Table). Тем самым у Вас получится более компактная |
|
|
921 |
таблица импорта, что приведет, в некоторых случаях, к созданию более |
|
|
922 |
компактного выходного файла. |
|
|
923 |
Return to contents. |
|
|
924 |
|
|
|
925 |
|
|
|
926 |
|
|
|
927 |
2.2 Директивы транслятора. |
|
|
928 |
|
|
|
929 |
|
|
|
930 |
C-- не содержит препроцессор. Тем не менее, есть несколько функций |
|
|
931 |
очень похожих на функции C препроцессора. |
|
|
932 |
|
|
|
933 |
Они даются как директивы транслятора. Все директивы транслятора |
|
|
934 |
начинаются с вопросительного знака ? либо с символа #. Вот список имеющихся |
|
|
935 |
директив и их назначение: |
|
|
936 |
|
|
|
937 |
? align [val] Выровнять данные программы на четный по |
|
|
938 |
умолчанию или на адрес кратный величине val. |
|
|
939 |
|
|
|
940 |
? aligncode [val] Выровнять код программы на четный по |
|
|
941 |
умолчанию или на адрес кратный величине val. |
|
|
942 |
Заполнение производится кодом 0x90. |
|
|
943 |
|
|
|
944 |
? aligner (aligner value) определить значение байта вставки. |
|
|
945 |
|
|
|
946 |
? alignword (TRUE or FALSE) разрешает или запрещает выравнивание на |
|
|
947 |
четный адрес переменных типа word и int, |
|
|
948 |
значение по умолчанию TRUE. |
|
|
949 |
|
|
|
950 |
? argc (TRUE or FALSE) Включить или отключить альтернативный |
|
|
951 |
обработчик командной строки. |
|
|
952 |
|
|
|
953 |
? atexit Вставляет в startup код поддержки процедуры |
|
|
954 |
ATEXIT(). |
|
|
955 |
|
|
|
956 |
? code32 (TRUE/FALSE) разрешает/запрещает генерацию 32-битного |
|
|
957 |
кода. |
|
|
958 |
|
|
|
959 |
? codesize оптимизация размера кода (в ущерб скорости). |
|
|
960 |
|
|
|
961 |
? compilerversion min-vers указывает, компилятор какой версии необходим |
|
|
962 |
для компиляции данной программы. |
|
|
963 |
|
|
|
964 |
? ctrl_c (TRUE or FALSE ) разрешает или запрещает игнорирование |
|
|
965 |
нажатия CTRL-C. |
|
|
966 |
|
|
|
967 |
? dataseg (value) указывает компилятору сегментный адрес ОЗУ |
|
|
968 |
для переменных при компиляции ROM-BIOS. |
|
|
969 |
|
|
|
970 |
? define (identifier) (token) определяет идентификатор. |
|
|
971 |
|
|
|
972 |
? DOSrequired (номер) устанавливает минимальную требуемую версию |
|
|
973 |
DOS: старший байт - номер версии, |
|
|
974 |
младший байт - номер модификации: |
|
|
975 |
0x0101 для версии 1.1 DOS |
|
|
976 |
0x0315 для версии 3.21 DOS |
|
|
977 |
0x0303 для версии 3.3 DOS |
|
|
978 |
0x0600 для версии 6.0 DOS |
|
|
979 |
0x0602 для версии 6.2 DOS и т.д. |
|
|
980 |
|
|
|
981 |
? dosstring (TRUE/FALSE) указывает компилятору, что в качестве |
|
|
982 |
терминатора строки надо использовать символ $ |
|
|
983 |
|
|
|
984 |
? else генерирует альтернативный код если ?ifdef или |
|
|
985 |
?ifndef принимают значение FALSE (пример |
|
|
986 |
использования смотрите в файле FPU.H--) |
|
|
987 |
|
|
|
988 |
? endif указывает на конец действия директив ifdef и |
|
|
989 |
ifndef |
|
|
990 |
|
|
|
991 |
? fastcallapi (FALSE/TRUE) запретить/разрешить генерацию быстрого вызова |
|
|
992 |
API-процедур (по умолчанию разрешено). |
|
|
993 |
Директива работает при компиляции программ |
|
|
994 |
под Windows. |
|
|
995 |
|
|
|
996 |
? fixuptable (TRUE/FALSE) разрешить/запретить создание FixUp таблицы |
|
|
997 |
(по умолчанию запрещено). Директива работает |
|
|
998 |
при компиляции программ под Windows. |
|
|
999 |
|
|
|
1000 |
? ifdef (identifier) если идентификатор определен, то возвращает |
|
|
1001 |
TRUE иначе FALSE |
|
|
1002 |
|
|
|
1003 |
? imagebase value задает адрес Image Base. По умолчанию этот |
|
|
1004 |
адрес равен 0x400000. Директива работает при |
|
|
1005 |
компиляции программ под Windows. |
|
|
1006 |
|
|
|
1007 |
? ifndef (identifier) если идентификатор определен, то возвращает |
|
|
1008 |
FALSE иначе TRUE |
|
|
1009 |
|
|
|
1010 |
? include ("filename") включает другой файл. |
|
|
1011 |
|
|
|
1012 |
? includepath ("path") указание компилятору, в какой директории надо |
|
|
1013 |
искать включаемые файлы |
|
|
1014 |
|
|
|
1015 |
? initallvar инициализирует 0 все неинициализированные |
|
|
1016 |
переменные. |
|
|
1017 |
|
|
|
1018 |
? jumptomain (NONE, SHORT, NEAR or FALSE) |
|
|
1019 |
устанавливает тип перехода к main(), |
|
|
1020 |
значение по умолчанию - NEAR. |
|
|
1021 |
|
|
|
1022 |
? maxerrors (number) максимальное количество найденных ошибок, |
|
|
1023 |
превысив которое транслятор прекращает |
|
|
1024 |
работу, значение по умолчанию - 16. |
|
|
1025 |
|
|
|
1026 |
? movedatarom (TRUE/FALSE) указывает компилятору о необходимости |
|
|
1027 |
переноса данных из ПЗУ в ОЗУ. |
|
|
1028 |
|
|
|
1029 |
? parsecommandline (TRUE or FALSE) |
|
|
1030 |
включает в программу блок кода для |
|
|
1031 |
синтаксического анализа командной строки |
|
|
1032 |
значение по умолчанию FALSE. |
|
|
1033 |
|
|
|
1034 |
? pragma может объявлять несколько других директив |
|
|
1035 |
|
|
|
1036 |
? print (number or string) выводит на экран строку или число. |
|
|
1037 |
|
|
|
1038 |
? printhex (number) выводит на экран число в шестнадцатеричном |
|
|
1039 |
коде. |
|
|
1040 |
|
|
|
1041 |
? randombyte вставляет в код программы байт случайного |
|
|
1042 |
значения. |
|
|
1043 |
|
|
|
1044 |
? resize (TRUE or FALSE) включает функцию изменения после запуска |
|
|
1045 |
размера выделенного программе блока памяти |
|
|
1046 |
на минимально требуемый объем, |
|
|
1047 |
значение по умолчанию TRUE. |
|
|
1048 |
|
|
|
1049 |
? resizemessage (string) сообщение, выводимое на экран перед |
|
|
1050 |
аварийным прерыванием выполнения программы, |
|
|
1051 |
если изменение размера выделенного программе |
|
|
1052 |
блока памяти не выполнено. |
|
|
1053 |
|
|
|
1054 |
? setdinproc по этой директиве компилятор немедленно |
|
|
1055 |
вставляет в код компилируемой программы все |
|
|
1056 |
вызывавшиеся ранее динамические процедуры. |
|
|
1057 |
|
|
|
1058 |
? sizerom (value) указывает компилятору размер ПЗУ. |
|
|
1059 |
|
|
|
1060 |
? speed оптимизация быстродействия (значение |
|
|
1061 |
по умолчанию) в ущерб размеру кода. |
|
|
1062 |
|
|
|
1063 |
? stack (number) определяет размер стека программы в байтах. |
|
|
1064 |
|
|
|
1065 |
? startaddress (number) устанавливает стартовый адрес начала кода, |
|
|
1066 |
значение по умолчанию 0x100. |
|
|
1067 |
|
|
|
1068 |
? startuptomain в com-файлах размещает startup-код в |
|
|
1069 |
процедуре main(). |
|
|
1070 |
|
|
|
1071 |
? startusevar (number) указывает адрес, с которого разрешено |
|
|
1072 |
использовать ячейки памяти под |
|
|
1073 |
неинициализированные переменные. |
|
|
1074 |
|
|
|
1075 |
? sysattribute (значение) эта директива передает компилятору атрибут |
|
|
1076 |
создаваемого драйвера. По умолчанию |
|
|
1077 |
устанавливается значение 0x2000. |
|
|
1078 |
Действует только с ключом /SYS. |
|
|
1079 |
|
|
|
1080 |
? sysname <текстовая строка> эта директива передает компилятору имя |
|
|
1081 |
будущего драйвера. По умолчанию |
|
|
1082 |
присваивается имя NO_NAME. Длина имени не |
|
|
1083 |
более 8 символов. Действует только с ключом |
|
|
1084 |
/SYS. |
|
|
1085 |
|
|
|
1086 |
? syscommand ,, ...; - эта директива |
|
|
1087 |
является обязательной при создании |
|
|
1088 |
драйверов. По этой директиве компилятору |
|
|
1089 |
передается список имен процедур обработки |
|
|
1090 |
команд драйвера. Действует только с ключом |
|
|
1091 |
/SYS. |
|
|
1092 |
|
|
|
1093 |
? warning (TRUE or FALSE) эта директива разрешает или запрещает выдачу |
|
|
1094 |
предупреждений. Директива действует только в |
|
|
1095 |
пределах текущего файла и не влияет на |
|
|
1096 |
включаемые файлы. |
|
|
1097 |
|
|
|
1098 |
? winmonoblock FALSE запрещает размещение таблиц файла формата PE |
|
|
1099 |
в одну секцию. |
|
|
1100 |
|
|
|
1101 |
? undef уничтожает константы объявленные директивой |
|
|
1102 |
? define |
|
|
1103 |
|
|
|
1104 |
? use8086 ограничивается при генерации объектного кода |
|
|
1105 |
командами 8088/8086 (значение по умолчанию). |
|
|
1106 |
|
|
|
1107 |
? use8088 ограничивается при генерации объектного кода |
|
|
1108 |
командами 8088/8086 (значение по умолчанию). |
|
|
1109 |
|
|
|
1110 |
? use80186 допускает при генерации объектного кода |
|
|
1111 |
команды и оптимизацию для процессора 80186. |
|
|
1112 |
|
|
|
1113 |
? use80286 допускает при генерации объектного кода |
|
|
1114 |
команды и оптимизацию для процессора 80286. |
|
|
1115 |
|
|
|
1116 |
? use80386 допускает при генерации объектного кода |
|
|
1117 |
команды и оптимизацию для процессора 80386. |
|
|
1118 |
|
|
|
1119 |
? use80486 допускает при генерации объектного кода |
|
|
1120 |
команды и оптимизацию для процессора 80486. |
|
|
1121 |
|
|
|
1122 |
? usePentium допускает при генерации объектного кода |
|
|
1123 |
команды и оптимизацию для процессора Pentium. |
|
|
1124 |
|
|
|
1125 |
? useMMX допускает при генерации объектного кода |
|
|
1126 |
команды и оптимизацию для процессора Pentium |
|
|
1127 |
MMX. |
|
|
1128 |
|
|
|
1129 |
? usestartup разрешает компилятору использовать ячейки |
|
|
1130 |
памяти, занимаемые кодом начальной |
|
|
1131 |
инициализации программы. |
|
|
1132 |
Return to contents. |
|
|
1133 |
|
|
|
1134 |
|
|
|
1135 |
|
|
|
1136 |
2.2.1 ?ifdef/?ifndef |
|
|
1137 |
|
|
|
1138 |
|
|
|
1139 |
Ранее директива ?ifdef срабатывала на наличие константы независимо |
|
|
1140 |
от значения ее величины, а директива ?ifndef срабатывала на отсутствие |
|
|
1141 |
константы в компилируемом файле. Теперь ?indef срабатывает лишь на |
|
|
1142 |
константу отличную от FALSE, а ?ifndef срабатывает как на отсутствие |
|
|
1143 |
константы в компилируемом файле, так и на константу имеющую значение |
|
|
1144 |
FALSE. |
|
|
1145 |
|
|
|
1146 |
Для директив ?ifdef/?ifndef зарезервированы константы codesize и |
|
|
1147 |
speed, которые принимают значение TRUE или FALSE в зависимости от режима |
|
|
1148 |
оптимизации. Это будет полезным для создания более гибких библиотек. |
|
|
1149 |
|
|
|
1150 |
Есть возможность проверки типа CPU для которого ведется компиляция. |
|
|
1151 |
Допустимые варианты синтаксиса: |
|
|
1152 |
|
|
|
1153 |
?ifdef cpu > 1 //если программа компилируется для CPU выше 80186 |
|
|
1154 |
?ifndef cpu >= 2 // -------//------------- не больше или равно 80286 |
|
|
1155 |
?ifdef cpu == 3 // -------//------------- равно 80386 |
|
|
1156 |
?ifdef cpu != 0 // -------//------------- не равен 8086 |
|
|
1157 |
?ifdef cpu < 3 // -------//------------- хуже 80386 |
|
|
1158 |
?ifdef cpu <= 2 // -------//------------- хуже или равен 80286 |
|
|
1159 |
|
|
|
1160 |
Эта директива позволит Вам писать одну процедуру для различных типов |
|
|
1161 |
CPU. |
|
|
1162 |
Return to contents. |
|
|
1163 |
|
|
|
1164 |
|
|
|
1165 |
|
|
|
1166 |
2.2.2 ?initallvar |
|
|
1167 |
|
|
|
1168 |
|
|
|
1169 |
Директивой ?initallvar TRUE включается режим при котором всем |
|
|
1170 |
неинициализированным переменным будет присвоено нулевое значение и они |
|
|
1171 |
будут располагаться в том месте, где были объявлены. Т.е. практически |
|
|
1172 |
исчезнут неинициализированные переменные. Это может быть полезным при |
|
|
1173 |
написании драйверов и резидентных программ. |
|
|
1174 |
|
|
|
1175 |
Параметр FALSE этой директивы отключает этот режим. |
|
|
1176 |
По умолчанию эта директива установлена в FALSE. |
|
|
1177 |
Return to contents. |
|
|
1178 |
|
|
|
1179 |
|
|
|
1180 |
|
|
|
1181 |
2.2.3 ?usestartup |
|
|
1182 |
|
|
|
1183 |
|
|
|
1184 |
Директива ?usestartup разрешает компилятору использовать ячейки кода |
|
|
1185 |
начальной инициализации программы (startup) для последующего размещения в |
|
|
1186 |
них неинициализированных переменных. Это может быть полезным для получения |
|
|
1187 |
более компактного кода, как обычных программ, так и в особенности |
|
|
1188 |
резидентных. |
|
|
1189 |
|
|
|
1190 |
Эту директиву применяют только для генерации *.COM файлов. |
|
|
1191 |
Return to contents. |
|
|
1192 |
|
|
|
1193 |
|
|
|
1194 |
|
|
|
1195 |
2.2.4 ?startusevar |
|
|
1196 |
|
|
|
1197 |
|
|
|
1198 |
Директивой ?startusevar можно указать начальный адрес с которого |
|
|
1199 |
компилятор будет распределять память для неинициализированных переменных. |
|
|
1200 |
Например, получив директиву ?startusevar 0x53 компилятор будет |
|
|
1201 |
располагать неинициализированные переменные, начиная с адреса 0x53. Это |
|
|
1202 |
может быть полезным для получения более компактного кода как для |
|
|
1203 |
резидентных, так и для обычных программ. |
|
|
1204 |
|
|
|
1205 |
Эту директиву применяют только для генерации *.COM файлов. |
|
|
1206 |
Return to contents. |
|
|
1207 |
|
|
|
1208 |
|
|
|
1209 |
|
|
|
1210 |
2.2.5 ?atexit |
|
|
1211 |
|
|
|
1212 |
|
|
|
1213 |
Директива ?atexit добавляет в startup программы код поддержки |
|
|
1214 |
процедуры ATEXIT, резервирует место для хранения 16 адресов процедур и |
|
|
1215 |
изменяет код процедур ABORT и EXIT. |
|
|
1216 |
|
|
|
1217 |
Процедура ATEXIT регистрирует процедуру, адрес которой передается ей в |
|
|
1218 |
качестве параметра, как процедуру завершения программы. Эта процедура |
|
|
1219 |
будет вызвана в момент завершения программы процедурами ABORT или EXIT |
|
|
1220 |
или инструкцией RET из main. |
|
|
1221 |
|
|
|
1222 |
Всего можно зарегистрировать до 16 процедур. Процедуры вызываются в |
|
|
1223 |
порядке обратном порядку их регистрации. |
|
|
1224 |
Return to contents. |
|
|
1225 |
|
|
|
1226 |
|
|
|
1227 |
|
|
|
1228 |
2.2.6 ?startuptomain |
|
|
1229 |
|
|
|
1230 |
|
|
|
1231 |
По этой директиве компилятор в начале файла делает jmp на начало |
|
|
1232 |
процедуры main(). Перед началом компиляции этой процедуры компилятор |
|
|
1233 |
начнет компиляцию startup кода и лишь затем будет продолжена компиляция |
|
|
1234 |
процедуры main(). Тем самым startup код окажется не в начале файла, как |
|
|
1235 |
это происходит обычно, а в теле процедуры main(). Это будет полезным при |
|
|
1236 |
компиляции резидентных программ (TSR). |
|
|
1237 |
|
|
|
1238 |
Директива ?startuptomain работает только при компиляции com-файлов. |
|
|
1239 |
Return to contents. |
|
|
1240 |
|
|
|
1241 |
|
|
|
1242 |
|
|
|
1243 |
2.2.7 ?undef |
|
|
1244 |
|
|
|
1245 |
|
|
|
1246 |
Эта директива уничтожает константы объявленные директивой ?define. Ее |
|
|
1247 |
можно применять для изменения в процессе компиляции значения какой-нибудь |
|
|
1248 |
константы. |
|
|
1249 |
Return to contents. |
|
|
1250 |
|
|
|
1251 |
|
|
|
1252 |
|
|
|
1253 |
2.2.8 ?align и ?aligncode |
|
|
1254 |
|
|
|
1255 |
|
|
|
1256 |
В C-- существует директива ?align, которая делает однократное |
|
|
1257 |
выравнивание данных на четный адрес. Но если к этой директиве добавить |
|
|
1258 |
число, то выравнивание будет произведено на адрес кратный этому числу. |
|
|
1259 |
Например директива ?align 4 дополнит сегмент данных до адреса кратного |
|
|
1260 |
4. При выравнивании будут вставляться байты, значения которых определяются |
|
|
1261 |
директивой ?aligner, по умолчанию это значение равно нулю. Директива |
|
|
1262 |
?align производит выравнивание только в сегменте данных. В тех моделях |
|
|
1263 |
памяти, в которых сегмент данных и кода совпадают эту директиву можно |
|
|
1264 |
применять и для выравнивания начала процедур. |
|
|
1265 |
|
|
|
1266 |
Директива ?aligncode [value] делает выравнивание в сегменте кода на |
|
|
1267 |
адрес кратный значению value, по умолчанию на четный адрес. Значение байта |
|
|
1268 |
заполнения в этой директиве является число 0x90 - код инструкции NOP. |
|
|
1269 |
Значение байта заполнения для этой директивы изменить нельзя. Т.о. эту |
|
|
1270 |
директиву можно применять и внутри исполняемого кода. Например, если Вы |
|
|
1271 |
хотите получить быстрый код на 486 процессоре, то рекомендуется делать |
|
|
1272 |
выравнивание начала процедур и циклов на адрес кратный 16. |
|
|
1273 |
Return to contents. |
|
|
1274 |
|
|
|
1275 |
|
|
|
1276 |
|
|
|
1277 |
2.2.9 ?pragma |
|
|
1278 |
|
|
|
1279 |
|
|
|
1280 |
Директива #pragma это многофункциональнальная директива, которая в |
|
|
1281 |
свою очередь имеет свои директивы: |
|
|
1282 |
|
|
|
1283 |
option |
|
|
1284 |
Директива option позволяет включить в Ваш код опции командной строки |
|
|
1285 |
компилятора. Некоторые опции не могут быть использованы в этой директиве; |
|
|
1286 |
другие должны помещаться в самом начале исходного текста. Пример: |
|
|
1287 |
|
|
|
1288 |
#pragma option w32c |
|
|
1289 |
|
|
|
1290 |
Эта директива объявляет компилятору, что надо создать консольный |
|
|
1291 |
32-битный файл под windows. |
|
|
1292 |
|
|
|
1293 |
startup |
|
|
1294 |
Директивой startup можно указать функцию, которая будет выполнена перед |
|
|
1295 |
запуском процедуры main. Эта директива имеет такой формат: |
|
|
1296 |
|
|
|
1297 |
#pragma startup procname |
|
|
1298 |
|
|
|
1299 |
Количество раз, которое можно применять эту директиву в одной |
|
|
1300 |
программе не ограничено, но реально можно использовать лишь несколько |
|
|
1301 |
тысяч раз. |
|
|
1302 |
|
|
|
1303 |
line |
|
|
1304 |
Директива line выводит на экран номер текущей строки и имя файла. |
|
|
1305 |
Дополнительно может выводиться содержимое строки находящееся после слова |
|
|
1306 |
line. Пример: |
|
|
1307 |
|
|
|
1308 |
#pragma line information |
|
|
1309 |
|
|
|
1310 |
Встретив эту директиву, компилятор выведет на экран номер строки и имя |
|
|
1311 |
файла. Также будет выведено сообщение справа от слова line, если оно |
|
|
1312 |
есть. |
|
|
1313 |
|
|
|
1314 |
resource |
|
|
1315 |
Эта директива может принимать значения start и end. Эти два |
|
|
1316 |
значения выделяют начало и конец блока ресурсов, если вы используете его |
|
|
1317 |
непосредственно в исходном коде файла, а не в отдельном файле. Пример: |
|
|
1318 |
|
|
|
1319 |
#pragma resource start |
|
|
1320 |
|
|
|
1321 |
MyMenu MENU DISCARDABLE |
|
|
1322 |
BEGIN POPUP "Files",HELP |
|
|
1323 |
BEGIN |
|
|
1324 |
MENUITEM "Open", ID_OPEN |
|
|
1325 |
MENUITEM "Save", ID_SAVE |
|
|
1326 |
MENUITEM SEPARATOR |
|
|
1327 |
MENUITEM "Exit", ID_EXIT |
|
|
1328 |
END |
|
|
1329 |
MENUITEM "Other", 65535 |
|
|
1330 |
END |
|
|
1331 |
|
|
|
1332 |
#pragma resource end |
|
|
1333 |
Return to contents. |
|
|
1334 |
|
|
|
1335 |
|
|
|
1336 |
|
|
|
1337 |
3. Константы. |
|
|
1338 |
|
|
|
1339 |
3.1 Числовые константы. |
|
|
1340 |
|
|
|
1341 |
|
|
|
1342 |
Представление числовых констант в виде десятичных чисел (чисел с |
|
|
1343 |
основанием 10) и шестнадцатеричных чисел (основание счисления 16) полностью |
|
|
1344 |
аналогично языку C. |
|
|
1345 |
|
|
|
1346 |
При двоичном представлении чисел (основание 2) число должно начинаться |
|
|
1347 |
с символов 0b, за которыми без пробела идет последовательность нулей и |
|
|
1348 |
единиц. |
|
|
1349 |
|
|
|
1350 |
При восьмеричном представлении чисел (основание 8) число должно |
|
|
1351 |
начинаться с символов 0o, за которыми без пробела идет последовательность |
|
|
1352 |
цифр. |
|
|
1353 |
|
|
|
1354 |
Вещественное число отличается от целого по наличию в нем точки. |
|
|
1355 |
Начинаться вещественное число должно либо цифрой от 0 до 9, либо знаком |
|
|
1356 |
минус. Необязательной частью вещественного числа является показатель |
|
|
1357 |
степени. Показатель степени отделяется от числа символом e или E. |
|
|
1358 |
Пробелы недопустимы. |
|
|
1359 |
|
|
|
1360 |
Примеры: |
|
|
1361 |
0b11111111 // двоичное представление числа 255 |
|
|
1362 |
0x00F // шестнадцатеричное представление числа 15 |
|
|
1363 |
0o10 // восьмеричное представление числа 8 |
|
|
1364 |
1.234567E-20 // вещественное число |
|
|
1365 |
|
|
|
1366 |
C-- вместе с традиционным C-стилем шестнадцатеричных чисел понимает и |
|
|
1367 |
числа записанные в стиле ассемблера. Для тех, кто вдруг не знает, сообщаю, |
|
|
1368 |
что шестнадцатеричные числа в ассемблере имеют на конце символ h или H. |
|
|
1369 |
Если первый символ шестнадцатеричного числа больше 9, то перед ним |
|
|
1370 |
обязательно должен быть записан символ 0. Примеры: |
|
|
1371 |
|
|
|
1372 |
1234h |
|
|
1373 |
0A000H |
|
|
1374 |
|
|
|
1375 |
К числовым константам можно писать суффиксы L, U и F. Фактически |
|
|
1376 |
эти суффиксы в C-- не играют никакой роли, компилятор их просто |
|
|
1377 |
проглатывает. Пример: |
|
|
1378 |
|
|
|
1379 |
#define DEF 1L |
|
|
1380 |
#define DEF2 2Lu |
|
|
1381 |
#define DEF3 3.0F |
|
|
1382 |
|
|
|
1383 |
Эти суффиксы не зависят от регистра, т.е. их можно писать как |
|
|
1384 |
маленькими, так и большими буквами. |
|
|
1385 |
Return to contents. |
|
|
1386 |
|
|
|
1387 |
|
|
|
1388 |
|
|
|
1389 |
3.2 Символьные константы. |
|
|
1390 |
|
|
|
1391 |
|
|
|
1392 |
Одиночные символьные константы, как и в C, должны заключаться в |
|
|
1393 |
одиночные кавычки '. |
|
|
1394 |
|
|
|
1395 |
Также как и в C, для обозначения специальных символов служит обратная |
|
|
1396 |
наклонная черта вправо \ с последующим за ней ключевым символом (или |
|
|
1397 |
несколькими символами). Поддерживаются следующие специальные символы: |
|
|
1398 |
|
|
|
1399 |
\a /* звуковой сигнал */ |
|
|
1400 |
\b /* забой */ |
|
|
1401 |
\f /* перевод страницы */ |
|
|
1402 |
\l /* перевод строки */ |
|
|
1403 |
\n /* возврат каретки*/ |
|
|
1404 |
\r /* возврат каретки*/ |
|
|
1405 |
\t /* табуляция */ |
|
|
1406 |
\x?? /* символ ASCII, соответствующий байтовому представлению, |
|
|
1407 |
состоящему из двух шестнадцатеричных цифр, расположенных |
|
|
1408 |
на месте знаков вопроса */ |
|
|
1409 |
\??? /* символ ASCII, соответствующий байтовому представлению, |
|
|
1410 |
состоящему из трех десятичных цифр, расположенных |
|
|
1411 |
на месте знаков вопроса */ |
|
|
1412 |
|
|
|
1413 |
Любой другой символ после обратной наклонной черты вправо будет принят |
|
|
1414 |
как простой символ. |
|
|
1415 |
|
|
|
1416 |
Символ "Одиночная кавычка" ' может быть введен при помощи конструкции |
|
|
1417 |
\' |
|
|
1418 |
|
|
|
1419 |
Символ NULL может быть введен как '' |
|
|
1420 |
|
|
|
1421 |
В C-- поддерживаются и многобуквенные символьные константы. Примеры |
|
|
1422 |
многобуквенных символьных констант: |
|
|
1423 |
|
|
|
1424 |
'ab' |
|
|
1425 |
'the' |
|
|
1426 |
'this is large' |
|
|
1427 |
|
|
|
1428 |
Никакого ограничения на число символов в символьной константе не |
|
|
1429 |
накладывается, но различаются только последние 4 символа. Это - максимум, |
|
|
1430 |
который может быть сохранен в 32-разрядной переменной. Например, константы |
|
|
1431 |
this is large и arge - одинаковы. |
|
|
1432 |
|
|
|
1433 |
C-- обрабатывает все символьные константы как числовые значения ASCII |
|
|
1434 |
символов. Для многобуквенных символьных констант первый символ |
|
|
1435 |
соответствует старшим разрядам, таким образом, значение для ab будет |
|
|
1436 |
закодировано как a*256+b. |
|
|
1437 |
Return to contents. |
|
|
1438 |
|
|
|
1439 |
|
|
|
1440 |
|
|
|
1441 |
3.3 Строковые константы. |
|
|
1442 |
|
|
|
1443 |
|
|
|
1444 |
Строковые константы, как и в C, заключаются в двойные кавычки ("). |
|
|
1445 |
Специальные символы внутри строк обозначаются так же, как и в символьных |
|
|
1446 |
константах. Все специальные символы имеют то же значение, что и в |
|
|
1447 |
символьных константах за исключением \n, который имеет значение новая |
|
|
1448 |
строка и заменяет собой пару символов возврат каретки и перевод |
|
|
1449 |
строки. |
|
|
1450 |
|
|
|
1451 |
В настоящее время наибольшая длина строковой константы - 2048 символов, |
|
|
1452 |
включая символ-ограничитель 0, таким образом, максимум 2047 значащих |
|
|
1453 |
символов. |
|
|
1454 |
Return to contents. |
|
|
1455 |
|
|
|
1456 |
|
|
|
1457 |
|
|
|
1458 |
3.4 Постоянные выражения. |
|
|
1459 |
|
|
|
1460 |
|
|
|
1461 |
Постоянное выражение - одиночная числовая константа или несколько |
|
|
1462 |
числовых констант, связанных между собой операторами. Числовое значение |
|
|
1463 |
выражения вычисляется один раз во время компиляции и далее используется |
|
|
1464 |
только его постоянное значение. |
|
|
1465 |
|
|
|
1466 |
Подобно всем выражениям в C--, постоянные выражения всегда вычисляются |
|
|
1467 |
слева направо, невзирая на правила арифметики! Это совершенно отлично от |
|
|
1468 |
других языков, и при написании выражений надо быть осторожным и помнить, |
|
|
1469 |
что 2+3*2=10 а не 8. |
|
|
1470 |
|
|
|
1471 |
Некоторые примеры постоянных выражений: |
|
|
1472 |
45 & 1 + 3 // равняется 4 |
|
|
1473 |
14 - 1 / 2 // равняется 6 (помните целочисленные значения) |
|
|
1474 |
1 * 2 * 3 / 2 + 4 // равняется 7 |
|
|
1475 |
Примеры с применением вещественных чисел: |
|
|
1476 |
3.23*1.53+2.0E2 // равняется 204.9419 |
|
|
1477 |
Return to contents. |
|
|
1478 |
|
|
|
1479 |
|
|
|
1480 |
|
|
|
1481 |
4. Выражения. |
|
|
1482 |
|
|
|
1483 |
4.1 Типы выражений. |
|
|
1484 |
|
|
|
1485 |
|
|
|
1486 |
Имеются три типа выражений в C--, не считая постоянных выражений. Это |
|
|
1487 |
выражения типа EAX/AX/AL, выражения типа неEAX/AX/AL и условные выражения. |
|
|
1488 |
Все C-- выражения вычисляются слева направо, независимо от старшинства |
|
|
1489 |
входящих в выражение математических операций. |
|
|
1490 |
Return to contents. |
|
|
1491 |
|
|
|
1492 |
|
|
|
1493 |
|
|
|
1494 |
4.2 Выражения типа EAX/AX/AL. |
|
|
1495 |
|
|
|
1496 |
|
|
|
1497 |
Этот тип выражений применяется в случае, когда его результат может быть |
|
|
1498 |
сохранен в переменной в памяти или в регистре EAX или AX или AL. |
|
|
1499 |
|
|
|
1500 |
Если результат может быть сохранен в переменных типа byte или char, |
|
|
1501 |
используется нотация AL. |
|
|
1502 |
|
|
|
1503 |
Если результат может быть сохранен в переменных типа word или int, |
|
|
1504 |
используется нотация AX. |
|
|
1505 |
|
|
|
1506 |
Если результат может быть сохранен в переменных типа dword, long или |
|
|
1507 |
float, используется нотация EAX. |
|
|
1508 |
Return to contents. |
|
|
1509 |
|
|
|
1510 |
|
|
|
1511 |
|
|
|
1512 |
4.3 Выражения использующие получатель при вычислении выражения. |
|
|
1513 |
|
|
|
1514 |
|
|
|
1515 |
Если в правой части выражения используется переменная являющаяся |
|
|
1516 |
одновременно и приемником, то такие выражения дают различные результаты в |
|
|
1517 |
зависимости от того является приемник регистром или переменной памяти. Это |
|
|
1518 |
связано с тем, что при вычислении выражения в переменную памяти, вычисление |
|
|
1519 |
производится сначала в регистр EAX/AX/AL, и лишь после окончания вычисления |
|
|
1520 |
результат будет записан в приемник. Если же приемником является регистр, то |
|
|
1521 |
его значение будет меняться после каждой операции вычисления. Пример: |
|
|
1522 |
|
|
|
1523 |
int var; |
|
|
1524 |
var = BX = 2; |
|
|
1525 |
var = 3 + var; // результатом будет 5 |
|
|
1526 |
BX = 3 + BX; // результатом будет 6 |
|
|
1527 |
Return to contents. |
|
|
1528 |
|
|
|
1529 |
|
|
|
1530 |
|
|
|
1531 |
4.4 Не - EAX/AX/AL выражения. |
|
|
1532 |
|
|
|
1533 |
|
|
|
1534 |
Этот тип выражений применяется в случае, когда его результат должен |
|
|
1535 |
быть сохранен в любом другом регистре, отличном от аккумулятора EAX, AX |
|
|
1536 |
или AL. В процессе вычисления выражения этого типа меняется только |
|
|
1537 |
содержимое указанного регистра-получателя, все другие регистры будут |
|
|
1538 |
сохранены. Если регистром-получателем служит байтовый регистр, а при |
|
|
1539 |
вычислении используются величины размером в слово, одновременно с записью в |
|
|
1540 |
младший байт может быть разрушено содержимое старшего байта |
|
|
1541 |
регистра-получателя. |
|
|
1542 |
|
|
|
1543 |
Это обстоятельство накладывает некоторые ограничения на операции и |
|
|
1544 |
операнды, допустимые в выражениях типа не EAX/AX/AL. Внутри выражений |
|
|
1545 |
байтового типа не допускается: |
|
|
1546 |
|
|
|
1547 |
- делать вызовы МАКРОКОМАНД, |
|
|
1548 |
- делать вызовы РЕГИСТРОВЫХ процедур |
|
|
1549 |
- делать вызовы СТЕКОВЫХ процедур |
|
|
1550 |
|
|
|
1551 |
Ранее в не-EAX/AX/AL выражениях было можно использовать лишь |
|
|
1552 |
операции: сложения, вычитания, XOR, OR, AND. Теперь для 16 и 32 битных |
|
|
1553 |
регистров почти все ограничения сняты. Но есть еще ограничения на регистры. |
|
|
1554 |
Например, если в выражении используется сдвиг на значение переменной, а |
|
|
1555 |
приемником являются регистры CX/ECX, то такое выражение компилятор не будет |
|
|
1556 |
компилировать: |
|
|
1557 |
|
|
|
1558 |
CX = var * SI * 3 * var >> 3; //вызовет сообщение об ошибке |
|
|
1559 |
|
|
|
1560 |
Примечание: для 8 битных не-AL выражений умножать можно только на |
|
|
1561 |
числа: 0, 1, 2, 4, 8, 16, 32, 64 и 128. Все эти ограничения связаны со |
|
|
1562 |
стремлением не разрушать другие регистры при использовании не-EAX/AX/AL |
|
|
1563 |
выражений. |
|
|
1564 |
Return to contents. |
|
|
1565 |
|
|
|
1566 |
|
|
|
1567 |
|
|
|
1568 |
4.5 Условные выражения. |
|
|
1569 |
|
|
|
1570 |
|
|
|
1571 |
Условные выражения - выражения, результатом вычисления которых является |
|
|
1572 |
логическое значение да или нет, используемое в операторе if и циклах do {} |
|
|
1573 |
while, while, for. |
|
|
1574 |
|
|
|
1575 |
Имеются два типа условных выражений, простые и сложные. |
|
|
1576 |
|
|
|
1577 |
Возможно логическое объединение условий. |
|
|
1578 |
Return to contents. |
|
|
1579 |
|
|
|
1580 |
|
|
|
1581 |
|
|
|
1582 |
4.5.1 Простые условные выражения. |
|
|
1583 |
|
|
|
1584 |
|
|
|
1585 |
Простые условные выражения - одиночная лексема или выражение, которое |
|
|
1586 |
примет значение да, если расчетное значение отлично от нуля, или значение |
|
|
1587 |
нет, если расчетное значение равно нулю. |
|
|
1588 |
Return to contents. |
|
|
1589 |
|
|
|
1590 |
|
|
|
1591 |
|
|
|
1592 |
4.5.2 Сложные условные выражения. |
|
|
1593 |
|
|
|
1594 |
|
|
|
1595 |
Сложные условные выражения имеют следующую форму: |
|
|
1596 |
|
|
|
1597 |
(левая_часть оператор_отношения правая_часть) |
|
|
1598 |
|
|
|
1599 |
Где: |
|
|
1600 |
левая_часть - любое выражение типа AL/AX/EAX или постоянное выражение. |
|
|
1601 |
Тип выражения определяется по типу первой лексемы |
|
|
1602 |
(регистра или переменной); значение типа по умолчанию - |
|
|
1603 |
word для 16-битных программ и dword для 32-битных. Если |
|
|
1604 |
желателен другой тип, перед выражением ставится |
|
|
1605 |
соответствующее ключевое слово, определяющее его тип: |
|
|
1606 |
byte, char, int, long, dword или float |
|
|
1607 |
|
|
|
1608 |
оператор_отношения - любой из операторов отношения: |
|
|
1609 |
==, !=, <>, <, >, <=, или >=. |
|
|
1610 |
|
|
|
1611 |
правая_часть - любой одиночный регистр, одиночная переменная или |
|
|
1612 |
постоянное выражение. |
|
|
1613 |
|
|
|
1614 |
Примеры правильных сложных условных выражений: |
|
|
1615 |
|
|
|
1616 |
(X + y > z) |
|
|
1617 |
(int CX*DX < = 12*3) |
|
|
1618 |
(byte first*second+hold == cnumber) |
|
|
1619 |
|
|
|
1620 |
Примеры недопустимых сложных условных выражений: |
|
|
1621 |
|
|
|
1622 |
(x+y >= x-y) // правая часть не является одиночной лексемой или |
|
|
1623 |
постоянным выражением. |
|
|
1624 |
(Z = y) // вместо == ошибочно поставлен = |
|
|
1625 |
Return to contents. |
|
|
1626 |
|
|
|
1627 |
|
|
|
1628 |
|
|
|
1629 |
4.6 Изменение типа выражения при присваивании. |
|
|
1630 |
|
|
|
1631 |
|
|
|
1632 |
Если после знака равенства написать тип отличный от типа вычисляемой |
|
|
1633 |
переменной, то все переменные участвующие в процессе вычисления, будут |
|
|
1634 |
преобразовываться к этому новому типу, и лишь конечный результат будет |
|
|
1635 |
преобразован к типу вычисляемой переменной. Пример: |
|
|
1636 |
|
|
|
1637 |
int i, a; |
|
|
1638 |
long b; |
|
|
1639 |
char c; |
|
|
1640 |
|
|
|
1641 |
i = a * b + c ; |
|
|
1642 |
|
|
|
1643 |
Значения переменных a, b, и c в этом примере перед вычислением будут |
|
|
1644 |
преобразованы к типу int (типу переменной i). Но если записать это |
|
|
1645 |
выражение вот так: |
|
|
1646 |
|
|
|
1647 |
i = long a * b + c ; |
|
|
1648 |
|
|
|
1649 |
то переменные a, b, и c в этом примере перед вычислением будут |
|
|
1650 |
преобразованы к типу long, а конечный результат будет преобразован к типу |
|
|
1651 |
переменной i - int. |
|
|
1652 |
Return to contents. |
|
|
1653 |
|
|
|
1654 |
|
|
|
1655 |
|
|
|
1656 |
4.7 Вычисление в регистры EAX/AX/AL со знаком. |
|
|
1657 |
|
|
|
1658 |
|
|
|
1659 |
По умолчанию все вычисления в регистры производятся как с без знаковыми |
|
|
1660 |
величинами. |
|
|
1661 |
|
|
|
1662 |
Например: |
|
|
1663 |
|
|
|
1664 |
int a,b,c; |
|
|
1665 |
AX = a * b / c ; |
|
|
1666 |
|
|
|
1667 |
При этом компилятор генерировал без знаковые инструкции div и mul, так как |
|
|
1668 |
регистры считаются без знаковыми переменными. Если написать вот так: |
|
|
1669 |
|
|
|
1670 |
AX = int a * b / c ; |
|
|
1671 |
|
|
|
1672 |
то компилятор сгенерирует инструкции idiv и imul. |
|
|
1673 |
|
|
|
1674 |
Обращаю ваше внимание, что для регистра AL можно использовать только |
|
|
1675 |
модификатор char, для AX соответственно только int, а для EAX - long. Для |
|
|
1676 |
остальных регистров подобное делать нельзя. |
|
|
1677 |
Return to contents. |
|
|
1678 |
|
|
|
1679 |
|
|
|
1680 |
|
|
|
1681 |
5. Идентификаторы. |
|
|
1682 |
|
|
|
1683 |
5.1 Формат идентификатора. |
|
|
1684 |
|
|
|
1685 |
|
|
|
1686 |
Идентификаторы в C-- должны начинаться или с символа подчеркивания _ |
|
|
1687 |
или заглавных или строчных букв. Следующие символы могут быть любой |
|
|
1688 |
комбинацией символов подчеркивания, заглавных или строчных букв или чисел |
|
|
1689 |
(от 0 до 9). Общая длина идентификатора не может превышать 64 символа. |
|
|
1690 |
Символы с кодом больше 0x7A (код символа z) недопустимы. |
|
|
1691 |
|
|
|
1692 |
Примеры допустимых идентификаторов: |
|
|
1693 |
|
|
|
1694 |
_DOG |
|
|
1695 |
Loony12 |
|
|
1696 |
HowdYBoys_AND_Girls |
|
|
1697 |
WOW___ |
|
|
1698 |
X |
|
|
1699 |
|
|
|
1700 |
Примеры недопустимых идентификаторов: |
|
|
1701 |
|
|
|
1702 |
12bogus /* не может начинаться с числа */ |
|
|
1703 |
WowisthisalongidentifieryupitsureisnotOyoulengthismorethat64chars |
|
|
1704 |
/*длина идентификатора превышает 64 */ |
|
|
1705 |
Y_es sir /* пробелы недопустимы */ |
|
|
1706 |
The-end /* дефисы недопустимы */ |
|
|
1707 |
Return to contents. |
|
|
1708 |
|
|
|
1709 |
|
|
|
1710 |
|
|
|
1711 |
5.2 Зарезервированные идентификаторы. |
|
|
1712 |
|
|
|
1713 |
|
|
|
1714 |
Список зарезервированных в C-- идентификаторов, которые не могут |
|
|
1715 |
использоваться как общие идентификаторы, поскольку они уже были определены |
|
|
1716 |
или зарезервированы для других целей: |
|
|
1717 |
|
|
|
1718 |
BREAK CASE CONTINUE ELSE EXTRACT FALSE FOR |
|
|
1719 |
FROM GOTO IF LOOPNZ RETURN SWITCH TRUE |
|
|
1720 |
WHILE |
|
|
1721 |
|
|
|
1722 |
CARRYFLAG MINUSFLAG NOTCARRYFLAG NOTOVERFLOW |
|
|
1723 |
NOTZEROFLAG OVERFLOW PLUSFLAG ZEROFLAG |
|
|
1724 |
|
|
|
1725 |
__CODEPTR__ __COMPILER__ __DATAPTR__ __DATESTR__ __DATE__ __DAY__ |
|
|
1726 |
__HOUR__ __LINE__ __MINUTE__ __MONTH__ __POSTPTR__ __SECOND__ |
|
|
1727 |
__TIME__ __VER1__ __VER2__ __WEEKDAY__ __YEAR__ |
|
|
1728 |
|
|
|
1729 |
_export asm break byte case cdecl char continue |
|
|
1730 |
default do dword else enum extern far fastcall |
|
|
1731 |
float for goto if inline int interrupt long |
|
|
1732 |
loop loopnz pascal return short signed sizeof static |
|
|
1733 |
stdcall struct switch union unsigned void while word |
|
|
1734 |
|
|
|
1735 |
ESCHAR ESBYTE ESINT ESWORD ESLONG ESDWORD ESFLOAT |
|
|
1736 |
CSCHAR CSBYTE CSINT CSWORD CSLONG CSDWORD CSFLOAT |
|
|
1737 |
SSCHAR SSBYTE SSINT SSWORD SSLONG SSDWORD SSFLOAT |
|
|
1738 |
DSCHAR DSBYTE DSINT DSWORD DSLONG DSDWORD DSFLOAT |
|
|
1739 |
FSCHAR FSBYTE FSINT FSWORD FSLONG FSDWORD FSFLOAT |
|
|
1740 |
GSCHAR GSBYTE GSINT GSWORD GSLONG GSDWORD GSFLOAT |
|
|
1741 |
|
|
|
1742 |
AX CX DX BX SP BP SI DI |
|
|
1743 |
EAX ECX EDX EBX ESP EBP ESI EDI |
|
|
1744 |
AL CL DL BL AH CH DH BH |
|
|
1745 |
ES CS SS DS FS GS |
|
|
1746 |
|
|
|
1747 |
ST(0) ST(1) ST(2) ST(3) ST(4) ST(5) ST(6) ST(7) ST |
|
|
1748 |
st(0) st(1) st(2) st(3) st(4) st(5) st(6) st(7) st |
|
|
1749 |
|
|
|
1750 |
Этот список может быть получен из C-- транслятора в любое время, |
|
|
1751 |
запуском его с опцией /WORDS из командной строки. |
|
|
1752 |
|
|
|
1753 |
Если Вы пользуетесь при компиляции опцией командной строки /ia, которая |
|
|
1754 |
позволяет использовать ассемблерные инструкции не заключая их в блоки asm и |
|
|
1755 |
без префикса $, то все имена ассемблерных инструкций становятся |
|
|
1756 |
зарезервированными словами. Причем имена ассемблерных инструкций компилятор |
|
|
1757 |
различает независимо от того, написаны они маленькими или большими буквами. |
|
|
1758 |
|
|
|
1759 |
Список имен поддерживаемых компилятором ассемблерных инструкции можно |
|
|
1760 |
получить запустив компилятор с опцией /LAI. |
|
|
1761 |
|
|
|
1762 |
Кроме этого в ассемблерных инструкциях становятся зарезервированными |
|
|
1763 |
следующие идентификаторы: |
|
|
1764 |
|
|
|
1765 |
ax cx dx bx sp bp si di |
|
|
1766 |
eax ecx edx ebx esp ebp esi edi |
|
|
1767 |
al cl dl bl ah ch dh bh |
|
|
1768 |
es cs ss ds fs gs |
|
|
1769 |
|
|
|
1770 |
DR0 DR1 DR2 DR3 DR4 DR5 DR6 DR7 |
|
|
1771 |
CR0 CR1 CR2 CR3 CR4 CR5 CR6 CR7 |
|
|
1772 |
TR0 TR1 TR2 TR3 TR4 TR5 TR6 TR7 |
|
|
1773 |
MM0 MM1 MM2 MM3 MM4 MM5 MM6 MM7 |
|
|
1774 |
XMM0 XMM1 XMM2 XMM3 XMM4 XMM5 XMM6 XMM7 |
|
|
1775 |
|
|
|
1776 |
dr0 dr1 dr2 dr3 dr4 dr5 dr6 dr7 |
|
|
1777 |
cr0 cr1 cr2 cr3 cr4 cr5 cr6 cr7 |
|
|
1778 |
tr0 tr1 tr2 tr3 tr4 tr5 tr6 tr7 |
|
|
1779 |
mm0 mm1 mm2 mm3 mm4 mm5 mm6 mm7 |
|
|
1780 |
xmm0 xmm1 xmm2 xmm3 xmm4 xmm5 xmm6 xmm7 |
|
|
1781 |
Return to contents. |
|
|
1782 |
|
|
|
1783 |
|
|
|
1784 |
|
|
|
1785 |
5.3 Универсальные регистры для 16 и 32-битного режима. |
|
|
1786 |
|
|
|
1787 |
|
|
|
1788 |
При создании библиотечных процедур очень часто приходится писать |
|
|
1789 |
варианты процедуры для работы в 16-битном и 32-битном режимах, которые |
|
|
1790 |
отличаются друг от друга лишь использованием в них либо 16-битных либо |
|
|
1791 |
32-битных регистров соответственно. Но можно писать лишь одну процедуру, |
|
|
1792 |
используя в ней новый синтаксис регистров. Если компилятор встретит вот |
|
|
1793 |
такой синтаксис: |
|
|
1794 |
|
|
|
1795 |
(E)AX=0; |
|
|
1796 |
|
|
|
1797 |
то компилятор будет использовать при компиляции 16-битного кода регистр |
|
|
1798 |
AX, а при компиляции 32-битного кода регистр EAX. |
|
|
1799 |
|
|
|
1800 |
Использование автоматических регистров позволит упростить библиотечные |
|
|
1801 |
файлы и сделать их более понятными. |
|
|
1802 |
Return to contents. |
|
|
1803 |
|
|
|
1804 |
|
|
|
1805 |
|
|
|
1806 |
5.4 Предопределенные идентификаторы. |
|
|
1807 |
|
|
|
1808 |
|
|
|
1809 |
Идентификаторы, определяемые компилятором в зависимости от режима |
|
|
1810 |
компиляции: |
|
|
1811 |
|
|
|
1812 |
__TLS__ идет компиляция под windows (w32, w32c, dll). |
|
|
1813 |
__DLL__ идет компиляция dll. |
|
|
1814 |
__CONSOLE__ идет компиляция консольного приложения windows |
|
|
1815 |
__WIN32__ идет компиляция GUI-шного приложения |
|
|
1816 |
__FLAT__ компилируется 32-битный код. |
|
|
1817 |
__MSDOS__ компилируется 16-битный код. |
|
|
1818 |
__TINY__ используется модель памяти tiny в 16-битном режиме |
|
|
1819 |
__SMALL__ используется модель памяти small в 16-битном режиме |
|
|
1820 |
__DOS32__ компилируется 32-битный код под DOS (d32) |
|
|
1821 |
__COM__ компилируется com-файл |
|
|
1822 |
__SYS__ компилируется sys-файл |
|
|
1823 |
__ROM__ компилируется rom-файл |
|
|
1824 |
__OBJ__ компилируется obj-файл |
|
|
1825 |
__TEXE__ компилируется exe-файл модели tiny |
|
|
1826 |
__EXE__ компилируется exe-файл модели small |
|
|
1827 |
__MEOS__ компилируется исполняемый файл для MenuetOS |
|
|
1828 |
codesize компиляция ведется с оптимизацией на размер кода |
|
|
1829 |
speed компиляция ведется с оптимизацией на быстродействие кода |
|
|
1830 |
cpu определяет тип процессора для которого ведется компиляция: |
|
|
1831 |
|
|
|
1832 |
1 - 80186 |
|
|
1833 |
2 - 80286 |
|
|
1834 |
3 - 80386 |
|
|
1835 |
4 - 80486 |
|
|
1836 |
5 - Pentium |
|
|
1837 |
6 - Pentium MMX |
|
|
1838 |
7 - Pentium II |
|
|
1839 |
|
|
|
1840 |
Эти идентификаторы могут быть проверены директивами #ifdef или #ifndef. |
|
|
1841 |
Идентификатор cpu может быть использован лишь с операторами проверки |
|
|
1842 |
условий: |
|
|
1843 |
|
|
|
1844 |
#ifdef cpu > 3 //если тип процессора больше 80386 |
|
|
1845 |
Return to contents. |
|
|
1846 |
|
|
|
1847 |
|
|
|
1848 |
|
|
|
1849 |
6. Переменные. |
|
|
1850 |
|
|
|
1851 |
6.1 Типы переменных. |
|
|
1852 |
|
|
|
1853 |
|
|
|
1854 |
В C-- имеется семь типов переменных (именованных областей памяти), это: |
|
|
1855 |
byte, word, dword, char, int, long, float. |
|
|
1856 |
|
|
|
1857 |
Следующая таблица показывает размер и диапазон представляемых величин |
|
|
1858 |
каждого из типов переменной: |
|
|
1859 |
|
|
|
1860 |
NAME | SIZE | VALUE RANGE | VALUE RANGE |
|
|
1861 |
тип |размер | диапазон представления | диапазон представления |
|
|
1862 |
|в байт.| в десятичной системе | в шестнадцатеричной системе |
|
|
1863 |
--------------------------------------------------------------------------- |
|
|
1864 |
byte | 1 | 0 to 255 | 0x00 to 0xFF |
|
|
1865 |
word | 2 | 0 to 65535 | 0x0000 to 0xFFFF |
|
|
1866 |
dword | 4 | 0 to 4294967295 | 0x00000000 to 0xFFFFFFFF |
|
|
1867 |
char | 1 | -128 to 127 | 0x80 to 0x7F |
|
|
1868 |
int | 2 | -32768 to 32767 | 0x8000 to 0x7FFF |
|
|
1869 |
long | 4 | -2147483648 to 2147483647 | 0x80000000 to 0x7FFFFFFF |
|
|
1870 |
float | 4 | -3,37E38 to +3,37E38 | 0xFF7FFFFF to 0x7FFFFFFF |
|
|
1871 |
|
|
|
1872 |
Примечание: для работы с типами float, dword и long используются |
|
|
1873 |
32-разрядные целочисленные команды, следовательно, для их выполнения нужно |
|
|
1874 |
иметь процессор не хуже 80386, что сейчас не является большой проблемой. |
|
|
1875 |
|
|
|
1876 |
Для совместимости со стандартом, принятом в языке C, введены |
|
|
1877 |
новые зарезервированные слова: short, signed, unsigned. Для типа int |
|
|
1878 |
в 32-битном режиме изменена разрядность. Вот таблица всех вариантов новых |
|
|
1879 |
типов данных: |
|
|
1880 |
|
|
|
1881 |
--------------------------------------------------------- |
|
|
1882 |
| полный тип |допустимые сокращения|старые аналоги| |
|
|
1883 |
--------------------------------------------------------- |
|
|
1884 |
|signed char |char | char | |
|
|
1885 |
|signed int |signed, int | int/long | |
|
|
1886 |
|signed short int |short, signed short | int | |
|
|
1887 |
|signed long int |long, signed long | long | |
|
|
1888 |
|unsigned char |--- | byte | |
|
|
1889 |
|unsigned int |unsigned | word/dword | |
|
|
1890 |
|unsigned short int|unsigned short | word | |
|
|
1891 |
|unsigned long int |unsigned long | dword | |
|
|
1892 |
--------------------------------------------------------- |
|
|
1893 |
|
|
|
1894 |
Старые типы byte, word и dword поддерживаются по прежнему и имеют |
|
|
1895 |
функционально прежнее значение. Изменения коснулись лишь типа int. Он в |
|
|
1896 |
16-битном режиме, также как и тип unsigned int, имеет 16-битный размер, а |
|
|
1897 |
в 32-битном режиме эти оба типа имеют размер в 32-бита. На первый взгляд |
|
|
1898 |
такие свойства типа int вносят некоторую путаницу, но это дает большой |
|
|
1899 |
выигрыш при использовании этого типа в библиотечных файлах, которые могут |
|
|
1900 |
быть использованы при компиляции 16-битных и 32-битных программ. |
|
|
1901 |
Return to contents. |
|
|
1902 |
|
|
|
1903 |
|
|
|
1904 |
|
|
|
1905 |
6.2 Объявление переменных. |
|
|
1906 |
|
|
|
1907 |
|
|
|
1908 |
Синтаксис для объявления переменных следующий: |
|
|
1909 |
|
|
|
1910 |
variable-type identifier; |
|
|
1911 |
|
|
|
1912 |
где variable-type - char, byte, int, word, long, dword или float. |
|
|
1913 |
|
|
|
1914 |
Одновременно могут быть объявлены несколько идентификаторов одного типа: |
|
|
1915 |
|
|
|
1916 |
variable-type identifier1, identifier2, ... , identifierN; |
|
|
1917 |
|
|
|
1918 |
Одномерные массивы могут быть объявлены следующим образом: |
|
|
1919 |
|
|
|
1920 |
variable-type identifier[elements]; |
|
|
1921 |
|
|
|
1922 |
где elements - постоянное выражение для количества переменных этого типа, |
|
|
1923 |
объединенных в массив. |
|
|
1924 |
|
|
|
1925 |
Инициализированные массивы можно объявлять без указания числа |
|
|
1926 |
элементов. При этом будет создан массив по фактическому числу элементов. |
|
|
1927 |
|
|
|
1928 |
variable-type identifier[] = { const1, const2 }; |
|
|
1929 |
|
|
|
1930 |
Переменные при объявлении могут быть проинициализированы следующим |
|
|
1931 |
образом: |
|
|
1932 |
|
|
|
1933 |
variable-type identifier = value; |
|
|
1934 |
|
|
|
1935 |
Некоторые примеры глобальных объявлений: |
|
|
1936 |
byte i,j; /* объявляет две переменные типа byte с именами i и j */ |
|
|
1937 |
word see[10] /* объявляет массив с именем see, состоящий из 10 |
|
|
1938 |
элементов типа word */ |
|
|
1939 |
int h,x[27] /* объявляет, переменную типа int с именем h, |
|
|
1940 |
и массив с именем x, состоящий из 27 элементов типа int */ |
|
|
1941 |
long size=0; /* объявлена переменная типа long с именем size и ей присвоено |
|
|
1942 |
значение 0. */ |
|
|
1943 |
Return to contents. |
|
|
1944 |
|
|
|
1945 |
|
|
|
1946 |
|
|
|
1947 |
6.3 Глобальные переменные. |
|
|
1948 |
|
|
|
1949 |
|
|
|
1950 |
Глобальные переменные - это переменные, область действия которых |
|
|
1951 |
распространяется на всю программу. В C-- использовать глобальные переменные |
|
|
1952 |
можно в процедурах, расположенных ниже места ее объявления. Т.е. если Вы |
|
|
1953 |
пишите процедуру, в которой используете переменную var, а саму переменную |
|
|
1954 |
объявляете ниже текста процедуры, то компилятор выдаст ошибку. Это связано |
|
|
1955 |
с тем, что компилятор может знать тип переменной только после их |
|
|
1956 |
объявления. Но для таких переменных можно использовать взятие их адреса, |
|
|
1957 |
так как адрес переменной не зависит от его типа. Пример: |
|
|
1958 |
|
|
|
1959 |
void Proc(){ |
|
|
1960 |
gvar = 0; /* компилятор выдаст сообщение об ошибке, т.к. он еще не знает |
|
|
1961 |
типа переменной gvar */ |
|
|
1962 |
AX = #gvar; /* несмотря на то, что компилятор не знает и адреса этой |
|
|
1963 |
переменной такое выражение будет откомпилировано */ |
|
|
1964 |
} |
|
|
1965 |
int gvar; |
|
|
1966 |
|
|
|
1967 |
Но все же ситуация не безнадежна и нам удастся добиться того, чего мы |
|
|
1968 |
задумали. В этом нам поможет альтернативный синтаксис обращения к |
|
|
1969 |
переменным: |
|
|
1970 |
|
|
|
1971 |
void Proc(){ |
|
|
1972 |
DSINT[#gvar] = 0; /* компилятор успешно откомпилирует это выражение т.к. |
|
|
1973 |
ему теперь известен тип переменной gvar */ |
|
|
1974 |
} |
|
|
1975 |
int gvar; |
|
|
1976 |
|
|
|
1977 |
Память под глобальные переменные выделяется в сегменте данных. Если |
|
|
1978 |
переменная при объявлении инициализируется (т.е. ей присвоено какое-то |
|
|
1979 |
значение), то переменная будет включена в код компилируемого файла. Если |
|
|
1980 |
переменная не инициализируется, то место для переменной будет |
|
|
1981 |
зарезервировано сразу же за последним байтом скомпилированной программы. |
|
|
1982 |
Return to contents. |
|
|
1983 |
|
|
|
1984 |
|
|
|
1985 |
|
|
|
1986 |
6.4 Локальные переменные. |
|
|
1987 |
|
|
|
1988 |
|
|
|
1989 |
Локальные переменные - это переменные область действия которых |
|
|
1990 |
распространяется лишь в пределах одной процедуры. Объявлять локальные |
|
|
1991 |
переменные, в отличии от современных версий C, можно между именем процедуры |
|
|
1992 |
и первой открывающейся фигурной скобкой. Пример: |
|
|
1993 |
|
|
|
1994 |
void PROC () |
|
|
1995 |
int i; //объявлена локальная переменная типа int с именем i |
|
|
1996 |
{ |
|
|
1997 |
for ( i=0; i<10; i++ ) WRITE(1); |
|
|
1998 |
} |
|
|
1999 |
|
|
|
2000 |
Память под локальные переменные отводится в сегменте стека. |
|
|
2001 |
|
|
|
2002 |
К локальным переменным можно отнести и параметры стековых процедур. Под |
|
|
2003 |
них также отводится память в стеке. |
|
|
2004 |
|
|
|
2005 |
Можно инициализировать локальные переменные при их объявлении. Но есть |
|
|
2006 |
некоторые ограничения. Нельзя инициализировать массивы и многомерные |
|
|
2007 |
структуры. Инициализировать можно одним значением, т.е нельзя при |
|
|
2008 |
инициализации локальных переменных пользоваться перечислением заключенным в |
|
|
2009 |
фигурные скобки и операторами FROM и EXTRACT. |
|
|
2010 |
|
|
|
2011 |
Имена локальных переменных могут совпадать с именами глобальных |
|
|
2012 |
переменных или процедур, но тогда Вы не сможете обратиться к глобальной |
|
|
2013 |
переменной или вызвать одноименную процедуру. |
|
|
2014 |
|
|
|
2015 |
Локальные переменные можно объявлять и в начале блока процедуры. Но |
|
|
2016 |
только до начала тела процедуры. Пример: |
|
|
2017 |
|
|
|
2018 |
void proc(){ |
|
|
2019 |
int locproc; // объявление локальной процедуры |
|
|
2020 |
locproc=0; // а теперь пошло тело процедуры |
|
|
2021 |
int locproc; // а на это объявление переменной компилятор выдаст сообщение |
|
|
2022 |
// об ошибке, т.к. уже началось тело процедуры |
|
|
2023 |
} |
|
|
2024 |
Return to contents. |
|
|
2025 |
|
|
|
2026 |
|
|
|
2027 |
|
|
|
2028 |
6.5 Динамические переменные и структуры. |
|
|
2029 |
|
|
|
2030 |
|
|
|
2031 |
Наряду с уже известными Вам динамическими процедурами в C-- есть |
|
|
2032 |
возможность использовать динамически и переменные и структуры. Динамические |
|
|
2033 |
переменные и структуры обозначаются также как и динамические процедуры - |
|
|
2034 |
символом двоеточия перед началом их объявления. И также как и динамическая |
|
|
2035 |
процедура, динамическая переменная или структура будет вставлена в код, |
|
|
2036 |
лишь в том случае, если она будет использована в программе. |
|
|
2037 |
|
|
|
2038 |
Динамические переменные и структуры найдут применение в библиотеках. |
|
|
2039 |
Использовать их непосредственно в программах нет смысла. |
|
|
2040 |
|
|
|
2041 |
У динамических переменных, структур также как и у процедур, есть один |
|
|
2042 |
недостаток - Вы не сможете знать, в каком месте откомпилированного кода они |
|
|
2043 |
будут расположены, и в каком порядке. Но необходимость это знать бывает |
|
|
2044 |
очень редко. |
|
|
2045 |
|
|
|
2046 |
Динамические инициализированные переменные и структуры в файле будут |
|
|
2047 |
расположены в его самом конце, после динамических процедур. Эту их |
|
|
2048 |
особенность можно использовать, если Вам будет необходимо, чтобы данные не |
|
|
2049 |
были разбросаны среди кода, а были сгруппированы в одном месте. |
|
|
2050 |
Return to contents. |
|
|
2051 |
|
|
|
2052 |
|
|
|
2053 |
|
|
|
2054 |
6.6 Присваивание одного значения нескольким переменным. |
|
|
2055 |
|
|
|
2056 |
|
|
|
2057 |
Если Вам необходимо присвоить нескольким переменным одинаковые значения: |
|
|
2058 |
|
|
|
2059 |
var1=0; |
|
|
2060 |
var2=0; |
|
|
2061 |
var3=0; |
|
|
2062 |
|
|
|
2063 |
то теперь это можно записать более коротко: |
|
|
2064 |
|
|
|
2065 |
var1=var2=var3=0; |
|
|
2066 |
|
|
|
2067 |
При использовании такой записи генерируется более компактный и более |
|
|
2068 |
быстрый код. |
|
|
2069 |
Return to contents. |
|
|
2070 |
|
|
|
2071 |
|
|
|
2072 |
|
|
|
2073 |
6.7 Переменные типа float. |
|
|
2074 |
|
|
|
2075 |
6.7.1 Формат переменных типа float. |
|
|
2076 |
|
|
|
2077 |
|
|
|
2078 |
Для представления значений с плавающей точкой в язык C-- введен тип |
|
|
2079 |
float. Этому типу соответствует действительное число одинарной точности |
|
|
2080 |
FPU. |
|
|
2081 |
|
|
|
2082 |
Формат представления данных с плавающей точкой включает три поля: |
|
|
2083 |
знака, мантиссы и порядка. Знак определяется старшим значащим разрядом. |
|
|
2084 |
Поле мантиссы содержит значащие биты числа, а поле порядка содержит |
|
|
2085 |
степень 2 и определяет масштабирующий множитель для мантиссы. |
|
|
2086 |
|
|
|
2087 |
31 30.....23 22........0 |
|
|
2088 |
| | | | | |
|
|
2089 |
| | | -------------- - поле мантиссы |
|
|
2090 |
| ------------------------ - поле порядка |
|
|
2091 |
--------------------------- - бит знака |
|
|
2092 |
Return to contents. |
|
|
2093 |
|
|
|
2094 |
|
|
|
2095 |
|
|
|
2096 |
6.7.2 Константы с плавающей точкой. |
|
|
2097 |
|
|
|
2098 |
|
|
|
2099 |
Компилятор отличает вещественное число от целого по наличию в нем |
|
|
2100 |
точки. Начинаться вещественное число должно либо цифрой от 0 до 9, либо |
|
|
2101 |
знаком минус. Необязательной частью вещественного числа является |
|
|
2102 |
показатель степени. Показатель степени отделяется от числа символом e или |
|
|
2103 |
E. Пробелы недопустимы. Вот примеры допустимого синтаксиса: |
|
|
2104 |
|
|
|
2105 |
0.98 |
|
|
2106 |
-15.75 |
|
|
2107 |
3.14e2 |
|
|
2108 |
1.234567E-20 |
|
|
2109 |
Return to contents. |
|
|
2110 |
|
|
|
2111 |
|
|
|
2112 |
|
|
|
2113 |
6.7.3 Диапазон допустимых значений. |
|
|
2114 |
|
|
|
2115 |
|
|
|
2116 |
Вещественное число типа float может находиться в диапазоне от 3.37E38 |
|
|
2117 |
до -3.37E38. Минимально близкое к нулю значение равняется 1.17E-38 и |
|
|
2118 |
-1.17E-38. Записывать вещественное число одинарной точности более чем 8 |
|
|
2119 |
цифрами не имеет смысла. Показатель степени может принимать значения от |
|
|
2120 |
+38 до -38. |
|
|
2121 |
Return to contents. |
|
|
2122 |
|
|
|
2123 |
|
|
|
2124 |
|
|
|
2125 |
6.7.4 Математические операции. |
|
|
2126 |
|
|
|
2127 |
|
|
|
2128 |
Компилятор поддерживает 4 основных действия над переменными типа |
|
|
2129 |
float: сложение, вычитание, умножение и деление. Поддерживается также |
|
|
2130 |
инкремент (var++ - увеличение на 1), декремент (var-- - уменьшение на 1), |
|
|
2131 |
смена знака (-var) и обмен значениями (var1 >< var2). Остальные |
|
|
2132 |
математические операции будут реализованы либо уже реализованы во внешних |
|
|
2133 |
библиотеках. При вычислении значения переменной float можно использовать |
|
|
2134 |
и переменные других типов, они будут автоматически преобразованы в тип |
|
|
2135 |
float. |
|
|
2136 |
|
|
|
2137 |
ВНИМАНИЕ! Составные математические операции выполняются в том |
|
|
2138 |
порядке, в котором они записаны, невзирая на правила арифметики. |
|
|
2139 |
Return to contents. |
|
|
2140 |
|
|
|
2141 |
|
|
|
2142 |
|
|
|
2143 |
6.7.5 Преобразования типов. |
|
|
2144 |
|
|
|
2145 |
|
|
|
2146 |
При математических операциях конечным итогом которых является |
|
|
2147 |
переменная типа float, все операнды других типов перед вычислением будут |
|
|
2148 |
преобразованы в тип float. При присваивании переменной типа float значения |
|
|
2149 |
переменной другого типа оно также будет преобразовано в тип float. |
|
|
2150 |
|
|
|
2151 |
Если при целочисленных вычислениях одним из операндов будет переменная |
|
|
2152 |
типа float, то из него будет выделена целая часть, которая и примет |
|
|
2153 |
участие в вычислениях. При присваивании целочисленной переменной значения |
|
|
2154 |
переменной типа float, из нее также будет выделена целая часть, которая и |
|
|
2155 |
будет присвоена целочисленной переменной. |
|
|
2156 |
Return to contents. |
|
|
2157 |
|
|
|
2158 |
|
|
|
2159 |
|
|
|
2160 |
6.7.6 Операции сравнения. |
|
|
2161 |
|
|
|
2162 |
|
|
|
2163 |
Если при операции сравнения левым операндом является переменная или |
|
|
2164 |
выражение типа float, а правым является целочисленное значение, то |
|
|
2165 |
целочисленное значение будет преобразовано в вещественный тип. Если же |
|
|
2166 |
левым операндом является целочисленное выражение или переменная, а правым |
|
|
2167 |
операндом значение типа float, то из правого операнда будет выделена целая |
|
|
2168 |
часть, которая и примет участие в сравнении. |
|
|
2169 |
Return to contents. |
|
|
2170 |
|
|
|
2171 |
|
|
|
2172 |
|
|
|
2173 |
6.7.7 Сравнение переменных типа float с 32-битным регистром. |
|
|
2174 |
|
|
|
2175 |
|
|
|
2176 |
В регистрах могут содержаться знаковые, без знаковые и вещественные |
|
|
2177 |
данные. По умолчанию считается, что в регистре находится без знаковое целое |
|
|
2178 |
число. При сравнении переменных типа float с 32-битным регистром можно |
|
|
2179 |
указывать тип данных содержащихся в регистре. Для этой цели можно |
|
|
2180 |
использовать модификаторы: signed, unsigned, float. Примеры: |
|
|
2181 |
|
|
|
2182 |
float f=1.0; |
|
|
2183 |
|
|
|
2184 |
void PROC() |
|
|
2185 |
{ |
|
|
2186 |
IF( f < signed ECX) //в регистре ECX находится знаковое число |
|
|
2187 |
IF( unsigned EBX > f) //в регистре EBX находится без знаковое число |
|
|
2188 |
IF( f == float EAX ) //в EAX находится число формата float |
|
|
2189 |
} |
|
|
2190 |
|
|
|
2191 |
ВНИМАНИЕ! При операции сравнения с участием переменой типа float, |
|
|
2192 |
содержимое регистра AX будет разрушено. |
|
|
2193 |
Return to contents. |
|
|
2194 |
|
|
|
2195 |
|
|
|
2196 |
|
|
|
2197 |
6.8 Указатели. |
|
|
2198 |
|
|
|
2199 |
|
|
|
2200 |
В C-- сейчас указатели реализованы не в полном объеме. Поэтому многие |
|
|
2201 |
вещи, которые возможны в обычных языках C, здесь будут недоступны. |
|
|
2202 |
|
|
|
2203 |
Пример применения указателей в C--: |
|
|
2204 |
|
|
|
2205 |
char *string[4]={"string1", "string2", "string3", 0}; //массив указателей |
|
|
2206 |
char *str="string4"; |
|
|
2207 |
|
|
|
2208 |
main() |
|
|
2209 |
int i; |
|
|
2210 |
char *tstr; |
|
|
2211 |
{ |
|
|
2212 |
FOR(i=0; string[i]!=0; i++){ |
|
|
2213 |
WRITESTR(string[i]); |
|
|
2214 |
WRITELN(); |
|
|
2215 |
} |
|
|
2216 |
FOR(tstr=str;byte *tstr!=0; tstr++){ |
|
|
2217 |
WRITE(byte *tstr); |
|
|
2218 |
} |
|
|
2219 |
} |
|
|
2220 |
|
|
|
2221 |
Указатели можно использовать при передаче параметров процедурам, а в |
|
|
2222 |
самих процедурах в качестве как локальных, так и параметрических |
|
|
2223 |
переменных. Указатели можно также использовать в структурах. Можно |
|
|
2224 |
использовать указатели на указатели. Введена поддержка указателей на |
|
|
2225 |
процедуры: |
|
|
2226 |
|
|
|
2227 |
void (*proc)(); //объявление указателя на процедуру |
|
|
2228 |
|
|
|
2229 |
По умолчанию указатели на процедуру являются указателями на процедуру в |
|
|
2230 |
стиле pascal, независимо от регистра, в котором написано имя процедуры и |
|
|
2231 |
режима компиляции. Если Вам необходимо, чтобы был использован другой тип |
|
|
2232 |
вызова, то его необходимо указать при объявлении указателя на процедуру. |
|
|
2233 |
|
|
|
2234 |
При инициализации указателей компилятор не контролирует то, чем |
|
|
2235 |
инициализируется указатель. Т.е. Вы можете указателю на char присвоить |
|
|
2236 |
указатель на int или указателю на процедуру присвоить адрес переменной. |
|
|
2237 |
Это может вызвать ошибку в работе программы. |
|
|
2238 |
Return to contents. |
|
|
2239 |
|
|
|
2240 |
|
|
|
2241 |
|
|
|
2242 |
7. Адресация. |
|
|
2243 |
|
|
|
2244 |
7.1 Относительная адресация. |
|
|
2245 |
|
|
|
2246 |
|
|
|
2247 |
Изначально индексный доступ к элементам в массивах любого типа в |
|
|
2248 |
компиляторе осуществлялся побайтно, независимо от объявленного типа данных. |
|
|
2249 |
Индексы ограничены форматом поля RM процессора 8086, таким образом, |
|
|
2250 |
доступны только следующие форматы индексов (где индекс - значение |
|
|
2251 |
16-разрядной константы или постоянного выражения): |
|
|
2252 |
|
|
|
2253 |
variable[index] |
|
|
2254 |
variable[index+BX+SI] |
|
|
2255 |
variable[index+BX+DI] |
|
|
2256 |
variable[index+BP+SI] |
|
|
2257 |
variable[index+BP+DI] |
|
|
2258 |
variable[index+SI] |
|
|
2259 |
variable[index+DI] |
|
|
2260 |
variable[index+BP] |
|
|
2261 |
variable[index+BX] |
|
|
2262 |
|
|
|
2263 |
Начиная с версии 0.210, появилась возможность использовать в качестве |
|
|
2264 |
индекса переменных типа char byte int word long dword. При этом |
|
|
2265 |
доступ к элементам массива осуществляется в зависимости от объявленного типа |
|
|
2266 |
массива. |
|
|
2267 |
|
|
|
2268 |
Также начиная с версии 0.210 появилась возможность использовать в |
|
|
2269 |
качестве индексных и базовых регистров при относительной адресации любые |
|
|
2270 |
32-битные регистры. |
|
|
2271 |
|
|
|
2272 |
Если Вы для адресации к элементам массива будете использовать регистры и |
|
|
2273 |
числовые константы, из которых можно получить поле RM для инструкций 8086 |
|
|
2274 |
процессора или комбинацию полей RM BASE и SIB для 80386 процессора, то |
|
|
2275 |
компилятор будет использовать эти регистры для генерации инструкции с этими |
|
|
2276 |
полями. В результате Вы получите относительную побайтную адресацию к |
|
|
2277 |
элементам массива. |
|
|
2278 |
|
|
|
2279 |
Если же из этих регистров невозможно получить поля RM, BASE, SIB, |
|
|
2280 |
или для адресации будет использована переменная, то компилятор сначала |
|
|
2281 |
вычислит это выражение в регистр (E)SI или другой, подходящий регистр, а |
|
|
2282 |
затем умножит содержимое этого регистра на разрядность Вашего массива. Таким |
|
|
2283 |
образом, в этом случае вы будете иметь поэлементную адресацию в массиве. |
|
|
2284 |
Пример: |
|
|
2285 |
|
|
|
2286 |
AX = var [ 5 ]; |
|
|
2287 |
AX = var [ BX + 5 ]; |
|
|
2288 |
AX = var [ BX + CX ]; |
|
|
2289 |
AX = var [ i ]; |
|
|
2290 |
|
|
|
2291 |
Компилятор сгенерирует следующий код: |
|
|
2292 |
test.c-- 7: AX=var[5]; |
|
|
2293 |
0100 A12501 mov ax,[125h] |
|
|
2294 |
|
|
|
2295 |
test.c-- 8: AX=var[BX+5]; |
|
|
2296 |
0103 8B872501 mov ax,[bx+125h] |
|
|
2297 |
|
|
|
2298 |
test.c-- 9: AX=var[BX+CX]; |
|
|
2299 |
0107 89DE mov si,bx |
|
|
2300 |
0109 01CE add si,cx |
|
|
2301 |
010B 01F6 add si,si |
|
|
2302 |
010D 8B842001 mov ax,[si+120h] |
|
|
2303 |
|
|
|
2304 |
test.c-- 10: AX=var[i]; |
|
|
2305 |
0111 8B362201 mov si,[122h] |
|
|
2306 |
0115 01F6 add si,si |
|
|
2307 |
0117 8B842001 mov ax,[si+120h] |
|
|
2308 |
|
|
|
2309 |
Как Вы видите, первые два выражения были преобразованы в одну |
|
|
2310 |
ассемблерную инструкцию, и получилась побайтная адресация. В двух следующих |
|
|
2311 |
выражениях получить одну ассемблерную инструкцию не удалось и компилятор |
|
|
2312 |
применил для этих выражений поэлементную адресацию. |
|
|
2313 |
|
|
|
2314 |
Такой двойственный подход реализован с целью сохранения совместимости |
|
|
2315 |
новых возможностей с предыдущими. |
|
|
2316 |
|
|
|
2317 |
Несмотря на кажущуюся для неискушенного пользователя путаницу, этот |
|
|
2318 |
механизм легко понять и запомнить по следующему простому правилу: если Вы |
|
|
2319 |
используете в качестве индекса только цифровое значение или регистр BX, SI, |
|
|
2320 |
DI, BP или любой 32-битный регистр, то компилятор сгенерирует код с |
|
|
2321 |
побайтной адресацией. Если же в качестве индекса будет использована |
|
|
2322 |
переменная, то компилятор сгенерирует код с поэлементной адресацией. Если |
|
|
2323 |
же Вы хорошо знакомы с ассемблером, то Вам не составит большого труда |
|
|
2324 |
понять в каких случаях Вы получите побайтную, а в каких поэлементную |
|
|
2325 |
адресацию. |
|
|
2326 |
|
|
|
2327 |
Иногда требуется иметь побайтный доступ к элементам массива используя в |
|
|
2328 |
качестве индекса переменную. Например |
|
|
2329 |
|
|
|
2330 |
AX=var[i]; |
|
|
2331 |
|
|
|
2332 |
Для этого выражения будет сгенерирована поэлементная адресация, а нам |
|
|
2333 |
нужна побайтовая. Для этого можно написать так: |
|
|
2334 |
|
|
|
2335 |
SI=i; |
|
|
2336 |
AX=var[SI]; |
|
|
2337 |
|
|
|
2338 |
Но можно это записать короче: |
|
|
2339 |
|
|
|
2340 |
AX=DSWORD[#var+i]; |
|
|
2341 |
|
|
|
2342 |
В обоих этих случаях Вы получите побайтную адресацию к элементам массива |
|
|
2343 |
var. В первом варианте Вы сможете контролировать какой регистр будет |
|
|
2344 |
использован в качестве индекса, а во втором варианте компилятор будет сам |
|
|
2345 |
выбирать регистр для использования в качестве индекса. |
|
|
2346 |
|
|
|
2347 |
Важно всегда помнить о двойственном подходе компилятора к вычислению |
|
|
2348 |
адреса в массиве. Еще раз кратко: если Вы в массиве адресуетесь используя |
|
|
2349 |
числовую константу или регистры BX,DI,SI,BP компилятор использует эти |
|
|
2350 |
значения без изменения. Во всех других случаях будет коррекция значения в |
|
|
2351 |
зависимости от типа массива. |
|
|
2352 |
Return to contents. |
|
|
2353 |
|
|
|
2354 |
|
|
|
2355 |
|
|
|
2356 |
7.2 Абсолютная адресация. |
|
|
2357 |
|
|
|
2358 |
|
|
|
2359 |
Абсолютная адресация также возможна. Действуют те же самые ограничения |
|
|
2360 |
на индексы, что и при относительной адресации. |
|
|
2361 |
|
|
|
2362 |
Вычисленный индекс будет абсолютен в сегменте, регистр которого указан. |
|
|
2363 |
Можно указывать любой из регистров DS, CS, SS и ES. На процессорах 80386 и |
|
|
2364 |
более новых можно указывать также регистры FS и GS. |
|
|
2365 |
|
|
|
2366 |
Синтаксис - точно такой же, как и в относительной адресации, за |
|
|
2367 |
исключением того, что указывается не переменная, а сегмент и тип данных. |
|
|
2368 |
Могут применяться следующие указатели: |
|
|
2369 |
|
|
|
2370 |
// адресация в сегменте данных |
|
|
2371 |
DSBYTE [смещение] // адресует байт в сегменте DS |
|
|
2372 |
DSWORD [смещение] // адресует слово в сегменте DS |
|
|
2373 |
DSCHAR [смещение] // адресует char в сегменте DS |
|
|
2374 |
DSINT [смещение] // адресует int в сегменте DS |
|
|
2375 |
DSDWORD [смещение] // адресует dword в сегменте DS |
|
|
2376 |
DSLONG [смещение] // адресует long в сегменте DS |
|
|
2377 |
DSFLOAT [смещение] // адресует float в сегменте DS |
|
|
2378 |
|
|
|
2379 |
// адресация в сегменте кода |
|
|
2380 |
CSBYTE [смещение] // адресует байт в сегменте CS |
|
|
2381 |
CSWORD [смещение] // адресует слово в сегменте CS |
|
|
2382 |
CSCHAR [смещение] // адресует char в сегменте CS |
|
|
2383 |
CSINT [смещение] // адресует int в сегменте CS |
|
|
2384 |
CSDWORD [смещение] // адресует dword в сегменте CS |
|
|
2385 |
CSLONG [смещение] // адресует long в сегменте CS |
|
|
2386 |
CSFLOAT [смещение] // адресует float в сегменте CS |
|
|
2387 |
|
|
|
2388 |
// адресация в сегменте стека |
|
|
2389 |
SSBYTE [смещение] // адресует байт в сегменте SS |
|
|
2390 |
SSWORD [смещение] // адресует слово в сегменте SS |
|
|
2391 |
SSCHAR [смещение] // адресует char в сегменте SS |
|
|
2392 |
SSINT [смещение] // адресует int в сегменте SS |
|
|
2393 |
SSDWORD [смещение] // адресует dword в сегменте SS |
|
|
2394 |
SSLONG [смещение] // адресует long в сегменте SS |
|
|
2395 |
SSFLOAT [смещение] // адресует float в сегменте SS |
|
|
2396 |
|
|
|
2397 |
// адресация в дополнительном сегменте данных |
|
|
2398 |
ESBYTE [смещение] // адресует байт в сегменте ES |
|
|
2399 |
ESWORD [смещение] // адресует слово в сегменте ES |
|
|
2400 |
ESCHAR [смещение] // адресует char в сегменте ES |
|
|
2401 |
ESINT [смещение] // адресует int в сегменте ES |
|
|
2402 |
ESDWORD [смещение] // адресует dword в сегменте ES |
|
|
2403 |
ESLONG [смещение] // адресует long в сегменте ES |
|
|
2404 |
ESFLOAT [смещение] // адресует float в сегменте ES |
|
|
2405 |
|
|
|
2406 |
// адресация в дополнительном сегменте 2 (80386) + |
|
|
2407 |
FSBYTE [смещение] // адресует байт в сегменте FS |
|
|
2408 |
FSWORD [смещение] // адресует слово в сегменте FS |
|
|
2409 |
FSCHAR [смещение] // адресует char в сегменте FS |
|
|
2410 |
FSINT [смещение] // адресует int в сегменте FS |
|
|
2411 |
FSDWORD [смещение] // адресует dword в сегменте FS |
|
|
2412 |
FSLONG [смещение] // адресует long в сегменте FS |
|
|
2413 |
FSFLOAT [смещение] // адресует float в сегменте FS |
|
|
2414 |
|
|
|
2415 |
// адресация в дополнительном сегменте 3 (80386) + |
|
|
2416 |
GSBYTE [смещение] // адресуют байт в сегменте GS |
|
|
2417 |
GSWORD [смещение] // адресуют слово в сегменте GS |
|
|
2418 |
GSCHAR [смещение] // адресуют char в сегменте GS |
|
|
2419 |
GSINT [смещение] // адресуют int в сегменте GS |
|
|
2420 |
GSDWORD [смещение] // адресуют dword в сегменте GS |
|
|
2421 |
GSLONG [смещение] // адресуют long в сегменте GS |
|
|
2422 |
GSFLOAT [смещение] // адресует float в сегменте GS |
|
|
2423 |
|
|
|
2424 |
Примеры: |
|
|
2425 |
Загрузить в AL байт из ячейки с шестнадцатеричным адресом 0000:0417 |
|
|
2426 |
ES = 0x0000; |
|
|
2427 |
AL = ESBYTE [0x417]; |
|
|
2428 |
|
|
|
2429 |
Переместить слово из ячейки с шестнадцатеричным адресом 2233:4455 |
|
|
2430 |
в ячейку с шестнадцатеричным адресом A000:0002 |
|
|
2431 |
$PUSH DS |
|
|
2432 |
DS = 0x2233; |
|
|
2433 |
ES = 0xA000; |
|
|
2434 |
ESWORD [0x0002] = DSWORD [0x4455]; |
|
|
2435 |
$POP DS |
|
|
2436 |
|
|
|
2437 |
Сохранить вычисленное значение выражения X + 2, имеющее |
|
|
2438 |
тип int в ячейке с шестнадцатеричным адресом FFFF:1234 |
|
|
2439 |
ES = 0xFFFF; |
|
|
2440 |
ESINT [0x1234] = X + 2; |
|
|
2441 |
|
|
|
2442 |
Сохранить BX в сегменте стека по смещению 42: |
|
|
2443 |
SSWORD [42] = BX; |
|
|
2444 |
Return to contents. |
|
|
2445 |
|
|
|
2446 |
|
|
|
2447 |
|
|
|
2448 |
8. Работа с блоками данных. |
|
|
2449 |
|
|
|
2450 |
8.1 Структуры. |
|
|
2451 |
|
|
|
2452 |
8.1.1 Что такое структуры. |
|
|
2453 |
|
|
|
2454 |
|
|
|
2455 |
Структура позволяет объединить в одном объекте совокупность значений, |
|
|
2456 |
которые могут иметь различные типы. |
|
|
2457 |
Return to contents. |
|
|
2458 |
|
|
|
2459 |
|
|
|
2460 |
|
|
|
2461 |
8.1.2 Синтаксис. |
|
|
2462 |
|
|
|
2463 |
|
|
|
2464 |
struct [<тег>] { <список-объявлений-элементов> } |
|
|
2465 |
<описатель>[,<описатель>...]; |
|
|
2466 |
struct <тег> <описатель> [,<описатель>]; |
|
|
2467 |
|
|
|
2468 |
Объявление структуры начинается с ключевого слова struct и имеет две |
|
|
2469 |
формы записи. |
|
|
2470 |
|
|
|
2471 |
В первой форме типы и имена элементов структуры специфицируются в |
|
|
2472 |
списке-объявлений-элементов. Необязательный в данном случае тег - это |
|
|
2473 |
идентификатор, который именует структурный тип, определенный данным |
|
|
2474 |
списком объявлений элементов. описатель специфицирует либо переменную |
|
|
2475 |
структурного типа, либо массив структур данного типа. |
|
|
2476 |
|
|
|
2477 |
Вторая синтаксическая форма объявления использует тег структуры для |
|
|
2478 |
ссылки на структурный тип, определенный где-то в другом месте программы. |
|
|
2479 |
|
|
|
2480 |
Список объявлений элементов представляет собой последовательность из |
|
|
2481 |
одной или более объявлений переменных. Каждая переменная, объявленная в |
|
|
2482 |
этом списке, называется элементом структуры. |
|
|
2483 |
|
|
|
2484 |
Элементы структуры запоминаются в памяти последовательно в том |
|
|
2485 |
порядке, в котором они объявляются. Выравнивание элементов внутри |
|
|
2486 |
структуры по умолчанию не производится. Но существует опция, включение |
|
|
2487 |
которой в командную строку позволяет иметь выравнивание и внутри |
|
|
2488 |
структуры. Сама структура выравнивается на четный адрес если включено |
|
|
2489 |
выравнивание. |
|
|
2490 |
|
|
|
2491 |
Примеры объявлений структур: |
|
|
2492 |
|
|
|
2493 |
struct test |
|
|
2494 |
{ |
|
|
2495 |
int a; |
|
|
2496 |
char b[8]; |
|
|
2497 |
long c; |
|
|
2498 |
} rr, ff[4]; |
|
|
2499 |
|
|
|
2500 |
В этом примере объявлены структура с именем rr и массив из 4 структур |
|
|
2501 |
с именем ff. Всему набору переменных присвоено название (тег) test. Этот |
|
|
2502 |
тег можно использовать для объявления других структур. Например: |
|
|
2503 |
|
|
|
2504 |
struct test dd; |
|
|
2505 |
|
|
|
2506 |
Здесь объявлена структура с именем dd, имеющая набор элементов |
|
|
2507 |
описанных в теге test. |
|
|
2508 |
|
|
|
2509 |
При объявлении структур с ранее объявленным тегом ключевое слово |
|
|
2510 |
struct можно не писать. Т.е можно написать вот так: |
|
|
2511 |
|
|
|
2512 |
test dd; |
|
|
2513 |
Return to contents. |
|
|
2514 |
|
|
|
2515 |
|
|
|
2516 |
|
|
|
2517 |
8.1.3 Инициализация структур при объявлении. |
|
|
2518 |
|
|
|
2519 |
|
|
|
2520 |
После объявления структуры ее элементы могут принимать произвольные |
|
|
2521 |
значения. Что бы этого не было надо структуры проинициализировать. |
|
|
2522 |
Инициализировать структуры при их объявлении можно только глобальные. C-- |
|
|
2523 |
поддерживает несколько способов инициализации структур при их объявлении: |
|
|
2524 |
|
|
|
2525 |
1. Одним значением: |
|
|
2526 |
|
|
|
2527 |
struct test dd=2; |
|
|
2528 |
|
|
|
2529 |
В этом примере всем элементам структуры dd присваивается значение 2. |
|
|
2530 |
|
|
|
2531 |
2. Массивом значений: |
|
|
2532 |
|
|
|
2533 |
struct test dd={1,2,,6}; |
|
|
2534 |
|
|
|
2535 |
В этом примере первому элементу структуры dd присваивается значение 1, |
|
|
2536 |
второму - 2, четвертому - 6. Пропущенным и не доинициализированным |
|
|
2537 |
значениям будет присвоено 0 значение. |
|
|
2538 |
|
|
|
2539 |
3. Командой FROM: |
|
|
2540 |
|
|
|
2541 |
struct test dd=FROM "file.dat"; |
|
|
2542 |
|
|
|
2543 |
В этом примере на место где расположена структура dd при компиляции будет |
|
|
2544 |
загружено содержимое файла . Если размер файла больше чем размер |
|
|
2545 |
структуры, то лишние байты будут загружены в код программы, но они не |
|
|
2546 |
будут востребованы. Если размер файла меньше чем размер структуры, то |
|
|
2547 |
недостающие байты структуры будут заполнены нулями. |
|
|
2548 |
|
|
|
2549 |
4. Командой EXTRACT: |
|
|
2550 |
|
|
|
2551 |
struct test dd=EXTRACT "file.dat", 24, 10; |
|
|
2552 |
|
|
|
2553 |
В этом примере на место где расположена структура dd при компиляции будет |
|
|
2554 |
загружен фрагмент из файла file.dat длиной 10 байт со смещения 24. |
|
|
2555 |
Недостающие байты будут заполнены нулями. |
|
|
2556 |
Return to contents. |
|
|
2557 |
|
|
|
2558 |
|
|
|
2559 |
|
|
|
2560 |
8.1.4 Инициализация структуры при выполнении программы. |
|
|
2561 |
|
|
|
2562 |
|
|
|
2563 |
При выполнении программы, кроме присвоения каждому элементу структуры |
|
|
2564 |
значения, можно проинициализировать всю структуру присвоением ей числа или |
|
|
2565 |
переменной. Примеры: |
|
|
2566 |
|
|
|
2567 |
void proc() |
|
|
2568 |
struct test aa[5],rr; |
|
|
2569 |
int i; |
|
|
2570 |
{ |
|
|
2571 |
aa[0]=0x12345678; |
|
|
2572 |
aa[i]=int 0x12345678; |
|
|
2573 |
aa=long 0x12345678; |
|
|
2574 |
rr=i; |
|
|
2575 |
|
|
|
2576 |
В первом примере память, занимаемая первой структурой массива из 5 |
|
|
2577 |
структур, будет заполнена байтом 0x78 (по умолчанию). |
|
|
2578 |
|
|
|
2579 |
Во втором примере память, занимаемая (i+1)-вой структурой массива из 5 |
|
|
2580 |
структур, будет заполнена словом 0x5678. |
|
|
2581 |
|
|
|
2582 |
В третьем примере память, занимаемая всем массивом из 5 структур, будет |
|
|
2583 |
заполнена длинным словом 0x12345678. |
|
|
2584 |
|
|
|
2585 |
В четвертом примере память, занимаемая структурой rr, будет заполнена |
|
|
2586 |
содержимым переменной i. |
|
|
2587 |
|
|
|
2588 |
Можно также копировать содержимое одной структуры в другую. Например: |
|
|
2589 |
|
|
|
2590 |
rr=aa[2]; |
|
|
2591 |
|
|
|
2592 |
Будет скопировано содержимое третьей структуры массива структур aa в |
|
|
2593 |
структуру rr. |
|
|
2594 |
Return to contents. |
|
|
2595 |
|
|
|
2596 |
|
|
|
2597 |
|
|
|
2598 |
8.1.5 Операции с элементами структур. |
|
|
2599 |
|
|
|
2600 |
|
|
|
2601 |
С элементами структур можно выполнять все те операции, которые |
|
|
2602 |
доступны для переменных соответствующего типа. Например: Объявлена |
|
|
2603 |
структура: |
|
|
2604 |
|
|
|
2605 |
struct test |
|
|
2606 |
{ |
|
|
2607 |
int a; |
|
|
2608 |
char b[8]; |
|
|
2609 |
long c; |
|
|
2610 |
} rr[3]; |
|
|
2611 |
Пример допустимого синтаксиса: |
|
|
2612 |
rr.a = rr.b[i] * rr[1].c + i ; |
|
|
2613 |
|
|
|
2614 |
Примечание: |
|
|
2615 |
При операциях с элементами массива структур и с индексированными |
|
|
2616 |
элементами, в которых в качестве индекса или номера структуры используется |
|
|
2617 |
переменная, компилятор может использовать регистры SI и DI, а в некоторых |
|
|
2618 |
ситуациях (например: rr[i].b[j] >< rr[i+1].b[j+2] ) будет задействован и |
|
|
2619 |
регистр DX. |
|
|
2620 |
|
|
|
2621 |
Для отдельных элементов структуры, можно получать их адрес, размер |
|
|
2622 |
и смещение в теге структуры. Вот пример: |
|
|
2623 |
|
|
|
2624 |
struct AA //объявление тега структуры |
|
|
2625 |
{ |
|
|
2626 |
word a[3]; // первый элемент структуры |
|
|
2627 |
char b; // второй элемент структуры |
|
|
2628 |
long c; // третий элемент структуры |
|
|
2629 |
}; |
|
|
2630 |
|
|
|
2631 |
struct BB //тег второй структуры |
|
|
2632 |
{ |
|
|
2633 |
word aa; // первый элемент |
|
|
2634 |
AA bb; // второй элемент - вложенная структура |
|
|
2635 |
}ss; // объявляем структуру с тегом BB |
|
|
2636 |
|
|
|
2637 |
void proc() |
|
|
2638 |
{ |
|
|
2639 |
AX=#ss.bb.b; // получить адрес элемента b структуры bb в структуре ss |
|
|
2640 |
AX=#BB.bb.b; // получить смещение этого же элемента в теге BB |
|
|
2641 |
AX=sizeof(ss.bb); // получить размер элемента bb в структуре ss |
|
|
2642 |
AX=sizeof(BB.bb); // получить размер элемента bb в теге BB |
|
|
2643 |
} |
|
|
2644 |
Return to contents. |
|
|
2645 |
|
|
|
2646 |
|
|
|
2647 |
|
|
|
2648 |
8.1.6 Вложенные структуры. |
|
|
2649 |
|
|
|
2650 |
|
|
|
2651 |
При объявлении тегов структур можно использовать теги других, |
|
|
2652 |
объявленных ранее структур. Пример вложенных структур: |
|
|
2653 |
|
|
|
2654 |
struct RGB |
|
|
2655 |
{ |
|
|
2656 |
byte Red; |
|
|
2657 |
byte Green; |
|
|
2658 |
byte Blue; |
|
|
2659 |
byte Reserved; |
|
|
2660 |
}; |
|
|
2661 |
|
|
|
2662 |
struct BMPINFO |
|
|
2663 |
{ |
|
|
2664 |
struct BMPHEADER header; //описание этой структуры пропущено |
|
|
2665 |
struct RGB color[256]; |
|
|
2666 |
}info; |
|
|
2667 |
|
|
|
2668 |
Предположим Вам нужно получить содержимое переменной Red десятого |
|
|
2669 |
элемента color. Это можно будет записать так: |
|
|
2670 |
|
|
|
2671 |
AL=info.color[10].Red; |
|
|
2672 |
|
|
|
2673 |
Но существует одно ограничение использования вложенных структур в C--. |
|
|
2674 |
Это невозможность использования переменной в качестве индекса более одного |
|
|
2675 |
раза при обращении к многоэкземплярным структурам. Поясним это на примере: |
|
|
2676 |
|
|
|
2677 |
struct ABC |
|
|
2678 |
{ |
|
|
2679 |
int a; |
|
|
2680 |
int b; |
|
|
2681 |
int c; |
|
|
2682 |
}; |
|
|
2683 |
|
|
|
2684 |
struct |
|
|
2685 |
{ |
|
|
2686 |
struct ABC first[4]; //4 экземпляра структуры ABC |
|
|
2687 |
int d; |
|
|
2688 |
}second[4]; |
|
|
2689 |
|
|
|
2690 |
int i,j; |
|
|
2691 |
|
|
|
2692 |
void proc() |
|
|
2693 |
{ |
|
|
2694 |
AX=second[i].first[j].a; //такая запись вызовет сообщение об ошибка, так |
|
|
2695 |
//как переменная использовалась в двух местах |
|
|
2696 |
AX=second[2].first[j].a; //а этот синтаксис допустим. |
|
|
2697 |
AX=second[i].first[3].a; |
|
|
2698 |
} |
|
|
2699 |
Return to contents. |
|
|
2700 |
|
|
|
2701 |
|
|
|
2702 |
|
|
|
2703 |
8.1.7 Отображение тега структуры на блок памяти. |
|
|
2704 |
|
|
|
2705 |
|
|
|
2706 |
Отображение тега структуры на блок памяти является альтернативой |
|
|
2707 |
указателям на структуры. |
|
|
2708 |
|
|
|
2709 |
Альтернативный способ использования указателей на структуры позволит |
|
|
2710 |
Вам самим выбрать регистр, в котором будет хранится адрес структуры и |
|
|
2711 |
самим следить за его сохранностью и по мере необходимости восстанавливать |
|
|
2712 |
его содержимое. |
|
|
2713 |
|
|
|
2714 |
Объяснить, как использовать отображение тега структуры на память, |
|
|
2715 |
наверное, будет проще на примере: |
|
|
2716 |
|
|
|
2717 |
struct AA //объявление тега структуры |
|
|
2718 |
{ |
|
|
2719 |
word a[3]; // первый элемент структуры |
|
|
2720 |
char b; // второй элемент структуры |
|
|
2721 |
long c; // третий элемент структуры |
|
|
2722 |
}; |
|
|
2723 |
|
|
|
2724 |
byte buf[256]; //блок памяти, на который будет отображен тег структуры |
|
|
2725 |
|
|
|
2726 |
void proc1() |
|
|
2727 |
{ |
|
|
2728 |
... |
|
|
2729 |
proc2 ( #buf ); // вызов процедуры с передачей ей в качестве параметра |
|
|
2730 |
// адреса блока памяти |
|
|
2731 |
... |
|
|
2732 |
} |
|
|
2733 |
|
|
|
2734 |
long proc2 (unsigned int pointer_to_mem) |
|
|
2735 |
{ |
|
|
2736 |
int i; |
|
|
2737 |
BX=pointer_to_mem; // в BX загрузим адрес блока памяти |
|
|
2738 |
FOR(i=0; i<3; i++){ // в массив элемента a записать -1 |
|
|
2739 |
BX.AA.a[i]=-1; |
|
|
2740 |
} |
|
|
2741 |
BX.AA.b=0; |
|
|
2742 |
ES:BX.AA.c=EAX; |
|
|
2743 |
return BX.AA.c; // вернуть содержимое элемента c |
|
|
2744 |
} |
|
|
2745 |
|
|
|
2746 |
В 16-битном режиме для хранения адреса структуры можно использовать |
|
|
2747 |
регистры: BX,DI,SI,BP. Но лучше для этого использовать регистр BX. |
|
|
2748 |
Регистры DI и SI может использовать компилятор при вычислении адреса |
|
|
2749 |
многоэлементных объектов. Регистр BP компилятор использует для работы с |
|
|
2750 |
локальными и параметрическими переменными. В 32-битном режиме можно |
|
|
2751 |
использовать любой кроме ESP и EBP регистр, а регистры EDI и ESI надо |
|
|
2752 |
использовать осторожно. |
|
|
2753 |
Return to contents. |
|
|
2754 |
|
|
|
2755 |
|
|
|
2756 |
|
|
|
2757 |
8.1.8 Битовые поля структур. |
|
|
2758 |
|
|
|
2759 |
|
|
|
2760 |
Битовые поля структур используются для экономии памяти, поскольку |
|
|
2761 |
позволяют плотно упаковать значения, и для организации удобного доступа к |
|
|
2762 |
регистрам внешних устройств, в которых различные биты могут иметь |
|
|
2763 |
самостоятельное функциональное назначение. |
|
|
2764 |
|
|
|
2765 |
Объявление битового поля имеет следующий синтаксис: |
|
|
2766 |
|
|
|
2767 |
<тип> [<идентификатор>]:<константа>; |
|
|
2768 |
|
|
|
2769 |
или на примере: |
|
|
2770 |
|
|
|
2771 |
int var:5; //объявление битового поля размером 5 бит с именем var |
|
|
2772 |
|
|
|
2773 |
Битовое поле состоит из некоторого числа битов, которое задается |
|
|
2774 |
числовым выражением константа. Его значение должно быть целым |
|
|
2775 |
положительным числом и его значение не должно превышать числа разрядов, |
|
|
2776 |
соответствующие типу определяемого битового поля. В C-- битовые поля |
|
|
2777 |
могут содержать только без знаковые значения. Нельзя использовать массивы |
|
|
2778 |
битовых полей, указатели на битовые поля. |
|
|
2779 |
|
|
|
2780 |
идентификатор именует битовое поле. Его наличие необязательно. |
|
|
2781 |
Неименованное битовое поле означает пропуск соответствующего числа битов |
|
|
2782 |
перед размещением следующего элемента структуры. Неименованное битовое |
|
|
2783 |
поле, для которого указан нулевой размер, имеет специальное назначение: |
|
|
2784 |
оно гарантирует, что память для следующего битового поля будет начинаться |
|
|
2785 |
на границе того типа, который задан для неименованного битового поля. |
|
|
2786 |
Т.е. будет произведено выравнивание битового поля на 8/16/32 бита. |
|
|
2787 |
|
|
|
2788 |
В C-- все битовые поля упаковываются одно за другим независимо от |
|
|
2789 |
границ типа идентификаторов. Если последующее поле не является битовым |
|
|
2790 |
полем, то оставшиеся до границы байта биты не будут использованы. |
|
|
2791 |
Максимальный размер битового поля равен 32 бита для типа dword/long, 16 |
|
|
2792 |
бит для типа word/int и 8 бит для типа byte/char. Битовые поля можно |
|
|
2793 |
объединять, т.е. использовать их в операторе union. sizeof |
|
|
2794 |
примененный к битовому полю вернет размер этого поля в битах. При |
|
|
2795 |
использовании битового поля, его содержимое будет расширятся в регистр |
|
|
2796 |
как без знаковое целое число. |
|
|
2797 |
Return to contents. |
|
|
2798 |
|
|
|
2799 |
|
|
|
2800 |
|
|
|
2801 |
8.2 Объединения. |
|
|
2802 |
|
|
|
2803 |
|
|
|
2804 |
Объединения позволяют в разные моменты времени хранить в одном объекте |
|
|
2805 |
значения различного типа. |
|
|
2806 |
|
|
|
2807 |
Память, которая выделяется под объединение, определяется размером |
|
|
2808 |
наиболее длинного из элементов объединения. Все элементы объединения |
|
|
2809 |
размещаются в одной и той же области памяти с одного и того же адреса. |
|
|
2810 |
Значение текущего элемента объединения теряется, когда другому элементу |
|
|
2811 |
объединения присваивается значение. |
|
|
2812 |
|
|
|
2813 |
В C-- реализованы так называемые анонимные объединения. Т.е. |
|
|
2814 |
объединениям не присваивается имя, а обращение к элементам объединения |
|
|
2815 |
происходит как к обычной переменной. Пример: |
|
|
2816 |
|
|
|
2817 |
union |
|
|
2818 |
{ |
|
|
2819 |
dword regEAX; |
|
|
2820 |
word regAX; |
|
|
2821 |
byte regAL; |
|
|
2822 |
}; // объявили, что 3 переменные расположены по одному и тому же |
|
|
2823 |
// физическому адресу |
|
|
2824 |
|
|
|
2825 |
void test() |
|
|
2826 |
{ |
|
|
2827 |
regEAX = 0x2C; |
|
|
2828 |
BL = regAL; //в регистре BL окажется значение 0x2C |
|
|
2829 |
} |
|
|
2830 |
|
|
|
2831 |
Объединять можно переменные различных типов, массивы, строковые |
|
|
2832 |
переменные и структуры. Объединения могут быть глобальными и локальными, а |
|
|
2833 |
также располагаться внутри структур (пока в объединениях внутри структур |
|
|
2834 |
нельзя использовать структуры). Глобальные объединения могут быть |
|
|
2835 |
инициализированными и неинициализированными. Чтобы получить |
|
|
2836 |
инициализированное объединение достаточно проинициализировать лишь первый |
|
|
2837 |
элемент объединения. Если же первый элемент объединения не инициализирован, |
|
|
2838 |
а следующие элементы инициализированы, то это вызовет сообщение компилятора |
|
|
2839 |
об ошибке. |
|
|
2840 |
Return to contents. |
|
|
2841 |
|
|
|
2842 |
|
|
|
2843 |
|
|
|
2844 |
8.3 Команды 'FROM' и 'EXTRACT'. |
|
|
2845 |
|
|
|
2846 |
|
|
|
2847 |
В C-- есть очень оригинальные команды, которых нет в других языках. Это |
|
|
2848 |
FROM и EXTRACT. |
|
|
2849 |
|
|
|
2850 |
Команда FROM имеет синтаксис: |
|
|
2851 |
|
|
|
2852 |
<тип_переменной> <имя_переменной> = FROM <имя_файла>; |
|
|
2853 |
|
|
|
2854 |
Встретив эту команду при компиляции, компилятор загрузит в выходной |
|
|
2855 |
файл содержимое файла имя_файла, а имя_переменной будет идентификатором |
|
|
2856 |
начала загруженного кода. Вот пример использования этой команды из файла |
|
|
2857 |
tinydraw.c--: |
|
|
2858 |
|
|
|
2859 |
byte palette[PALSIZE] = FROM "TINYDRAW.PAL"; // buffer for palette |
|
|
2860 |
|
|
|
2861 |
Команда EXTRACT имеет синтаксис: |
|
|
2862 |
|
|
|
2863 |
<тип_переменной> <имя_переменной> = EXTRACT <имя_файла>, <начало>, <длина>; |
|
|
2864 |
|
|
|
2865 |
Встретив эту команду при компиляции, компилятор загрузит в выходной |
|
|
2866 |
файл из файла имя_файла число байт равное длина со смещения начало, а |
|
|
2867 |
имя_переменной будет идентификатором начала загруженного кода. Вот пример |
|
|
2868 |
использования этой команды: |
|
|
2869 |
|
|
|
2870 |
byte LIT128 = EXTRACT "8X16.FNT", 16*128, 16; |
|
|
2871 |
byte LIT130 = EXTRACT "8X16.FNT", 16*130, 16; |
|
|
2872 |
Return to contents. |
|
|
2873 |
|
|
|
2874 |
|
|
|
2875 |
|
|
|
2876 |
9. Операторы. |
|
|
2877 |
|
|
|
2878 |
9.1 Условные инструкции. |
|
|
2879 |
|
|
|
2880 |
|
|
|
2881 |
Условные инструкции, при помощи которых осуществляется ветвление, такие |
|
|
2882 |
же как в C. |
|
|
2883 |
|
|
|
2884 |
C-- имеет две инструкции ветвления. if и IF. |
|
|
2885 |
|
|
|
2886 |
if делает близкий условный переход, а IF делает короткий |
|
|
2887 |
(8-разрядный) условный переход. IF выполняется быстрее и может экономить |
|
|
2888 |
до 3 байт в размере кода, но может осуществлять переходы только в пределах |
|
|
2889 |
127 байтов кода. |
|
|
2890 |
|
|
|
2891 |
Условные инструкции, как и в C, могут сопровождаться, как одиночной |
|
|
2892 |
командой, так и блоком из нескольких команд, заключенных в фигурные скобки |
|
|
2893 |
{ и }. Условные инструкции имеют те же ограничения, что и условные |
|
|
2894 |
выражения. |
|
|
2895 |
|
|
|
2896 |
Если за инструкцией IF следует больше чем 127 байтов кода, транслятор |
|
|
2897 |
выдаст следующее сообщение об ошибке: |
|
|
2898 |
|
|
|
2899 |
IF jump distance too far, use if. |
|
|
2900 |
|
|
|
2901 |
Это можно просто исправить, заменив в этом месте инструкцию IF на if. |
|
|
2902 |
|
|
|
2903 |
Команды else и ELSE используются точно так же, как в языке C. |
|
|
2904 |
Отличие их в том, что ELSE имеет ограничение адреса перехода 127 байт, |
|
|
2905 |
такое же как IF. else генерирует код на 1 байт длиннее, чем ELSE. |
|
|
2906 |
|
|
|
2907 |
Команды IF и else, а также if и ELSE могут свободно смешиваться |
|
|
2908 |
как в следующем примере: |
|
|
2909 |
|
|
|
2910 |
if( x == 2 ) |
|
|
2911 |
WRITESTR("Two"); |
|
|
2912 |
ELSE{ WRITESTR("not two."); |
|
|
2913 |
printmorestuff(); |
|
|
2914 |
} |
|
|
2915 |
|
|
|
2916 |
Если за инструкцией ELSE следует больше чем 127 байтов кода, |
|
|
2917 |
транслятор выдаст следующее сообщение об ошибке: |
|
|
2918 |
|
|
|
2919 |
ELSE jump distance too far, use else. |
|
|
2920 |
|
|
|
2921 |
Это можно просто исправить, заменив в этом месте инструкцию ELSE на |
|
|
2922 |
else. |
|
|
2923 |
Return to contents. |
|
|
2924 |
|
|
|
2925 |
|
|
|
2926 |
|
|
|
2927 |
9.2 Циклы do{} while. |
|
|
2928 |
|
|
|
2929 |
|
|
|
2930 |
В таком цикле блок кода, составляющий тело цикла, будет повторяться, |
|
|
2931 |
пока условное выражение имеет значение истинно. |
|
|
2932 |
|
|
|
2933 |
Истинность условного выражения проверяется после выполнения тела цикла, |
|
|
2934 |
поэтому блок кода будет выполнен, по крайней мере, один раз. |
|
|
2935 |
|
|
|
2936 |
Пример do {} while цикла, в котором тело будет исполнено пять раз: |
|
|
2937 |
|
|
|
2938 |
count = 0; |
|
|
2939 |
do { |
|
|
2940 |
count++; |
|
|
2941 |
WRITEWORD(count); |
|
|
2942 |
WRITELN(); |
|
|
2943 |
} while (count < 5); |
|
|
2944 |
|
|
|
2945 |
Условное выражение в do {} while инструкции должно соответствовать тем же |
|
|
2946 |
правилам, что и в инструкциях IF и if. |
|
|
2947 |
Return to contents. |
|
|
2948 |
|
|
|
2949 |
|
|
|
2950 |
|
|
|
2951 |
9.3 Циклы loop, LOOPNZ, loopnz. |
|
|
2952 |
|
|
|
2953 |
|
|
|
2954 |
Циклы loop повторяют блок кода, пока определенная переменная или |
|
|
2955 |
регистр, выполняющие роль счетчика цикла, содержат значение, отличное от |
|
|
2956 |
нуля. В конце выполнения блока кода, составляющего тело цикла, указанная |
|
|
2957 |
переменная или регистр - уменьшается на 1, а затем проверяется на равенство |
|
|
2958 |
нулю. Если переменная (или регистр) не равна нулю, тело цикла будет |
|
|
2959 |
выполнено снова, и процесс повторится. |
|
|
2960 |
|
|
|
2961 |
Пример использования цикла loop в котором в качестве счетчика цикла |
|
|
2962 |
использована переменная: |
|
|
2963 |
|
|
|
2964 |
count = 5; |
|
|
2965 |
loop( count ) |
|
|
2966 |
{WRITEWORD(count); |
|
|
2967 |
WRITELN(); |
|
|
2968 |
} |
|
|
2969 |
|
|
|
2970 |
Наибольший эффект дает использование регистра CX для циклов с небольшим |
|
|
2971 |
телом, поскольку в этом случае компилятором генерируется цикл с применением |
|
|
2972 |
машинной команды LOOP. |
|
|
2973 |
|
|
|
2974 |
Если перед стартом счетчик циклов содержит нулевое значение, команды |
|
|
2975 |
тела цикла будут выполнены максимальное число раз для диапазона переменной |
|
|
2976 |
(256 раз для 8-битного счетчика (переменной типа byte или char), 65536 для |
|
|
2977 |
16-битного счетчика (переменной типа word или int), и 4294967296 для |
|
|
2978 |
32-битного счетчика (переменной типа dword или long). |
|
|
2979 |
|
|
|
2980 |
В следующем примере цикл будет выполнен 256 раз: |
|
|
2981 |
|
|
|
2982 |
BH = 0; |
|
|
2983 |
loop (BH) |
|
|
2984 |
{ |
|
|
2985 |
} |
|
|
2986 |
|
|
|
2987 |
Если в команде не указано никакого счетчика цикла, цикл будет |
|
|
2988 |
продолжаться бесконечно. |
|
|
2989 |
|
|
|
2990 |
Следующий пример будет непрерывно выводить символ звездочки (*) на |
|
|
2991 |
экран: |
|
|
2992 |
|
|
|
2993 |
loop() |
|
|
2994 |
WRITE('*'); |
|
|
2995 |
|
|
|
2996 |
Программист, если хочет, может использовать или изменять значение |
|
|
2997 |
переменной счетчика цикла внутри цикла. |
|
|
2998 |
|
|
|
2999 |
Например, следующий цикл выполнится только 3 раза: |
|
|
3000 |
|
|
|
3001 |
CX = 1000; |
|
|
3002 |
loop( CX ) |
|
|
3003 |
{ |
|
|
3004 |
IF( CX > 3 ) |
|
|
3005 |
CX = 3; |
|
|
3006 |
} |
|
|
3007 |
|
|
|
3008 |
Цикл можно также прервать оператором разрыва BREAK или break. Вот |
|
|
3009 |
тот же пример с использованием BREAK: |
|
|
3010 |
|
|
|
3011 |
CX = 1000; |
|
|
3012 |
loop( CX ) |
|
|
3013 |
{ |
|
|
3014 |
IF( CX > 3 ) |
|
|
3015 |
BREAK; |
|
|
3016 |
} |
|
|
3017 |
|
|
|
3018 |
Циклы LOOPNZ/loopnz отличаются от цикла loop, тем, что перед входом |
|
|
3019 |
в цикл проверяется равенство нулю аргумента цикла. Если аргумент равен |
|
|
3020 |
нулю, то тело цикла ни разу не выполнится (в цикле loop в этом случае |
|
|
3021 |
тело цикла выполнится максимальное число раз). Цикл LOOPNZ получается |
|
|
3022 |
максимально эффективным при оптимизации на размер кода, если в качестве |
|
|
3023 |
параметра-счетчика используется регистр CX/ECX. При этом компилятор |
|
|
3024 |
использует ассемблерные инструкции JCXZ/JECXZ и LOOP. |
|
|
3025 |
Return to contents. |
|
|
3026 |
|
|
|
3027 |
|
|
|
3028 |
|
|
|
3029 |
9.4 Цикл while, WHILE. |
|
|
3030 |
|
|
|
3031 |
|
|
|
3032 |
Синтаксис: |
|
|
3033 |
while(<выражение>) |
|
|
3034 |
<оператор> |
|
|
3035 |
|
|
|
3036 |
Цикл выполняется до тех пор, пока значение выражения не станет |
|
|
3037 |
ложным. Вначале вычисляется выражение. Если выражение изначально ложно, |
|
|
3038 |
то тело оператора while вообще не выполняется и управление сразу |
|
|
3039 |
передается на следующий оператор программы. |
|
|
3040 |
|
|
|
3041 |
Цикл WHILE аналогичен циклу while, но при этом генерируется код на |
|
|
3042 |
3 байта короче. Размер сгенерированного кода в цикле WHILE должен быть |
|
|
3043 |
меньше 127 байт. |
|
|
3044 |
|
|
|
3045 |
Примеры: |
|
|
3046 |
while ( i < 20 ){ |
|
|
3047 |
WRITEWORD(i); |
|
|
3048 |
i++; |
|
|
3049 |
} |
|
|
3050 |
|
|
|
3051 |
WHILE (i < 20 ) @WRITEWORD(i); //цикл либо будет бесконечным либо не |
|
|
3052 |
//выполнится ни разу |
|
|
3053 |
Return to contents. |
|
|
3054 |
|
|
|
3055 |
|
|
|
3056 |
|
|
|
3057 |
9.5 Цикл for, FOR. |
|
|
3058 |
|
|
|
3059 |
|
|
|
3060 |
Синтаксис: |
|
|
3061 |
for ([<начальное выражение>]; [<условие>]; [<приращение>]) |
|
|
3062 |
<оператор> |
|
|
3063 |
|
|
|
3064 |
Цикл for выполняется до тех пор, пока значение условия не станет |
|
|
3065 |
ложным. Если условие изначально ложно, то тело оператора for вообще не |
|
|
3066 |
выполняется и управление сразу передается на следующий оператор программы. |
|
|
3067 |
Начальное выражение и приращение обычно используются для инициализации |
|
|
3068 |
и модификации параметров цикла. |
|
|
3069 |
|
|
|
3070 |
Первым шагом при выполнении for является вычисление начального |
|
|
3071 |
выражения, если оно имеется. Затем вычисляется условие и производится |
|
|
3072 |
его оценка следующим образом: |
|
|
3073 |
|
|
|
3074 |
1) Если условие истинно, то выполняется тело оператора. Затем |
|
|
3075 |
вычисляется приращение (если оно есть), и процесс повторяется. |
|
|
3076 |
|
|
|
3077 |
2) Если условие опущено, то его значение принимается за истину. В |
|
|
3078 |
этом случае цикл for представляет бесконечный цикл, который может |
|
|
3079 |
завершиться только при выполнении в его теле операторов break, goto, |
|
|
3080 |
return. |
|
|
3081 |
|
|
|
3082 |
3) Если условие ложно, то выполнение цикла for заканчивается и |
|
|
3083 |
управление передается следующему оператору. |
|
|
3084 |
|
|
|
3085 |
Цикл FOR аналогичен циклу for, но при этом генерируется код на 3 |
|
|
3086 |
байта короче. Размер сгенерированного кода в цикле FOR должен быть меньше |
|
|
3087 |
127 байт. |
|
|
3088 |
|
|
|
3089 |
Примеры: |
|
|
3090 |
for(i=0;i<5;i++){ |
|
|
3091 |
WRITESTR("СТРОКА "); |
|
|
3092 |
WRITEWORD(i); |
|
|
3093 |
WRITELN(); |
|
|
3094 |
} |
|
|
3095 |
|
|
|
3096 |
Число начальных выражений и число приращений не ограничено. Каждый |
|
|
3097 |
оператор в начальных выражениях и приращениях должен разделяться |
|
|
3098 |
запятой. Пример: |
|
|
3099 |
|
|
|
3100 |
for ( a=1, b=2 ; a<5 ; a++, b+=a ) {... |
|
|
3101 |
|
|
|
3102 |
Также есть возможность логического объединения условий. Объединять |
|
|
3103 |
можно до 32 условий. Каждое объединяемое условие должно быть заключено в |
|
|
3104 |
скобки. Пример: |
|
|
3105 |
|
|
|
3106 |
for ( a=0 ; (a>=0) && (a<10) ; a++ ){... |
|
|
3107 |
Return to contents. |
|
|
3108 |
|
|
|
3109 |
|
|
|
3110 |
|
|
|
3111 |
9.6 Оператор переключатель switch. |
|
|
3112 |
|
|
|
3113 |
|
|
|
3114 |
Синтаксис: |
|
|
3115 |
switch(<выражение>){ |
|
|
3116 |
case <константа>: |
|
|
3117 |
<оператор> |
|
|
3118 |
... |
|
|
3119 |
case <константа>: |
|
|
3120 |
<оператор> |
|
|
3121 |
... |
|
|
3122 |
... |
|
|
3123 |
default: |
|
|
3124 |
<оператор> |
|
|
3125 |
} |
|
|
3126 |
Оператор переключатель switch предназначен для выбора одного из |
|
|
3127 |
нескольких альтернативных путей выполнения программы. Выполнение начинается |
|
|
3128 |
с вычисления значения выражения. После этого управление передается одному |
|
|
3129 |
из операторов тела переключателя. В теле переключателя содержатся |
|
|
3130 |
конструкции: case константа:, которые синтаксически представляют собой |
|
|
3131 |
метки операторов. Оператор, получающий управление, - это тот оператор, |
|
|
3132 |
значение константы которого совпадают со значением выражения |
|
|
3133 |
переключателя. Значение константы должно быть уникальным. |
|
|
3134 |
|
|
|
3135 |
Выполнение тела оператора-переключателя switch начинается с выбранного |
|
|
3136 |
таким образом оператора, и продолжается до конца тела или до тех пор, пока |
|
|
3137 |
какой-либо оператор не передаст управление за пределы тела. |
|
|
3138 |
|
|
|
3139 |
Оператор, следующий за ключевым словом default, выполняется, если ни |
|
|
3140 |
одна из констант не равна значению выражения. Если default опущено, то |
|
|
3141 |
ни один оператор в теле переключателя не выполняется, и управление |
|
|
3142 |
передается на оператор, следующий за switch. |
|
|
3143 |
|
|
|
3144 |
Для выхода из тела переключателя обычно используется оператор разрыва |
|
|
3145 |
break (BREAK). |
|
|
3146 |
|
|
|
3147 |
Пример: |
|
|
3148 |
switch (i){ |
|
|
3149 |
case 'A': |
|
|
3150 |
WRITE(i); |
|
|
3151 |
i++; |
|
|
3152 |
BREAK; |
|
|
3153 |
case 32: |
|
|
3154 |
WRITE('_'); |
|
|
3155 |
i++; |
|
|
3156 |
BREAK; |
|
|
3157 |
default: |
|
|
3158 |
WRITE('i'); |
|
|
3159 |
} |
|
|
3160 |
|
|
|
3161 |
Оператор switch сейчас в компиляторе может реализовываться трем |
|
|
3162 |
способами: двухтабличным, табличным и методом последовательных проверок. |
|
|
3163 |
|
|
|
3164 |
Табличный метод является самым быстрым, а при большом числе операторов |
|
|
3165 |
case и при незначительной разнице между максимальным и минимальным |
|
|
3166 |
значениями case он еще может быть и более компактным. Но у него есть и |
|
|
3167 |
недостатки: в 16-битном режиме компилятор всегда использует регистр BX, а в |
|
|
3168 |
32-битном режиме, если операндом switch является регистр, то его значение |
|
|
3169 |
будет разрушено. |
|
|
3170 |
|
|
|
3171 |
В методе последовательных проверок блок сравнений находится в начале |
|
|
3172 |
тела оператора switch, это позволяет избавиться от 1-2 лишних jmp. Но |
|
|
3173 |
компилятор не может определить, какой тип перехода использовать при |
|
|
3174 |
проверке значений case. Это будет Вашей заботой. Если размер кода от |
|
|
3175 |
начала тела оператора switch до места расположения оператора case |
|
|
3176 |
меньше 128 байт, можно использовать короткий переход. В этом случае Вы |
|
|
3177 |
можете указать оператор CASE, что приведет к генерации более компактного |
|
|
3178 |
кода. Компилятор в предупреждениях будет Вам подсказывать о возможности |
|
|
3179 |
использования операторов CASE. Использование оператора CASE в случаях, |
|
|
3180 |
когда размер блока кода более 128 байт приведет к выдаче компилятором |
|
|
3181 |
сообщения об ошибке. |
|
|
3182 |
|
|
|
3183 |
При двухтабличном методе создаются две таблицы - таблица адресов входа в |
|
|
3184 |
тело оператора switch/SWITCH и таблица значений case. Генерируется |
|
|
3185 |
процедура сравнения входного значения со значениями во второй таблице. Если |
|
|
3186 |
есть совпадение, то делается переход по адресу из второй таблицы. Этот |
|
|
3187 |
метод является самым медленным, но при большом числе значений case (более |
|
|
3188 |
15) он становится самым компактным. |
|
|
3189 |
|
|
|
3190 |
При оптимизации кода на размер, компилятор предварительно вычисляет |
|
|
3191 |
размер кода, который может быть получен всеми методами и реализует самый |
|
|
3192 |
компактный. При оптимизации на скорость преимущество отдается табличному |
|
|
3193 |
методу, если размер таблицы получается не слишком большим. |
|
|
3194 |
|
|
|
3195 |
Для оператора switch введена также и короткая его форма - SWITCH. |
|
|
3196 |
Ее можно применять в случае, если размер блока кода между началом тела |
|
|
3197 |
оператора и оператором default (если он отсутствует, то концом тела |
|
|
3198 |
оператора switch) меньше 128 байт. О возможности использования короткой |
|
|
3199 |
формы компилятор будет сообщать в предупреждениях. |
|
|
3200 |
|
|
|
3201 |
Для оператора case/CASE, который может использоваться только в теле |
|
|
3202 |
блока оператора switch/SWITCH, можно указывать диапазон значений. Сначала |
|
|
3203 |
надо указывать меньшее значение, затем после многоточия большее. Пример: |
|
|
3204 |
|
|
|
3205 |
switch(AX){ |
|
|
3206 |
case 1...5: |
|
|
3207 |
WRITESTR("Range AX from 1 to 5"); |
|
|
3208 |
BREAK; |
|
|
3209 |
}; |
|
|
3210 |
|
|
|
3211 |
Раньше Вам бы пришлось писать более громоздкую конструкцию: |
|
|
3212 |
|
|
|
3213 |
switch(AX){ |
|
|
3214 |
case 1: |
|
|
3215 |
case 2: |
|
|
3216 |
case 3: |
|
|
3217 |
case 4: |
|
|
3218 |
case 5: |
|
|
3219 |
WRITESTR("Range AX from 1 to 5"); |
|
|
3220 |
BREAK; |
|
|
3221 |
}; |
|
|
3222 |
|
|
|
3223 |
Кроме того, что новый формат записи более компактен и более читабелен, |
|
|
3224 |
но еще при этом компилятор создает более компактный и быстрый код. |
|
|
3225 |
Return to contents. |
|
|
3226 |
|
|
|
3227 |
|
|
|
3228 |
|
|
|
3229 |
9.7 Оператор перехода goto, GOTO. |
|
|
3230 |
|
|
|
3231 |
|
|
|
3232 |
Синтаксис: |
|
|
3233 |
goto <метка>; |
|
|
3234 |
. |
|
|
3235 |
. |
|
|
3236 |
. |
|
|
3237 |
<метка>: |
|
|
3238 |
|
|
|
3239 |
Оператор перехода goto передает управление на оператор помеченный |
|
|
3240 |
меткой. Аналогом в ассемблере оператору goto является команда jmp near. |
|
|
3241 |
Аналогом в ассемблере оператору GOTO является команда jmp short. |
|
|
3242 |
Return to contents. |
|
|
3243 |
|
|
|
3244 |
|
|
|
3245 |
|
|
|
3246 |
9.8 Оператор разрыва break, BREAK. |
|
|
3247 |
|
|
|
3248 |
|
|
|
3249 |
Оператор разрыва break прерывает выполнение операторов do-while, |
|
|
3250 |
for, switch, while, loop, loopnz, LOOPNZ. Он может содержаться |
|
|
3251 |
только в теле этих операторов. Управление передается оператору, следующему |
|
|
3252 |
за прерванным циклом. |
|
|
3253 |
|
|
|
3254 |
Оператор BREAK аналогичен break, но при этом генерируется код на 1 |
|
|
3255 |
байт короче. Размер сгенерированного кода от места где применяется BREAK |
|
|
3256 |
до конца цикла должен быть меньше 127 байт. |
|
|
3257 |
|
|
|
3258 |
Примеры: |
|
|
3259 |
FOR (i=0; ; i++){ |
|
|
3260 |
FOR(j=0; j < WIDTH; j++){ |
|
|
3261 |
IF(i==5)BREAK; |
|
|
3262 |
} |
|
|
3263 |
IF(i==10)BREAK; |
|
|
3264 |
} |
|
|
3265 |
Return to contents. |
|
|
3266 |
|
|
|
3267 |
|
|
|
3268 |
|
|
|
3269 |
9.9 Оператор продолжения continue, CONTINUE. |
|
|
3270 |
|
|
|
3271 |
|
|
|
3272 |
Оператор продолжения continue передает управление на следующую |
|
|
3273 |
итерацию в циклах do-while, for, while, loop, loopnz. В циклах |
|
|
3274 |
do-while, while, loop следующая итерация начинается с вычисления |
|
|
3275 |
условного выражения. Для цикла for следующая итерация начинается с |
|
|
3276 |
вычисления выражения приращения, а затем происходит вычисление условного |
|
|
3277 |
выражения. |
|
|
3278 |
|
|
|
3279 |
Оператор CONTINUE аналогичен continue, но при этом генерируется код на |
|
|
3280 |
1 байт короче. Размер сгенерированного кода от места где применяется |
|
|
3281 |
CONTINUE до начала итерации должен быть меньше 127 байт. |
|
|
3282 |
Return to contents. |
|
|
3283 |
|
|
|
3284 |
|
|
|
3285 |
|
|
|
3286 |
9.10 Логическое объединение условий. |
|
|
3287 |
|
|
|
3288 |
|
|
|
3289 |
Существует возможность логического объединения сравнений в условиях |
|
|
3290 |
IF и if, циклах do{}while, while{}, WHILE{}, for{} и FOR{}. |
|
|
3291 |
Каждое сравнение при их логическом объединении должно быть заключено в |
|
|
3292 |
скобки. Объединять можно не более 32 сравнений. |
|
|
3293 |
|
|
|
3294 |
В отличие от C в C-- анализ логических объединений происходит слева |
|
|
3295 |
направо и все лишние скобки будут восприняты компилятором как ошибочные. |
|
|
3296 |
Это несколько снижает гибкость и возможности применения этих объединений, |
|
|
3297 |
но такова традиция и философия, заложенная в C--. |
|
|
3298 |
|
|
|
3299 |
Пример: |
|
|
3300 |
|
|
|
3301 |
if ( (a>3) && (b>4) || (c<8) ){ |
|
|
3302 |
|
|
|
3303 |
Т.е. если произвести расшифровку этого условия, то получится следующее: |
|
|
3304 |
условие выполнится если a>3 и b>4 или a>3 и c<8. |
|
|
3305 |
Return to contents. |
|
|
3306 |
|
|
|
3307 |
|
|
|
3308 |
|
|
|
3309 |
9.11 Переход через циклы. |
|
|
3310 |
|
|
|
3311 |
|
|
|
3312 |
Для операторов BREAK, break, CONTINUE, continue введена |
|
|
3313 |
поддержка числового параметра, определяющего, сколько циклов надо |
|
|
3314 |
пропустить, прежде чем будет выполнен этот оператор. Например, мы имеем три |
|
|
3315 |
вложенных цикла: |
|
|
3316 |
|
|
|
3317 |
do{ |
|
|
3318 |
loop(CX){ |
|
|
3319 |
for(BX=0;BX<10;BX++){ |
|
|
3320 |
break; //стандартный оператор |
|
|
3321 |
break 0; //break с параметром - пропустить 0 циклов |
|
|
3322 |
break 1; //break с параметром - пропустить 1 цикл |
|
|
3323 |
break 2; //break с параметром - пропустить 2 цикла |
|
|
3324 |
} |
|
|
3325 |
LABL0: |
|
|
3326 |
} |
|
|
3327 |
LABL1: |
|
|
3328 |
}while (DX!=0); |
|
|
3329 |
LABL2: |
|
|
3330 |
|
|
|
3331 |
В третьем цикле находится группа различных вариантов оператора break. |
|
|
3332 |
Первым стоит стандартный оператор break, при выполнении которого |
|
|
3333 |
управление будет передаваться за пределы третьего цикла - на метку LABL0. |
|
|
3334 |
Вторым идет оператор break 0, при выполнении которого будет пропущено 0 |
|
|
3335 |
циклов и управление будет передано опять же на метку LABL0. Таким |
|
|
3336 |
образом, запись break и break 0 являются синонимами. Третьим идет |
|
|
3337 |
оператор break 1, при выполнении которого будет пропущен один цикл и |
|
|
3338 |
управление будет передано за пределы второго цикла на метку LABL1. Ну и |
|
|
3339 |
наконец, последним идет оператор break 2, при выполнении которого |
|
|
3340 |
компилятор пропустит два цикла и передаст управление за пределы третьего, |
|
|
3341 |
на метку LABL2. Метки в этом примере проставлены для удобства объяснения. |
|
|
3342 |
Ну и я надеюсь, Вам понятно, что значение параметра не может превышать |
|
|
3343 |
числа циклов находящихся перед текущим. Так для одиночного цикла этот |
|
|
3344 |
параметр может принимать максимальное и единственное значение - 0. |
|
|
3345 |
Return to contents. |
|
|
3346 |
|
|
|
3347 |
|
|
|
3348 |
|
|
|
3349 |
9.12 Инвертирование флага проверки условий. |
|
|
3350 |
|
|
|
3351 |
|
|
|
3352 |
Инвертирование флага проверки условий в операциях сравнения if/IF |
|
|
3353 |
for/FOR while/WHILE происходит с помощью символа ! - not. |
|
|
3354 |
|
|
|
3355 |
Выражени |
|
|
3356 |
|
|
|
3357 |
IF ( NOTCARRYFLAG )... и IF ( ! CARRYFLAG )... |
|
|
3358 |
IF ( proc() == 0 )... и IF ( ! proc() ) ... |
|
|
3359 |
|
|
|
3360 |
являются синонимами. |
|
|
3361 |
Return to contents. |
|
|
3362 |
|
|
|
3363 |
|
|
|
3364 |
|
|
|
3365 |
9.13 Вычисление выражения, а затем проверка условия. |
|
|
3366 |
|
|
|
3367 |
|
|
|
3368 |
В операциях сравнения в левом операнде теперь допустимо использовать |
|
|
3369 |
вычисления выражения с присваиванием и операции инкремента, декремента. |
|
|
3370 |
Например: |
|
|
3371 |
|
|
|
3372 |
IF (i=a+2 != 0 )... |
|
|
3373 |
IF ( i++ )... |
|
|
3374 |
IF ( a-- )... |
|
|
3375 |
IF ( i+=4 == 0 )... |
|
|
3376 |
|
|
|
3377 |
Во всех этих примерах сначала произойдет вычисление выражения в левой |
|
|
3378 |
части операции сравнения, а потом будет произведено сравнение результата с |
|
|
3379 |
правой частью выражения сравнения. |
|
|
3380 |
Return to contents. |
|
|
3381 |
|
|
|
3382 |
|
|
|
3383 |
|
|
|
3384 |
9.14 Проверка битов при операции сравнения. |
|
|
3385 |
|
|
|
3386 |
|
|
|
3387 |
Если в левой части выражения сравнения написано: BX & 5, то при |
|
|
3388 |
вычислении выражения содержимое регистра BX будет изменено инструкцией |
|
|
3389 |
and. Но иногда возникает необходимость в проверке битов без изменения |
|
|
3390 |
содержимого регистра BX. Для этих целей надо использовать инструкцию |
|
|
3391 |
test. Как же указать компилятору, в каких ситуациях использовать |
|
|
3392 |
инструкцию and, а в каких test? В стандартных языках C для этого |
|
|
3393 |
используется механизм приоритетов - если выражение заключено в скобки, то |
|
|
3394 |
производится его вычисление, если нет, то производится проверка. Но C-- не |
|
|
3395 |
поддерживает приоритетов. Для разрешения этой проблемы в C-- решено |
|
|
3396 |
использовать непосредственно саму инструкцию test. Вот допустимые |
|
|
3397 |
варианты синтаксиса: |
|
|
3398 |
|
|
|
3399 |
IF ( $test AX,5 ) |
|
|
3400 |
IF ( ! $test AX,5) |
|
|
3401 |
IF ( asm test AX,5) |
|
|
3402 |
IF ( ! asm { test AX,5 } ) |
|
|
3403 |
Return to contents. |
|
|
3404 |
|
|
|
3405 |
|
|
|
3406 |
|
|
|
3407 |
9.15 Оператор перестановки. |
|
|
3408 |
|
|
|
3409 |
|
|
|
3410 |
В C-- есть оператор, который не встречается в других языках, это |
|
|
3411 |
оператор перестановки. Оператор перестановки меняет местами содержимое двух |
|
|
3412 |
переменных. Символьное обозначение этого оператора ><. Переменные с обеих |
|
|
3413 |
сторон оператора перестановки должны иметь одинаковый размер, 8 бит и 8 |
|
|
3414 |
бит, 16 бит и 16 бит, или 32 бита и 32 бита. |
|
|
3415 |
|
|
|
3416 |
Вот некоторые примеры: |
|
|
3417 |
|
|
|
3418 |
AX >< BX; // сохраняет значение BX в AX и значение AX в BX |
|
|
3419 |
CH >< BL; // меняет местами содержимое регистров CH и BL |
|
|
3420 |
dog >< cat; /* меняет местами значения переменной dog и переменной cat*/ |
|
|
3421 |
counter >< CX; // меняет местами значения переменной counter |
|
|
3422 |
// и содержимое регистра CX |
|
|
3423 |
|
|
|
3424 |
Если перестановка осуществляется между двумя 8-разрядными переменными в |
|
|
3425 |
памяти, будет разрушено содержимое регистра AL. Если перестановка - между |
|
|
3426 |
двумя 16-разрядными переменными в памяти, будет разрушено содержимое |
|
|
3427 |
регистра AX. Если перестановка - между двумя 32-разрядными переменными в |
|
|
3428 |
памяти, будет разрушено содержимое EAX. В любом другом случае, например, |
|
|
3429 |
между переменной в памяти и регистром, значения всех регистров будут |
|
|
3430 |
сохранены. |
|
|
3431 |
Return to contents. |
|
|
3432 |
|
|
|
3433 |
|
|
|
3434 |
|
|
|
3435 |
9.16 Оператор отрицания. |
|
|
3436 |
|
|
|
3437 |
|
|
|
3438 |
C-- поддерживает быстрый синтаксис смены знака переменной - оператор |
|
|
3439 |
отрицания. Поставив - (знак минус) перед идентификатором переменной памяти |
|
|
3440 |
или регистра и ; (точку с запятой) после идентификатора, вы смените знак |
|
|
3441 |
переменной памяти или регистра. |
|
|
3442 |
|
|
|
3443 |
Вот некоторые примеры: |
|
|
3444 |
|
|
|
3445 |
-AX; // результат тот же, что и при 'AX = -AX;' ,но быстрее. |
|
|
3446 |
-tree; // то же самое, что 'tree = -tree;' ,но быстрее. |
|
|
3447 |
-BH; // меняет знак BH. |
|
|
3448 |
Return to contents. |
|
|
3449 |
|
|
|
3450 |
|
|
|
3451 |
|
|
|
3452 |
9.17 Оператор инверсии. |
|
|
3453 |
|
|
|
3454 |
|
|
|
3455 |
C-- поддерживает быстрый синтаксис выполнения логической инверсии |
|
|
3456 |
значения переменной - оператор инверсии. Поставив ! (восклицательный знак) |
|
|
3457 |
перед идентификатором переменной памяти или регистром и ; (точку с |
|
|
3458 |
запятой) после идентификатора, вы выполните логическую (выполнится |
|
|
3459 |
ассемблерная команда NOT) инверсию текущего значения переменной. Вот |
|
|
3460 |
некоторые примеры: |
|
|
3461 |
|
|
|
3462 |
!AX; // то же самое, что ' AX ^ = 0xFFFF; ' но быстрее. |
|
|
3463 |
!node; // заменяет значение 'node' его логической инверсией. |
|
|
3464 |
!CL; // то же самое, что ' CL ^ = 0xFF ' но быстрее. |
|
|
3465 |
Return to contents. |
|
|
3466 |
|
|
|
3467 |
|
|
|
3468 |
|
|
|
3469 |
9.18 Специальные условные выражения. |
|
|
3470 |
|
|
|
3471 |
|
|
|
3472 |
C-- поддерживает восемь специальных условных выражений: |
|
|
3473 |
|
|
|
3474 |
CARRYFLAG |
|
|
3475 |
NOTCARRYFLAG |
|
|
3476 |
OVERFLOW |
|
|
3477 |
NOTOVERFLOW |
|
|
3478 |
ZEROFLAG |
|
|
3479 |
NOTZEROFLAG |
|
|
3480 |
MINUSFLAG |
|
|
3481 |
PLUSFLAG |
|
|
3482 |
|
|
|
3483 |
Они могут использоваться вместо любых нормальных условных выражений. |
|
|
3484 |
Если Вы желаете, например, выполнить блок кода только если установлен флаг |
|
|
3485 |
переноса, Вам следует использовать следующую последовательность команд: |
|
|
3486 |
|
|
|
3487 |
IF( CARRYFLAG ) |
|
|
3488 |
{ |
|
|
3489 |
// здесь вы чего-то делаете |
|
|
3490 |
} |
|
|
3491 |
|
|
|
3492 |
Если Вы желаете непрерывно выполнять блок кода до тех пор, пока не |
|
|
3493 |
установится флаг переполнения, Вам следует использовать нечто подобное |
|
|
3494 |
следующему куску кода: |
|
|
3495 |
|
|
|
3496 |
do { |
|
|
3497 |
// здесь вы опять чего-то делаете |
|
|
3498 |
} while( NOTOVERFLOW ); |
|
|
3499 |
Return to contents. |
|
|
3500 |
|
|
|
3501 |
|
|
|
3502 |
|
|
|
3503 |
9.19 Символ $ - вставляет текущий адрес программы. |
|
|
3504 |
|
|
|
3505 |
|
|
|
3506 |
Символ $, кроме того, что является признаком последующей ассемблерной |
|
|
3507 |
инструкции, в языке C--, как и в языке Assembler может указывать текущий |
|
|
3508 |
адрес (смещение) компилируемой программы. Но в C-- он имел ограниченные |
|
|
3509 |
возможности. Он мог быть использован лишь как аргумент в операторах |
|
|
3510 |
GOTO/goto и ассемблерных инструкциях DW/DD/JMP. |
|
|
3511 |
|
|
|
3512 |
Этот символ может находиться в любом месте вычисляемого числового |
|
|
3513 |
выражения и может быть применен в любом месте совместно с другими числовыми |
|
|
3514 |
выражениями. |
|
|
3515 |
|
|
|
3516 |
Примеры применения: |
|
|
3517 |
|
|
|
3518 |
DW #main-$ //записать расстояние от процедуры main до текущего места |
|
|
3519 |
GOTO $+2; //перейти по адресу на 2 больше, чем текущий адрес |
|
|
3520 |
Return to contents. |
|
|
3521 |
|
|
|
3522 |
|
|
|
3523 |
|
|
|
3524 |
9.20 Ключевое слово static и оператор ::. |
|
|
3525 |
|
|
|
3526 |
|
|
|
3527 |
Если перед объявлением глобальной переменной, структуры или процедуры |
|
|
3528 |
указать слово static, то эти переменная, структура или процедура будут |
|
|
3529 |
доступны только в том файле, в котором они были объявлены. Т.е. если Вы |
|
|
3530 |
включите этот файл в другой директивой include, то переменные объявленные |
|
|
3531 |
во включаемом файле со словом static не будут доступны в основном файле, |
|
|
3532 |
и Вы можете в основном файле объявить другие переменные с такими же |
|
|
3533 |
именами. |
|
|
3534 |
|
|
|
3535 |
Если Вы примените слово static при объявлении локальной переменной в |
|
|
3536 |
процедуре, то память для этой переменной будет выделена не в стеке, а в |
|
|
3537 |
области данных процедуры. Но эта переменная будет доступна только внутри |
|
|
3538 |
процедуры, в которой она была объявлена. Применение static к локальным |
|
|
3539 |
переменным дает возможность сохранять значение переменной для следующего |
|
|
3540 |
входа в процедуру. |
|
|
3541 |
|
|
|
3542 |
Слово static можно применять к любому глобальному объекту |
|
|
3543 |
(переменной, структуре, процедуре). Для локального использования это слово |
|
|
3544 |
можно применять только к переменным. |
|
|
3545 |
|
|
|
3546 |
Если в Вашей программе есть глобальная и локальная переменная с |
|
|
3547 |
одинаковыми именами, то в процедуре, в которой объявлена эта локальная |
|
|
3548 |
переменная, Вы не имели доступа к одноименной глобальной переменной. |
|
|
3549 |
Применив перед именем переменной оператор ::, Вы получите доступ к |
|
|
3550 |
глобальной переменной. Пример: |
|
|
3551 |
|
|
|
3552 |
int var; //объявляем глобальную переменную |
|
|
3553 |
|
|
|
3554 |
void proc() |
|
|
3555 |
int var; //объявляем локальную переменную с именем уже существующей |
|
|
3556 |
//глобальной переменной |
|
|
3557 |
{ |
|
|
3558 |
(E)AX=var; //имеем доступ только к локальной переменной |
|
|
3559 |
(E)AX=::var; //а так можно получить доступ к глобальной переменной |
|
|
3560 |
} |
|
|
3561 |
Return to contents. |
|
|
3562 |
|
|
|
3563 |
|
|
|
3564 |
|
|
|
3565 |
9.21 Оператор sizeof. |
|
|
3566 |
|
|
|
3567 |
|
|
|
3568 |
Операция sizeof определяет размер памяти, который соответствует объекту |
|
|
3569 |
или типу. Операция sizeof имеет следующий вид: |
|
|
3570 |
|
|
|
3571 |
sizeof (<имя типа>) |
|
|
3572 |
|
|
|
3573 |
Результатом операции sizeof является размер памяти в байтах, |
|
|
3574 |
соответствующий заданному объекту или типу. |
|
|
3575 |
|
|
|
3576 |
В C-- оператор sizeof можно применять к переменным, регистрам, типам |
|
|
3577 |
переменных, структурам, процедурам, текстовым строкам и файлам. |
|
|
3578 |
|
|
|
3579 |
Если операция sizeof применяется к типу структуры, то результатом |
|
|
3580 |
является размер тега данной структуры. |
|
|
3581 |
|
|
|
3582 |
Если операция sizeof применяется к текстовой строке, то результатом |
|
|
3583 |
операции является размер строки плюс завершающий нуль. Например: |
|
|
3584 |
|
|
|
3585 |
sizeof ("Test") |
|
|
3586 |
|
|
|
3587 |
результатом этой операции будет число 5. Если Вы напишите такую |
|
|
3588 |
конструкцию: |
|
|
3589 |
|
|
|
3590 |
char a="Test"; |
|
|
3591 |
|
|
|
3592 |
sizeof(a) |
|
|
3593 |
|
|
|
3594 |
то результатом будет 5 - размер памяти, отведенный для переменной a. |
|
|
3595 |
|
|
|
3596 |
При использовании оператора sizeof с именем структуры вставляет |
|
|
3597 |
фактический размер памяти, занимаемый структурой. Это особенно важно, если |
|
|
3598 |
Вы объявили массив структур. |
|
|
3599 |
|
|
|
3600 |
Оператор sizeof можно применять и к имени определенной ранее |
|
|
3601 |
процедуры. Результатом будет размер этой процедуры. Но для динамических |
|
|
3602 |
процедур всегда будет ноль. |
|
|
3603 |
|
|
|
3604 |
Операцию sizeof можно применять и к файлам. Это бывает очень полезным |
|
|
3605 |
при использовании оператора FROM, но может применяться и в других случаях. |
|
|
3606 |
Пример применения оператора sizeof к файлам: |
|
|
3607 |
|
|
|
3608 |
sizeof ( file "filename.dat" ) |
|
|
3609 |
|
|
|
3610 |
Результатом этой операции будет размер файла "filename.dat". |
|
|
3611 |
Return to contents. |
|
|
3612 |
|
|
|
3613 |
|
|
|
3614 |
|
|
|
3615 |
9.22 Метки перехода. |
|
|
3616 |
|
|
|
3617 |
|
|
|
3618 |
Метки перехода применяются для указания начальных точек участков кода, |
|
|
3619 |
используемых командами перехода встроенного ассемблера и операторами |
|
|
3620 |
goto/GOTO. |
|
|
3621 |
|
|
|
3622 |
Имеются два типа меток перехода: глобальные и локальные. Глобальные |
|
|
3623 |
метки, как следует из названия, это метки, которые видимы из любого места в |
|
|
3624 |
программе. Локальные метки видны только в пределах своего процедурного |
|
|
3625 |
блока, и не определены за его пределами. |
|
|
3626 |
|
|
|
3627 |
Метки определяются идентификатором, оканчивающимися двоеточием. Если |
|
|
3628 |
идентификатор содержит хотя бы один символ строчных букв (букв нижнего |
|
|
3629 |
регистра, маленьких букв), это глобальная метка перехода, в противном |
|
|
3630 |
случае, это локальная метка перехода. |
|
|
3631 |
|
|
|
3632 |
Глобальные метки перехода не должны использоваться внутри динамических |
|
|
3633 |
процедур; там можно использовать только локальные метки. Это важно помнить, |
|
|
3634 |
поскольку, из-за применения такого средства как макрокоманды, динамическая |
|
|
3635 |
процедура может присутствовать в нескольких местах кода, что будет |
|
|
3636 |
означать, что метке соответствует больше чем один адрес. |
|
|
3637 |
|
|
|
3638 |
Метки вне процедур фактически располагаются в области данных программы. |
|
|
3639 |
Если данные и код находятся в одном сегменте (а именно так организованна |
|
|
3640 |
программа, написанная на C--), то метки вне процедур становятся простым и |
|
|
3641 |
эффективным методом для получения расстояний между частями программы. В |
|
|
3642 |
качестве имен для меток вне процедур могут быть использованы уникальные |
|
|
3643 |
идентификаторы, в которых можно использовать большие, маленькие и смесь |
|
|
3644 |
больших и маленьких букв. |
|
|
3645 |
Return to contents. |
|
|
3646 |
|
|
|
3647 |
|
|
|
3648 |
|
|
|
3649 |
10. Ассемблер. |
|
|
3650 |
|
|
|
3651 |
10.1 Поддержка команд ассемблера. |
|
|
3652 |
|
|
|
3653 |
|
|
|
3654 |
Встроенный в C-- ассемблер поддерживает все инструкции 8088/8086, |
|
|
3655 |
80286, 80386, 80486, Pentium, Pentium II и Pentium III процессоров. |
|
|
3656 |
|
|
|
3657 |
Все инструкции встроенного ассемблера должны начинаться с символа |
|
|
3658 |
доллара $. Поддерживается также ключевое слово asm, которое являясь |
|
|
3659 |
синонимом к символу доллара, еще и поддерживает объединение ассемблерных |
|
|
3660 |
инструкций в блоки. |
|
|
3661 |
Return to contents. |
|
|
3662 |
|
|
|
3663 |
|
|
|
3664 |
|
|
|
3665 |
10.2 Ключевое слово asm. |
|
|
3666 |
|
|
|
3667 |
|
|
|
3668 |
Ключевое слово asm является синонимом к $ - префикс ассемблерной |
|
|
3669 |
команды. После слова asm можно писать блок ассемблерных команд. Пример: |
|
|
3670 |
|
|
|
3671 |
asm { |
|
|
3672 |
. |
|
|
3673 |
. |
|
|
3674 |
push AX |
|
|
3675 |
labl: |
|
|
3676 |
push BX |
|
|
3677 |
mov AX,0x1234 |
|
|
3678 |
jmp short labl |
|
|
3679 |
. |
|
|
3680 |
. |
|
|
3681 |
. |
|
|
3682 |
} |
|
|
3683 |
|
|
|
3684 |
Метки внутри блока ассемблерных команд допустимы. |
|
|
3685 |
Return to contents. |
|
|
3686 |
|
|
|
3687 |
|
|
|
3688 |
|
|
|
3689 |
10.3 Префикс dup - повторение инструкций DB/DW/DD. |
|
|
3690 |
|
|
|
3691 |
|
|
|
3692 |
Для ассемблерных инструкции DB, DW, DD введена возможность использовать |
|
|
3693 |
префикс повторений dup. Применение этого префикса имеет следующий |
|
|
3694 |
синтаксис: |
|
|
3695 |
|
|
|
3696 |
$DW NUMREP dup VALTOREP |
|
|
3697 |
|
|
|
3698 |
NUMREP - число повторов инструкции DW. |
|
|
3699 |
VALTOREP - величина, которая будет повторена NUMREP раз. |
|
|
3700 |
|
|
|
3701 |
В отличие от аналога этого префикса из ассемблера повторяемую величину |
|
|
3702 |
заключать в скобки нельзя. |
|
|
3703 |
Return to contents. |
|
|
3704 |
|
|
|
3705 |
|
|
|
3706 |
|
|
|
3707 |
10.4 Инструкции процессора Pentium III. |
|
|
3708 |
|
|
|
3709 |
|
|
|
3710 |
В компилятор добавлена поддержка 19 новых инструкций MMX расширения |
|
|
3711 |
|
|
|
3712 |
MASKMOVQ mmx,mmx |
|
|
3713 |
MOVNTQ m64,mmx |
|
|
3714 |
PAVGB mmx,mmx/m64 |
|
|
3715 |
PAVGW mmx,mmx/m64 |
|
|
3716 |
PEXTRW r32,mmx,i8 |
|
|
3717 |
PINSRW mmx,r32/m16,i8 |
|
|
3718 |
PMAXUB mmx,mmx/m64 |
|
|
3719 |
PMAXSW mmx,mmx/m64 |
|
|
3720 |
PMINUB mmx,mmx/m64 |
|
|
3721 |
PMINSW mmx,mmx/m64 |
|
|
3722 |
PMOVMSKB r32,mmx |
|
|
3723 |
PMULHUW mmx,mmx/m64 |
|
|
3724 |
PREFETCHT0 mem |
|
|
3725 |
PREFETCHT1 mem |
|
|
3726 |
PREFETCHT2 mem |
|
|
3727 |
PREFETCHNTA mem |
|
|
3728 |
SFENCE |
|
|
3729 |
PSADBW mmx,mmx/m64 |
|
|
3730 |
PSHUFW mmx,mmx/m64,i8 |
|
|
3731 |
|
|
|
3732 |
и 46 инструкций SSE расширения. |
|
|
3733 |
|
|
|
3734 |
ADDPS xmm,m128/xmm |
|
|
3735 |
ADDSS xmm,xmm/m32 |
|
|
3736 |
ANDNPS xmm,xmm/m128 |
|
|
3737 |
ANDPS xmm,xmm/m128 |
|
|
3738 |
COMISS xmm,xmm/m32 |
|
|
3739 |
DIVPS xmm,m128/xmm |
|
|
3740 |
DIVSS xmm,xmm/m32 |
|
|
3741 |
MAXPS xmm,m128/xmm |
|
|
3742 |
MAXSS xmm,xmm/m32 |
|
|
3743 |
MINPS xmm,m128/xmm |
|
|
3744 |
MINSS xmm,xmm/m32 |
|
|
3745 |
MULPS xmm,m128/xmm |
|
|
3746 |
MULSS xmm,xmm/m32 |
|
|
3747 |
ORPS xmm,xmm/m128 |
|
|
3748 |
RCPPS xmm,xmm/m128 |
|
|
3749 |
RCPSS xmm,xmm/m32 |
|
|
3750 |
RSQRTPS xmm,xmm/m128 |
|
|
3751 |
RSQRTSS xmm,xmm/m32 |
|
|
3752 |
SQRTPS xmm,m128/xmm |
|
|
3753 |
SQRTSS xmm,xmm/m32 |
|
|
3754 |
SUBPS xmm,m128/xmm |
|
|
3755 |
SUBSS xmm,xmm/m32 |
|
|
3756 |
UCOMISS xmm,xmm/m32 |
|
|
3757 |
UNPCKHPS xmm,xmm/m128 |
|
|
3758 |
UNPCKLPS xmm,xmm/m128 |
|
|
3759 |
XORPS xmm,xmm/m128 |
|
|
3760 |
CMPPS xmm,xmm/m128,i8 |
|
|
3761 |
CMPSS xmm,xmm/m32,i8 |
|
|
3762 |
SHUFPS xmm,xmm/m128,i8 |
|
|
3763 |
CVTPI2PS xmm,m64/mmx |
|
|
3764 |
CVTSI2SS xmm,m32/r32 |
|
|
3765 |
CVTPS2PI mmx,m128/xmm |
|
|
3766 |
CVTTPS2PI mmx,xmm/m128 |
|
|
3767 |
CVTSS2SI r32,xmm/m128 |
|
|
3768 |
CVTTSS2SI r32,xmm/m128 |
|
|
3769 |
LDMXCSR m32 |
|
|
3770 |
STMXCSR m32 |
|
|
3771 |
MOVHLPS xmm,xmm |
|
|
3772 |
MOVLHPS xmm,xmm |
|
|
3773 |
MOVMSKPS r32,xmm |
|
|
3774 |
MOVNTPS m128,xmm |
|
|
3775 |
MOVAPS m128/xmm,xmm/m128 |
|
|
3776 |
MOVSS xmm/m32,xmm/m32 |
|
|
3777 |
MOVUPS xmm/m128,m128/xmm |
|
|
3778 |
MOVHPS xmm/m64,m64/xmm |
|
|
3779 |
MOVLPS xmm/m64,m64/xmm |
|
|
3780 |
|
|
|
3781 |
Многие из этих инструкций могут использовать в качестве операнда |
|
|
3782 |
64-битные и 128-битные ячейки памяти. Компилятор C-- сейчас может работать |
|
|
3783 |
только с 32-битными переменными. Поэтому для инструкций использующих в |
|
|
3784 |
качестве операнда ячейки памяти размером больше 32-бит можно использовать |
|
|
3785 |
переменные любых типов. Компилятор не будет выдавать на это сообщений об |
|
|
3786 |
ошибке, будет использован адрес этой переменной, а сама инструкция будет |
|
|
3787 |
использовать нужное ей число битов памяти, начиная с адреса указанной |
|
|
3788 |
переменной. Например: |
|
|
3789 |
|
|
|
3790 |
Для инструкции movaps один из операндов может быть 128-битной |
|
|
3791 |
ячейкой памяти. Для этой инструкции допустимы следующий синтаксис: |
|
|
3792 |
|
|
|
3793 |
byte var8_128[16]; |
|
|
3794 |
word var16_128[8]; |
|
|
3795 |
dword var32_128[4]; |
|
|
3796 |
|
|
|
3797 |
void proc() |
|
|
3798 |
{ |
|
|
3799 |
asm{ |
|
|
3800 |
movaps var8_128,xmm0 //в массив из 16 байт будет записано содержимое XMM0 |
|
|
3801 |
movaps xmm1,var16_128 //в XMM1 будет записано содержимое 8 слов |
|
|
3802 |
movaps var32_128,xmm1 //в массив из 4 двойных слов будет записано XMM1 |
|
|
3803 |
} |
|
|
3804 |
} |
|
|
3805 |
Return to contents. |
|
|
3806 |
|
|
|
3807 |
|
|
|
3808 |
|
|
|
3809 |
11. Процедуры. |
|
|
3810 |
|
|
|
3811 |
11.1 Типы процедур, функций и макрокоманд. |
|
|
3812 |
|
|
|
3813 |
|
|
|
3814 |
Сейчас C-- поддерживает 4 типа вызова процедур: cdecl, pascal, stdcall |
|
|
3815 |
и fastcall. Вот краткие характеристики этих типов вызовов процедур: |
|
|
3816 |
|
|
|
3817 |
cdecl Этот тип вызова процедур является по умолчанию для языка С. Он |
|
|
3818 |
характеризуется тем, что параметры процедуры передаются в порядке обратном |
|
|
3819 |
их записи. Очистка стека от параметров производится после завершения работы |
|
|
3820 |
процедуры. Этот способ вызова процедур очень удобен для процедур с |
|
|
3821 |
переменным числом параметров. |
|
|
3822 |
|
|
|
3823 |
pascal Этот тип вызова предполагает, что параметры передаются в том |
|
|
3824 |
порядке, в котором они записаны в программе. Освобождение стека от |
|
|
3825 |
параметров производит сама вызываемая процедура. Этот тип вызова является |
|
|
3826 |
более компактным, чем cdecl. |
|
|
3827 |
|
|
|
3828 |
stdcall Этот тип вызова является гибридом первых двух. Параметры |
|
|
3829 |
передаются процедуре в порядке обратном, тому в котором они записаны в |
|
|
3830 |
программе. Освобождение стека от параметров производится в самой вызываемой |
|
|
3831 |
процедуре. |
|
|
3832 |
|
|
|
3833 |
fastcall Этот тип вызова процедур предполагает что передача параметров |
|
|
3834 |
процедуре производится через регистры, тем самым отпадает необходимость |
|
|
3835 |
освобождения стека от параметров. Для этого типа вызова процедуры |
|
|
3836 |
существуют ограничения по числу передаваемых параметров. Для C это три |
|
|
3837 |
параметра, а для C-- шесть. В C-- параметры передаются по умолчанию в |
|
|
3838 |
следующем порядке: 1-й - AX/EAX, 2-й - BX/EBX, 3 - CX/ECX, 4 - DX/EDX, 5 - |
|
|
3839 |
DI/EDI, 6 - SI/ESI. Параметры типов char или byte могут передаваться в |
|
|
3840 |
количестве не более 4 или только в первых 4 регистрах: 1 - AL, 2 - BL, 3 - |
|
|
3841 |
CL, 4 - DL. Этот порядок регистров может быть изменен, если явно указать |
|
|
3842 |
его либо при объявлении процедуры, либо при ее определении. Процедуры типа |
|
|
3843 |
fastcall иногда еще называют регистровыми. |
|
|
3844 |
|
|
|
3845 |
В C-- по умолчанию, если имя процедуры написано большими буквами, то |
|
|
3846 |
считается, что эта процедура имеет тип вызова fastcall. Если же в имени |
|
|
3847 |
процедуры есть хотя бы одна маленькая буква, то по умолчанию считается, что |
|
|
3848 |
эта процедура имеет тип вызова pascal, за исключением программ |
|
|
3849 |
компилируемых с ключом /w32 /w32c или /DLL. В них по умолчанию применяется |
|
|
3850 |
тип вызова процедур stdcall. Если же Вы хотите изменить тип вызова процедур |
|
|
3851 |
из по умолчанию на любой другой, то эту процедуру надо обязательно объявить |
|
|
3852 |
с указанием типа желаемого вызова. |
|
|
3853 |
|
|
|
3854 |
Объявление процедур введено для того, чтобы сообщать компилятору о |
|
|
3855 |
типе возврата из процедур, способе передачи параметров процедуре и их числе. |
|
|
3856 |
Return to contents. |
|
|
3857 |
|
|
|
3858 |
|
|
|
3859 |
|
|
|
3860 |
11.2 Стековые процедуры. |
|
|
3861 |
|
|
|
3862 |
|
|
|
3863 |
Стековые процедуры по умолчанию объявляются при помощи идентификатора, |
|
|
3864 |
который содержит, по крайней мере, один символ строчных букв (букв нижнего |
|
|
3865 |
регистра, маленьких букв). Таким образом, стековые процедуры легко отличимы |
|
|
3866 |
от регистровых процедур, поскольку для имен регистровых процедур символы |
|
|
3867 |
строчных букв запрещены. |
|
|
3868 |
|
|
|
3869 |
Параметры для стековых процедур, если они есть, могут иметь любой тип |
|
|
3870 |
byte, char, word, int, dword, long или float. |
|
|
3871 |
|
|
|
3872 |
Параметры передаются в соответствии с правилами, принятыми для данного |
|
|
3873 |
типа процедур. Если процедура не имеет объявления, то компилятор не следит |
|
|
3874 |
за числом и типом передаваемых параметров. В этом случае у Вас появляется |
|
|
3875 |
свобода в их использовании, но Вы должны осознавать и последстви |
|
|
3876 |
неправильного их использования. |
|
|
3877 |
|
|
|
3878 |
В списке параметров для каждого параметра указывается его тип. |
|
|
3879 |
Параметры одного типа, идущие подряд, разделяются запятыми. Формальные |
|
|
3880 |
параметры разного типа в объявлении функции разделяются символом ;. |
|
|
3881 |
|
|
|
3882 |
В следующем примере стековая процедура возвращает сумму всех своих |
|
|
3883 |
параметров (имеющих различные типы) как величину типа word: |
|
|
3884 |
|
|
|
3885 |
word add_them_all (int a,b,c; byte d,e; word x,y) |
|
|
3886 |
{ |
|
|
3887 |
return( a+b+c+d+e+x+y ); |
|
|
3888 |
} |
|
|
3889 |
|
|
|
3890 |
Ранее C-- делал вызовы стековых процедур лишь в стиле pascal. |
|
|
3891 |
Преимуществом этого способа вызова процедур является компактность и более |
|
|
3892 |
простой механизм генерации кода. К недостаткам, а соответственно и |
|
|
3893 |
преимуществам С-стиля, можно отнести жесткую привязанность паскалевских |
|
|
3894 |
процедур к числу и типу передаваемых параметров (попробуйте при вызове |
|
|
3895 |
процедуры в стиле pascal опустить один параметр и получите 100% зависание). |
|
|
3896 |
Напомню некоторые технические детали обоих типов вызовов процедур. |
|
|
3897 |
|
|
|
3898 |
Кадр стека C-- для близких процедур стека в стиле pascal: |
|
|
3899 |
АДРЕС |
|
|
3900 |
... |
|
|
3901 |
BP + FFFE предпоследний байта локальных переменных |
|
|
3902 |
BP + FFFF последний байт локальных переменных |
|
|
3903 |
BP + 0000 Сохраненный BP |
|
|
3904 |
BP + 0002 RET адрес |
|
|
3905 |
BP + 0004 последнее слово передаваемых процедуре параметров (если они |
|
|
3906 |
есть) |
|
|
3907 |
BP + 0006 предпоследнее слово передаваемых процедуре параметров |
|
|
3908 |
... |
|
|
3909 |
BP + nnnn первое слово передаваемых процедуре параметров |
|
|
3910 |
|
|
|
3911 |
Освобождение стека от переданных процедуре параметров происходит прямо |
|
|
3912 |
в самой процедуре командой RET nnnn - где nnnn является размером переданных |
|
|
3913 |
в стек параметров. |
|
|
3914 |
|
|
|
3915 |
Кадр стека C-- для близких процедур стека в стиле си: |
|
|
3916 |
АДРЕС |
|
|
3917 |
... |
|
|
3918 |
BP + FFFE предпоследний байта локальных переменных |
|
|
3919 |
BP + FFFF последний байт локальных переменных |
|
|
3920 |
BP + 0000 Сохраненный BP |
|
|
3921 |
BP + 0002 RET адрес |
|
|
3922 |
BP + 0004 первое слово передаваемых процедуре параметров (если они |
|
|
3923 |
есть) |
|
|
3924 |
BP + 0006 второе слово передаваемых процедуре параметров |
|
|
3925 |
... |
|
|
3926 |
BP + nnnn последнее слово передаваемых процедуре параметров |
|
|
3927 |
|
|
|
3928 |
Процедуры в стиле С заканчиваются командой RET. Освобождение стека от |
|
|
3929 |
параметров происходит в том месте откуда была вызвана процедура. Обычно это |
|
|
3930 |
делается командой ADD SP,nnnn. Т.е. компилятор может точно знать сколько и |
|
|
3931 |
каких параметров Вы передаете в данном случае процедуре и соответственно |
|
|
3932 |
освобождает стек после завершения процедуры. Это очень удобно для процедур, |
|
|
3933 |
которые могут обрабатывать переменное число параметров (например, процедуры |
|
|
3934 |
типа printf). |
|
|
3935 |
|
|
|
3936 |
Объявление процедуры имеет следующий вид: |
|
|
3937 |
|
|
|
3938 |
rettype modif procname(); |
|
|
3939 |
|
|
|
3940 |
Первым идет необязательный тип возврата из процедур. По умолчанию он |
|
|
3941 |
для 16-битных программ равен word, а для 32-битных dword. Затем должен идти |
|
|
3942 |
также необязательный модификатор. По умолчанию все стековые процедуры в C-- |
|
|
3943 |
(за исключением режима компиляции программ под Windows, где по умолчанию |
|
|
3944 |
действует стиль вызова процедур stdcall) имеют стиль pascal. Далее идет им |
|
|
3945 |
процедуры со скобками, которые являются признаком того что Вы объявляете |
|
|
3946 |
процедуру, а не переменную. Завершает объявление символ точка с запятой. |
|
|
3947 |
|
|
|
3948 |
При объявлении процедур в C-- прописывать параметры процедуры |
|
|
3949 |
необязательно (тогда компилятор не будет контролировать число и тип |
|
|
3950 |
передаваемых параметров), но если Вы их вставите, то включится механизм |
|
|
3951 |
контроля за числом и типом параметров. |
|
|
3952 |
Return to contents. |
|
|
3953 |
|
|
|
3954 |
|
|
|
3955 |
|
|
|
3956 |
11.3 Регистровые процедуры. |
|
|
3957 |
|
|
|
3958 |
|
|
|
3959 |
Регистровые процедуры определяются, по умолчанию, при помощи |
|
|
3960 |
идентификатора, который не содержит символов строчных букв. Или же явным |
|
|
3961 |
указанием что это регистровая процедура с помощью ключевого слова fastcall. |
|
|
3962 |
|
|
|
3963 |
Как уже было сказано, параметры (если они есть) для регистровой |
|
|
3964 |
процедуры передаются через регистры. Регистровые процедуры могут иметь не |
|
|
3965 |
более 6 параметров. Если параметры имеют тип int или word, регистры по |
|
|
3966 |
умолчанию используются в следующем порядке: AX, BX, CX, DX, DI, и SI. |
|
|
3967 |
Первые четыре параметра могут также иметь тип char или byte, в этом случае |
|
|
3968 |
задействуются регистры AL, BL, CL и DL соответственно. Любой из шести |
|
|
3969 |
параметров может иметь тип long, dword или float, тогда для него |
|
|
3970 |
используется регистр EAX, EBX, ECX, EDX, EDI, или ESI. |
|
|
3971 |
|
|
|
3972 |
В следующем примере регистровая процедура с именем TOGETHER возвращает |
|
|
3973 |
значение типа word как результат умножения первого параметра, имеющего тип |
|
|
3974 |
word, на второй параметр того же типа: |
|
|
3975 |
|
|
|
3976 |
word TOGETHER() /* AX = первый параметр, BX = второй параметр */ |
|
|
3977 |
{ |
|
|
3978 |
return (AX * BX); |
|
|
3979 |
} |
|
|
3980 |
|
|
|
3981 |
В следующем примере регистровая процедура с именем SHOW_NUM, которая не |
|
|
3982 |
возвращает никакого значения, зато выводит на экран первый параметр |
|
|
3983 |
(имеющий тип int), затем разделительный знак в виде двоеточия :, а затем |
|
|
3984 |
второй параметр (имеющий тип byte) : |
|
|
3985 |
|
|
|
3986 |
void SHOW_NUM () /* AX = первое число, BL = второе число */ |
|
|
3987 |
{ |
|
|
3988 |
$ PUSH BX |
|
|
3989 |
WRITEINT (int AX); |
|
|
3990 |
WRITE (':'); |
|
|
3991 |
$ POP BX |
|
|
3992 |
WRITEWORD (BL); |
|
|
3993 |
} |
|
|
3994 |
|
|
|
3995 |
Но если в процедуре сделать объявление порядка и типов используемых |
|
|
3996 |
регистров, то возможно произвольное использование регистров. Более подробно |
|
|
3997 |
об этом можно почитать в разделе об объявлениях параметров в регистровых |
|
|
3998 |
процедурах. |
|
|
3999 |
|
|
|
4000 |
Для того, чтобы использовать регистровую процедуру как макрокоманду, |
|
|
4001 |
она должна быть объявлена как динамическая процедура. Динамические |
|
|
4002 |
процедуры описаны в следующем подразделе. |
|
|
4003 |
Return to contents. |
|
|
4004 |
|
|
|
4005 |
|
|
|
4006 |
|
|
|
4007 |
11.4 Динамические процедуры. |
|
|
4008 |
|
|
|
4009 |
|
|
|
4010 |
Динамические процедуры - процедуры, которые определены, но вставляются |
|
|
4011 |
в код программы, только если есть вызов. Динамические процедуры могут |
|
|
4012 |
использоваться как макрокоманды. |
|
|
4013 |
|
|
|
4014 |
Определение динамической процедуры начинается с символа двоеточия ':'. |
|
|
4015 |
|
|
|
4016 |
Пример динамической процедуры стека: |
|
|
4017 |
|
|
|
4018 |
: void setvideomode (byte mode) |
|
|
4019 |
{ |
|
|
4020 |
AL = mode; |
|
|
4021 |
AH = 0; |
|
|
4022 |
$ INT 0x10 |
|
|
4023 |
} |
|
|
4024 |
|
|
|
4025 |
Пример динамической регистровой процедуры: |
|
|
4026 |
|
|
|
4027 |
: int ABS () /* AX = число, абсолютное значение которого ищется*/ |
|
|
4028 |
{ |
|
|
4029 |
IF (int AX < 0) |
|
|
4030 |
-AX; |
|
|
4031 |
} |
|
|
4032 |
Return to contents. |
|
|
4033 |
|
|
|
4034 |
|
|
|
4035 |
|
|
|
4036 |
11.4.1 Установка динамической процедуры в определенное место программы. |
|
|
4037 |
|
|
|
4038 |
|
|
|
4039 |
Динамические процедуры, если они не используются как макросы и если |
|
|
4040 |
они были востребованы в программе, вставляются в код программы в самом |
|
|
4041 |
конце компиляции. В каком точно месте Вашей программы они окажутся узнать |
|
|
4042 |
невозможно. Если же Вам необходимо, чтобы какая-то динамическая процедура |
|
|
4043 |
находилась в конкретном месте программы, то это можно сделать таким |
|
|
4044 |
образом: |
|
|
4045 |
|
|
|
4046 |
:void proc ( int par1, par2) |
|
|
4047 |
{ |
|
|
4048 |
... |
|
|
4049 |
} |
|
|
4050 |
|
|
|
4051 |
Мы имеем динамическую процедуру, код которой был бы расположен ранее |
|
|
4052 |
кода обычной процедуры нашей программы. Для этого перед определением этой |
|
|
4053 |
процедуры надо написать такую строку: |
|
|
4054 |
|
|
|
4055 |
@ void proc (); |
|
|
4056 |
|
|
|
4057 |
В итоге динамическая процедура будет вставлена в код программы не в |
|
|
4058 |
конце ее, как обычно, а в месте, где будет расположена эта строка. Если |
|
|
4059 |
динамическая процедура имеет параметры, то прописывать эти параметры |
|
|
4060 |
необязательно. |
|
|
4061 |
|
|
|
4062 |
В компиляторе есть еще более мощное средство, позволяющее все |
|
|
4063 |
динамические объекты ( процедуры, переменные, структуры ) расположить в |
|
|
4064 |
указанном месте, а не в конце программы, как обычно. Это директива |
|
|
4065 |
#setdinproc. Встретив эту директиву, компилятор немедленно расположит все |
|
|
4066 |
известные ему на этот момент динамические объекты в месте объявления этой |
|
|
4067 |
директивы. Последующие динамические объекты будут располагаться как |
|
|
4068 |
обычно, в конце программы, если конечно, не будет повторно применена |
|
|
4069 |
директива #setdinproc. |
|
|
4070 |
|
|
|
4071 |
Это может быть применено и быть полезным при создании резидентных |
|
|
4072 |
программ (TSR) и драйверов устройств. |
|
|
4073 |
Return to contents. |
|
|
4074 |
|
|
|
4075 |
|
|
|
4076 |
|
|
|
4077 |
11.5 inline-процедуры. |
|
|
4078 |
|
|
|
4079 |
|
|
|
4080 |
inline-процедурами могут быть динамические процедуры, которые можно |
|
|
4081 |
использовать как макросы. Но в отличие от макросов, inline-процедуры, при |
|
|
4082 |
включенной оптимизации на скорость, автоматически вставляются в код, а при |
|
|
4083 |
оптимизации кода на размер, делается вызов их, как динамических процедур. |
|
|
4084 |
|
|
|
4085 |
Но иногда бывает нужно при включенной оптимизации на размер кода, чтобы |
|
|
4086 |
процедуры вставлялись в код, а не делался их вызов. Для этих целей введена |
|
|
4087 |
директива #inline TRUE. Этой же директивой ( #inline FALSE ), можно при |
|
|
4088 |
оптимизации на скорость делать вызовы процедур, вместо их вставки. |
|
|
4089 |
|
|
|
4090 |
Важно помнить, что статус директивы #inline автоматически меняется при |
|
|
4091 |
смене режима оптимизации. При установке оптимизации на скорость статус |
|
|
4092 |
директивы #inline устанавливается в TRUE, а при смене режима оптимизации по |
|
|
4093 |
размеру кода, устанавливается в FALSE. Поэтому применяйте директиву #inline |
|
|
4094 |
лишь после смены режима оптимизации. |
|
|
4095 |
|
|
|
4096 |
Директивы меняющие режим оптимизации #codesize, #speed и директива |
|
|
4097 |
#inline, объявленные внутри процедуры распространяются только на оставшуюся |
|
|
4098 |
часть процедуры, т.е. они становятся локальными. Для того чтобы изменения |
|
|
4099 |
были глобальными эти директивы надо объявлять вне тела процедуры. |
|
|
4100 |
|
|
|
4101 |
Для того чтобы определить inline-процедуру, надо в первой строке с |
|
|
4102 |
именем процедуры вместо символа динамической процедуры (:) написать |
|
|
4103 |
ключевое слово inline. Пример определения inline-процедуры: |
|
|
4104 |
|
|
|
4105 |
inline int fastcall abs(AX) |
|
|
4106 |
{ |
|
|
4107 |
IF ( int AX < 0 ) -AX ; |
|
|
4108 |
} |
|
|
4109 |
Return to contents. |
|
|
4110 |
|
|
|
4111 |
|
|
|
4112 |
|
|
|
4113 |
11.5.1 Другое применение inline. |
|
|
4114 |
|
|
|
4115 |
|
|
|
4116 |
Ключевое слово inline имеет в процедурах и другое применение. Если |
|
|
4117 |
это слово расположено перед началом блока процедуры, то для такой |
|
|
4118 |
процедуры не создается кадр стека и не генерируется завершающий процедуру |
|
|
4119 |
ret. Пример: |
|
|
4120 |
|
|
|
4121 |
void PROC () |
|
|
4122 |
inline |
|
|
4123 |
{ |
|
|
4124 |
... |
|
|
4125 |
} |
|
|
4126 |
|
|
|
4127 |
Такие процедуры не должны содержать локальных переменных. Если |
|
|
4128 |
процедура является регистровой (тип fastcall), то с передачей ей |
|
|
4129 |
параметров нет проблем. Если же процедура является стековой, то передать |
|
|
4130 |
в такую процедуру параметры Вы можете, но воспользоваться этими |
|
|
4131 |
параметрами используя их имена, Вы уже не сможете. Это происходит потому, |
|
|
4132 |
что в этих процедурах кадр стека не формируется. Пример: |
|
|
4133 |
|
|
|
4134 |
void proc (int par1, par2) |
|
|
4135 |
inline |
|
|
4136 |
{ |
|
|
4137 |
AX=par1; /* компилятор обратится с параметру 'par1' через регистр BP. |
|
|
4138 |
Но так как кадр стека не был создан, при выполнении этого |
|
|
4139 |
кода программа будет работать не правильно. */ |
|
|
4140 |
... |
|
|
4141 |
} |
|
|
4142 |
|
|
|
4143 |
Встретив такое определение процедуры, компилятор выдаст предупреждение |
|
|
4144 |
о том, что в таких процедурах использовать локальные и параметрические |
|
|
4145 |
переменные нельзя. |
|
|
4146 |
Return to contents. |
|
|
4147 |
|
|
|
4148 |
|
|
|
4149 |
|
|
|
4150 |
11.6 Процедуры обработки прерываний. |
|
|
4151 |
|
|
|
4152 |
|
|
|
4153 |
Процедуры обработки прерываний определяются следующим способом: |
|
|
4154 |
|
|
|
4155 |
interrupt procedure_name () |
|
|
4156 |
{ |
|
|
4157 |
// put code here (здесь должен быть код обработки) |
|
|
4158 |
} |
|
|
4159 |
|
|
|
4160 |
Процедуры обработки прерываний не сохраняют никаких регистров |
|
|
4161 |
автоматически, и никакие регистры сами по себе не загружаются перед |
|
|
4162 |
передачей управления обработчику прерывания, следовательно, на Вашей |
|
|
4163 |
совести сохранение значений регистров в стеке и последующий их возврат, а |
|
|
4164 |
также загрузка регистра DS нужным значением. |
|
|
4165 |
|
|
|
4166 |
Вот пример обработчика прерывания, который сохраняет значения всех |
|
|
4167 |
регистров и загружает регистр DS: |
|
|
4168 |
|
|
|
4169 |
interrupt safe_handle () |
|
|
4170 |
{ |
|
|
4171 |
$ PUSH DS |
|
|
4172 |
$ PUSH ES |
|
|
4173 |
$ PUSHA // для выполнения этой команды нужен процессор не хуже 80286 |
|
|
4174 |
DS = CS; // здесь DS загружается для работы с моделью памяти типа tiny |
|
|
4175 |
|
|
|
4176 |
|
|
|
4177 |
/* do your thing here (здесь вы делаете свою обработку)*/ |
|
|
4178 |
|
|
|
4179 |
$ POPA // для выполнения этой команды нужен процессор не хуже 80286 |
|
|
4180 |
$ POP ES |
|
|
4181 |
$ POP DS |
|
|
4182 |
} |
|
|
4183 |
|
|
|
4184 |
При завершении процедуры прерывания будет автоматически сгенерирована |
|
|
4185 |
инструкция выхода из обработчика прерывания - IRET. |
|
|
4186 |
Return to contents. |
|
|
4187 |
|
|
|
4188 |
|
|
|
4189 |
|
|
|
4190 |
11.7 Замена return на goto. |
|
|
4191 |
|
|
|
4192 |
|
|
|
4193 |
В некоторых ситуациях, при компиляции программы, оператор return |
|
|
4194 |
будет заменяться на goto. Это происходит при разрешенной оптимизации по |
|
|
4195 |
размеру кода для операторов return, которые расположены внутри процедуры |
|
|
4196 |
и, естественно, если размер кода для выполнения return больше, чем размер |
|
|
4197 |
кода для реализации goto. Для динамических процедур, которые используются |
|
|
4198 |
как макросы, такая замена будет производится всегда. Оператор goto будет |
|
|
4199 |
выполнен на конец процедуры, там, где будет располагаться единственный |
|
|
4200 |
выход из процедуры. В динамических процедурах, используемых в качестве |
|
|
4201 |
макросов, return в конце процедуры будет пропущен компилятором. |
|
|
4202 |
|
|
|
4203 |
Таким образом, снято последнее ограничение на использование |
|
|
4204 |
динамических процедур в качестве макросов. Любая динамическая процедура |
|
|
4205 |
может быть использована как макрос. |
|
|
4206 |
|
|
|
4207 |
Для оператора goto существует его более короткий аналог - GOTO. |
|
|
4208 |
Для получения более компактного кода для оператора return введен также |
|
|
4209 |
более короткий оператор RETURN. Его можно использовать, если от места |
|
|
4210 |
его применения до конца процедуры находится не более 128 байт. Если Вы |
|
|
4211 |
будете использовать RETURN на большем расстоянии до конца процедуры, то |
|
|
4212 |
компилятор выдаст сообщение об ошибке. При использовании return на |
|
|
4213 |
расстоянии меньше 128 байт до конца кода, компилятор выдаст вам |
|
|
4214 |
предупреждение о возможном использовании RETURN. |
|
|
4215 |
Return to contents. |
|
|
4216 |
|
|
|
4217 |
|
|
|
4218 |
|
|
|
4219 |
11.8 Возвращаемые значения. |
|
|
4220 |
|
|
|
4221 |
|
|
|
4222 |
Возвращаемые из функций значения располагаются в регистрах. В таблице |
|
|
4223 |
показано, какой регистр используется для каждого из возвращаемых типов: |
|
|
4224 |
|
|
|
4225 |
-------------------------------------------- |
|
|
4226 |
| возвращаемый тип | используемый регистр | |
|
|
4227 |
-------------------------------------------- |
|
|
4228 |
| byte | AL | |
|
|
4229 |
| word | AX | |
|
|
4230 |
| dword | EAX | |
|
|
4231 |
| char | AL | |
|
|
4232 |
| int | AX | |
|
|
4233 |
| long | EAX | |
|
|
4234 |
| float | EAX | |
|
|
4235 |
-------------------------------------------- |
|
|
4236 |
|
|
|
4237 |
Самый простой способ вернуть значение из функции состоит в том, чтобы |
|
|
4238 |
использовать команду return(), но вместо этого можно напрямую загрузить |
|
|
4239 |
возвращаемое значение в соответствующий регистр. Например, следующие две |
|
|
4240 |
функции возвращают одно и то же значение: |
|
|
4241 |
|
|
|
4242 |
byte proc_one () |
|
|
4243 |
{ |
|
|
4244 |
return (42); |
|
|
4245 |
} |
|
|
4246 |
|
|
|
4247 |
byte proc_two () |
|
|
4248 |
{ |
|
|
4249 |
AL = 42; |
|
|
4250 |
} |
|
|
4251 |
|
|
|
4252 |
Многие DOS функции 0x21 прерывания в качестве индикатора успешного |
|
|
4253 |
выполнения используют установку или сброс carry флага. Использовать флаги |
|
|
4254 |
процессора при возврате из процедур можно и в других случаях, когда надо |
|
|
4255 |
иметь статус успешного или не успешного выполнения процедуры. Это позволит |
|
|
4256 |
более полно использовать возможности процессора и соответственно уменьшит |
|
|
4257 |
размер кода и повысит быстродействие программы. |
|
|
4258 |
|
|
|
4259 |
Наряду с флагами, при возврате из процедур, по прежнему остается |
|
|
4260 |
возврат различных типов и через регистр AL/AX/EAX. Если для процедуры |
|
|
4261 |
объявлено, что она имеет тип возврата int и CARRYFLAG, то при использовании |
|
|
4262 |
такой процедуры в операциях сравнения IF, WHILE... будет делаться проверка |
|
|
4263 |
carry флага, а не сравнение регистра AX. Пример использования возврата |
|
|
4264 |
флагов из процедур: |
|
|
4265 |
|
|
|
4266 |
int CARRYFLAG FOPEN(); // объявление процедуры |
|
|
4267 |
|
|
|
4268 |
void proc() |
|
|
4269 |
{ |
|
|
4270 |
IF ( FOPEN(name,0) ) Error ( "Not open file" ); |
|
|
4271 |
} |
|
|
4272 |
|
|
|
4273 |
Варианты допустимого синтаксиса для использования возврата флага: |
|
|
4274 |
|
|
|
4275 |
IF ( ! FOPEN() )... |
|
|
4276 |
IF ( @ FOPEN() )... |
|
|
4277 |
IF ( ! @ FOPEN() )... |
|
|
4278 |
IF ( handl = FOPEN() )... |
|
|
4279 |
IF ( handl = @ FOPEN() )... |
|
|
4280 |
IF ( ! handl = FOPEN() )... |
|
|
4281 |
IF ( ! handl = @ FOPEN() )... |
|
|
4282 |
|
|
|
4283 |
А вот варианты, в которых, несмотря на то, что для процедуры объявлен |
|
|
4284 |
возврат флага, будет производиться сравнение регистра AX: |
|
|
4285 |
|
|
|
4286 |
IF ( FOPEN() == 5 )... // производится сравнение |
|
|
4287 |
IF ( FOPEN() + 2 )... // результат процедуры подвергается дальнейшему |
|
|
4288 |
// вычислению, в результате которого флаги будут |
|
|
4289 |
// изменены. |
|
|
4290 |
Return to contents. |
|
|
4291 |
|
|
|
4292 |
|
|
|
4293 |
|
|
|
4294 |
11.9 Объявление параметров в регистровых процедурах. |
|
|
4295 |
|
|
|
4296 |
|
|
|
4297 |
Ранее каждому параметру регистровой процедуры соответствовал строго |
|
|
4298 |
определенный регистр. Например, для переменных типа int или word первый |
|
|
4299 |
параметр передавался через регистр AX, 2-й - BX, 3-й - CX, 4-й - DX, 5-й - |
|
|
4300 |
DI, 6-й - SI. Поэтому, если Вам было необходимо передать только один |
|
|
4301 |
параметр через регистр SI, то приходилось перед ним писать пять запятых. |
|
|
4302 |
Вот как, например, выглядит вызов процедуры STRCPY: |
|
|
4303 |
|
|
|
4304 |
void main () |
|
|
4305 |
{ |
|
|
4306 |
STRCPY ( , , , , #dest, #sourc ) ; |
|
|
4307 |
} |
|
|
4308 |
|
|
|
4309 |
Теперь регистры могут располагаться при передаче параметров |
|
|
4310 |
произвольным образом. Надо только объявить компилятору о том, какой регистр |
|
|
4311 |
закреплен за каким параметром данной процедуры. После такого объявления |
|
|
4312 |
компилятор будет сам следить за тем, через какой регистр передавать |
|
|
4313 |
параметр процедуре, его размерностью и числом передаваемых параметров. Вот |
|
|
4314 |
как будет выглядеть объявление и использование процедуры STRCPY: |
|
|
4315 |
|
|
|
4316 |
void STRCPY ( DI, SI ) ; //это объявление процедуры |
|
|
4317 |
|
|
|
4318 |
void main () |
|
|
4319 |
{ |
|
|
4320 |
STRCPY ( #dest, #sourc ) ; //а это вызов процедуры |
|
|
4321 |
} |
|
|
4322 |
|
|
|
4323 |
Можно не делать объявления процедуры, а указать расположение регистров |
|
|
4324 |
в заголовке процедуры. Но тогда такая процедура должна вызываться только |
|
|
4325 |
после ее определения. Вот пример процедуры выводящей на экран несколько |
|
|
4326 |
одинаковых символов: |
|
|
4327 |
|
|
|
4328 |
void PUTNCHAR(AL,CX,BL,BH) |
|
|
4329 |
/* 1 параметр в AL - код символа, который будет выведен |
|
|
4330 |
2 параметр в CX - число выводимых символов |
|
|
4331 |
3 параметр в BL - цветовой атрибут |
|
|
4332 |
4 параметр в BH - номер видеостраницы |
|
|
4333 |
*/ |
|
|
4334 |
{ |
|
|
4335 |
AH=9; |
|
|
4336 |
$INT 0x10 |
|
|
4337 |
} |
|
|
4338 |
|
|
|
4339 |
При объявлении регистровой процедуры можно также указывать какой тип |
|
|
4340 |
переменной ожидает процедура (знаковый/без знаковый или вещественный). По |
|
|
4341 |
умолчанию считается без знаковый тип. Однако знаковый тип указывать есть |
|
|
4342 |
смысл только если параметр передается через регистр AL/AX/EAX. Через другие |
|
|
4343 |
регистры переменная всегда передается как без знаковая. Пример объявления |
|
|
4344 |
регистровой процедуры с указанием типов: |
|
|
4345 |
|
|
|
4346 |
int fastcall Exampl( word CX, int AX, DX, float ESI ) ; |
|
|
4347 |
| | | | | | | |
|
|
4348 |
| | | | | | |---- 4-й парам. имеет тип float и |
|
|
4349 |
| | | | | | перед. через регистр ESI. |
|
|
4350 |
| | | | | |-------- 3-й парам. имеет по умолч. |
|
|
4351 |
| | | | | тип word и перед. через DX. |
|
|
4352 |
| | | | |------------ 2-й парам. имеет тип int и |
|
|
4353 |
| | | | передается через регистр AX. |
|
|
4354 |
| | | |---------------------- 1-й парам. имеет тип word и |
|
|
4355 |
| | | передается через регистр CX. |
|
|
4356 |
| | |------------------------------- Имя объявляемой процедуры. |
|
|
4357 |
| |---------------------------------------- Модификатор, указывающий, что |
|
|
4358 |
| эта проц. явл. регистровой. |
|
|
4359 |
|--------------------------------------------- Процедура возвращает перемен. |
|
|
4360 |
типа int. |
|
|
4361 |
|
|
|
4362 |
Если Вы сделали объявление регистров процедуры, то компилятор будет |
|
|
4363 |
строго следить за количеством указанных параметров при вызове этой |
|
|
4364 |
процедуры и выдавать сообщения об ошибке, если их будет меньше или больше. |
|
|
4365 |
С одной стороны это хорошо - есть контроль за тем, что Вы ничего не забыли |
|
|
4366 |
или не добавили лишнего при вызове процедуры. С другой стороны иногда |
|
|
4367 |
бывают необязательные параметры, а их теперь придется прописывать. Но если |
|
|
4368 |
Вы при вызове процедуры не укажете ни одного параметра, то компилятор не |
|
|
4369 |
будет Вам выдавать сообщение об ошибке. Это дает Вам возможность |
|
|
4370 |
проинициализировать регистры, через которые Вы передаете параметры, вне |
|
|
4371 |
вызова процедуры. Но если Вы укажете, хоть один параметр, то Вам придется |
|
|
4372 |
указывать и остальные, иначе компилятор будет считать, что Вы их случайно |
|
|
4373 |
пропустили и выдаст сообщение об ошибке. |
|
|
4374 |
|
|
|
4375 |
Если Вы не объявили регистры ни при объявлении регистровой процедуры, |
|
|
4376 |
ни в заголовке самой процедуры, то компилятор будет считать, что параметры |
|
|
4377 |
в эту процедуру передаются старым способом. Таким образом, достигается |
|
|
4378 |
полная совместимость с предыдущими версиями компилятора. |
|
|
4379 |
Return to contents. |
|
|
4380 |
|
|
|
4381 |
|
|
|
4382 |
|
|
|
4383 |
11.10 Объявление параметров в стековых процедурах. |
|
|
4384 |
|
|
|
4385 |
|
|
|
4386 |
Как известно, ранее в C-- контроль за числом и типом передаваемых |
|
|
4387 |
процедуре параметров возлагался на программиста. Поэтому возникла непростая |
|
|
4388 |
задача, совместить одновременно отсутствие контроля за параметрами (для |
|
|
4389 |
совместимости с предыдущими версиями) и ее наличие. В результате |
|
|
4390 |
компромиссов появился вариант немного отличающийся от традиционно принятого |
|
|
4391 |
в языках C. |
|
|
4392 |
|
|
|
4393 |
Главное отличие - это то, что параметры, определяемые при определении |
|
|
4394 |
процедуры, не будут восприниматься компилятором для контроля за ними. Во |
|
|
4395 |
всех языках C допускается совмещение прототипа процедуры и ее объявления. |
|
|
4396 |
В C-- для того, чтобы включился контроль за параметрами стековой процедуры, |
|
|
4397 |
надо эту процедуру обязательно объявить. Но не всякое объявление процедуры |
|
|
4398 |
будет сигналом компилятору о включении контроля за параметрами этой |
|
|
4399 |
процедуры. Если при объявлении в круглых скобках ничего не будет, то |
|
|
4400 |
компилятор не будет отслеживать параметры, передаваемые этой процедуре. В |
|
|
4401 |
C++ такое объявление означает, что процедуре не передаются никакие |
|
|
4402 |
параметры. В C-- для этого надо при объявлении процедуры в круглых скобках |
|
|
4403 |
обязательно написать void. Например: |
|
|
4404 |
|
|
|
4405 |
int proc ( void ) ; |
|
|
4406 |
|
|
|
4407 |
Встретив такое объявление процедуры, компилятор будет следить за тем, |
|
|
4408 |
чтобы этой процедуре не были переданы параметры. |
|
|
4409 |
|
|
|
4410 |
При объявлении процедуры имена параметров можно опускать. Как известно, |
|
|
4411 |
в C-- параметры процедуры одного типа записываются через запятую. Для смены |
|
|
4412 |
типа используют точку с запятой. При объявлении смену типа можно |
|
|
4413 |
производить и после запятой: |
|
|
4414 |
|
|
|
4415 |
void ptoc ( int a, b, c; word d ); |
|
|
4416 |
void proc ( int, int, int, word ); |
|
|
4417 |
void proc ( int, int, int; word ); |
|
|
4418 |
|
|
|
4419 |
Все эти примеры объявлений являются идентичными и допустимыми. |
|
|
4420 |
|
|
|
4421 |
Для контроля за процедурами с переменным числом параметров был введен |
|
|
4422 |
новый для C-- элемент синтаксиса - многоточие или его еще называют эллипс. |
|
|
4423 |
Вот как будет выглядеть объявление процедуры printf: |
|
|
4424 |
|
|
|
4425 |
void cdecl printf ( word, ... ); |
|
|
4426 |
Return to contents. |
|
|
4427 |
|
|
|
4428 |
|
|
|
4429 |
|
|
|
4430 |
11.11 Использование макрокоманд. |
|
|
4431 |
|
|
|
4432 |
|
|
|
4433 |
Теперь любая динамическая процедура может быть использована как макрос. |
|
|
4434 |
Если перед вызовом динамической процедуры поставить символ @, то код этой |
|
|
4435 |
процедуры будет вставлен, а не вызван инструкцией CALL. |
|
|
4436 |
|
|
|
4437 |
При использовании стековых динамических процедур в качестве макросов |
|
|
4438 |
очистка стека от переданных параметров производится ассемблерной |
|
|
4439 |
инструкцией ADD SP,SIZE_PARAMETRS сразу после окончания кода вставленного |
|
|
4440 |
макроса. Поэтому, если эта процедура использовала флаги в качестве |
|
|
4441 |
возврата, то они будут разрушены. |
|
|
4442 |
Return to contents. |
|
|
4443 |
|
|
|
4444 |
|
|
|
4445 |
|
|
|
4446 |
11.12 Передача параметров в стековые процедуры через регистры. |
|
|
4447 |
|
|
|
4448 |
|
|
|
4449 |
При передаче параметров через регистры, чаще всего получается более |
|
|
4450 |
компактный и быстрый код. Но содержимое регистров может быть легко |
|
|
4451 |
разрушено. Если в Вашей процедуре, какой-то из параметров используется |
|
|
4452 |
однократно для того, чтобы в начале процедуры инициализировать какой-то |
|
|
4453 |
регистр, то Вы можете передать это значение в процедуру сразу через |
|
|
4454 |
регистр, минуя стадию засовывания и извлечения содержимого в стек. Пример: |
|
|
4455 |
|
|
|
4456 |
int proc (int param1, param2, param3) |
|
|
4457 |
{ |
|
|
4458 |
(E)BX = param3; |
|
|
4459 |
(E)BX.TEG_STRUCT.var = proc2 (param1,papra2); |
|
|
4460 |
proc3 (param1,param2); |
|
|
4461 |
} |
|
|
4462 |
|
|
|
4463 |
В этом примере параметр param3 используется лишь для того, чтобы |
|
|
4464 |
инициализировать регистр (E)BX, поэтому его можно сразу передать через |
|
|
4465 |
регистр: |
|
|
4466 |
|
|
|
4467 |
int proc (int param1, param2, (E)BX) |
|
|
4468 |
{ |
|
|
4469 |
(E)BX.TEG_STRUCT.var = proc2 (param1,papra2); |
|
|
4470 |
proc3 (param1,param2); |
|
|
4471 |
} |
|
|
4472 |
|
|
|
4473 |
Как Вы видите, процедура немного упростилась. |
|
|
4474 |
|
|
|
4475 |
В принципе, порядок расположения стековых и регистровых параметров не |
|
|
4476 |
принципиален. Но надо помнить, что содержимое регистров может быть легко |
|
|
4477 |
разрушено, и поэтому лучше всего регистровые параметры инициализировать |
|
|
4478 |
лишь после того, как были засунуты в стек все стековые параметры. Для |
|
|
4479 |
процедур типа pascal регистровые параметры лучше располагать после |
|
|
4480 |
стековых параметров. Для процедур типа cdecl и stdcall сначала лучше |
|
|
4481 |
располагать регистровые параметры. |
|
|
4482 |
Return to contents. |
|
|
4483 |
|
|
|
4484 |
|
|
|
4485 |
|
|
|
4486 |
11.13 Вызов процедур с адресом в регистре. |
|
|
4487 |
|
|
|
4488 |
|
|
|
4489 |
В C-- допустимо делать вызов процедуры, адрес которой находится в |
|
|
4490 |
регистре. Параметры для такого вызова передаются только через стек. Тип |
|
|
4491 |
вызова процедуры для программ под Windows stdcall, для остальных pascal. |
|
|
4492 |
Адрес процедуры для 32-битных программ должен находится в 32-битном |
|
|
4493 |
регистре, а для 16-битных программ в 16-битном регистре. Считается, что |
|
|
4494 |
такой вызов имеет возврат типа unsigned int. Пример: |
|
|
4495 |
|
|
|
4496 |
BX = # proc; |
|
|
4497 |
BX (a); |
|
|
4498 |
IF ( BX(b) == 0 ) AX=2; |
|
|
4499 |
|
|
|
4500 |
Вы получите следующий код: |
|
|
4501 |
|
|
|
4502 |
test.c-- 8: BX=#proc; |
|
|
4503 |
0104 BB1A01 mov bx,11Ah |
|
|
4504 |
|
|
|
4505 |
test.c-- 9: BX(a); |
|
|
4506 |
0107 FF76FC push word ptr [bp-4] |
|
|
4507 |
010A FFD3 call near bx |
|
|
4508 |
|
|
|
4509 |
test.c-- 10: IF (BX(b) == 0)AX=2; |
|
|
4510 |
010C FF76FE push word ptr [bp-2] |
|
|
4511 |
010F FFD3 call near bx |
|
|
4512 |
0111 85C0 test ax,ax |
|
|
4513 |
0113 7503 jne 118h |
|
|
4514 |
0115 B80200 mov ax,2 |
|
|
4515 |
Return to contents. |
|
|
4516 |
|
|
|
4517 |
|
|
|
4518 |
|
|
|
4519 |
11.14 Встроенные в компилятор процедуры. |
|
|
4520 |
|
|
|
4521 |
|
|
|
4522 |
Для некоторых процедур Вы не найдете их исходные тексты в библиотеках |
|
|
4523 |
компилятора. Код этих процедур генерирует компилятор. Вот список этих |
|
|
4524 |
процедур: |
|
|
4525 |
|
|
|
4526 |
ABORT Прекращение выполнения программы |
|
|
4527 |
atan Вычислить арктангенс числа |
|
|
4528 |
atan2 Вычислить арктангенс числа |
|
|
4529 |
ATEXIT Зарегистрировать функцию выполняющуюся при выходе. |
|
|
4530 |
cos Возвращает косинус угла |
|
|
4531 |
EXIT Закончить программу с кодом ошибки |
|
|
4532 |
exp Возвращает экспоненту числа |
|
|
4533 |
inp/inportb Считать один байт из порта |
|
|
4534 |
inport Считать слово из порта |
|
|
4535 |
inportd Считать двойное слово из порта |
|
|
4536 |
fabs Возвращает абсолютное значение числа |
|
|
4537 |
log Вычисляет натуральный логарифм числа |
|
|
4538 |
log10 Вычисляет десятичный логарифм числа |
|
|
4539 |
outp/outportb Записать один байт в порт |
|
|
4540 |
outport Записать слово в порт |
|
|
4541 |
outportd Записать двойное слово в порт |
|
|
4542 |
sin Возвращает синус угла |
|
|
4543 |
sqrt Извлечь квадратный корень через FPU. |
|
|
4544 |
tan Возвращает тангенс угла |
|
|
4545 |
|
|
|
4546 |
Размещение этих процедур непосредственно в компиляторе, связано с тем, |
|
|
4547 |
что в настоящий момент компилятор может таким образом генерировать более |
|
|
4548 |
эффективный код, чем если бы эти процедуры располагались в библиотеках. |
|
|
4549 |
В будущем, по мере развития компилятора, эти процедуры постепенно будут |
|
|
4550 |
выносится из компилятора в библиотеки. |
|
|
4551 |
|
|
|
4552 |
Но ничто не мешает Вам уже сейчас написать свои одноименные |
|
|
4553 |
библиотечные процедуры. Встретив определение такой процедуры, компилятор не |
|
|
4554 |
будет выдавать никаких сообщение, он просто будет применять Ваш вариант |
|
|
4555 |
процедуры. |
|
|
4556 |
Return to contents. |
|
|
4557 |
|
|
|
4558 |
|
|
|
4559 |
|
|
|
4560 |
11.14.1 Процедуры ABORT, ATEXIT и EXIT. |
|
|
4561 |
|
|
|
4562 |
|
|
|
4563 |
Процедуры ABORT и EXIT связаны с работой директивы #atexit и |
|
|
4564 |
процедурой ATEXIT. Наиболее оптимальную их реализацию и взаимную |
|
|
4565 |
интеграцию может сделать только компилятор. Именно поэтому эти процедуры |
|
|
4566 |
поддерживаются компилятором. |
|
|
4567 |
|
|
|
4568 |
Процедура ATEXIT - регистровая процедура, которая регистрирует |
|
|
4569 |
функцию, адрес которой передается ей в качестве параметра, т.е. через |
|
|
4570 |
регистр (E)AX, как функцию завершения программы. При успешной регистрации |
|
|
4571 |
ATEXIT возвращает 0. Всего можно зарегистрировать до 16 функций. |
|
|
4572 |
|
|
|
4573 |
Завершающие функции не должны иметь параметров и возврата. Эти |
|
|
4574 |
функции будут выполняться в порядке обратном очередности регистрации в |
|
|
4575 |
случае, если Вы будете завершать работу программы через вызовы процедур |
|
|
4576 |
ABORT или EXIT или закончится работа процедуры main. Если Вы |
|
|
4577 |
завершите работу программы вызовом процедуры ExitProcess под Windows или |
|
|
4578 |
вызовом AH=0x4C; $int 0x21 под DOS, выход из программы произойдет без |
|
|
4579 |
запуска зарегистрированных функций. |
|
|
4580 |
|
|
|
4581 |
Процедура ABORT и EXIT, если не включена директива #atexit делают |
|
|
4582 |
вызов процедуры ExitProcess под Windows и вызов AH=0x4C; $int 0x21 под |
|
|
4583 |
DOS. Процедуре ABORT не передаются никакие параметры, и она завершает |
|
|
4584 |
работу программы с кодом возврата 0. Процедуре EXIT передается в |
|
|
4585 |
качестве параметра код возврата, с которым она и завершает работу |
|
|
4586 |
программы. |
|
|
4587 |
Return to contents. |
|
|
4588 |
|
|
|
4589 |
|
|
|
4590 |
|
|
|
4591 |
11.14.2 Процедуры inp/inportb, inport, inportd, outp/outportb, outport и |
|
|
4592 |
outportd |
|
|
4593 |
|
|
|
4594 |
|
|
|
4595 |
Эти процедуры всегда вставляются в код как макросы, т.е. для этих |
|
|
4596 |
процедур никогда не генерируется вызов процедуры. В зависимости от |
|
|
4597 |
значения порта, с которым работают эти процедуры, генерируется разный |
|
|
4598 |
код. Все это позволяет получать более компактный код. |
|
|
4599 |
|
|
|
4600 |
Процедуры чтения из порта имеют такой прототип: |
|
|
4601 |
|
|
|
4602 |
byte inp ( word port ); |
|
|
4603 |
word inport ( word port ); |
|
|
4604 |
dword inportd ( word port ); |
|
|
4605 |
|
|
|
4606 |
Процедуры записи в порт имеют такой прототип: |
|
|
4607 |
|
|
|
4608 |
void outp ( byte val; word port ); |
|
|
4609 |
void outport ( word val; word port ); |
|
|
4610 |
void outportd ( dword val; word port ); |
|
|
4611 |
|
|
|
4612 |
Имена процедур inp и inportb, также как и имена outp и outportb |
|
|
4613 |
являются синонимами. |
|
|
4614 |
Return to contents. |
|
|
4615 |
|
|
|
4616 |
|
|
|
4617 |
|
|
|
4618 |
11.14.3 Процедуры для работы с вещественными числами. |
|
|
4619 |
|
|
|
4620 |
|
|
|
4621 |
Эти процедуры реализуются компилятором и всегда вставляются в код как |
|
|
4622 |
макросы, т.е. для них никогда не генерируется вызов процедуры. Кроме |
|
|
4623 |
этого, если параметром одной процедуры является вызов другой, то |
|
|
4624 |
результат работы второй процедуры остается в стеке FPU, а первая |
|
|
4625 |
процедура использует этот результат непосредственно из стека. Таким |
|
|
4626 |
образом получаются более компактный код. Вот вымышленный пример: |
|
|
4627 |
|
|
|
4628 |
test.c-- 7: f = sin( sqrt(1) ); |
|
|
4629 |
0100 D9061C01 fld [11Ch] |
|
|
4630 |
0104 D9FA fsqrt |
|
|
4631 |
0106 D9FE fsin |
|
|
4632 |
0108 D91E2001 fstp [120h] |
|
|
4633 |
010C 9B fwait |
|
|
4634 |
|
|
|
4635 |
Эти процедуры имеют следующий прототип: |
|
|
4636 |
|
|
|
4637 |
float atan ( float val ); |
|
|
4638 |
float atan ( float val, val2 ); |
|
|
4639 |
float cos ( float val ); |
|
|
4640 |
float exp ( float val ); |
|
|
4641 |
float fabs ( float val ); |
|
|
4642 |
float log ( float val ); |
|
|
4643 |
float log10 ( float val ); |
|
|
4644 |
float sin ( float val ); |
|
|
4645 |
float sqrt ( float val ); |
|
|
4646 |
float tan ( float val ); |
|
|
4647 |
Return to contents. |
|
|
4648 |
|
|
|
4649 |
|
|
|
4650 |
|
|
|
4651 |
11.15 Классы. |
|
|
4652 |
|
|
|
4653 |
11.15.1 Объявление процедур в структурах. |
|
|
4654 |
|
|
|
4655 |
|
|
|
4656 |
С введение поддержки объявления процедур в структурах, структура |
|
|
4657 |
становится подобной классу в C++. Т.е. такая процедура становится методом |
|
|
4658 |
класса. Пример: |
|
|
4659 |
|
|
|
4660 |
struct Point // объявление класса |
|
|
4661 |
{ |
|
|
4662 |
int x; // элементы данных |
|
|
4663 |
int y; // класса типа Point |
|
|
4664 |
void SetX(int); // объявление методов |
|
|
4665 |
void SetY(int); // класса Point |
|
|
4666 |
}; |
|
|
4667 |
|
|
|
4668 |
void Point::SetX(int _x) //определение процедуры класса Point |
|
|
4669 |
{ |
|
|
4670 |
IF((_x>=0)&&(_x<=MAX_X)) x=_x; |
|
|
4671 |
// переменные x, y являются членами этого класса и поэтому доступ к ним из |
|
|
4672 |
// процедур этого же класса осуществляется напрямую. |
|
|
4673 |
} |
|
|
4674 |
|
|
|
4675 |
void main() |
|
|
4676 |
Point p; //определяем структуру в стеке |
|
|
4677 |
{ |
|
|
4678 |
p.y = p.x = 0; |
|
|
4679 |
p.SetX(1); |
|
|
4680 |
} |
|
|
4681 |
|
|
|
4682 |
При вызове процедуры являющейся методом класса ей неявным образом |
|
|
4683 |
передается адрес этого класса (структуры). В самой процедуре этот адрес |
|
|
4684 |
доступен через имя параметрической переменной this. Эту переменную |
|
|
4685 |
автоматически генерирует компилятор. Если в объявление процедуры в |
|
|
4686 |
структуре указать ключевое слово static, то такой процедуре адрес |
|
|
4687 |
класса не передается и переменная this не генерируется. |
|
|
4688 |
|
|
|
4689 |
Процедура объявленная в структуре может быть динамической. Для этого, |
|
|
4690 |
при ее определении, в самом ее начале, надо написать символ двоеточия : |
|
|
4691 |
(также как и для обычных динамических процедур). Но такая динамическая |
|
|
4692 |
процедура не может быть использована как макрос. |
|
|
4693 |
Return to contents. |
|
|
4694 |
|
|
|
4695 |
|
|
|
4696 |
|
|
|
4697 |
11.15.2 Наследование. |
|
|
4698 |
|
|
|
4699 |
|
|
|
4700 |
В C-- поддерживаются простые и множественные наследования. Объявление |
|
|
4701 |
структуры с наследованием имеет следующий синтаксис: |
|
|
4702 |
|
|
|
4703 |
struct Derived : Base1, Base2, ... Basen |
|
|
4704 |
{ |
|
|
4705 |
int x0; |
|
|
4706 |
}; |
|
|
4707 |
|
|
|
4708 |
Число базовых структур в производном не ограничено. При множественном |
|
|
4709 |
наследовании структура может наследовать два и более экземпляра базовой |
|
|
4710 |
структуры. При этом возникает неоднозначность. Пример: |
|
|
4711 |
|
|
|
4712 |
struct A |
|
|
4713 |
{ |
|
|
4714 |
int x,y; |
|
|
4715 |
. . . |
|
|
4716 |
}; |
|
|
4717 |
|
|
|
4718 |
struct B : A //структура B наследует A |
|
|
4719 |
{ |
|
|
4720 |
. . . |
|
|
4721 |
|
|
|
4722 |
}; |
|
|
4723 |
|
|
|
4724 |
struct C : A //структура C наследует A |
|
|
4725 |
{ |
|
|
4726 |
. . . |
|
|
4727 |
}; |
|
|
4728 |
|
|
|
4729 |
struct D : B, C //структура D наследует B и C |
|
|
4730 |
{ |
|
|
4731 |
. . . |
|
|
4732 |
}; |
|
|
4733 |
|
|
|
4734 |
void main() |
|
|
4735 |
D d; //выделяем для структуры D память в стеке и присваиваем ей имя d |
|
|
4736 |
{ |
|
|
4737 |
d.x0=0; |
|
|
4738 |
|
|
|
4739 |
В этом примере структура D наследует два экземпляра структуры A и |
|
|
4740 |
в ней находятся два элемента с именем x0. Компиляторы C++ при записи |
|
|
4741 |
типа d.x0=0 выдают сообщение об ошибке. C-- эту запись обрабатывает, |
|
|
4742 |
присваивание производится по умолчанию в элемент из последней базовой |
|
|
4743 |
структуры, имеющей элемент x0. Для того чтобы получить доступ ко |
|
|
4744 |
второму элементу x0 (физически этот элемент находится в структуре |
|
|
4745 |
первым), необходимо применить операцию разрешения видимости: |
|
|
4746 |
|
|
|
4747 |
d.B::x0=0; |
|
|
4748 |
|
|
|
4749 |
Из всего этого следует, что записи: |
|
|
4750 |
|
|
|
4751 |
d.x0=0; |
|
|
4752 |
и |
|
|
4753 |
d.C::x0=0; |
|
|
4754 |
|
|
|
4755 |
являются равнозначными. |
|
|
4756 |
Return to contents. |
|
|
4757 |
|
|
|
4758 |
|
|
|
4759 |
|
|
|
4760 |
11.15.3 Наследование процедур. |
|
|
4761 |
|
|
|
4762 |
|
|
|
4763 |
Если в базовом классе есть процедура, а в производном классе Вы эту |
|
|
4764 |
процедуру переопределили, то эта процедура будет переопределена и в |
|
|
4765 |
базовом классе. Таким образом процедура определенная в базовом классе |
|
|
4766 |
будет потеряна. Пример: |
|
|
4767 |
|
|
|
4768 |
struct Point // базовый класс |
|
|
4769 |
{ |
|
|
4770 |
int x; // элементы данных |
|
|
4771 |
int y; // класса типа Point |
|
|
4772 |
void SetX(int); // объявление методов |
|
|
4773 |
void SetY(int); // класса Point |
|
|
4774 |
}; |
|
|
4775 |
|
|
|
4776 |
void Point::SetX(int _x) // определение процедуры класса Point |
|
|
4777 |
{ |
|
|
4778 |
IF((_x>=0)&&(_x<=MAX_X)) x=_x; |
|
|
4779 |
} |
|
|
4780 |
|
|
|
4781 |
struct Point2 : Point // производный класс |
|
|
4782 |
{ |
|
|
4783 |
int x2; |
|
|
4784 |
} |
|
|
4785 |
|
|
|
4786 |
struct Point3 : Point // еще один производный класс |
|
|
4787 |
{ |
|
|
4788 |
int z; |
|
|
4789 |
} |
|
|
4790 |
|
|
|
4791 |
void Point3::SetX(int _x) // в этом производном классе переопределяем |
|
|
4792 |
{ // процедуру SetX |
|
|
4793 |
IF((_x>=80)&&(_x<=MAX_X)) x=_x; |
|
|
4794 |
} |
|
|
4795 |
|
|
|
4796 |
Процедура SetX, определенная в базовом классе Point, теперь будет |
|
|
4797 |
недоступна. Вместо кода определенного в этом классе, будет вызываться код |
|
|
4798 |
процедуры, определенный в наследуемом классе Point3. При вызове процедуры |
|
|
4799 |
SetX из другого производного класса Point2 будет также вызываться код |
|
|
4800 |
процедуры, определенный в производном классе Point3. Переопределяя |
|
|
4801 |
процедуру таким образом, Вы замените код этой процедуры в базовом классе и |
|
|
4802 |
во всех его наследуемых классах. |
|
|
4803 |
|
|
|
4804 |
Если Вам необходимо, чтобы код новой процедуры был доступен |
|
|
4805 |
одновременно с кодом старой процедуры, то в производном классе Вам |
|
|
4806 |
необходимо сделать еще одно объявление этой процедуры. Пример: |
|
|
4807 |
|
|
|
4808 |
struct Point // базовый класс |
|
|
4809 |
{ |
|
|
4810 |
int x; // элементы данных |
|
|
4811 |
int y; // класса типа Point |
|
|
4812 |
void SetX(int); // объявление методов |
|
|
4813 |
void SetY(int); // класса Point |
|
|
4814 |
}; |
|
|
4815 |
|
|
|
4816 |
void Point::SetX(int _x) // определение процедуры класса Point |
|
|
4817 |
{ |
|
|
4818 |
IF((_x>=0)&&(_x<=MAX_X)) x=_x; |
|
|
4819 |
} |
|
|
4820 |
|
|
|
4821 |
struct Point2 : Point // производный класс |
|
|
4822 |
{ |
|
|
4823 |
int x2; |
|
|
4824 |
} |
|
|
4825 |
|
|
|
4826 |
struct Point3 : Point // еще один производный класс |
|
|
4827 |
{ |
|
|
4828 |
int z; |
|
|
4829 |
void SetX(int); // в наследуемом классе делаем еще одно объявление |
|
|
4830 |
// процедуры SetX |
|
|
4831 |
} |
|
|
4832 |
|
|
|
4833 |
void Point3::SetX(int _x) // в этом производном классе переопределяем |
|
|
4834 |
{ // процедуру SetX |
|
|
4835 |
IF((_x>=80)&&(_x<=MAX_X)) x=_x; |
|
|
4836 |
EDI=this; |
|
|
4837 |
EDI.Point.SetX(_x); // делаем вызов одноименной процедуры из |
|
|
4838 |
// базового класса |
|
|
4839 |
} |
|
|
4840 |
|
|
|
4841 |
Теперь из производного класса Point3 Вам доступны две различные |
|
|
4842 |
процедуры с одним именем SetX. А из базового класса Point и из другого |
|
|
4843 |
производного класса Point2 будет по прежнему доступен только базовый |
|
|
4844 |
вариант процедуры SetX. |
|
|
4845 |
Return to contents. |
|
|
4846 |
|
|
|
4847 |
|
|
|
4848 |
|
|
|
4849 |
12. Типы выходных файлов. |
|
|
4850 |
|
|
|
4851 |
12.1 Выходные файлы типа COM. |
|
|
4852 |
|
|
|
4853 |
|
|
|
4854 |
Этот тип выходного файла получается автоматически по умолчанию. |
|
|
4855 |
|
|
|
4856 |
Изначально C-- мог делать только файлы формата типа COM. В настоящее |
|
|
4857 |
время появилась возможность получать файла типа EXE с моделями памяти tiny |
|
|
4858 |
и small для 16-битного кода, а также 32-битные для DOS и Windows. Также |
|
|
4859 |
есть возможность получения выходного файла в формате OBJ, что позволяет |
|
|
4860 |
связывать программы на C-- с программами на других языках. |
|
|
4861 |
Return to contents. |
|
|
4862 |
|
|
|
4863 |
|
|
|
4864 |
|
|
|
4865 |
12.2 Выходные файлы типа EXE. |
|
|
4866 |
|
|
|
4867 |
|
|
|
4868 |
Этот формат файла можно получить, если компилировать с ключом командной |
|
|
4869 |
строки /exe или /e. |
|
|
4870 |
|
|
|
4871 |
Возможно также поддержка EXE-формата через выходной файл формата OBJ, |
|
|
4872 |
который можно затем обработать линковщиком, не входящим в пакет C--. |
|
|
4873 |
Return to contents. |
|
|
4874 |
|
|
|
4875 |
|
|
|
4876 |
|
|
|
4877 |
12.3 Выходной файл *.EXE с моделью памяти tiny. |
|
|
4878 |
|
|
|
4879 |
|
|
|
4880 |
Фактически код файла *.exe модели tiny ничем не отличается от кода |
|
|
4881 |
*.com. В сущности, это тот же com-файл, к которому добавлен 32-байтный |
|
|
4882 |
заголовок exe-файла. Единственное отличие возникает, когда Вы компилируете |
|
|
4883 |
файл с директивой ?resize TRUE. В com-файле, по этой директиве, в код |
|
|
4884 |
программы добавляется соответствующий код, изменяющий размер доступной |
|
|
4885 |
памяти. В exe-файле для этих целей будет скорректирован заголовок |
|
|
4886 |
exe-файла. |
|
|
4887 |
|
|
|
4888 |
Чтобы получить exe-файл с моделью памяти tiny, надо запустить |
|
|
4889 |
компилятор с ключом в командной строке /TEXE. |
|
|
4890 |
Return to contents. |
|
|
4891 |
|
|
|
4892 |
|
|
|
4893 |
|
|
|
4894 |
12.4 Объектный выходной файл OBJ. |
|
|
4895 |
|
|
|
4896 |
|
|
|
4897 |
В настоящее время C-- может только создавать OBJ-файлы, но не может их |
|
|
4898 |
компоновать. |
|
|
4899 |
|
|
|
4900 |
Ранее C-- создавал obj-файлы, которые могли быть подключены к проектам |
|
|
4901 |
созданным на других языках, т.е. ведомые (slave) модули. Причем из C-- |
|
|
4902 |
модулей для основного проекта были доступны только процедуры и эти |
|
|
4903 |
процедуры не должны были использовать глобальные переменные. |
|
|
4904 |
|
|
|
4905 |
Теперь же C-- может создавать основной модуль (master), который может |
|
|
4906 |
быть слинкован в самостоятельный файл. |
|
|
4907 |
|
|
|
4908 |
Для obj-файлов появилась возможность использовать внешние (extern) |
|
|
4909 |
процедуры, переменные или структуры. Для этого достаточно их объявить как |
|
|
4910 |
extern. Причем ключевое слово extern должно быть всегда первым. Пример |
|
|
4911 |
объявления внешних объектов: |
|
|
4912 |
|
|
|
4913 |
extern void cdecl _printf(); // объявление внешней процедуры _printf имеющей |
|
|
4914 |
// тип cdecl и тип возврата void |
|
|
4915 |
extern int buts,cubs; // объявление двух внешних переменных типа int |
|
|
4916 |
extern struct IPXL ipxl; // объявление внешней структуры ipxl имеющей тег |
|
|
4917 |
// IPXL, причем тег этой структуры должен быть |
|
|
4918 |
// описан ранее. |
|
|
4919 |
|
|
|
4920 |
Появление возможности объявлять внешние объекты позволяет подключать к |
|
|
4921 |
obj-модулю на C-- модули написанные на других языках или подключать к |
|
|
4922 |
программе на C-- процедуры из библиотек на других языках. При объявлении |
|
|
4923 |
внешних объектов очень важно правильно указать тип процедуры и ее имя. Если |
|
|
4924 |
Вы будете использовать внешние процедуры, написанные на C то чаще всего, |
|
|
4925 |
Вам нужно будет указывать модификатор cdecl, а к имени процедуры или |
|
|
4926 |
переменной добавлять префикс _. |
|
|
4927 |
|
|
|
4928 |
Из основного (master) obj-файла написанного на C-- для других |
|
|
4929 |
obj-модулей доступны все процедуры, глобальные переменные и глобальные |
|
|
4930 |
структуры. |
|
|
4931 |
|
|
|
4932 |
Чтобы получить ведомый obj-модуль при компиляции надо использовать ключ |
|
|
4933 |
/sobj. |
|
|
4934 |
|
|
|
4935 |
C-- может создавать obj-файлы с моделью памяти tiny и small. По |
|
|
4936 |
умолчанию создаются модули с моделью tiny. Чтобы получить obj-файл с |
|
|
4937 |
моделью памяти small надо запустить компилятор с ключами /obj и /exe. |
|
|
4938 |
|
|
|
4939 |
Для создания obj-файлов для 32-битного DOS в командной строке Вам |
|
|
4940 |
необходимо указать ключи /d32 и /obj. Использовать полученный obj-файл мне |
|
|
4941 |
удалось лишь с помощью wlink и расширителя zrdx.exe. |
|
|
4942 |
|
|
|
4943 |
Создание obj-файлов под windows не предусмотрено. |
|
|
4944 |
Return to contents. |
|
|
4945 |
|
|
|
4946 |
|
|
|
4947 |
|
|
|
4948 |
12.5 COM файл symbiosis. |
|
|
4949 |
|
|
|
4950 |
12.5.1 СИМБИОЗ - что это такое? |
|
|
4951 |
|
|
|
4952 |
|
|
|
4953 |
Транслятор C-- имеет ключ, позволяющий добавлять компилируемую |
|
|
4954 |
программу к концу уже имеющегося COM файла. Это называют COM-файл |
|
|
4955 |
Symbiosis. Когда такая программа запускается, управление сначала получает |
|
|
4956 |
добавленный код C--, и только после выполнения его процедуры main() |
|
|
4957 |
управление получит первоначальный код COM-файла. |
|
|
4958 |
|
|
|
4959 |
Если добавленный вами код завершается EXIT() или ABORT(), программа |
|
|
4960 |
прекратится, и первоначальный код COM-файла не будет выполнен. Это |
|
|
4961 |
позволяет программе, добавленной к COM файлу, определять, будет ли |
|
|
4962 |
управление передано на первоначальный код. |
|
|
4963 |
Return to contents. |
|
|
4964 |
|
|
|
4965 |
|
|
|
4966 |
|
|
|
4967 |
12.5.2 Как это делать. |
|
|
4968 |
|
|
|
4969 |
|
|
|
4970 |
Чтобы сделать это, Вы должны использовать ключ /SYM в командной |
|
|
4971 |
строке компилятора, в которой указывается полное имя COM-файла, к |
|
|
4972 |
которому что-то добавляется. При этом оригинал COM-файла не меняется, а |
|
|
4973 |
новый файл содержит его в себе. Например, чтобы откомпилировать программу |
|
|
4974 |
HELLO.C-- к концу копии C:\command.сом используют следующую команду: |
|
|
4975 |
|
|
|
4976 |
C-- /SYM C:\COMMAND.COM HELLO.C-- |
|
|
4977 |
|
|
|
4978 |
Будет создан выходной файл HELLO.COM . |
|
|
4979 |
Return to contents. |
|
|
4980 |
|
|
|
4981 |
|
|
|
4982 |
|
|
|
4983 |
12.5.3 Использование. |
|
|
4984 |
|
|
|
4985 |
|
|
|
4986 |
Вы можете, вероятно, придумать большое количество путей использования |
|
|
4987 |
этой функции, типа: |
|
|
4988 |
|
|
|
4989 |
- Добавление защиты с использованием пароля к некоторым |
|
|
4990 |
специальным COM файлам. |
|
|
4991 |
- Уменьшение памяти, доступной COM файлу при запуске. |
|
|
4992 |
- Инициализация режима видео для COM файла. |
|
|
4993 |
Return to contents. |
|
|
4994 |
|
|
|
4995 |
|
|
|
4996 |
|
|
|
4997 |
12.5.4 Злоупотребления. |
|
|
4998 |
|
|
|
4999 |
|
|
|
5000 |
Любой злоумышленник может придумать и вредные применения для этой |
|
|
5001 |
функции. Наиболее очевидное из них - создание троянских коней. Я хотел бы |
|
|
5002 |
указать, что это неконструктивное использование C--, и любое |
|
|
5003 |
разрушительное использование симбиозов COM-файлов запрещено. |
|
|
5004 |
Return to contents. |
|
|
5005 |
|
|
|
5006 |
|
|
|
5007 |
|
|
|
5008 |
12.6 SYS - драйверы устройств. |
|
|
5009 |
|
|
|
5010 |
|
|
|
5011 |
Компилятор значительно облегчит Ваш труд при написании драйверов. |
|
|
5012 |
Компилятор сам создаст заголовок драйвера и процедуры СТРАТЕГИЯ и |
|
|
5013 |
ПРЕРЫВАНИЕ. Вам остается лишь написать код обработки команд. |
|
|
5014 |
|
|
|
5015 |
Что бы откомпилировать файл драйвера устройства, надо добавить в |
|
|
5016 |
командную строку ключ /SYS. Кроме того, появились новые директивы |
|
|
5017 |
компилятору, которые действуют только с этим ключом. Вот они: |
|
|
5018 |
|
|
|
5019 |
?sysattribute значение - эта директива передает компилятору |
|
|
5020 |
атрибут создаваемого драйвера. По умолчанию устанавливается значение |
|
|
5021 |
0x2000. |
|
|
5022 |
|
|
|
5023 |
?sysname <текстовая строка> - эта директива передает компилятору |
|
|
5024 |
имя будущего драйвера. По умолчанию присваивается имя "NO_NAME". Длина |
|
|
5025 |
имени не более 8 символов. |
|
|
5026 |
|
|
|
5027 |
?syscommand command_0,command_1, ... command_n; - эта директива |
|
|
5028 |
является обязательной. По этой директиве компилятору передается список имен |
|
|
5029 |
процедур обработки команд драйвера. Имена разделены запятыми. Список должен |
|
|
5030 |
заканчиваться символом точка-с-запятой. Можно передать не более 25 команд. |
|
|
5031 |
Если какая-то команда не имеет кода поддержки, то в список надо записать |
|
|
5032 |
слово NONE. |
|
|
5033 |
|
|
|
5034 |
По умолчанию компилятор для драйвера не создает стек. Драйвер может |
|
|
5035 |
пользоваться системным стеком. Но, говорят, что он имеет маленькую глубину. |
|
|
5036 |
Если Ваши процедуры активно используют стек, и Вы не надеетесь на системный, |
|
|
5037 |
то директивой ?stack <величина> можно заставить драйвер пользоваться своим |
|
|
5038 |
стеком. |
|
|
5039 |
|
|
|
5040 |
Вашим процедурам обработки команд при передаче управления в регистрах |
|
|
5041 |
ES:BX будет передан адрес заголовка запроса. Регистр DS равен CS. При |
|
|
5042 |
возврате управления ваши процедуры должны сохранить регистр DS. В регистре |
|
|
5043 |
AX должен находиться код возврата. Остальные регистры могут быть |
|
|
5044 |
использованы произвольным образом. |
|
|
5045 |
|
|
|
5046 |
Процедуру обработки команды инициализации желательно располагать |
|
|
5047 |
последней (чтобы иметь возможность отдать адресное пространство занимаемое |
|
|
5048 |
этой процедурой операционной системе). Перед этой процедурой, если Вы в |
|
|
5049 |
других процедурах обработки команд используете динамические процедуры, |
|
|
5050 |
обязательно должна быть директива ?setdinproc. Глобальные переменные должны |
|
|
5051 |
быть обязательно проинициализированы. |
|
|
5052 |
Return to contents. |
|
|
5053 |
|
|
|
5054 |
|
|
|
5055 |
|
|
|
5056 |
12.7 Компиляция кода расширителей ROM-BIOS. |
|
|
5057 |
|
|
|
5058 |
|
|
|
5059 |
Расширители ROM-BIOS (BIOS видеоконтроллеров, сетевых карт...) имеют |
|
|
5060 |
определенную структуру и требования. C-- теперь может облегчить Вам процесс |
|
|
5061 |
создания кода ROM-BIOS. Если запустить компилятор на компиляцию с ключом |
|
|
5062 |
командной строки /ROM, то компилятор создаст сигнатуру (заголовок) |
|
|
5063 |
ROM-BIOS, заполнит оставшееся свободное место до указанного размера ПЗУ |
|
|
5064 |
кодом заполнения, подсчитает и скорректирует контрольную сумму ПЗУ. |
|
|
5065 |
|
|
|
5066 |
Для этого режима компиляции есть несколько специфических директив: |
|
|
5067 |
|
|
|
5068 |
1. ?sizerom value - эта директива сообщает компилятору размер ПЗУ в |
|
|
5069 |
байтах. Если эта директива не указана, то компилятор сам выберет |
|
|
5070 |
минимальный подходящий размер ПЗУ из ряда: 1024, 2048, 4096, 8192, 16384, |
|
|
5071 |
32762 или 65536. Свободное от кода и данных место будут заполнены до конца |
|
|
5072 |
размера ПЗУ байтом заполнения определяемого директивой ?aligner. По |
|
|
5073 |
умолчанию он равен нулю, для РПЗУ типа 27ххх этот байт имеет смысл сделать |
|
|
5074 |
равным 0xFF. Последний байт ПЗУ будет скорректирован компилятором таким |
|
|
5075 |
образом, чтобы контрольная сумма равнялась нулю. |
|
|
5076 |
|
|
|
5077 |
2. ?movedatarom TRUE/FALSE - эта директива сообщает компилятору есть ли |
|
|
5078 |
необходимость копировать данные из ПЗУ в ОЗУ. По умолчанию она установлена |
|
|
5079 |
в FALSE. Если эту директиву определить TRUE, то компилятор вставит в |
|
|
5080 |
область инициализации код перемещающий данные из ПЗУ в ОЗУ. При этом |
|
|
5081 |
регистр DS будет установлен на сегмент ОЗУ. Стек также будет переустановлен |
|
|
5082 |
на этот сегмент. Таким образом, процедура main получит управление с |
|
|
5083 |
регистрами AX = ES = DS = SS = сегменту ОЗУ с перенесенными в него данными. |
|
|
5084 |
Если эту директиву установить в FALSE, регистр DS все равно будет |
|
|
5085 |
переустановлен на адрес сегмента ОЗУ, так как Ваш код будет использовать |
|
|
5086 |
этот сегмент для неинициализированных глобальных переменных. |
|
|
5087 |
Инициализированные переменные останутся в ПЗУ и все обращения к ним будут |
|
|
5088 |
производиться через регистр CS. Так же останется не тронутым (таким, каким |
|
|
5089 |
его установил главный BIOS) и стек. |
|
|
5090 |
|
|
|
5091 |
3. ?dataseg value - этой директивой компилятору сообщается сегментный |
|
|
5092 |
адрес ОЗУ, который может быть использован вашим кодом. По умолчанию он |
|
|
5093 |
равен 0x70. Этот адрес вы можете узнать в любой момент, считав его из вашего |
|
|
5094 |
кода по смещению 4. Например: DS = CSWORD[4]; |
|
|
5095 |
|
|
|
5096 |
Некоторые замечания: |
|
|
5097 |
|
|
|
5098 |
1. Не забывайте, что в момент инициализации ROM-BIOS, DOS еще не |
|
|
5099 |
загружен, и соответственно все процедуры использующие вызовы DOS работать |
|
|
5100 |
не будут. |
|
|
5101 |
|
|
|
5102 |
2. Нельзя завершать работу программы процедурами ABORT() или EXIT() и им |
|
|
5103 |
подобным. Работа расширителя ROM-BIOS должна завершаться только выходом из |
|
|
5104 |
процедуры main(). |
|
|
5105 |
|
|
|
5106 |
3. Если директива ?movedatarom установлена в FALSE, то будьте внимательны |
|
|
5107 |
при работе с инициализированными переменными. Они в этом режиме доступны |
|
|
5108 |
только для чтения, и адресуются через регистр CS. |
|
|
5109 |
Return to contents. |
|
|
5110 |
|
|
|
5111 |
|
|
|
5112 |
|
|
|
5113 |
12.8 32-битные файлы. |
|
|
5114 |
|
|
|
5115 |
12.8.1 32-битный код под DOS. |
|
|
5116 |
|
|
|
5117 |
|
|
|
5118 |
Для того чтобы откомпилировать 32-битную программу под DOS надо |
|
|
5119 |
запустить компилятор с ключом командной строки /d32. Но работа 32-битной |
|
|
5120 |
программы под DOS-ом невозможна без расширителя DOS. Для C-- можно |
|
|
5121 |
использовать DOS4GW или zrdx.exe или любой другой расширитель DOS. Чтобы |
|
|
5122 |
компилятор знал, где искать stub файл и его имя, надо в файл c--.ini |
|
|
5123 |
прописать строку stub=path_name_to_stub_file. Пример: |
|
|
5124 |
|
|
|
5125 |
stub=c:\c--\zrdx.exe |
|
|
5126 |
|
|
|
5127 |
Если не добавлять в c--.ini эту строку, то компилятор сгенерирует |
|
|
5128 |
32-битный exe-файл, но без расширителя DOS. Если в командной строке |
|
|
5129 |
вместе с ключом /d32 указать и ключ /ns, то строка с переменной stub из |
|
|
5130 |
файла c--.ini будет аннулирована, и вы получите файл без расширителя DOS. |
|
|
5131 |
|
|
|
5132 |
Для 32-битного DOS-файла можно использовать директивы компилятора |
|
|
5133 |
?parsecommandline TRUE/FALSE или его расширенный вариант ?argc |
|
|
5134 |
TRUE/FALSE. Реализована и поддержка директивы ?atexit TRUE/FALSE. |
|
|
5135 |
|
|
|
5136 |
Сейчас для 32-битных DOS-файлов используется LE-формат. Так как LE |
|
|
5137 |
формат является стандартным, то теперь можно использовать почти любой |
|
|
5138 |
stub, понимающий этот формат. Файлы LE формата можно сжимать программами |
|
|
5139 |
типа UPX.EXE и ей подобными. |
|
|
5140 |
|
|
|
5141 |
Если Вы используете stub, который затем загружает DOS4GW.EXE, то |
|
|
5142 |
начало Вашей программы должно иметь специальную сигнатуру. Компилятор |
|
|
5143 |
автоматически сформирует ее, если Вы в командной строке или в c--.ini |
|
|
5144 |
файле укажете ключ /DOS4GW. Такой ключ Вам необходимо будет применять, |
|
|
5145 |
если Вы будете использовать в качестве stub 4gs.exe. |
|
|
5146 |
|
|
|
5147 |
Существует также поддержка блока кода использующего для перехода и |
|
|
5148 |
работы в 32-битном режиме возможности DPMI сервиса. Исходный текст этого |
|
|
5149 |
блока находится в файле startup.h-- и компилируется, если в командной |
|
|
5150 |
строке указана опция /stub=dpmi или в файле c--.ini написать строку |
|
|
5151 |
stub=dpmi. Недостатком этого способа перехода и работы в 32-битном |
|
|
5152 |
режиме являются необходимость обязательного функционирования на |
|
|
5153 |
запускаемом компьютере DPMI сервиса. Так как, программа загружается как |
|
|
5154 |
обычная DOS программа, и лишь в процессе работы переходит в 32-битный |
|
|
5155 |
режим работы, размер программы ограничен размером свободной DOS памяти. |
|
|
5156 |
Ну а преимуществом его является компактный размер исполняемого файла. |
|
|
5157 |
Return to contents. |
|
|
5158 |
|
|
|
5159 |
|
|
|
5160 |
|
|
|
5161 |
12.8.2 32-битный код под Windows. |
|
|
5162 |
|
|
|
5163 |
|
|
|
5164 |
Для того чтобы откомпилировать программу, написанную под Windows надо |
|
|
5165 |
запустить компилятор с ключом командной строки /w32. |
|
|
5166 |
|
|
|
5167 |
Если Вы в своей программе используете вызовы API-процедур, то эти |
|
|
5168 |
процедуры надо предварительно обязательно объявить. Объявление процедур |
|
|
5169 |
имеет следующую форму: |
|
|
5170 |
|
|
|
5171 |
extern WINAPI "DLL_name" |
|
|
5172 |
{ |
|
|
5173 |
returncode procname1(); |
|
|
5174 |
returncode procname2(); |
|
|
5175 |
procname3(); |
|
|
5176 |
} |
|
|
5177 |
|
|
|
5178 |
где: |
|
|
5179 |
DLL_name - имя и расширение dll-библиотеки, в которой находятся эти |
|
|
5180 |
процедуры. |
|
|
5181 |
returncode - тип возврата из api-процедур. По умолчанию он равен dword. |
|
|
5182 |
|
|
|
5183 |
Программы, написанные под Windows, имеют одну немаловажную |
|
|
5184 |
особенность - все параметры в стековые процедуры передаются в обратном |
|
|
5185 |
порядке (так называемый C-стиль), но очистка стека от параметров |
|
|
5186 |
происходит в самих процедурах. Получается своеобразный гибрид C и pascal |
|
|
5187 |
стилей - stdcall. |
|
|
5188 |
|
|
|
5189 |
С помощю ключа /W32C компилятор создает консольный файл под Windows. |
|
|
5190 |
|
|
|
5191 |
Если при компиляции указывали опцию командной строки /j0 или |
|
|
5192 |
директиву #jumptomain NONE, то Ваша программа будет компилироваться без |
|
|
5193 |
использования кода начальной инициализации, описание которого находится в |
|
|
5194 |
файле startup.h--. |
|
|
5195 |
|
|
|
5196 |
Код начальной инициализации для программ под Windows имеет следующий |
|
|
5197 |
вид: |
|
|
5198 |
|
|
|
5199 |
hThisInst=GetModuleHandleA(0); |
|
|
5200 |
#ifdef __CONSOLE__ |
|
|
5201 |
hStdOut=GetStdHandle(-11); |
|
|
5202 |
#endif |
|
|
5203 |
lpszArgs=GetCommandLineA(); |
|
|
5204 |
#ifdef __environ; |
|
|
5205 |
environ=GetEnvironmentStringsA(); |
|
|
5206 |
#endif |
|
|
5207 |
main(); |
|
|
5208 |
ExitProcess(EAX); |
|
|
5209 |
|
|
|
5210 |
Таким образом, в глобальных переменных hThisInst будет находится |
|
|
5211 |
handl запущенного файла, а в lpszArgs адрес командной строки Вашего |
|
|
5212 |
файла. Если Вы в командной строке указали опции /p или /argc или в |
|
|
5213 |
начале вашего файла есть директивы #parsecommandline TRUE или argc TRUE, |
|
|
5214 |
то компилятор создаст дополнительный код сделающий разборку этой |
|
|
5215 |
командной строки на части. Если Вы компилируете консольную программу, то |
|
|
5216 |
в вашей программе будет еще одна глобальная переменная - hStdOut. В этой |
|
|
5217 |
переменной хранится handl стандартного вывода (экрана). Если Вы при |
|
|
5218 |
компиляции программы указали опцию /env, то в глобальной переменной |
|
|
5219 |
environ хранится адрес переменной окружения программы. |
|
|
5220 |
|
|
|
5221 |
После завершения работы процедуры main выполнятся процедура |
|
|
5222 |
ExitProcess, которой в качестве параметра передается регистр EAX. Т.о. |
|
|
5223 |
Вам для завершения работы программы будет достаточно сделать выход из |
|
|
5224 |
процедуры main, предварительно загрузив в регистр EAX нужный Вам код |
|
|
5225 |
возврата. |
|
|
5226 |
|
|
|
5227 |
Некоторые компиляторы создают DLL, в которых имена экспортируемых |
|
|
5228 |
процедур имеют такой формат: |
|
|
5229 |
|
|
|
5230 |
ProcName@8 |
|
|
5231 |
|
|
|
5232 |
В этом имени после символа @ указывается размер стека с |
|
|
5233 |
параметрами, передаваемых процедуре. |
|
|
5234 |
|
|
|
5235 |
Объявлять такие процедуры нужно так: |
|
|
5236 |
|
|
|
5237 |
extern WINAPI "name.dll" |
|
|
5238 |
{ |
|
|
5239 |
ProcName@8 ; |
|
|
5240 |
} |
|
|
5241 |
|
|
|
5242 |
т.е. без круглых скобок. В программе, при обращении к такой процедуре, ее |
|
|
5243 |
имя надо писать без суффикса @8, т.е. вот так - ProcName(param1,param2); |
|
|
5244 |
Return to contents. |
|
|
5245 |
|
|
|
5246 |
|
|
|
5247 |
|
|
|
5248 |
12.8.3 Вызов API процедур по ординалам. |
|
|
5249 |
|
|
|
5250 |
|
|
|
5251 |
В динамически подключаемых библиотеках (DLL) каждой процедуре, кроме |
|
|
5252 |
ее имени, соответствует уникальное число, которое называется ординалом. И |
|
|
5253 |
поэтому, кроме общепринятого вызова API-процедуры по имени, можно делать |
|
|
5254 |
вызов и по ординалу. Теоретически, при использовании вызова по ординалу, |
|
|
5255 |
загрузка файла должна происходить быстрее. Так как в выходной файл не |
|
|
5256 |
будут включены списки имен процедур, вызов которых производится по |
|
|
5257 |
ординалам, то выходной файл может получиться немного меньшим по размеру. |
|
|
5258 |
|
|
|
5259 |
Чтобы компилятор создал файл, использующий вызов API-процедур по |
|
|
5260 |
ординалам, надо сделать две вещи: |
|
|
5261 |
|
|
|
5262 |
1. Разрешить компилятору это делать. Для этого надо в опциях командной |
|
|
5263 |
строки (или в файле C--.INI) указать ключ WO. |
|
|
5264 |
|
|
|
5265 |
2. Сообщить компилятору - какой номер ординала соответствует какому |
|
|
5266 |
имени процедуры. Процедуры, для которых не был указан ординал, будет |
|
|
5267 |
создан вызов по имени. Установить соответствие имен процедур ординалу |
|
|
5268 |
можно двумя способами: |
|
|
5269 |
|
|
|
5270 |
a). Автоматически, с помощью опции командной строки IND=name.dll, |
|
|
5271 |
по которой компилятор просканирует эту библиотеку и импортирует из |
|
|
5272 |
нее все имена и ординалы процедур. (Импорт возможет только из |
|
|
5273 |
библиотек имеющих формат PE). |
|
|
5274 |
|
|
|
5275 |
b). В ручную указать в объявлении API-процедур и ее ординал. Делается |
|
|
5276 |
это так: после имени процедуры ставится точка, а за ней указывается |
|
|
5277 |
номер ординала. Вот пример объявления API-процедуры с указанием ее |
|
|
5278 |
ординала: |
|
|
5279 |
|
|
|
5280 |
extern WINAPI "user32.dll" |
|
|
5281 |
{ |
|
|
5282 |
............ |
|
|
5283 |
long MessageBoxA.429(); |
|
|
5284 |
............ |
|
|
5285 |
} |
|
|
5286 |
|
|
|
5287 |
В библиотеках (DLL), иногда существуют процедуры, для которых не |
|
|
5288 |
указано их имя, но указан номер ординала. Вызов таких процедур по имени |
|
|
5289 |
не возможен, но можно это сделать по ординалу (если, конечно Вы знаете, |
|
|
5290 |
для чего эта процедура и что она делает). Для этого в объявлении |
|
|
5291 |
API-процедуры Вам надо придумать для этой процедуры уникальное имя и |
|
|
5292 |
указать реальный ординал. Затем в программе Вы будете обращаться к этой |
|
|
5293 |
процедуре по вымышленному имени. Но если Вы случайно откомпилируете такой |
|
|
5294 |
файл без ключа WO, то при запуске этой программы Вы получите сообщение, |
|
|
5295 |
о том, что данного имени в библиотеке нет. |
|
|
5296 |
|
|
|
5297 |
К сожалению, нет никаких гарантий того, что номер ординала для данной |
|
|
5298 |
процедуры не изменится при смене версии динамической библиотеки. Поэтому |
|
|
5299 |
использовать ординалы надо осторожно. |
|
|
5300 |
Return to contents. |
|
|
5301 |
|
|
|
5302 |
|
|
|
5303 |
|
|
|
5304 |
12.8.4 Создание DLL под Windows. |
|
|
5305 |
|
|
|
5306 |
|
|
|
5307 |
Динамически подключаемые библиотеки позволят получать более |
|
|
5308 |
компактные программы и ускорить процесс компиляции. К минусам |
|
|
5309 |
использования DLL можно отнести необходимость наличия самих файлов DLL на |
|
|
5310 |
запускаемом компьютере и немного увеличивается время запуска программы. |
|
|
5311 |
|
|
|
5312 |
Для того чтобы процедура стала доступной для других программ надо в |
|
|
5313 |
исходном тексте перед именем процедуры прописать ключевое слово - _export. |
|
|
5314 |
Пример: |
|
|
5315 |
|
|
|
5316 |
void _export testproc() |
|
|
5317 |
{ |
|
|
5318 |
.... |
|
|
5319 |
} |
|
|
5320 |
|
|
|
5321 |
Для того чтобы создать DLL, нужно написать файл, в котором будут |
|
|
5322 |
процедуры с ключевыми словами _export. Вспомогательные процедуры, которые |
|
|
5323 |
могут понадобиться для работы основных экспортируемых процедур, объявлять |
|
|
5324 |
как _export необязательно. Затем этот файл нужно откомпилировать с ключом |
|
|
5325 |
/dll. В результате Вы получите готовую динамически подключаемую |
|
|
5326 |
библиотеку. |
|
|
5327 |
Return to contents. |
|
|
5328 |
|
|
|
5329 |
|
|
|
5330 |
|
|
|
5331 |
12.8.5 Инициализация DLL при загрузке. |
|
|
5332 |
|
|
|
5333 |
|
|
|
5334 |
Иногда, для работы процедур из динамических библиотек (DLL), бывает |
|
|
5335 |
необходимым инициализировать некоторые переменные значениями, зависящими |
|
|
5336 |
от текущего состояния операционной системы, например, получить дескриптор |
|
|
5337 |
этой библиотеки. |
|
|
5338 |
|
|
|
5339 |
Директивой #jumptomain NONE (-j0) управление при запуске передается |
|
|
5340 |
сразу на процедуру main. |
|
|
5341 |
|
|
|
5342 |
Во всех остальных случаях генерируется код заглушки и управление на |
|
|
5343 |
процедуру main не передается. Фактически процедура main в этом случае не |
|
|
5344 |
нужна. |
|
|
5345 |
|
|
|
5346 |
Процедура main при создании файлов DLL должна выглядеть немного иначе, |
|
|
5347 |
чем в других случаях: |
|
|
5348 |
|
|
|
5349 |
dword main ( dword hInstDLL, reason, reserv ) |
|
|
5350 |
{ |
|
|
5351 |
... |
|
|
5352 |
} |
|
|
5353 |
Return to contents. |
|
|
5354 |
|
|
|
5355 |
|
|
|
5356 |
|
|
|
5357 |
12.8.6 Компиляция ресурсов. |
|
|
5358 |
|
|
|
5359 |
|
|
|
5360 |
Встроенный в C-- компилятор ресурсов по своим возможностям уступает |
|
|
5361 |
специализированным компиляторам ресурсов, но этих возможностей, как мне |
|
|
5362 |
кажется, будет достаточно для большинства Ваших задач. |
|
|
5363 |
|
|
|
5364 |
Будет проще перечислить то, что встроенный в C-- компилятор ресурсов |
|
|
5365 |
не умеет делать. Не обрабатываются операторы ресурсов: VERSION, |
|
|
5366 |
VERSIONINFO и определяемые пользователем ресурсы. При необходимости, |
|
|
5367 |
данные, вводимые с помощью этих операторов, можно ввести с помощью |
|
|
5368 |
оператора RCDATA. У многих операторов ресурсов есть необязательные |
|
|
5369 |
параметры loading и 'memory'. Поддержка этих параметров не |
|
|
5370 |
реализована. Встретив эти параметры, компилятор их просто пропустит. |
|
|
5371 |
|
|
|
5372 |
Заставить компилятор C-- обрабатывать ресурсы можно двумя способами: |
|
|
5373 |
|
|
|
5374 |
1. Включить в свой проект директивой #include файл с расширением |
|
|
5375 |
.rc. Файлы с таким расширением компилятор считает файлом с ресурсами. |
|
|
5376 |
Файл ресурсов необходимо включать в Ваш проект лишь после включения |
|
|
5377 |
заголовочных файлов Windows. |
|
|
5378 |
|
|
|
5379 |
2. Ресурсы можно располагать в теле исходного текста программы в |
|
|
5380 |
произвольном месте. Текст ресурсов должен начинаться с директивы #pragma |
|
|
5381 |
resource start, а заканчиваться директивой #pragma resoutce end. |
|
|
5382 |
Ресурсы могут быть разделенными на части и эти части можно располагать в |
|
|
5383 |
любом удобном для Вас месте (глупо располагать ресурсы в блоке |
|
|
5384 |
комментариев и потом удивляться, почему они не были откомпилированы). |
|
|
5385 |
Компилятор соберет эти части и откомпилирует. |
|
|
5386 |
|
|
|
5387 |
Имена операторов можно писать как большими, так и маленькими буквами, |
|
|
5388 |
но имена идентификаторов чувствительны к регистру. В тексте ресурсов |
|
|
5389 |
можно использовать директивы и комментарии. |
|
|
5390 |
|
|
|
5391 |
Ничто не мешает Вам использовать компиляторы ресурсов от других |
|
|
5392 |
языков. Главное, чтобы синтаксис файла ресурсов соответствовал выбранному |
|
|
5393 |
компилятору. |
|
|
5394 |
Return to contents. |
|
|
5395 |
|
|
|
5396 |
|
|
|
5397 |
|
|
|
5398 |
12.9 Выходные файлы для MeOS. |
|
|
5399 |
|
|
|
5400 |
|
|
|
5401 |
Исполняемые файлы для операционной системы MenuetOS поддерживаются |
|
|
5402 |
компилятором совсем недавно. Для того, чтобы откомпилировать файл для |
|
|
5403 |
MenuetOS, нужно в опциях компилятору указать /meos. Вы получите файл без |
|
|
5404 |
расширения, который потом можно будет выполнить в среде операционной |
|
|
5405 |
системы MenuetOS. |
|
|
5406 |
|
|
|
5407 |
Если при компиляции файла Вы не указывали опцию /j0 или не |
|
|
5408 |
использовали директиву #jumptomain NONE, то компилятор будет использовать |
|
|
5409 |
файл начальной инициализации startup.h--, в котором для операционной |
|
|
5410 |
системы MenuetOS создан блок инициализации и завершения программы. |
|
|
5411 |
Завершать выполнение таких программ можно просто выйдя из процедуры main. |
|
|
5412 |
Return to contents. |
|
|
5413 |
|
|
|
5414 |
|
|
|
5415 |
|
|
|
5416 |
13. Приложения. |
|
|
5417 |
|
|
|
5418 |
13.1 Поиск включаемых файлов. |
|
|
5419 |
|
|
|
5420 |
|
|
|
5421 |
Поиск включаемого в вашу программу файла, имя которого объявляется |
|
|
5422 |
директивой include и заключено в двойные кавычки "", производится |
|
|
5423 |
компилятором по такой схеме: |
|
|
5424 |
|
|
|
5425 |
сначала делается попытка открыть файл в текущей директории. Если файла там |
|
|
5426 |
нет, то далее делается попытка открыть файл в директории указанной |
|
|
5427 |
директивой #includepath. Если директива не была задана или файла в этой |
|
|
5428 |
директории не оказалось, то делается попытка открыть файл в директории |
|
|
5429 |
указанной в командной строке командой /ip=path. Если эта команда не была |
|
|
5430 |
задана или файла в указанной директории не оказалось, то делается попытка |
|
|
5431 |
открыть файл в директории указанной в файле C--.INI командой ip=. Если эта |
|
|
5432 |
команда не была задана или файла в указанной директории не оказалось, то |
|
|
5433 |
делается попытка открыть файл в директории, на которую указывает переменная |
|
|
5434 |
окружения C--. Если переменная окружения не была задана или файла в этой |
|
|
5435 |
директории не оказалось, то делается последняя попытка открыть файл в |
|
|
5436 |
директории, откуда был запущен компилятор. |
|
|
5437 |
|
|
|
5438 |
Если имя включаемого файла заключено в угловые скобки < >, то поиск |
|
|
5439 |
этого файла производится в противоположном направлении, за исключением |
|
|
5440 |
того, что поиск в текущей директории не производится. |
|
|
5441 |
|
|
|
5442 |
Для консольной версии компилятора имена главного модуля и включаемых |
|
|
5443 |
файлов могут иметь длину более 8 символов. |
|
|
5444 |
Return to contents. |
|
|
5445 |
|
|
|
5446 |
|
|
|
5447 |
|
|
|
5448 |
13.2 Регистры, которые должны быть сохранены. |
|
|
5449 |
|
|
|
5450 |
|
|
|
5451 |
Регистры, которые должны сохраняться - BP, DI, SI, DS, SS, SP, CS и IP. |
|
|
5452 |
|
|
|
5453 |
BP используется как указатель на локальные и параметрические |
|
|
5454 |
переменные в стеке, что и требует его сохранения. |
|
|
5455 |
|
|
|
5456 |
DI и SI сохранять не обязательно, если программист осознает |
|
|
5457 |
последствия. DI и SI часто используются для индексации массивов, как |
|
|
5458 |
например в формуле: |
|
|
5459 |
|
|
|
5460 |
dog = firehydrant(1,red) + legs[DI]; |
|
|
5461 |
|
|
|
5462 |
Если DI не сохранялся в процедуре firehydrant, значение, присвоенное |
|
|
5463 |
переменной dog, скорее всего, будет неправильным, поскольку индекс для |
|
|
5464 |
массива legs был изменен. В сущности, для точного согласования все |
|
|
5465 |
процедуры должны иметь специальное указание в комментарии на то, что в них |
|
|
5466 |
не сохраняется содержимое регистров DI и/или SI. |
|
|
5467 |
|
|
|
5468 |
DS указывает на сегмент данных, и все операции с глобальными |
|
|
5469 |
переменными пользуются этим значением. |
|
|
5470 |
|
|
|
5471 |
SS хранит сегмент стека и должен сохраняться. SP указывает на текущую |
|
|
5472 |
позицию в стеке и тоже должен сохраняться. |
|
|
5473 |
|
|
|
5474 |
CS хранит сегмент кода программы. Все команды выбираются с |
|
|
5475 |
использованием CS и IP, следовательно их значения должны сохраняться. IP, |
|
|
5476 |
как известно, указатель адреса команды, и CS и IP непосредственно не могут |
|
|
5477 |
изменяться в процессорах 8086, 8088, 80286, 80386, 80486,... |
|
|
5478 |
Return to contents. |
|
|
5479 |
|
|
|
5480 |
|
|
|
5481 |
|
|
|
5482 |
13.3 C--.ini файл. |
|
|
5483 |
|
|
|
5484 |
|
|
|
5485 |
C--.ini файл предназначен для предустановки по умолчанию параметров |
|
|
5486 |
компилятора. |
|
|
5487 |
|
|
|
5488 |
Сейчас компилятор поддерживает огромное число параметров командной |
|
|
5489 |
строки. Правильное их использование позволит Вам получать более компактный |
|
|
5490 |
код и может значительно облегчить Вам отладку программы. Но так как этих |
|
|
5491 |
параметров очень много набирать их каждый раз в командной строке бывает |
|
|
5492 |
утомительно и не исключена возможность пропустить какой-нибудь параметр. |
|
|
5493 |
Чтобы избавить Вас от всех этих напастей и был введен c--.ini файл. |
|
|
5494 |
|
|
|
5495 |
Параметры командной строки прописываются в этом файле построчно. |
|
|
5496 |
Синтаксис тот же, что и в командной строке, но без ведущего обратного слэша |
|
|
5497 |
или минуса. Если файл расположен в директории, на которую указывает |
|
|
5498 |
переменная окружения set c--= или если эта переменная не определена, |
|
|
5499 |
то в той же директории где и файл c--.exe, то эти параметры |
|
|
5500 |
распространяются на все компилируемые программы. Если же файл c--.ini |
|
|
5501 |
расположен в текущей директории, то параметры считываются только из этого |
|
|
5502 |
файла и действуют только для текущего проекта. |
|
|
5503 |
|
|
|
5504 |
Допустимо использование комментариев. Признаком начала комментария |
|
|
5505 |
является символ ;. Все последующие символы после ; и до конца строки |
|
|
5506 |
считаются комментарием. |
|
|
5507 |
|
|
|
5508 |
Пример C--.ini файла: |
|
|
5509 |
|
|
|
5510 |
r- |
|
|
5511 |
X |
|
|
5512 |
3 ;это комментарий |
|
|
5513 |
os |
|
|
5514 |
|
|
|
5515 |
ini-файл может иметь любое имя (но расширение должно быть обязательно |
|
|
5516 |
ini). Имя этого файла с расширением должно быть передано компилятору в |
|
|
5517 |
командной строке. Файл c--.ini загружается и обрабатывается автоматически |
|
|
5518 |
до загрузки файла указанного в командной строке. |
|
|
5519 |
|
|
|
5520 |
Таким образом, файл *.ini можно использовать подобно make-файлу - в нем |
|
|
5521 |
Вы можете указать и имя главного компилируемого модуля, и все необходимые |
|
|
5522 |
для его компиляции настройки. |
|
|
5523 |
|
|
|
5524 |
Как альтернативу c--.ini файлу, параметры командной строки можно |
|
|
5525 |
прописывать непосредственно в начале главного файла компилируемого проекта, |
|
|
5526 |
используя директиву pragma option. С одной стороны это обеспечит Вашему |
|
|
5527 |
проекту независимость от настроек компилятора, если Ваш проект будет |
|
|
5528 |
компилироваться на другом компьютере. Но с другой стороны некоторые |
|
|
5529 |
настройки являются индивидуальными для данного компьютера (это расположение |
|
|
5530 |
библиотек, имена и расположение stub-файлов). Какой вариант использовать |
|
|
5531 |
решать Вам, но как говорят, и я с этим согласен, лучше пользоваться золотой |
|
|
5532 |
серединой - Часть параметров прописать в c--.ini файле, а другую |
|
|
5533 |
непосредственно в компилируемом файле. |
|
|
5534 |
Return to contents. |
|
|
5535 |
|
|
|
5536 |
|
|
|
5537 |
|
|
|
5538 |
13.4 startup.h-- файл. |
|
|
5539 |
|
|
|
5540 |
|
|
|
5541 |
В этом файле находятся исходные тексты, которые компилируются |
|
|
5542 |
компилятором в код начальной инициализации файла, для всех поддерживаемых |
|
|
5543 |
компилятором типов выходных файлов. Этот файл должен находится либо в |
|
|
5544 |
директории вместе с компилятором, либо в директории с библиотечными файлами. |
|
|
5545 |
Этот файл включается компилятором в проект автоматически, а включение его |
|
|
5546 |
директивой include может привести к нежелательным результатам. |
|
|
5547 |
|
|
|
5548 |
В блоке начальной инициализации программы может производится (если Вы |
|
|
5549 |
это укажете с помощью опций командной строки или используя директивы), |
|
|
5550 |
разбор командной строки на параметры, сохранение переменой окружения, |
|
|
5551 |
поддержка работы процедуры ATEXIT, изменение размера доступной памяти для |
|
|
5552 |
*.com файлов и многие другие подготовительные операции. Если Вы |
|
|
5553 |
откомпилируете свой файл не используя никаких опций командной строки и у |
|
|
5554 |
Вас будет отсутствовать c--.ini файл, а в самом компилируемом файле у Вас |
|
|
5555 |
будут отсутствовать директивы, то при компиляции *.com файла в него будет |
|
|
5556 |
включен блок изменяющий размер доступной памяти и сигнатура SPHINXC--. |
|
|
5557 |
|
|
|
5558 |
Если Вы компилируете файл типа *.exe (кроме файла модели tiny для DOS) |
|
|
5559 |
и используете директиву jumptomain NONE или ключ командной строки /j0, |
|
|
5560 |
то для этого проекта файл startup.h-- компилятором не используется. Не |
|
|
5561 |
используется этот файл также при компиляции *.com файлов если, кроме /j0, |
|
|
5562 |
в этом проекте не используется разбор командной строки (/p /argc), не |
|
|
5563 |
применяется процедура ATEXIT (/at), не используется адрес переменной |
|
|
5564 |
окружения (/env), не используется очистка области post-адресов (/cpa), не |
|
|
5565 |
используется уменьшение доступной программе памяти (/r) и не используется |
|
|
5566 |
заглушка нажатий CTRL-C (/c). |
|
|
5567 |
|
|
|
5568 |
Кроме блока начальной инициализации программы в файле startup.h-- |
|
|
5569 |
находятся динамические процедуры: |
|
|
5570 |
|
|
|
5571 |
void CLEARPOSTAREA( (E)AX ); - очистка post-области данных. |
|
|
5572 |
unsigned int PARAMSTR( ECX ); - получить адрес элемента командной строки |
|
|
5573 |
unsigned int PARAMCOUNT(); - получить число элементов в командной строке |
|
|
5574 |
|
|
|
5575 |
При разборе командной строки на составляющие ее элементы для 32-битных |
|
|
5576 |
программ реализована поддержка длинных имен. Для 16-битных программ |
|
|
5577 |
поддержка разбора командной строки с учетом длинных имен подключается, если |
|
|
5578 |
Вы в начале свой программы укажете директиву: |
|
|
5579 |
|
|
|
5580 |
#define _USELONGNAME TRUE |
|
|
5581 |
|
|
|
5582 |
либо в c--.ini файле или в командной строке компилятора укажете опцию |
|
|
5583 |
d=_USELONGNAME. |
|
|
5584 |
Return to contents. |
|
|
5585 |
|
|
|
5586 |
|
|
|
5587 |
|
|
|
5588 |
13.5 mainlib.ldp файл. |
|
|
5589 |
|
|
|
5590 |
|
|
|
5591 |
В этом файле находится большое число процедур из основной библиотеки |
|
|
5592 |
компилятора в уже откомпилированном виде. Все процедуры откомпилированы в |
|
|
5593 |
4-х различных режимах оптимизации. В этот файл также вынесены многие |
|
|
5594 |
процедуры, которые ранее были внутри компилятора. Использование ранее |
|
|
5595 |
откомпилированных процедур повышает скорость компиляции. |
|
|
5596 |
|
|
|
5597 |
Эти процедуры откомпилированы только для 16-битного режима работы |
|
|
5598 |
программы. Если Вы будете использовать эти процедуры в 32-битной программе, |
|
|
5599 |
то компилятор на это не выдаст никаких сообщений и включит эту процедуру в |
|
|
5600 |
Ваш код. Но при запуске такой программы она неизбежно потерпит крах. |
|
|
5601 |
|
|
|
5602 |
Использовать эту библиотеку очень просто. Все что нужно, это |
|
|
5603 |
расположить эту библиотеку в одной с компилятором директории. Тогда |
|
|
5604 |
компилятор, если встретит в вашей программе вызов процедуры, которая не |
|
|
5605 |
была определена ни во включаемых в программу библиотечных файлах, ни в |
|
|
5606 |
вашей программе, будет искать эту процедуру в файле mainlib.ldp. Если эта |
|
|
5607 |
процедура будет найдена в этом файле, то ее код будет перенесен в Ваш файл, |
|
|
5608 |
иначе будет выдано сообщение о неизвестной процедуре. Таким образом, чтобы |
|
|
5609 |
процедура была вставлена в вашу программу из библиотеки mainlib.ldp Вам |
|
|
5610 |
нужно в свою программу не включать библиотечный файл, содержащий процедуру с |
|
|
5611 |
таким же именем. |
|
|
5612 |
|
|
|
5613 |
Список процедур находящихся в этой библиотеке можно получить с помощью |
|
|
5614 |
специальной программы cmmlib.exe. Эту программу можно найти в архиве |
|
|
5615 |
cmmlib.rar. Извлеките программу cmmlib.exe из этого архива и расположите ее |
|
|
5616 |
в одной с компилятором директории. Затем запустите эту программу с ключом |
|
|
5617 |
/L и Вы получите список процедур находящихся в этой библиотеке. |
|
|
5618 |
Return to contents. |
|
|
5619 |
|
|
|
5620 |
|
|
|
5621 |
|
|
|
5622 |
13.6 C-- символы. |
|
|
5623 |
|
|
|
5624 |
|
|
|
5625 |
SYMBOL|FUNCTION |EXAMPLE |
|
|
5626 |
-------------------------------------------------------------------- |
|
|
5627 |
/* |начинают блок комментария |/* комментарий */ |
|
|
5628 |
*/ |завершают блок комментария|/* комментарий */ |
|
|
5629 |
| | |
|
|
5630 |
// |комментарий до конца линии|// комментарий |
|
|
5631 |
| | |
|
|
5632 |
= |присвоение |AX = 12; |
|
|
5633 |
+ |сложение |AX = BX + 12; |
|
|
5634 |
- |вычитание |house = dog - church; |
|
|
5635 |
* |умножение или указатель |x = y * z; AL = * var; |
|
|
5636 |
/ |деление |x1 = dog / legs; |
|
|
5637 |
& |поразрядное логическое И |polution = stupid & pointless; |
|
|
5638 |
| |поразрядное логическое ИЛИ|yes = i | mabe; |
|
|
5639 |
^ |поразрядн. исключающее ИЛИ|snap = got ^ power; |
|
|
5640 |
<< |битовый сдвиг влево |x = y << z; |
|
|
5641 |
>> |битовый сдвиг вправо |x = y >> z; |
|
|
5642 |
| | |
|
|
5643 |
+= |сложение |fox += 12; // fox = fox +12; |
|
|
5644 |
-= |вычитание |cow -= BX; // cow = cow - BX; |
|
|
5645 |
*= |умножение |a *= b; // a = a * b; |
|
|
5646 |
/= |деление |a /= b; // a = a / b; |
|
|
5647 |
&= |поразрядное логическое И |p &= q; // p = p & q; |
|
|
5648 |
|= |поразрядное логическое ИЛИ|p |= z; // p = p | z; |
|
|
5649 |
^= |поразрядн. исключающее ИЛИ|u ^= s; // u = u ^ s; |
|
|
5650 |
<<= |битовый сдвиг влево |x <<= z; // x = x << z |
|
|
5651 |
>>= |битовый сдвиг вправо |x >>= z; // x = x >> z |
|
|
5652 |
| | |
|
|
5653 |
>< |обмен значениями |x >< y; /* меняет местами значения x и y */ |
|
|
5654 |
| | |
|
|
5655 |
== |проверка на равенство |IF(AX == 12) |
|
|
5656 |
> |проверка на больше чем |IF(junk > BOGUS) |
|
|
5657 |
< |проверка на меньше чем |if( x < y ) |
|
|
5658 |
>= |проверка больше или равно |if(AX >= 12) |
|
|
5659 |
<= |проверка меньше или равно |IF(BL >= CH) |
|
|
5660 |
!= |проверка на неравенство |IF(girl != boy) |
|
|
5661 |
<> |проверка на отличие |if (cat<>dog) /* та же функция что != */ |
|
|
5662 |
| | |
|
|
5663 |
@ |вставка кода |@ COLDBOOT(); /* вставляет COLDBOOT код */ |
|
|
5664 |
: |динамическая процедура |: functionname () //объявляет functionname |
|
|
5665 |
$ |ассемблерная команда |$ PUSH AX /* заносит AX в стек */ |
|
|
5666 |
# |получение адреса(смещения)|loc = #cow; /* loc = address of cow */ |
|
|
5667 |
|или директива | #resize FALSE |
|
|
5668 |
! |оператор NOT или смена |!x_var; if(!proc()) |
|
|
5669 |
|флага операции сравнения. | |
|
|
5670 |
... |любое число параметров в | void proc(...); |
|
|
5671 |
:: |разрешение видимости | ::var=0; |
|
|
5672 |
Return to contents. |
|
|
5673 |
|
|
|
5674 |
|
|
|
5675 |
|
|
|
5676 |
|
|
|
5677 |
|
|
|
5678 |
|
|
|
5679 |
|
|
|
5680 |
|