Начало программирования в Виндовс 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, типа твоего начальника, смог бы потом свободно в них разбираться. Господа Столманы и иже с ними, а не слишком ли всё это для нас прямолинейно?