* C for Dummies * Aleph * 2000-10-02 * 012 * -----[00]----------[Quote of Day]----------------------------------------- No man is rich enough to buy back his past. Oscar Wilde -----[01]----------[Hot News]--------------------------------------------- Лучшая, из известных мне, утилита для визуального сравнения файлов. Необходимый инструмент для любого серьезного программиста. ExamDiff Pro is a Windows 95/98 and Windows NT/2000 tool for visual file and directory comparison. It has a number of simple and convenient features that many users have been asking for a long time from a comparison tool. http://members.nbci.com/c4dummies/files/diff26b1.zip (392,987) Превосходный HEX-вьювер, редактор, дизассемблер. Незаменимый инструмент для тех, кто хочет знать "а что там, внутри ..." HIEW release VI ---------------------------------------- * executable files for Win32, OS/2 & DOS * view files any length in text, hex, decode modes * Pentium-III(R) & AMD 3DNow!(R) dissasembler & assembler * support newexecutable format file NE,LE,LX,PE * support Netware Loadable Modules NLM,DSK,LAN,... * one-touch-jump for direct call/jmp instructions in any executable file * build-in small decrypt/crypt system * build-in powerful calculator * any length block operations: read, write, fill, copy, move, insert, delete * multifile search/replace * edit of NewExecutables headers http://members.nbci.com/c4dummies/files/hiew655.zip (565,452) Очень старая, но все еще очень полезная гипертекстовая справочная система по BIOS/DOS/PC/Asm. Если Вы пишите программы под DOS, Вы не сможете обойтись без нее. Welcome to TECH Help! The Electronic Technical Reference Manual TECH Help! is a reference tool for programmers - a fully-indexed hypertext source of technical facts, topics, and data layouts, instantly available from your program editor or debugger. Its compressed data file contains over 500 printed pages of text; the boiled-down essence of the most often needed information found in the DOS Technical Reference and PC,AT,and PS/2 BIOS and Hardware References. http://members.nbci.com/c4dummies/files/techelp60.zip (760,111) -----[02]----------[Blah-Blah-Blah]--------------------------------------- Веселости из FidoNet http://www.citycat.ru/funny/fido/ * Запечатал : Rainbow ------------------------ Безотносительно темы разговора, просто для ознакомления господам эхо-переводчикам. Наткнулся как-то на днях на забавное изречение: Пеpевод - как женщина. Если кpасив, то неверен, если верен, то некрасив. -*--------------------------------------------------------------------- Eugene Dolgodvorov. Home Page: http://www.citycat.ru/~deb/ Должен сказать, что программируя на языке высокого уровня (таком как С), нечасто испытываешь потребность в карте памяти, ассемблерном листинге или шестнадцатеричном дампе. Создается опасная оторванность от реального кода - программист начинает думать высокоуровневыми абстракциями (классы, каркасные библиотеки) и, к сожалению, забывает _как_ это работает. А результат - тот ужасный многомегабайтный код, который мы встречаем повседневно и повсеместно. Безусловно, Ассемблер никогда больше не станет "массовым" языком - эпоха "профи" прошла, программирование превратилось в массовую профессию и пока удвоение емкости диска и быстродействия процессора будет обходиться дешевле повышения квалификации разработчиков, "хорошего" кода ждать не приходится. Но подобно тому, как сохраняются заповедники дикой природы, мне хочется сохранить для Вас дух тех времен, когда компьютеры были большими, а программы маленькими; когда плохого кода просто не было - потому что он не помещался в несколько килобайт оперативной памяти; когда считали каждый такт процессора, потому что 100 kHz было "приличной" частотой для процессора; и когда программы были действительно хороши, потому что люди вкладывали в них душу, а не продавали ее дьяволу. -----[03]----------[Topic]------------------------------------------------ Declarations & Initializations. -----[04]----------[Body]------------------------------------------------- Declarations & Initializations ------------------------------ Язык C допускает определение (definition) одновременно с декларацией. Иными словами, Вы можете присвоить переменной значение (сделать ее определенной, имеющей определенное значение) прямо в момент объявления. Больше того - хороший стиль программирования требует, чтобы Вы всегда поступали именно таким образом ! Пока переменная неопределенна, ее содержимое произвольно - в таких случаях говорят, что она содержит "мусор" (garbage). Присваивание переменным определенных значений при объявлении позволяет избежать как мусора в Ваших программах, так и необъяснимых эффектов при отладке. _Что_ следует присваивать переменным ? Хороший вопрос ... Если в момент объявления у Вас еще нет представления о том, что должна содержать переменная, то обычной практикой является присваивание нуля - очистка. Возможно, в каких-то других случаях Вы захотите использовать другие значения, например, пробел для строковых переменных или, скажем, ассемблерную команду NOP (код 90h). Утилита DOS Format.com, к примеру, заливала неиспользованные области диска кодом F6, а отладочная версия MSVC заливает неинициализированную память кодом CDh. Любое подходящее значение может служить для Вас сигналом опасности, подобно красному свету светофора, но 0 - самое простое, самое удобное и общепринятое решение. С учетом сказанного, слегка модифицируем нашу программу: /* ****************************************************************** ** @@ DECL2 - A program for testing Declarations ** ****************************************************************** */ void main() { unsigned char c1 = '\0'; signed char c2 = '\0'; char c3 = '\0'; unsigned short s1 = 0; signed short s2 = 0; short s3 = 0; unsigned int i1 = 0; signed int i2 = 0; int i3 = 0; unsigned long l1 = 0L; signed long l2 = 0L; long l3 = 0L; float f = 0.0F; double d = 0.0; long double ld = 0.0L; } /* ****************************************************************** ** @@ The End ** ****************************************************************** */ Скомпилируем ее. Я воспользовался консольной версией компилятора для получения ассемблерного листинга. Прежде всего, мы получим значительное число предупреждений (warnings) - это сообщения об ошибках не представляющих, по мнению компилятора, серьезной опасности. Тем не менее, будьте всегда внимательны к любому диагностическому сообщению - компиляторы пишутся очень неглупыми людьми, и, если они посчитали необходимым Вас предостеречь, - не пренебрегайте их знаниями и опытом. Кроме того, нередко, за безобидным, на первый взгляд, предупреждением может таиться серьезная ошибка в коде. Будьте внимательны. Как первый шаг отладки, Ваша программа должна проходить компиляцию без выдачи диагностических сообщений - Zero Warning Level ! Итак, о чем же уведомил нас компилятор ? Turbo C Version 2.01 Copyright (c) 1987, 1988 Borland International decl2.c: Warning decl2.c 26: 'ld' is assigned a value which is never used in function main Warning decl2.c 26: 'd' is assigned a value which is never used in function main Warning decl2.c 26: 'f' is assigned a value which is never used in function main Warning decl2.c 26: 'l3' is assigned a value which is never used in function main Warning decl2.c 26: 'l2' is assigned a value which is never used in function main Warning decl2.c 26: 'l1' is assigned a value which is never used in function main Warning decl2.c 26: 'i3' is assigned a value which is never used in function main Warning decl2.c 26: 'i2' is assigned a value which is never used in function main Warning decl2.c 26: 'i1' is assigned a value which is never used in function main Warning decl2.c 26: 's3' is assigned a value which is never used in function main Warning decl2.c 26: 's2' is assigned a value which is never used in function main Warning decl2.c 26: 's1' is assigned a value which is never used in function main Warning decl2.c 26: 'c3' is assigned a value which is never used in function main Warning decl2.c 26: 'c2' is assigned a value which is never used in function main Warning decl2.c 26: 'c1' is assigned a value which is never used in function main Обратите внимание на забавную деталь - порядок выдачи сообщений об ошибках прямо противоположен порядку объявления переменных - это объясняется особенностями алгоритма грамматического разбора (в данном компиляторе). Теперь посмотрим в ассемблерный листинг. На этот раз он намного содержательнее. > ifndef ??version ; Информация для отладчика > ?debug macro > endm > endif > ?debug S "decl2.c" > _TEXT segment byte public 'CODE' ; Описания программных сегментов. > DGROUP group _DATA,_BSS > assume cs:_TEXT,ds:DGROUP,ss:DGROUP > _TEXT ends > _DATA segment word public 'DATA' > d@ label byte > d@w label word > _DATA ends > _BSS segment word public 'BSS' > b@ label byte > b@w label word > ?debug C E94EBA3829076465636C322E63 > _BSS ends > _TEXT segment byte public 'CODE' ; Начало сегмента кода. > ; ?debug L 5 // Здесь начинается собственно наш код. > _main proc near > push bp ; Стандартный пролог. > mov bp,sp ; Указатель на базу сохраняется ; в стеке. Указатель на стек ; становится новым базовым адресом. ; Говорят что создан новый кадр ; (frame) стека. Вся дальнейшая ; драматургия разворачивается ; в этом кадре. > sub sp,50 ; Выделяется 50 байт в стеке ; для хранения локальных переменных. > ; ?debug L 7 ; Инициализируются три переменные > mov byte ptr [bp-49],0 ; символьного типа (char) имеющие > ; ?debug L 8 ; размер 1 байт > mov byte ptr [bp-48],0 ; Строки вида '?debug L #' указывают > ; ?debug L 9 ; на строчку исходного текста, которой > mov byte ptr [bp-47],0 ; соответствует данный код. > ; ?debug L 11 > mov word ptr [bp-46],0 ; Инициализируются три переменные > ; ?debug L 12 ; целого типа (short) имеющие размер > mov word ptr [bp-44],0 ; 2 байта (одно слово - word). > ; ?debug L 13 > mov word ptr [bp-42],0 > ; ?debug L 15 > mov word ptr [bp-40],0 ; Инициализируются три переменные > ; ?debug L 16 ; целого типа (int) имеющие размер > mov word ptr [bp-38],0 ; 2 байта (одно слово - word). > ; ?debug L 17 > mov word ptr [bp-36],0 > ; ?debug L 19 > mov word ptr [bp-32],0 ; Инициализируются три переменные > mov word ptr [bp-34],0 ; целого типа (long) имеющие размер > ; ?debug L 20 ; 4 байта (два слово - dword). > mov word ptr [bp-28],0 ; Инициализация производится засылкой > mov word ptr [bp-30],0 ; двух последовательных слов. > ; ?debug L 21 > mov word ptr [bp-24],0 > mov word ptr [bp-26],0 > ; ?debug L 23 ; Обратите внимание на очень характерный > xor dx,dx ; трюк: обнуление регистра выполняется не > xor ax,ax ; с использованием "длинной" команды MOV, > mov word ptr [bp-20],dx ; а с помощью "короткой" инструкции > mov word ptr [bp-22],ax ; процессора - сложение по модулю 2. ; Как Вы, несомненно, помните, сложение ; числа с самим собой по модулю 2 (операция ; "неравнозначность") неизбежно дает 0. ; Таким образом, короткая команда ; 'xor register,register' выполняет очень ; быстрое (точнее, самое быстрое) обнуление ; регистра, экономя как процессорное время, ; так и оперативную память. ; Затем регистровая пара ax,dx - они ; постоянно встречаются в таком сочетании ; и, вспоминая О'Генри, "неразлучны как ; два кладбищенских вора" - используется ; для установки значения переменной ; плавающего типа (float), размером 4 байта. > ; ?debug L 24 ; Здесь выполняется очистка переменной > mov word ptr [bp-12],0 ; плавающего типа (double) размером 8 > mov word ptr [bp-14],0 ; байт. Очистка выполняется обнулением > mov word ptr [bp-16],0 ; четырех последовательных слов - обратите > mov word ptr [bp-18],0 ; внимание на рост смещений в стеке. > ; ?debug L 25 ; Wow ! > FLD tbyte ptr DGROUP:s@ ; Три инструкции сопроцессора (для > FSTP tbyte ptr [bp-10] ; работы с плавающей точкой) и указатель > FWAIT ; на десятибайтовое целое, хранящееся > @1: ; в сегменте данных. ; Последняя инструкция (FWAIT) переводит ; процессор в режим ожидания ответа от ; сопроцессора. Если у Вас Pentium, то ; это будет простой формальностью, но ; во времена Turbo XT / 4.77 MHz и ; программной эмуляции чертовски дорогого ; сопроцессора 8087 это ожидание было ; довольно значительным. ; На примере этого совершено элементарного кода можно видеть, как сильно ; влияет выбранная разрядность (тип) операндов на объем кода и ; быстродействие программы. Я не буду в дальнейшем анализировать подобным ; образом каждую строчку кода - но всегда помните об этом. Ваша программа ; будет тем лучше, чем больше Вы из нее выбросите ! > ; ?debug L 26 > mov sp,bp ; Стандартный эпилог. Восстанавливается > pop bp ; сохраненный указатель на стек. Из > ret ; стека выталкивается сохраненный ; указатель на базу. Выполняется возврат. > _main endp ; А здесь наш код "официально" закончился. > _TEXT ends ; Конец сегмента кода. > ?debug C E9 > _DATA segment word public 'DATA' ; Начало сегмента данных > s@ label byte > db 0 ; Не поленитесь посчитать количество > db 0 ; нулевых байт - их ровно десять. > db 0 ; Они используются подпрограммой > db 0 ; установки значения длинного > db 0 ; плавающего числа (long double) > db 0 ; размером 10 байт (80 bits). > db 0 ; Hell ! > db 0 ; 10 байт памяти, библиотечная > db 0 ; функция, вызов сопроцессора - > db 0 ; точность обходится недешево. > _DATA ends ; Конец сегмента данных. > extrn FIDRQQ:far ; Вызов библиотечной функции > _TEXT segment byte public 'CODE' ; Обратите внимание на модификатор > _TEXT ends ; far - это дальний (межсегментный) ; вызов. > public _main ; Конец программы > end Для полноты картины, изменим инициализирующее значение и снова перекомпилируем программу. /* ****************************************************************** ** @@ DECL3 - A program for testing Declarations ** ****************************************************************** */ void main() { unsigned char c1 = '\xFF'; signed char c2 = '\xFF'; char c3 = '\xFF'; unsigned short s1 = 255; signed short s2 = 255; short s3 = 255; unsigned int i1 = 255; signed int i2 = 255; int i3 = 255; unsigned long l1 = 255L; signed long l2 = 255L; long l3 = 255L; float f = 255.255F; double d = 255.255; long double ld = 255.255L; } /* ****************************************************************** ** @@ The End ** ****************************************************************** */ На этот раз я не стану ее комментировать - все строчки остались на тех же местах - разберитесь самостоятельно. Обратите особое внимание на инициализацию знаковых и беззнаковых переменных того же самого типа. Сравните инициализацию типов различной точности (разрядности) тем же самым начальным значением. > ifndef ??version > ?debug macro > endm > endif > ?debug S "decl3.c" > _TEXT segment byte public 'CODE' > DGROUP group _DATA,_BSS > assume cs:_TEXT,ds:DGROUP,ss:DGROUP > _TEXT ends > _DATA segment word public 'DATA' > d@ label byte > d@w label word > _DATA ends > _BSS segment word public 'BSS' > b@ label byte > b@w label word > ?debug C E97B023A29076465636C332E63 > _BSS ends > _TEXT segment byte public 'CODE' > ; ?debug L 5 > _main proc near > push bp > mov bp,sp > sub sp,50 > ; ?debug L 7 > mov byte ptr [bp-49],255 > ; ?debug L 8 > mov byte ptr [bp-48],-1 > ; ?debug L 9 > mov byte ptr [bp-47],-1 > ; ?debug L 11 > mov word ptr [bp-46],255 > ; ?debug L 12 > mov word ptr [bp-44],255 > ; ?debug L 13 > mov word ptr [bp-42],255 > ; ?debug L 15 > mov word ptr [bp-40],255 > ; ?debug L 16 > mov word ptr [bp-38],255 > ; ?debug L 17 > mov word ptr [bp-36],255 > ; ?debug L 19 > mov word ptr [bp-32],0 > mov word ptr [bp-34],255 > ; ?debug L 20 > mov word ptr [bp-28],0 > mov word ptr [bp-30],255 > ; ?debug L 21 > mov word ptr [bp-24],0 > mov word ptr [bp-26],255 > ; ?debug L 23 > mov dx,17279 > mov ax,16712 > mov word ptr [bp-20],dx > mov word ptr [bp-22],ax > ; ?debug L 24 > mov word ptr [bp-12],16495 > mov word ptr [bp-14],-6104 > mov word ptr [bp-16],-2622 > mov word ptr [bp-18],-28836 > ; ?debug L 25 > FLD tbyte ptr DGROUP:s@ > FSTP tbyte ptr [bp-10] > FWAIT > @1: > ; ?debug L 26 > mov sp,bp > pop bp > ret > _main endp > _TEXT ends > ?debug C E9 > _DATA segment word public 'DATA' > s@ label byte > db 72 > db 225 > db 122 > db 20 > db 174 > db 71 > db 65 > db 255 > db 6 > db 64 > _DATA ends > extrn FIDRQQ:far > _TEXT segment byte public 'CODE' > _TEXT ends > public _main > end Используя ассемблерный листинг, построим карту фрейма стека. В данном случае, я буду использовать десятичные смещения. +--------+--------+-------------------+------+-------+ | Offset | Value | Variable | Size | Name | +--------+--------+-------------------+------+-------+ | | | | | | Previous Stack Frame | | | | | | +--------+--------+-------------------+------+-------+ | - 0001 | 64 | | | | | - 0002 | 6 | | | | | - 0003 | 255 | | | | | - 0004 | 65 | | | | | - 0005 | 71 | | | | | - 0006 | 174 | | | | | - 0007 | 20 | | | | | - 0008 | 122 | | | | | - 0009 | 255 | | | | | - 0010 | 72 | long double | 10 | ld | +--------+--------+-------------------+------+-------+ | - 0011 | | | | | | - 0012 | 16495 | | | | +--------+--------+ | | | | - 0013 | | | | | | - 0014 | -6104 | | | | +--------+--------+ | | | | - 0015 | | | | | | - 0016 | -2622 | | | | +--------+--------+ | | | | - 0017 | | | | | | - 0018 | -28836 | double | 8 | d | +--------+--------+-------------------+------+-------+ | - 0019 | | | | | | - 0020 | 17279 | | | | +--------+--------+ | | | | - 0021 | | | | | | - 0022 | 16712 | float | 4 | f | +--------+--------+-------------------+------+-------+ | - 0023 | | | | | | - 0024 | 0 | | | | +--------+--------+ | | | | - 0025 | | | | | | - 0026 | 255 | long | 4 | l3 | +--------+--------+-------------------+------+-------+ | - 0027 | | | | | | - 0028 | 0 | | | | +--------+--------+ | | | | - 0029 | | | | | | - 0030 | 255 | signed long | 4 | l2 | +--------+--------+-------------------+------+-------+ | - 0031 | | | | | | - 0032 | 0 | | | | +--------+--------+ | | | | - 0033 | | | | | | - 0034 | 255 | unsigned long | 4 | l1 | +--------+--------+-------------------+------+-------+ | - 0035 | | | | | | - 0036 | 255 | int | 2 | i3 | +--------+--------+-------------------+------+-------+ | - 0037 | | | | | | - 0038 | 255 | signed int | 2 | i2 | +--------+--------+-------------------+------+-------+ | - 0039 | | | | | | - 0040 | 255 | unsigned int | 2 | i1 | +--------+--------+-------------------+------+-------+ | - 0041 | | | | | | - 0042 | 255 | short | 2 | s3 | +--------+--------+-------------------+------+-------+ | - 0043 | | | | | | - 0044 | 255 | signed short | 2 | s2 | +--------+--------+-------------------+------+-------+ | - 0045 | | | | | | - 0046 | 255 | unsigned short | 2 | s1 | +--------+--------+-------------------+------+-------+ | - 0047 | -1 | char | 1 | c3 | +--------+--------+-------------------+------+-------+ | - 0048 | -1 | signed char | 1 | c2 | +--------+--------+-------------------+------+-------+ | - 0049 | 255 | unsigned char | 1 | c1 | <<--- TOS +--------+--------+-------------------+------+-------+ | | | | | | ( Top of Stack ) Free Space Обратите внимание, что стек заполнен плотно, без зазоров. Как Вы помните, стек всегда растет вниз, а на верхушке стека находится последнее введенное значение. Но здесь мы видим совершенно обратное - идентификаторы втолкнуты в стек в порядке, обратном их появлению в программе. Это позволяет предположить, что фрейм стека был выстроен "задним числом", после анализа всего блока и оптимизации вычислений. Это подтверждается и диагностическими сообщениями компилятора - переменные, для которых не был выставлен флаг использования, печатались в порядке, обратном их появлению в программе. > Warning decl2.c 26: 'Variable' is assigned a value which is never used in function <FuncName> Вот так и получилось, что первым было выдано предупреждение для переменной ld, и последним - для c1. -----[05]----------[Homework]--------------------------------------------- Цель: дать Вам попробовать "на вкус" собственный код. Вам потребуются Hex-редактор и дизассемблер. Рекомендую Hiew - все "в одном флаконе". Рассмотрите дамп программы. Можете ли Вы зрительно разбить его на секции ? Определить их назначение ? Перейдите в режим дизассемблера. Отыщите Ваш собственный код и попробуйте понять, к чему относится все остальное, зачем оно нужно и почему _его_ так много :-) -----[06]----------[Glossary]--------------------------------------------- References from TECH Help! The Electronic Technical Reference Manual: MOV dest,src transfer (copy) data to/from register, to/from memory XOR dest,src exclusive OR (toggle dest bits which are 1s in src) FLD src Load real: st(0) <- src (mem32/mem64/mem80) FSTP dest Store real: dest <- st(0) (mem32/64/80); pop stack WAIT/FWAIT Synchronize FPU & CPU: Halt CPU until FPU finishes current opcode -----[07]----------[Info]------------------------------------------------- Вопросы и замечания принимаются по адресу: aleph@canada.com Предыдущие выпуски доступны со страницы архива: http://www.subscribe.ru/archive/comp.prog.c4dummies/ или (в упакованном виде): http://members.nbci.com/c4dummies/archive/000.zip . . . http://members.nbci.com/c4dummies/archive/011.zip Рассылка дублируется (только текстовый вариант) с сервера www.egroups.com Подписка: c4d-subscribe@egroups.com -----[--]----------[The End]----------------------------------------------

© Gazlan 2009 * gazlan@yandex.ru

Hit Counter