Начало программирования в Виндовс 9
BACK TO THE MSVC
Каждый компилятор представляет собой топкое болото, малознакомое даже самим господам аффторам этого компилятора. Какие-либо умозрительные заключения, предположения и догадки, относительно его(компилятора) внутреннего устройства, работы или поведения, недопустимы. Всё требуется знать наверняка. Компиляторов C/C++, это касается особенно.
Я уже рассказывал, как мне однажды не повезло с MSVC. Естественно, я с этим давно разобрался и сейчас обо всём доложу. Но для начала, протестируем вот такой пример.
//stupid_msvc_tcc.c (c) Oleg E Tereshkov 2020
#define STR_ME "(c) Oleg E Tereshkov 2020\n\
http://sites.google.com/site/excelmidi\n\
Welcome Universal Student IDE for all compilers\n\n"
extern int printf();
int main(void){
printf(STR_ME);printf(STR_ME);printf(STR_ME);
printf(STR_ME);printf(STR_ME);printf(STR_ME);
printf(STR_ME);printf(STR_ME);printf(STR_ME);
printf(STR_ME);printf(STR_ME);printf(STR_ME);
printf(STR_ME);printf(STR_ME);printf(STR_ME);
printf(STR_ME);printf(STR_ME);printf(STR_ME);
printf(STR_ME);printf(STR_ME);printf(STR_ME);}
Компилируем в MSVC, запускаем. Всё работает и даже без проблем. А теперь, берём Total Commander и по кнопочке F3 пытаемся заглянуть во внутренний мир исполняемого файла.
И что же мы там видим? Стринг STR_ME продублирован 21 раз. Не будем сейчас обсуждать на сколько это разумно, гуманно, правильно, нормально, оправданно или оптимально(если кто помнит, MSVC оптимизирующий компилятор :) - просто примем к сведению. Следом за MSVC, тем же коридором ползут TCC и OrangeC.
Остальные компиляторы, включая GCC, создают единственную копию STR_ME и 21 раз ссылаются на неё. И всё это по прошествии 50-ти лет победоносного, торжествующего шествия языка Си по планете с вечным анекдотом о полной переносимости кода.
Давно реализован 4-й по счёту стандарт языка, но финальную точку ставить пока рано. И что это за язык Си такой? - стандарты которого запрещают свободный доступ к произвольному чтению памяти, выделенной системой программе. Но об этом чуть далее.
В чём состоял мой промах? В том, что я пытался пользоваться STR_ME, как буфером. Естественно, что при таком раскладе, ничего не получилось. Буфером, который при каждом обращении создаётся компилятором MSVC заново и в новом месте, пользоваться не возможно.
Хоть, правилами языка Си всё это и не запрещено. Да и MSVC совершенно не протестовал против использования STR_ME в качестве ссылки-указателя. А то, что значение STR_ME, как указателя, раз от раза постоянно менялось компилятор MSVC совершенно не волновало. Видимо, #define для Microsoft написал какой-то стажёр или ученик. Возможно даже, сам Билл Гейтс в мальчишестве. Да так всё и осталось.
Чем меня прельстил cc386? - красивым ассемблерным листингом. А нельзя ли и MSVC подтянуть до такого же приемлемого уровня? Конечно можно! Вот программа.
//Test_comm.c (c) Oleg E Tereshkov 2020
extern void printf (const char *_Format,...);
int LookAtMe, AtMeToo, AtMeTwo;
int num[50];
void main(void);
void start(void){
_asm{ call main}}
void main(){
mmn:
LookAtMe=67; AtMeToo=45; AtMeTwo=29;
LookAtMe = LookAtMe+AtMeToo+AtMeTwo;
printf(" %d\n", LookAtMe);
goto mmn;}
А вот и ассемблерный листинг под неё.
;MSVC_2003_asm to better asm converter (c) Oleg E Tereshkov 2020
;http://sites.google.com/site/excelmidi
; Listing generated by Microsoft (R) Optimizing Compiler Version 13.10.3077
TITLE E:\Msvc_2003\_wrk_msvc\Test_comm.c
.386P
;####################################################################
if @Version gt 510
.model FLAT
else
_TEXT SEGMENT PARA USE32 PUBLIC 'CODE'
_TEXT ENDS
_DATA SEGMENT DWORD USE32 PUBLIC 'DATA'
_DATA ENDS
CONST SEGMENT DWORD USE32 PUBLIC 'CONST'
CONST ENDS
_BSS SEGMENT DWORD USE32 PUBLIC 'BSS'
_BSS ENDS
$$SYMBOLS SEGMENT BYTE USE32 'DEBSYM'
$$SYMBOLS ENDS
_TLS SEGMENT DWORD USE32 PUBLIC 'TLS'
_TLS ENDS
FLAT GROUP _DATA, CONST, _BSS
ASSUME CS: FLAT, DS: FLAT, SS: FLAT
endif
;####################################################################
;####################################################################
_DATA SEGMENT
COMM _LookAtMe:DWORD
COMM _AtMeToo:DWORD
COMM _AtMeTwo:DWORD
COMM _num:DWORD:032H
;_____________________________________________________
$SG482 DB ' %d', 0aH, 00H
_DATA ENDS
;####################################################################
;####################################################################
PUBLIC _main
PUBLIC _start
; Function compile flags: /Odt
_TEXT SEGMENT
_start PROC NEAR
; File e:\msvc_2003\_wrk_msvc\test_comm.c
;_____________________________________________________
; Line 7 void start(void){
push ebp
mov ebp, esp
;_____________________________________________________
; Line 8 _asm{ call main}}
call _main
pop ebp
ret 0
_start ENDP
_TEXT ENDS
;####################################################################
;####################################################################
EXTRN _printf:NEAR
; Function compile flags: /Odt
_TEXT SEGMENT
_main PROC NEAR
;_____________________________________________________
; Line 10 void main(){
push ebp
mov ebp, esp
;_____________________________________________________
$mmn$481:
;_____________________________________________________
; Line 12 LookAtMe=67; AtMeToo=45; AtMeTwo=29;
mov DWORD PTR _LookAtMe, 67 ; 00000043H
mov DWORD PTR _AtMeToo, 45 ; 0000002dH
mov DWORD PTR _AtMeTwo, 29 ; 0000001dH
;_____________________________________________________
; Line 13 LookAtMe = LookAtMe+AtMeToo+AtMeTwo;
mov eax, DWORD PTR _LookAtMe
add eax, DWORD PTR _AtMeToo
add eax, DWORD PTR _AtMeTwo
mov DWORD PTR _LookAtMe, eax
;_____________________________________________________
; Line 14 printf(" %d\n", LookAtMe);
mov ecx, DWORD PTR _LookAtMe
push ecx
push OFFSET FLAT:$SG482
call _printf
add esp, 8
;_____________________________________________________
; Line 15 goto mmn;
jmp SHORT $mmn$481
_main ENDP
_TEXT ENDS
;####################################################################
;####################################################################
END
Красота! Выглядит великолепно. Сравни с оригиналом из MSVC. Написание постмутатора заняло примерно 40 минут. И ещё денёк на постепенное окрасивливание. Зато теперь!
cc386 & OrangeC с постмутатором не справились. Хоть, к его созданию послужили толчком и предтечей. Мне эти их заскоки, равно, как и заскоки Pelle C, окончательно надоели и я от этих трёх компиляторов вполне осознанно полностью отказался, использую только для контроля и уточнения.
Ну вот. Если MSVC теперь типа-лучший, стоит ли упираться в него рогами и больше ничего не видеть и не слышать, вокруг? Конечно нет! Скачайте, установите и попробуйте, обязательно, ВСЁ. ВСЁ, что найдёте. Другие компиляторы могут оказаться интересными вам своими уникальными утилитами. И прежде всего, линкерами. Успехов. :)
(с) Терешков Олег Евгеньевич. Июнь 2020.
В качестве бонуса, asm-распечатка первой программы. Obj2asm теперь не нужен. :)
;MSVC_2003_asm to better asm converter (c) Oleg E Tereshkov 2020
;http://sites.google.com/site/excelmidi
; Listing generated by Microsoft (R) Optimizing Compiler Version 13.10.3077
TITLE E:\Msvc_2003\_wrk_msvc\stupid_msvc_tcc.c
.386P
;####################################################################
if @Version gt 510
.model FLAT
else
_TEXT SEGMENT PARA USE32 PUBLIC 'CODE'
_TEXT ENDS
_DATA SEGMENT DWORD USE32 PUBLIC 'DATA'
_DATA ENDS
CONST SEGMENT DWORD USE32 PUBLIC 'CONST'
CONST ENDS
_BSS SEGMENT DWORD USE32 PUBLIC 'BSS'
_BSS ENDS
$$SYMBOLS SEGMENT BYTE USE32 'DEBSYM'
$$SYMBOLS ENDS
_TLS SEGMENT DWORD USE32 PUBLIC 'TLS'
_TLS ENDS
FLAT GROUP _DATA, CONST, _BSS
ASSUME CS: FLAT, DS: FLAT, SS: FLAT
endif
;####################################################################
;####################################################################
_DATA SEGMENT
;_____________________________________________________
$SG472 DB '(c) Oleg E Tereshkov 2020', 0aH, 'http://sites.google.co'
DB 'm/site/excelmidi', 0aH, 'Welcome Universal Student IDE for al'
DB 'l compilers', 0aH, 0aH, 00H
ORG $+5
;_____________________________________________________
$SG473 DB '(c) Oleg E Tereshkov 2020', 0aH, 'http://sites.google.co'
DB 'm/site/excelmidi', 0aH, 'Welcome Universal Student IDE for al'
DB 'l compilers', 0aH, 0aH, 00H
ORG $+5
;_____________________________________________________
$SG474 DB '(c) Oleg E Tereshkov 2020', 0aH, 'http://sites.google.co'
DB 'm/site/excelmidi', 0aH, 'Welcome Universal Student IDE for al'
DB 'l compilers', 0aH, 0aH, 00H
ORG $+5
;_____________________________________________________
$SG475 DB '(c) Oleg E Tereshkov 2020', 0aH, 'http://sites.google.co'
DB 'm/site/excelmidi', 0aH, 'Welcome Universal Student IDE for al'
DB 'l compilers', 0aH, 0aH, 00H
ORG $+5
;_____________________________________________________
$SG476 DB '(c) Oleg E Tereshkov 2020', 0aH, 'http://sites.google.co'
DB 'm/site/excelmidi', 0aH, 'Welcome Universal Student IDE for al'
DB 'l compilers', 0aH, 0aH, 00H
ORG $+5
;_____________________________________________________
$SG477 DB '(c) Oleg E Tereshkov 2020', 0aH, 'http://sites.google.co'
DB 'm/site/excelmidi', 0aH, 'Welcome Universal Student IDE for al'
DB 'l compilers', 0aH, 0aH, 00H
ORG $+5
;_____________________________________________________
$SG478 DB '(c) Oleg E Tereshkov 2020', 0aH, 'http://sites.google.co'
DB 'm/site/excelmidi', 0aH, 'Welcome Universal Student IDE for al'
DB 'l compilers', 0aH, 0aH, 00H
ORG $+5
;_____________________________________________________
$SG479 DB '(c) Oleg E Tereshkov 2020', 0aH, 'http://sites.google.co'
DB 'm/site/excelmidi', 0aH, 'Welcome Universal Student IDE for al'
DB 'l compilers', 0aH, 0aH, 00H
ORG $+5
;_____________________________________________________
$SG480 DB '(c) Oleg E Tereshkov 2020', 0aH, 'http://sites.google.co'
DB 'm/site/excelmidi', 0aH, 'Welcome Universal Student IDE for al'
DB 'l compilers', 0aH, 0aH, 00H
ORG $+5
;_____________________________________________________
$SG481 DB '(c) Oleg E Tereshkov 2020', 0aH, 'http://sites.google.co'
DB 'm/site/excelmidi', 0aH, 'Welcome Universal Student IDE for al'
DB 'l compilers', 0aH, 0aH, 00H
ORG $+5
;_____________________________________________________
$SG482 DB '(c) Oleg E Tereshkov 2020', 0aH, 'http://sites.google.co'
DB 'm/site/excelmidi', 0aH, 'Welcome Universal Student IDE for al'
DB 'l compilers', 0aH, 0aH, 00H
ORG $+5
;_____________________________________________________
$SG483 DB '(c) Oleg E Tereshkov 2020', 0aH, 'http://sites.google.co'
DB 'm/site/excelmidi', 0aH, 'Welcome Universal Student IDE for al'
DB 'l compilers', 0aH, 0aH, 00H
ORG $+5
;_____________________________________________________
$SG484 DB '(c) Oleg E Tereshkov 2020', 0aH, 'http://sites.google.co'
DB 'm/site/excelmidi', 0aH, 'Welcome Universal Student IDE for al'
DB 'l compilers', 0aH, 0aH, 00H
ORG $+5
;_____________________________________________________
$SG485 DB '(c) Oleg E Tereshkov 2020', 0aH, 'http://sites.google.co'
DB 'm/site/excelmidi', 0aH, 'Welcome Universal Student IDE for al'
DB 'l compilers', 0aH, 0aH, 00H
ORG $+5
;_____________________________________________________
$SG486 DB '(c) Oleg E Tereshkov 2020', 0aH, 'http://sites.google.co'
DB 'm/site/excelmidi', 0aH, 'Welcome Universal Student IDE for al'
DB 'l compilers', 0aH, 0aH, 00H
ORG $+5
;_____________________________________________________
$SG487 DB '(c) Oleg E Tereshkov 2020', 0aH, 'http://sites.google.co'
DB 'm/site/excelmidi', 0aH, 'Welcome Universal Student IDE for al'
DB 'l compilers', 0aH, 0aH, 00H
ORG $+5
;_____________________________________________________
$SG488 DB '(c) Oleg E Tereshkov 2020', 0aH, 'http://sites.google.co'
DB 'm/site/excelmidi', 0aH, 'Welcome Universal Student IDE for al'
DB 'l compilers', 0aH, 0aH, 00H
ORG $+5
;_____________________________________________________
$SG489 DB '(c) Oleg E Tereshkov 2020', 0aH, 'http://sites.google.co'
DB 'm/site/excelmidi', 0aH, 'Welcome Universal Student IDE for al'
DB 'l compilers', 0aH, 0aH, 00H
ORG $+5
;_____________________________________________________
$SG490 DB '(c) Oleg E Tereshkov 2020', 0aH, 'http://sites.google.co'
DB 'm/site/excelmidi', 0aH, 'Welcome Universal Student IDE for al'
DB 'l compilers', 0aH, 0aH, 00H
ORG $+5
;_____________________________________________________
$SG491 DB '(c) Oleg E Tereshkov 2020', 0aH, 'http://sites.google.co'
DB 'm/site/excelmidi', 0aH, 'Welcome Universal Student IDE for al'
DB 'l compilers', 0aH, 0aH, 00H
ORG $+5
;_____________________________________________________
$SG492 DB '(c) Oleg E Tereshkov 2020', 0aH, 'http://sites.google.co'
DB 'm/site/excelmidi', 0aH, 'Welcome Universal Student IDE for al'
DB 'l compilers', 0aH, 0aH, 00H
_DATA ENDS
;####################################################################
;####################################################################
PUBLIC _main
EXTRN _printf:NEAR
; Function compile flags: /Odt
_TEXT SEGMENT
_main PROC NEAR
; File e:\msvc_2003\_wrk_msvc\stupid_msvc_tcc.c
;_____________________________________________________
; Line 6 int main(void){
push ebp
mov ebp, esp
;_____________________________________________________
; Line 7 printf(STR_ME);printf(STR_ME);printf(STR_ME);
push OFFSET FLAT:$SG472
call _printf
add esp, 4
push OFFSET FLAT:$SG473
call _printf
add esp, 4
push OFFSET FLAT:$SG474
call _printf
add esp, 4
;_____________________________________________________
; Line 8 printf(STR_ME);printf(STR_ME);printf(STR_ME);
push OFFSET FLAT:$SG475
call _printf
add esp, 4
push OFFSET FLAT:$SG476
call _printf
add esp, 4
push OFFSET FLAT:$SG477
call _printf
add esp, 4
;_____________________________________________________
; Line 9 printf(STR_ME);printf(STR_ME);printf(STR_ME);
push OFFSET FLAT:$SG478
call _printf
add esp, 4
push OFFSET FLAT:$SG479
call _printf
add esp, 4
push OFFSET FLAT:$SG480
call _printf
add esp, 4
;_____________________________________________________
; Line 10 printf(STR_ME);printf(STR_ME);printf(STR_ME);
push OFFSET FLAT:$SG481
call _printf
add esp, 4
push OFFSET FLAT:$SG482
call _printf
add esp, 4
push OFFSET FLAT:$SG483
call _printf
add esp, 4
;_____________________________________________________
; Line 11 printf(STR_ME);printf(STR_ME);printf(STR_ME);
push OFFSET FLAT:$SG484
call _printf
add esp, 4
push OFFSET FLAT:$SG485
call _printf
add esp, 4
push OFFSET FLAT:$SG486
call _printf
add esp, 4
;_____________________________________________________
; Line 12 printf(STR_ME);printf(STR_ME);printf(STR_ME);
push OFFSET FLAT:$SG487
call _printf
add esp, 4
push OFFSET FLAT:$SG488
call _printf
add esp, 4
push OFFSET FLAT:$SG489
call _printf
add esp, 4
;_____________________________________________________
; Line 13 printf(STR_ME);printf(STR_ME);printf(STR_ME);}
push OFFSET FLAT:$SG490
call _printf
add esp, 4
push OFFSET FLAT:$SG491
call _printf
add esp, 4
push OFFSET FLAT:$SG492
call _printf
add esp, 4
xor eax, eax
pop ebp
ret 0
_main ENDP
_TEXT ENDS
;####################################################################
;####################################################################
END
Зацени листинг. Я лучше, чем вся команда Microsoft. :)
Пора ставить большую жирную точку. Статей больше не будет. Да и не нужны они, теперь. Вопрос с выбором инструментов решён. Это MSVC_2003 и PelleC вперемешку с всевозможными линкерами и ассемблерами.
Что же дальше? А дальше, батенька - Эксперименты, эксперименты и ещё раз эксперименты, практика. У каждого свой путь. Что хочу, то и наворочу.
Ну, например, почтовые системы настолько "поумнели", что не пропускают даже файлы *.obj. Естественная реакция на это - написание дампера и дедампера. (Уже :). Если и это не поможет, какой-нибудь Энигмы в довесок. Было бы зачем. :)
Поумнели - значит, создают максимальные не удобства. :) Кстати, во многих текстовых редакторах нарушается нумерация строк, если включён их перенос и строки эти действительно переносятся. Например, в моём любимом AkelPad 2.1.6. Это может сильно затруднить поиск ошибок по сообщениям компилятора. Следите за этим внимательно.
Какие интересные цитаты:Язык формирует способ нашего мышления и предопределяет, о чем мы можем думать. Б.Л. Ворф Одни языки создаются для решения задачи, другие – для доказательства той или иной точки зрения. Деннис Ричи При помощи этих языков программирования можно сделать всё, что угодно, даже если вы того и не хотите.Ален И. Голуб «С и С++. Правила программирования» А теперь ещё раз взглянем на эту картинку, повнимательнее.
Как рассказал мне сам Pelle, стандарт Си запрещает такого рода преобразования. Вот уж действительно, Язык формирует способ нашего мышления и предопределяет, о чем мы можем думать. А с каких-таких херов запрещает?
При помощи стандарта языка программирования Си можно заставить вас сделать всё, что угодно, даже если вы того и не желаете. Вот тебе и юниассемблер. Приплыли-Си. :(
В любом случае, всегда, заканчивайте свои исходники хотя бы одной пустой строкой. Лучше будет. И поверьте, на общем фоне, PelleC по-прежнему едва ли не идеальный вариант для проверки синтаксиса программы на соответствие стандартам. Особенно, из командной строки, разобравшись с настройками и причесав ассемблер на выходе :).
Чтобы продолжить тему о бесконечном величии языка Си, пару слов о многострадальном операторе GOTO. Он не только пинаем и презираем всеми "великими гуру", но и беспредельно ограничен. Скорее, это не оператор, а костыль. Значения меток перехода не доступны в программе. goto loop - возможна, а int a=loop - нет. Тем более int a=loop; int n=4; goto (a+n); - трэш :)
Пару опусов назад, я предлагал уточнить у препода, как выйти из функции по goto. Можешь не уточнять - никак. Все метки в языке Си локальны и не видны из других функций, даже при использовании ассемблерных вставок. Придётся извращаться :).
Почему? - потому, что обучать компилятор Си правильно переваривать такой весьма тривиальный для Ассемблера фарш - лишние заморочки. Особенно, в виду не менее эротической перспективы работы линкера с уже готовыми *.obj. По крайней мере и старина Ритчти, и комитет ANSI C, и MSVC, и GCC решили точно не заморачиваться. Чего уж говорить об остальных.
А как обстоят дела при использовании ссылок из ассемблера? А никак.
//Hello_asm.c (c)Tereshkov_OE
//PellesC & MsvC
//sites.google.com/site/excelmidi
int printf();
//#define bb "Hello world!"
//char bb[]={'H','e','l','l','o',' ','w','o','r','l','d','!',0};
//--------------------------------//
void aa (void){
//char bb[]={'H','e','l','l','o',' ','w','o','r','l','d','!',0};
int cc;_asm{
mov eax, offset bb
mov cc, eax
jmp d}
d: printf(cc);
return;}
//--------------------------------//
int main(void){
aa();
return 0;}
//-------------------------------//
Из трёх вариантов с bb работает только один с глобальным массивом. Мои поздравления :)
И разумеется, никаких подпрограмм по GOSUB внутри функций Си нет, не было и не будет. Но это секрет. Никому о нём не говори :)
А ещё, никому не рассказывай о том, что ты никогда и нигде не найдёшь сколько-нибудь здравого и полного описания RTL, тем более на русском и тем более с примерами. Файл msvcrt.dll - та самая RTL, содержит более 900 функций, без полного описания которых, совершенно бесполезных. И если тебе когда-нибудь повезёт найти ну хоть какой-нибудь справочник по msvcrt.dll, дай мне знать - мне лично не удалось :) При этом, msvcrt.dll существует уже более 30 лет.
Основная масса литературы по Си, это или сборники анекдотов вида extern void printf(...);, или разной степени ущербности сухие библиотечные каталоги, или сплошные умолчания и недомолвки.
К примеру, старина Ритчи тупо молчит о том, что, при использовании указателей (и не только), все многомерные массивы в Си выглядят, как фарш из одномерных, с разным шагом размещения структурных элементов массива каждой размерности в памяти одного и того же многомерного массива, в количестве штук этих элементов определённом номинальным размером каждой размерности этого массива умноженном на номиналы всех более левых размерностей. К примеру, m[2][3][4] это и m[0][0][0-23] , и m[0][0-5][0] , и m[0-1][0][0] , и вообще - всё, что хочешь. Касперский убивался о том, как опасна printf . Но о том, что с помощью любого одноэлементного массива a[1] можно прочитать весь дамп, как-то промолчал. Тупак? - тупак! :)
Но это и хорошо :). А иначе, как же тогда всякие там Андрюшки Столяровы будут кичится своим "Знанием" с большой буквы ЗЭ, книжки там всякие продавать, лохов тупорылых мудрости всякой поучати. Типа проверок в программе количества параметров полученных функцией scanf .
Вроде, как можно назначить функции scanf 5 параметров, а ввести только 3 или 8. Хотелось бы на это посмотреть :). scanf действительно возвращает, но не вообще, а только количество правильно соответсвующих параметров. Обнаружив первый неправильный, работа функции завершается, ввод прекращается. Буфер ввода при этом не очищается. Почему бы не рассказати лохам о том, что надо сбрасывать буфер перед каждым повторным использованием scanf и в цикле? И именно с этого и начать. Но нет - язык в заднице. По причине собственной тупорылости мудруна. И не только у Андрюшки :)
А почему? А потому, что функция scanf, сама по себе анекдот. Я абсолютно точно знаю, что мудак, который её сочинил, был сапёром. Принцип "Сапёр ошибается только раз" влили в него с молоком матери. Таким же макаром реализована и scanf. Не веришь? :) Тогда, просто попробуй отменить неправильный ввод, при её использовании. Андрюшки Столяровы об этом тупо молачат.
Ну вот. А ты думал, что знаешь RTL? :) И так в Си, куда не ткнись, везде!
Как анекдот выглядит и эта цитата: "объявление arr[4][3] порождает в программе три разных объекта: указатель с идентификатором arr, безымянный массив из четырех указателей и безымянный массив из двенадцати чисел типа int. Для доступа к безымянным массивам используются адресные выражения с указателем arr. Доступ к элементам массива указателей осуществляется в форме arr[2] (с указанием одного индексного выражения) или *(arr+2)."
Почему анекдот? - потому, что указатель, согласно Завета, это переменная для хранения адресов переменных. А слово метка мы ой, как не любим. Так же трудно будет аффторам цитаты предъявить нам и безымянный массив из четырех указателей. А следующая программка
//Array.c (c)Tereshkov_OE
//sites.google.com/site/excelmidi
extern int printf();
int main(void) {
int n;
int m[2][3][4];
int *mm=(int*)m;
for (n=0;n<=23;n++){
*mm=n;
printf("%d %d %d %d\n",mm,m[n],m[0][0][n],n);
mm++;}
printf("\n");
return 0;
}
со всей убедительностью доказывает, что m[0] совсем не равно m[0][0][0] уже по самой своей сути. Программка имеет большой потенциал для осмысления бытия и экспериментов. Замени m[0][0][n], ну скажем, на m[0][3][n] :). И сходу получишь хер его знает что :). Язык Си, он такой - не пальцем деланный :) Но и вариант с m[0][2][n] ничем не лучше. Подсунь эту прогу преподу :). Проверь, допрёт он или нет почему m[0]!=m[0][0][0]. Но только с умом подсунь - вначале, сам вкури, что почём.
Программка действительно имеет большой потенциал. Не буду обременять вас её окончательным вариантом, но вертя его во все стороны и одновременно выворачивая MSVC на изнанку, с нулевыми знаниями на входе, я очень быстро пришёл к ниже следующим неутешительным выводам. Бери и пользуйся. :)
А скажи-ка мне, друг :) Сколько из твоих знакомых могут навскидку сказать, что тут написано - int * ( * comp [10]) (); или char * ( * ( * var ) () ) [10];. То-то и оно. Или, как объяснить int *ip; ? Кое-что уже зная, я бы сказал, что объявлена переменная-указатель ip для хранения адресов переменных типа int и только int. Адрес переменной типа char в неё уже не засунешь. А шо пышуть K&R в русском переводе? - "выражение *ip имеет тип int". Какое выражение? В куда оно этот тип имеет? :)
Поэтому, если ты, как и я :) программируешь постольку-поскольку, не морочь себе голову, выбрось из неё Си, избавься от страданий :). Пользуйся Purebasic. Хоть и в нём, тоже, многое сделано per anus - как присобаченный линкер от PellesC, который, без и из-за своих библиотек, не позволяет воспользоваться даже функцией printf(). Но в стандартной поставке Purebasic есть хотя бы описание встроенных в него 2000 функций с примерами, сгруппированное по смыслу. Это поможет тебе сэкономить годы жизни :)
Уверен, что многих страждущих заставит прослезиться :) BCX - The Basic To C/C++ Translator. Возможно, он не плох и для общего развития, и, как ни с чем не совместимая обучалка. Но, как же всё-таки умудрённым авторам удалось натянуть оператор gosub на компилятор Си?
Оказывается, никак. gosub внутри ВСХ суть тот же сишный вызов функции с собственными локальными переменными, а не никакая не подпрограмма в канонах классического BASIC. В Purebasic всё по чесноку :). Хотя, для меня лично был бы интереснее конвертер на автомате переводящий char * ( * ( * var ) () ) [10]; во всякие там пики-поки.
Если нельзя, но очень хочется - значит можна :). Возьмём для примера программку проверяющую глубину стека и ассемблер под неё.
//depth_of_stack.c
//(c) Oleg E Tereshkov 23:56 02.09.2020
//http://sites.google.com/site/excelmidi
extern void printf (const char *,...);
int n1=0;
void main(void);
void next(void){
_asm{ mov eax, main
jmp eax}
}
void main(void){
int n2=0;n1++;n2++;
printf("%d %d\n",n2,n1);
_asm{ mov eax, next
jmp eax}
}
; MSVC_2003_asm to better asm converter (c) Oleg E Tereshkov 2020
; http://sites.google.com/site/excelmidi
; Listing generated by Microsoft (R) Optimizing Compiler Version 13.10.3077
TITLE E:\00\_my_Cmplrs\compilers\Msvc_2003\_wrk_msvc\depth_of_stack.c
.386P
;##########################################################################
if @Version gt 510
.model FLAT
else
_TEXT SEGMENT PARA USE32 PUBLIC 'CODE'
_TEXT ENDS
_DATA SEGMENT DWORD USE32 PUBLIC 'DATA'
_DATA ENDS
CONST SEGMENT DWORD USE32 PUBLIC 'CONST'
CONST ENDS
_BSS SEGMENT DWORD USE32 PUBLIC 'BSS'
_BSS ENDS
$$SYMBOLS SEGMENT BYTE USE32 'DEBSYM'
$$SYMBOLS ENDS
_TLS SEGMENT DWORD USE32 PUBLIC 'TLS'
_TLS ENDS
FLAT GROUP _DATA, CONST, _BSS
ASSUME CS: FLAT, DS: FLAT, SS: FLAT
endif
;##########################################################################
;##########################################################################
PUBLIC _n1
_BSS SEGMENT
_n1 DD 01H DUP (?)
_BSS ENDS
;##########################################################################
;##########################################################################
_DATA SEGMENT
;_____________________________________________________
$SG479 DB '%d %d', 0aH, 00H
_DATA ENDS
;##########################################################################
;##########################################################################
PUBLIC _main
PUBLIC _next
; Function compile flags: /Odt
_TEXT SEGMENT
_next PROC NEAR
; File e:\00\_my_cmplrs\compilers\msvc_2003\_wrk_msvc\depth_of_stack.c
;_____________________________________________________
; Line 10 void next(void){
push ebp
mov ebp, esp
;_____________________________________________________
; Line 11 _asm{ mov eax, main
mov eax, OFFSET FLAT:_main
;_____________________________________________________
; Line 12 jmp eax}
jmp eax
;_____________________________________________________
; Line 13 }
pop ebp
ret 0
_next ENDP
_TEXT ENDS
;##########################################################################
;##########################################################################
EXTRN _printf:NEAR
; Function compile flags: /Odt
_TEXT SEGMENT
_n2$ = -4 ; size = 4
_main PROC NEAR
;_____________________________________________________
; Line 15 void main(void){
push ebp
mov ebp, esp
push ecx
;_____________________________________________________
; Line 16 int n2=0;n1++;n2++;
mov DWORD PTR _n2$[ebp], 0
mov eax, DWORD PTR _n1
add eax, 1
mov DWORD PTR _n1, eax
mov ecx, DWORD PTR _n2$[ebp]
add ecx, 1
mov DWORD PTR _n2$[ebp], ecx
;_____________________________________________________
; Line 17 printf("%d %d\n",n2,n1);
mov edx, DWORD PTR _n1
push edx
mov eax, DWORD PTR _n2$[ebp]
push eax
push OFFSET FLAT:$SG479
call _printf
add esp, 12 ; 0000000cH
;_____________________________________________________
; Line 18 _asm{ mov eax, next
mov eax, OFFSET FLAT:_next
;_____________________________________________________
; Line 19 jmp eax}
jmp eax
;_____________________________________________________
; Line 20 }
mov esp, ebp
pop ebp
ret 0
_main ENDP
_TEXT ENDS
;##########################################################################
;##########################################################################
END
PellesC и MSVC справились с задачей на отлично. А вот DMС - Digital Mars С эту программку не потянул. Инструкция mov eax, OFFSET FLAT: в нём превращается в mov eax, [eax], что как бы не хорошо :). Не справились с заданием и GCC, и TCC. Вот листинг и дисассемблер под эти два компилятора.
//depth_of_stack.c
//(c) Oleg E Tereshkov 23:56 02.09.2020
//http://sites.google.com/site/excelmidi
extern void printf (const char *,...);
int n1=0;
void main(void);
void next(void){
asm("movl main, %eax");
asm("jmp *%eax");
}
void main(void){
int n2=0;n1++;n2++;
printf("%d %d\n",n2,n1);
asm("movl next, %eax");
asm("jmp *%eax");
}
.file "depth_of_stack.c"
.globl _n1
.section .bss
.p2align 2
_n1:
.space 4
.section .text
.globl _next
_next:
pushl %ebp
movl %esp, %ebp
/APP
movl main, %eax
jmp *%eax
/NO_APP
leave
ret
LC0:
.ascii "%d %d\12\0"
.globl _main
_main:
pushl %ebp
movl %esp, %ebp
subl $8, %esp
andl $-16, %esp
movl $0, %eax
addl $15, %eax
addl $15, %eax
shrl $4, %eax
sall $4, %eax
subl %eax, %esp
movl $0, -4(%ebp)
incl _n1
leal -4(%ebp), %eax
incl (%eax)
subl $4, %esp
pushl _n1
pushl -4(%ebp)
pushl $LC0
call _printf
addl $16, %esp
/APP
movl next, %eax
jmp *%eax
/NO_APP
leave
ret
.ident "GCC: (GNU) 3.4.6"
Как оригинально интерпретирует GCC строку int n2=0;n1++;n2++;! Офигеть!! Стринг "%d %d\n" канонически :) впечатан прямо в середину кода - .ascii "%d %d\12\0", а число 12 в оном, в результате всевозможных чудесных, невидимых и таинственных преобразований, по итогу, превращается в 10-ку в объектном коде. ВАУ! Не.. Ну.. Восьмеричная система, она конечно хороша, но не в 2020-м году. :)
Но всё равно не работает :). Но на то он и GCC - сакральный плод коллективного труда :). Глядя на эту ассемблерную белиберду, забавна сама мысль о преимуществе Линух над Виндой. Маленькая оговорка - в качестве линкера под GCC, я использовал GoLnk. Но ведь для TCC-то сторонние линкеры не нужны, а результат тот же. Между тем, как связки MSVC-Uasm-GoLnk и MSVC-GoLnk работают отлично.
Что из этого следует? - то, что кодишь ты не на абстрактном Си, Паскаль или Бейсик, а на конкретном компиляторе, который писали конкретные люди в меру их ума и развития. При этом, общемировая тенденция такова, что ума у этих людей становится всё меньше, а возможностей с него сойти - всё больше. Не стреляйте в онанистов :) - кто, как хочет - так и дро#ит :). При этом, тема внутренней организации и устройства линкеров не менее заманчива и интересна, чем аналогичная тема для компиляторов.
GCC: (GNU) 3.4.6 не запускается под Win7/64. MSVC 10 - под XP. PellesC после 8-й версии перестал быть 32-х разрядным. Редкий компилятор Си в состоянии переварить выдавленный из себя же ассемблер исходника.
Можно ли обойти весь этот бесконечный маразм? - и нет, и да! Только по кругу :). Как? - используя гибридные техники, в итоге, сведя всё к банальному Ассемблеру. Но вряд ли это тема для обсуждения здесь и сейчас.
Просто помни, если реальный компилятор самого сверхновового/ненового и самого сверхсовременного/протухшего языка является простой/сложной надстройкой над компилятором Си(Eiffel,Nim,FreeBasic) - чуда не произойдёт. Диссертацию защитить можно, а вот обойти ограничения или превысить исходную эффективность исходного компилятора Си - никогда.
А чтобы ты был уверен в своём MSVC на все 100%, ещё один пример.
void printf();
int new(int a, int b, int v){
return a+b+v;}
int s =4;
int f =3;
int p =8;
int x =0;
int main (void){
x=new(s,p,f);
printf("%d\n", x);}
;###################################
PUBLIC _s
PUBLIC _f
PUBLIC _p
PUBLIC _x
_BSS SEGMENT
_x DD 01H DUP (?)
_BSS ENDS
;####################################
;####################################
_DATA SEGMENT
_s DD 04H
_f DD 03H
_p DD 08H
;_____________________________________________________
$SG484 DB '%d', 0aH, 00H
_DATA ENDS
;####################################
Как видишь, переменная x инициируется
совсем не нолём, а чем бог пошлёт. И в этот раз, компилятор MSVC оказался умнее тебя и, как всегда, знает лучше, что для тебя лучше. :)
Компилятор формирует способ нашего мышления и предопределяет то, о чем мы можем думать. ???
И ещё одна маленькая печалька :). Такие красивые asm-распечатки достаточно искусственны и в общем случае возможны только для отдельно стоящих файлов языка Си без директив #include. Получить такие красоты в C++ весьма проблематично. Ассемблерный файл MSVC даже для такой простой программки
#include <iostream>
#include <cstdlib>
using namespace std;
int main()
{
cout << "Hello, world!" << endl;
system("pause"); // Только для тех, у кого MS Visual Studio
return 0;
}
разрастается свыше 1000 строк, абсолютно не читаем и естественно, не может быть обработан моим постмутатором :(. Объектный файл имеет размер 16367, а ексешка - 77824. Ни о каком понимании того, что происходит на уровне машинного кода, естественно, речь вообще не идёт. Очевидно, что на C++ определённо трудно писать операционки :) Зато, очень легко навсегда абстрагироваться от регистров и команд микропроцессора :)
Некто Bjarne Stroustrup объясняет такую ситуацию отсутствием хорошего компилятора C++ в любой системе :). При этом, сам он - лично, никогда не видел программу, которая была бы написана на C лучше, чем на C++. И ключевых слов тут два: "не видел" - потому, что не смотрел и "лучше" - потому, шо C++ вааще лучше :). Короче, ты понял :).
Ты не поверишь, но именно Bjarne Stroustrup ты обязан анальными радостями от возни с прототипами функций :). И именно он придумал комментарии // - революционный прорыв! :)
Просто-Си будет жить всегда. Он хоть и тупой, и ограниченный, но вполне обозримый и достаточно хорошо ложится в выходной ассемблерный файл. Хотя, возможны и варианты. В меру ума и развития :)
;MSVC_________________________________________
; Line 30 temp1=temp1 - temp11;
mov eax, DWORD PTR _temp1$[ebp]
sub eax, DWORD PTR _temp11$[ebp]
mov DWORD PTR _temp1$[ebp], eax
;cc386_________________________________________
; Line 30: temp1=temp1 - temp11
MOV EAX,DWORD [EBP-02CH]
SUB DWORD [EBP-054H],EAX
второй вариант работает на 20% быстрее.
Простенькая программка:
#include <stdio.h>
int main(int argc, char *argv[]) {
FILE* f = fopen("file.c", "r");
if (f)
{
while (!feof(f))
putchar(fgetc(f));
fclose(f);
}
return 0;
}
И сложненький ассемблер под неё:
; MSVC_2003_asm to better asm converter (c) Oleg E Tereshkov 2020
; http://sites.google.com/site/excelmidi
; Listing generated by Microsoft (R) Optimizing Compiler Version 13.10.3077
TITLE E:\00\_my_Cmplrs\compilers\Msvc_2003\_wrk_msvc\file.c
.386P
;#####################################################
if @Version gt 510
.model FLAT
else
_TEXT SEGMENT PARA USE32 PUBLIC 'CODE'
_TEXT ENDS
_DATA SEGMENT DWORD USE32 PUBLIC 'DATA'
_DATA ENDS
CONST SEGMENT DWORD USE32 PUBLIC 'CONST'
CONST ENDS
_BSS SEGMENT DWORD USE32 PUBLIC 'BSS'
_BSS ENDS
$$SYMBOLS SEGMENT BYTE USE32 'DEBSYM'
$$SYMBOLS ENDS
_TLS SEGMENT DWORD USE32 PUBLIC 'TLS'
_TLS ENDS
FLAT GROUP _DATA, CONST, _BSS
ASSUME CS: FLAT, DS: FLAT, SS: FLAT
endif
;#####################################################
;#####################################################
_DATA SEGMENT
;_____________________________________________________
$SG797 DB 'r', 00H
ORG $+2
;_____________________________________________________
$SG798 DB 'file.c', 00H
_DATA ENDS
;#####################################################
;#####################################################
PUBLIC _main
EXTRN __iob:BYTE
EXTRN __flsbuf:NEAR
EXTRN _fclose:NEAR
EXTRN _fgetc:NEAR
EXTRN _fopen:NEAR
; Function compile flags: /Odt
_TEXT SEGMENT
tv92 = -8 ; size = 4
_f$ = -4 ; size = 4
_argc$ = 8 ; size = 4
_argv$ = 12 ; size = 4
_main PROC NEAR
; File e:\00\_my_cmplrs\compilers\msvc_2003\_wrk_msvc\file.c
;_____________________________________________________
; Line 3 int main(int argc, char *argv[]) {
push ebp
mov ebp, esp
sub esp, 8
;_____________________________________________________
; Line 5 FILE* f = fopen("file.c", "r");
push OFFSET FLAT:$SG797
push OFFSET FLAT:$SG798
call _fopen
add esp, 8
mov DWORD PTR _f$[ebp], eax
;_____________________________________________________
; Line 7 if (f)
cmp DWORD PTR _f$[ebp], 0
je SHORT $L799
;_____________________________________________________
$L801:
; Line 9 while (!feof(f))
mov eax, DWORD PTR _f$[ebp]
mov ecx, DWORD PTR [eax+12]
and ecx, 16 ; 00000010H
jne SHORT $L802
;_____________________________________________________
; Line 10 putchar(fgetc(f));
mov edx, DWORD PTR __iob+36
sub edx, 1
mov DWORD PTR __iob+36, edx
js SHORT $L810
mov eax, DWORD PTR _f$[ebp]
push eax
call _fgetc
add esp, 4
mov ecx, DWORD PTR __iob+32
mov BYTE PTR [ecx], al
mov edx, DWORD PTR __iob+32
movsx eax, BYTE PTR [edx]
and eax, 255 ; 000000ffH
mov DWORD PTR tv92[ebp], eax
mov ecx, DWORD PTR __iob+32
add ecx, 1
mov DWORD PTR __iob+32, ecx
jmp SHORT $L811
;_____________________________________________________
$L810:
push OFFSET FLAT:__iob+32
mov edx, DWORD PTR _f$[ebp]
push edx
call _fgetc
add esp, 4
push eax
call __flsbuf
add esp, 8
mov DWORD PTR tv92[ebp], eax
;_____________________________________________________
$L811:
jmp SHORT $L801
;_____________________________________________________
$L802:
; Line 11 fclose(f);
mov eax, DWORD PTR _f$[ebp]
push eax
call _fclose
add esp, 4
;_____________________________________________________
$L799:
; Line 13 return 0;
xor eax, eax
;_____________________________________________________
; Line 14 }
mov esp, ebp
pop ebp
ret 0
_main ENDP
;#####################################################
;#####################################################
_TEXT ENDS
;#####################################################
;#####################################################
END
Как по мне, немного далеко от оригинала :). И в этот раз, компилятор MSVC оказался умнее тебя и, как всегда, знает лучше, что для тебя лучше. :) Вызова функции feof нигде нет. Видимо, проверка _f$+12 и есть feof :).
Ичсо примерчик :)
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
int main(){
int N = 10;
int mas[10], i;
getchar();
srand ( (unsigned)clock() & 32767) ;
for (i = 0; i < N; i++)
mas[i] = rand();
for (i = 0; i < N; i++)
printf("%d ", mas[i]);
return 0;}
;_____________________________________________________
; Line 7 getchar();
mov eax, DWORD PTR __iob+4
sub eax, 1
mov DWORD PTR __iob+4, eax
js SHORT $L1240
mov ecx, DWORD PTR __iob
movsx edx, BYTE PTR [ecx]
and edx, 255 ; 000000ffH
mov DWORD PTR tv71[ebp], edx
mov eax, DWORD PTR __iob
add eax, 1
mov DWORD PTR __iob, eax
jmp SHORT $L1241
;_____________________________________________________
Вызов getchar() выглядит немного странным и неожиданным. Без объяснений - особенно. Хоть стоит MSVC не три копейки.
И на прощание. Что лучше? Готовый дистрибутив или та же прога в исходниках. Не знаешь, что и ответить? Тогда представь, как бы тебе пришлось собирать 25 гигабайт Win10 самому. :) Привет GNU.
Но особенно умиляет не это. Умиляет то, что я должен/обязан, по задумке хитро сделанных Столманов, не просто бесплатно писать правильный открытый код на основе дебильных и увечных компиляторов, ущербного хэлпа к ним, в отсутствие свободного доступа к стандартам и библиотекам, но и кроме того, писать эти исходники таким образом, чтобы всякий идиот с качеством интеллекта IQ = 1 или 2, типа твоего начальника, смог бы потом свободно в них разбираться. Господа Столманы и иже с ними, а не слишком ли всё это для нас прямолинейно?
(с) Терешков Олег Евгеньевич. .Август 2020.