Начало программирования в Виндовс 8

cc386

Код, который я только что написал слишком сложен и хрупок, что даже я с трудом его понимаю. Я не верю, что Деннис Ритчи, создатель языка C, понимал последствия того, что он делает. Я подозреваю, что пописывая код, он походу выдумывал и синтаксис. Но синтаксис оказался вариативнее, ограниченнее и неопределённее, чем самому автору ожидалось. И, в итоге, был стандартизирован комитетом ANSI C. Трудно реализовывать стандартизированный язык, потому что должно всё сделать правильно. Скорее и легче написать свой собственный игрушечный язык. Rui Ueyama.

Итак, подведём кое-какие итоги. С вероятностью 100%, компилятор Си, которым ты сейчас пользуешься, - мягко говоря, не идеален и содержит множественные скрытые дефекты о которых ты, пока, не знаешь, не подозреваешь и не догадываешься. Если ты думаешь по-другому, значит, кроме "Hello world" и программ, сутью которых является последовательный вызов библиотечных функций, ты ничего не писал и не видел. Как только в твоих программах появится кое-какая нестандартная логика, твой компилятор тут же проявит себя и попытается поставить "хозяина" и "владыку" на колени.

Не веришь? Вспоминается рассказ-откровение одного доброго молодца, о том, как он подсел/пересел на Nim после C/C++. Как утверждают просветлённые, теперь, они думают только о том, что им делать и никогда над тем, как именно они будут это делать.

Практически, это означает, что данные везунчики никогда не имели проблем с компилятором Nim, не обходили подводные камни, не сталкивались с отсутствующими, не реализованными возможностями языка. Не знаю, на сколько хуже, в этом отношении, был бы для них Basic, но сам факт якобы отсутствия проблем с компилятором вдохновляет. Тем более, что упираться рожками в GCC приходится не им, а Nim.

В Си всё по-другому. С одной стороны, есть бесконечное количество макулатуры с описанием никем не виданных возможностей языка и реальные компиляторы, которые имеют всю эту писанину, как только могут - с другой. Принцип - что не запрещено - то разрешено, в Си не действует. Действует другой принцип - как получилось/захотелось, так и хорошо - AS IS. А тратить часы на поиски вариантов написания strp_i=(char*)strp_i+7; далеко не fair.

Что есть хорошая/правильная программа? Во-первых, есть программа, как таковая, в виде исходника. И есть реальный код на исполнение, полученный в результате компиляции и линковки. Это, как проект и реальное здание с дырами в стенах. Две абсолютно разные разницы. Поэтому, если компилятор тупит, приходится насиловать себя и исходник или прощаться с таким компилятором, навсегда.

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

Скажу сразу, что в cc386, применительно к Винде, наиболее интересен файл cc386.exe. Остальное содержимое, включая IDE, может сходу в вас просто не зайти. При самостоятельной сборке, в комплект к cc386.exe следует добавить новый Nasm/Yasm и TCC, как линкер, но не второй компилятор.

У cc386 и TCC несовместимые заголовочные файлы. Эти файлы не могут находиться в одной и той же папке include , иначе, жди проблем при переходе из компилятора в компилятор. В любом случае, сложи всё это добро - cc386.exe, nasm.exe и tcc.exe - в одну кучку, слепи батники *.bat или приобрети микро IDE, типа этой и всё - можно изощряться.

Ибо, всё это счастье достанется тебе совершенно бесплатно, под лицензией GNU, без всяких обязательств, денежных и прочих издержек, сейчас и в будущем. Т.е. время потраченное на освоение этих жемчужин не пропадёт даром. Единственное неудобство, свой рабочий пакет ты должен будешь смастерить сам, из доступных запчастей. Отнесись к этому обстоятельству с должным вниманием. Приобретёшь же ты весь мир.

Почему не OrangeC за подписью того же автора? - потому, что не Nim - требует доработки. Господину Аффтору есть ещё над чем потрудиться. Ver.6.0.43.1 уже раритет.

Конечно, если ты кому-нибудь скажешь, что тебя возбуждает cc386 - тебя засмеют. А ты не говори. Лучше, подумай о том, какой красивый ассемблерный листинг на выходе компилятора ты получишь. Не понять, после этого, разницу между strp_i=(char*)strp_i+7; и (char*)strp_i=(char*)strp_i+7; смогут ну только очень тупые, как я, Pelle Orinius, Jacob Navia, Microsoft GCC и имя им Легион. :)

//pointer_1.c for cc386

//Fuck your compiler

//(c) Oleg E Tereshkov 27.04.2020 13:02

//https://sites.google.com/site/excelmidi

#define STR1 ";Fuck your compiler\n"

int *strp_i;

int printf();

int getchar();

void _start(void){

strp_i=STR1;

strp_i=strp_i+7;

(char*)strp_i=strp_i+7;

strp_i=(char*)strp_i+7;

(char*)strp_i=(char*)strp_i+7;

(char)strp_i=strp_i+7;

strp_i=(char)strp_i+7;

(char)strp_i=(char)strp_i+7;

getchar();

}

SECTION .text

SECTION .data

SECTION .bss

SECTION .text

[BITS 32]

[GLOBAL _start]

_start:

; Line 9: void _start(void){

PUSH EBP

MOV EBP,ESP

L_3:

; Line 10: strp_i=STR1;

MOV DWORD [strp_i],DWORD L_1

; Line 11: strp_i=strp_i+7;

ADD DWORD [strp_i],BYTE 01CH

; Line 12: (char*)strp_i=strp_i+7;

MOV EAX,DWORD [strp_i]

ADD EAX,BYTE 01CH

MOV DWORD [strp_i],EAX

; Line 13: strp_i=(char*)strp_i+7;

MOV EAX,DWORD [strp_i]

ADD EAX,BYTE 07H

MOV DWORD [strp_i],EAX

; Line 14: (char*)strp_i=(char*)strp_i+7;

ADD DWORD [strp_i],BYTE 07H

; Line 15: (char)strp_i=strp_i+7;

MOV EAX,DWORD [strp_i]

ADD EAX,BYTE 01CH

MOV CL,BYTE [strp_i]

MOV CL,AL

; Line 16: strp_i=(char)strp_i+7;

MOVSX EAX,BYTE [strp_i]

ADD EAX,BYTE 07H

MOV DWORD [strp_i],EAX

; Line 17: (char)strp_i=(char)strp_i+7;

MOVSX EAX,BYTE [strp_i]

ADD EAX,BYTE 07H

MOV CL,BYTE [strp_i]

MOV CL,AL

; Line 18: getchar();

CALL getchar

; Line 19: }

L_2:

POP EBP

RET

SECTION .data

L_1:

DB 03BH,046H,075H,063H,06BH,020H,079H,06FH,075H,072H,020H,063H

DB 06FH,06DH,070H,069H,06CH,065H,072H,0AH,00H

SECTION .bss

[GLOBAL strp_i]

strp_i RESB 04H

SECTION .text

[BITS 32]

SECTION .text

[BITS 32]

[EXTERN getchar]

Иначе, cc386 превосходен для обучения и развлечения. Ассемблерный файл на выходе позволяет моментально корректировать все промахи и недочёты, а не безуспешно страдать неделями и месяцами в гаданиях и тыкании пальцем в небо. Препод становится ненужным. Ты сам себе господин, слуга и начальник. Ибо, всё, что тебе надо cc386 покажет тебе сам. Красота! Бери и пользуйся.

Забыл сказать, что если вдруг ты возжелаешь C99 в такой минимальной комплектации, всякие там специфические функции, для этого, тебе придётся компилировать самому. Но тем многоопытнее ты станешь. Исходники есть.

Как часто в таких случаях бывает, не ассемблерный файл красит программиста - слишком надеяться не стоит. И в данном конкретном случае ассемблерный листинг не сильно поможет. Майкрософт есть ещё над чем поработать. :)

//num_count_1.c

#define EOF (-1)

extern printf();

extern getchar();

void main(void){

int nc;

for(nc=0;getchar()!=EOF;++nc);

printf("%.0f\n", nc); }

Винда - дерьмо. Любой компилятор - более, чем дерьмо. Препод - мошенник. А ты, как волшебник. :)

Только не подумай, что я не знаю о том, что cc386 такой же ограниченный, читай тупой, как и все остальные компиляторы.

//(c) Oleg E Tereshkov 2020

char aname4[4][10] = {"01234567", "01234567899", "01234567899", "01234567899"};

char aname5[10] = {"01234567899"};

extern int printf();

int main(int argc, char **argv[]){

int k,i;

// aname4[0][0]='0';

// aname4[1][0]='01234567890';

for(i=0;i<4;i++){

for(k=0;k<10;k++){

printf("%x ", aname4[i][k]);}

printf ("\n");}

for(k=0;k<10;k++){

printf("%x ", aname5[k]);}

printf ("\n");

return 0; }

В этой программе лишние девятки. Но cc386 глотает их на ура, без единой запинки. А родилась эта программа из этой строчки. char aname[][15] = {"Неправ. месяц", "Янв", "Февр", "Март"};. Как утверждают издатели русской версии K&R, во времена Ритчи, компиляторы хавали такое аж набегом. Я даже подумал, а не схожу ли я с ума? И оформил кое-что для проверки.

char aname1[4][10] = {"0123456789", "0123456789", "0123456789", "0123456789"};

char aname2[4][10] = {"0123456789", "012345678", "01234567", "01234567"};

char aname3[4][10] = {"01234567890", "012345678901", "0123456789012", "01234567890123"};

А набрёл я на это изучая работу int main(int argc, char **argv[]){. Как утверждают издатели русской версии K&R, во времена Ритчи вполне катил и такой вариант int main(int argc, char *argv[]){. Короче, ты понял.

А началось всё с того, что Gmail запретил пересылку архивов. Типа горе от ума, борьба с вирусами и т.д. Ну и захотелось мне заиметь маленький дампер и дедампер. Размер архива удваивается. Но зато, его можно переслать прямо в теле письма буквами. Дампер у меня получился за 15 минут, но не заработал.

//(c) Oleg E Tereshkov 2020

//dumper.exe

char hx[16]={"0123456789ABCDEF"};

extern int printf();

extern int feof(int);

extern int putc(int,int);

extern int getc(int);

extern int fopen(char *name, char *mode);

extern int fclose(int);

int main(int argc, char **argv[]){

char *name_in=argv[1]; int a,b,i,file_in,file_out;

file_in=fopen(name_in,"r");

if (!file_in) goto error;

file_out=fopen( "dump.txt", "w");

if (!file_out) goto error;

loop:

for(i=0;i<16;i++){

a=getc(file_in); b=a;

if (feof(file_in)!=0) goto cls_file;

b=b>>4; b=hx[b];putc(b,file_out);

a=a&0x0f; a=hx[a];putc(a,file_out);

putc(32,file_out);}

putc(10,file_out);

goto loop;

cls_file:

fclose(file_in);fclose(file_out);return 0;

error:

printf ("Something wrong\n");return 1;}

Как оказалось int getc(int) ворует байты со значением 13 из сочетаний 13,10. И не может дочитать некоторые файлы до конца. Но в этом-то и состоит величие Си - высокий порог вхождения под названием - Вынеси себе моск! При этом копирование текстовых файлов происходит правильно, без всяких потерь содержимого. Потому, что int putc(int,int) сама добавляет байты 13 перед байтами 10. Преподы и старина Ритчи об этом тупо молчат. А хули? Строки в Си из под Винды, это особый секс под названием - Пойми меня! Без WinAPI никак. Привыкай. :)

//(c) Oleg E Tereshkov 2020

//copy_txt.exe

extern int printf();

extern int feof(int);

extern int putc(int,int);

extern int getc(int);

extern int fopen(char *name, char *mode);

extern int fclose(int);

int main(int argc, char **argv[]){

char *name_in=argv[1]; int a,i,file_in,file_out;

file_in=fopen(name_in,"r");

if (!file_in) goto error;

file_out=fopen( "dump.txt", "w");

if (!file_out) goto error;

loop:

a=getc(file_in);

if (feof(file_in)!=0) goto cls_file;

putc(a,file_out);

goto loop;

cls_file:

fclose(file_in);fclose(file_out);return 0;

error:

printf ("Something wrong\n");return 1;}

Написать такую же программку на PureBasic - те же 15 минут. Но работать она будет правильно, как и задумывалось + окошко с менюшками и никакой командной строки. Всех благ. :)

Кстати, имя файла, для дампа, в командной строке - dumper.exe my.zip, например, а командная строка в cmd.exe, а в cmd.exe - cd C:\my_folder, например, если кто в первый раз. И естественно, при написании всех этих примеров использовалась вот эта удобненькая среда. А ты думал почему за 15 минут? А-то. Просто тыкаешь мышкой и усё.

(с) Терешков Олег Евгеньевич. Май 2020.

Есть такой компилятор Euroasm. В лицензии на который, автор специально подчёркивает, что это не GNU GPL и делает мерзость сию, как он говорит, для того, чтобы на корню пресечь неизбежный базар из будущих ни с чем не совместимых форков, как в GCC . В отличие от Nasm. Так, в компиляторе OrangeC-6.0.43.1 и далее была специально нарушена возможность прямой передачи ассемблерного файла в Nasm из жлобских побуждений. Пришлось написать переводчик.

И тут пошли недостатки системы. Нормальный выход выглядит так.

;OrangeC-6.0.43.1 to Nasm

;File o2n_conv+5.c

;Compiler version 6.0.43.1

use32

section .text align=2

section .data align=8

section .bss align=4

section .const align=8

section .string align=2

section .tls align=8

section .cstartup align=2

section .crundown align=2

section .text

[global str_to_file]

str_to_file:

; Line 38: void str_to_file(char *s,int fo,int end){

push ebp

mov ebp,esp

push ecx

L_1:

; Line 39: int c;

....

С заданием справились cc386, lcc32, Gcc, Pcc.

MSVC и Tcc с заданием не справились. Их выход выглядит так.

;OrangeC-6.0.43.1 to Nasm

;File o2n_conv+5.c

;Compiler version 6.0.43.1

use32

section .text align=2

section .data align=8

section .bss align=4

section .const align=8

section .string align=2

section .tls align=8

section .cstartup align=2

section .crundown align=2

use32

use32

use32

use32

use32

........ to the end.

Программа после PelleC была отвергнута системой, как не рабочая. А OrangeC-6.0.43.1 и вовсе пропустил инструкции. В связи с этим, перекодировщик был доработан и дефект OrangeC стал высвечиваться автоматически.

Риторический вопрос. Что лучше? - дефектный компилятор, дефекты которого видны автоматически и легко устранимы или PelleC, MSVC и Tcc? Чтобы ответить, поковыряйся в ассемблерном выводе MSVC и сравни его с OrangeC, для интереса.

Но интересно не ЭТО само по себе. Интересно то, что я не одинок. Команда Google смотрела на всё ЭТО, смотрела и создала Go. Зачем? Ведь есть же MSVC - прекрасный и чудесный, Gcc.

Но то-то и оно, что путь к звёздам начинается с написания собственного компилятора, создания процессора, операционки и т.д. Иначе, в аптеку - за вазелином. Он вам ещё понадобится. И не раз. :)