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