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

А можно ли без #include?

Отвечу сразу. Без #include можно. И можно, и нужно. А во многих случаях и естественно необходимо. Опустим, даже, вопрос о копирастии. Но и без неё, сухое плавание в тумане не очень хороший вид спорта.

Добровольная исповедь Великого Практика, крик души: - «С вопросами "а можно ли без #include" и т.д. вылезают как раз особо умные. Ну а любому грамотно программирующему преподавателю поставить их на место труда не составит. И в "хаки" лезет тоже самое грамотное меньшинство. У середнячков на это просто времени и сил не остается. Имея трудности они легко принимают принцип "не понимаешь - не используй"». Ну что ж, очень откровенно - вылезают, особо умные, поставить на место, лезет, не понимаешь - не пользуй. Педагог с большой буквы. Не хватает только концовки: - Своими руками бы душил! Бедные дети.

Как видишь, если ты «особо умный», тебе конец. Препод тебя только терпит, но не потерпит. Ну, а как они ставят на место, ты уже знаешь. Или скоро узнаешь. Good есть только «середнячок». А унизить и отиметь «особо умного», для Практиков, дело чести. Ибо, не препод для тебя, а ты для препода. Или под преподом. В моём ..НТУ у этих хапуг методичку не допросишься, за всё только плати. А то, что оно при этом «грамотно» не отличает тоже от то же, никого е?ать не должно.

Но даже, если ты допросишься, это тебе мало чем поможет:

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

Поэтому, сиди тихо. Не рыпайся. :) Умные вопросы не задавай. Держи рот закрытым (обоими руками). На поца смотри восхищённо. Так, будто он один знает откуда берутся дети. А все свои сомнения и гени(т)альные мысли держи при себе. Фиксируй и проверяй их дома в эксперименте. Этим и занимайся.

Итак, препод это овца, которая непрестанно рядится в волчьи шкуры. Рычит и скалит зубы. Естественно, что такая овца видит в тебе ещё более паршивую овцу, чем она сама есть. Что же делать?

Схема проста. Пишешь исходник. Компилируешь. Устраняешь ошибки. Компилируешь снова. Дисассемблируешь объектный файл. Анализируешь. Корректируешь исходник. Повторяешь всё сначала. Повторяешь всё сначала. Повторяешь всё сначала. Много раз всё сначала. Осознаёшь, что добиться желаемого не возможно. Осознаёшь, что ты Хакер. :)

Меняешь компилятор. Повторяешь всё сначала. Ещё раз всё сначала. Окончательно осознаёшь, что ты офигенный Хакер, но добиться желаемого не возможно. Садишься писать собственный компилятор, стандартную библиотеку. Вскоре, кладёшь хер и на то, и на другое. :)

Сам становишься Педагогом. Учишься видеть паршивых овец в других. Что в итоге? - опыт! Ты приобрёл опыт! Закалил характер, ожесточил душу, на мякине тебя не проведёшь - ты (под)стрелянный воробей. Воробей. Не орёл. Пока.. Пока? :) Жизнь удалась.

Это многих славный путь. И как же пройти его по-быстрее? :) - не мучься, сразу переходи к последнему пункту. Ты сможешь! Доверься своему преподу. У него опыт. Уж он-то точно знает, кто мы есть. :)

Но вернёмся к нашим баранам. Не ведись на тупые разговоры, что Free BSD это круто и тому подобное. Твой препод гоняет на Бентли, но ездит тебе по ушам, что кодит только под Бсдюхой? - сними лапшу, это он так своих будущих конкурентов устраняет. То есть тебя. Очнись! Время пройдёт и вернуть его будет не возможно. Думай, чем ты семью будешь кормить! Рассказами о Free BSD?

Я уже рассказывал, какой крутой Pelles C компилятор, для обучения и развлечения. Типа в нём есть db и отладчики теперь не страшны. Не долго музыка играла, не долго фраер танцевал. Вот программка,

void bb(void){

_asm {db 'Hello world!',10,0}

}

void aa (void){

_asm {mov eax,bb; add eax,3; mov bb,eax}

printf(bb);

}

int main(void){

aa();

return 0;}

а вот дисасм под неё

_aa:

push EBP

mov EBP,ESP

mov EAX,offset FLAT:_bb

push offset FLAT:_bb

call _printf

pop ECX

mov ESP,EBP

pop EBP

ret

nop

Малёха не совпадает. :) mov eax,bb; add eax,3; mov bb,eax нигде нет. А вот ещё.

void aa (void){

int cc;_asm {mov eax,bb; add eax,3; mov cc,eax}

printf(cc);

return;

bb:

_asm {db 'Hello world!',10,0}

}

----------------------------------------

_aa:

push EBP

mov EBP,ESP

push EBX

mov EAX,offset FLAT:_aa[012h]

push EBX

call _printf

pop ECX

jmp short L12

L12: pop EBX

mov ESP,EBP

pop EBP

ret

nop

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

А теперь, ответ на главный вопрос. Как можно обойтись без использования #include? Очень просто. Скачайте нормальный файловый менеджер. Я люблю Total Commander. Установите, освойтесь. Попробуйте скомпилировать в вашем компиляторе простую программку.

int main(){

printf("Hello World\n");

return 0;}

Всё пройдёт на ура, кроме небольшого предупреждения, которое ни на что не влияет. Можно просто добавить строку int printf(); и это уже откровенный хак.

int printf();

int main(){

printf("Hello World\n");

return 0;}

А можно открыть Total Commander, в нём папку include, в ней, кнопочкой F3 - stdio.h, а в stdio.h ещё раз кнопочкой F3 найти определение printf, которое должно выглядеть примерно так int printf(const char* format, ...);. Теперь всё компилируется без предупреждений.

int printf(const char* format, ...);

int main(){

printf("Hello World\n");

return 0;}

С остальными непонятками поступаем так же. Есть поиск в группе файлов, в папке.

Ориентир простой. Искомое определение функции в *.h должно быть раньше её использования, т.е. быть самым первым появлением имени функции в данном файле и начинаться с типа - char, int и т.д. С константами ещё проще. Что делать, если определение функции не находится? Использовать пустое определение по типу int printf(); или написать самому.

Отказ от хедеров делает новичка осознанным. Приобретение опыта ускоряется в разы. Кроме того, в хедерах тоже бывают ошибки. А самое главное, комплекты хедеров одного компилятора совершенно не подходят к другим. А часто и к самому себе.

Поэтому, умение совсем обходиться без хедеров - неоценимый навык и увлекательный вид спорта - не секс.:)

Например, простая вроде бы программка, но у меня в Pelle C из командной строки компилируется с ошибками, которые вылезают, уже, при запуске :(. А при использовании чужих хедеров, вываливается куча ошибок, вообще.

#include <stdio.h>

int main(void) {

FILE *f = fopen("file.c", "r");


if (f)

{

while (!feof(f))

putchar(fgetc(f));

fclose(f);

}

return 0;

}

Можно применить хак, который вполне подходит ситуационно.

int main(void) {

int f;

f = fopen("file.c", "r");


if (f)

{

while (!feof(f))

putchar(fgetc(f));

fclose(f);

}

return 0;

}

А можно досконально восстановить всё самому из хедеров и это будет совершенно другой уровень понимания. Вот, что получилось лично у меня. Как пример :)

//(c)TereshkovOE

//file2.c tcc 9.27

//sites.google.com/site/excelmidi

typedef long unsigned int size_t;

typedef long signed int ssize_t;

typedef struct {

ssize_t _cnt;

char *_ptr;

char *_base;

size_t _bufsiz;

int _flag;

int _file;

char *_name_to_remove;

size_t _fillsize;

} FILE;

FILE * fopen(const char *_filename, const char *_mode);

int feof(FILE *_stream);

int putchar(int _c);

int fgetc(FILE *_stream);

int fclose(FILE *_stream);

int main(void) {

FILE* f = fopen("file2.c", "r");


if (f)

{

while (!feof(f))

putchar(fgetc(f));

fclose(f);

}

return 0;

}

Однако, всё не так просто. И всё то, о чём я уже рассказал, относительно. Так, например, в Dev-C++ v.5.11 из коробки с TDM-GCC 4.9.2. нельзя воспользоваться функцией int clock();. А определение printf выглядит более, чем оригинально:

__mingw_ovr

__attribute__((__format__ (gnu_printf, 1, 2))) __MINGW_ATTRIB_NONNULL(1)

int printf (const char *__format, ...)

{

register int __retval;

__builtin_va_list __local_argv; __builtin_va_start( __local_argv, __format );

__retval = __mingw_vprintf( __format, __local_argv );

__builtin_va_end( __local_argv );

return __retval;

}

Поэтому, не морочь голову преподу. Он всё равно по сути, как и ты, ни фига не знает, а если знает, то не скажет, а если скажет, то намеренно соврёт - нафига ему выращенные собственноручно конкуренты? У каждого свой путь, свой желудок, свой кошелёк. И каждый сам для себя решает на чьей он стороне. Что отдашь - твоим пребудет. Что не отдашь - потеряешь навсегда. Подумай.

Я не преувеличиваю. Оглянись вокруг и убедись в том, как охотно люди делятся друг с другом своей глупостью, подлостью, злобой, ненавистью, желанием утопить, уничтожить, обложить налогами, оштрафовать, лишить жилья, органов, пустить по миру. Как видишь, правило действует. И действует абсолютно. Всегда. Вопрос в том, что собираешься отдавать Ты?

Кстати, о преподах. Как-то Андрюшка Столяров загадал загадку. «Куда указывает выражение (m+1) в массиве int m[10][20][30];». При этом указал, что мы все - дети, студенты, ученики - тупые и неспособны ответить на этот вопрос, равно, как и вообще, этот вопрос понять или осознать. Особенно, без предварительного изучения Паскаля. Я решил Андрюшке помочь. Радикально. Пусть успокоится.

Паскаль я не изучал, как и Денис Ритчи :). Ибо, нафиг? И вот, что у меня получилось. Слева индексы. Справа n из выражения (m+n). Выводим результат в файл ...>dim.exe >> dim.txt. Заглядывая в который, Андрюшка, теперь, и не такое сможет залупить - А куда указывает выражение (m+1425) в массиве int m[10][20][30];, бля!? А вот сюда - m[2][7][15] :)

При компиляции использовался tcc 9.27 и вот эта

весьма удобная, простая и универсальная среда.

Написать ниже следующую прогу на асме, покумекать надо :). Имеется в виду - правильно написать :).

//(c)TereshkovOE

//dim.c tcc 9.27

//sites.google.com/site/excelmidi

extern int printf();

int main(void) {

int a,b,c,n;

int m[10][20][30];

n=0;

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

for (b=0;b<20;b++){

for (c=0;c<30;c++){

m[a][b][c]=n;n++;

printf("%d %d %d %d\n", a,b,c,m[a][b][c]);

}}}

return 0;

}

Естественно, что выше приведенная программка - шутка, основанная на моём наивном умозрительном предположении, что при помощи базового указателя и смещения к нему можно обратиться к любому элементу любого массива . Иначе, зачем бы Андгюшка Столягов и огород городил?

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

//(c)TereshkovOE

//dim6.c tcc 9.27

//sites.google.com/site/excelmidi

extern int printf();

int main(void) {

int a,b,c,n;

int *mm;

int m[5][4][3];

n=0;

for (a=0;a<5;a++){

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

for (c=0;c<3;c++){

m[a][b][c]=n;n++;}}}

// mm=&m[0][0][0];

// mm=(m + 1); // или mm=**(m + 1);

for (n=0;n<60;n++){

printf("%d %d\n", mm,*mm);

mm++;}

return 0;

}

Что эти две мои программки доказывают? То, что старина Ритчи нигде и никак не определил правила использования указателей применительно к массивам в таком виде, как тут предложено. Ему это просто не требовалось, в отличие от Андрюшки Столярова. Видимо, патриарх, который в общем-то не страдал от недостатка фантазии, в данном конкретном случае полагал, что простых индексов, для получения неограниченного доступа ко всем элементам любого массива, вполне достаточно. Наивный. :)

Испытываешь затруднения с массивами? Всё достаточно просто. Идея одномерного массива, обычно, понятна всем. Это некий непрерывный фрагмент памяти, к ячейкам которого можно обратиться в произвольном порядке, согласно заранее присвоенным индексам. Первой ячейке - 0, второй - 1, третьей - 2 и т.д. Причём, ячейка памяти и ячейка массива это разные понятия. В общем случае, они не совпадают. Полное соответствие только для символьных массивов.

Определяется массив так: тип-размерность базового элемента, имя, количество доступных элементов. Все подробности в учебниках. Например, int m[1000] - массив m из 1000-и целых. Поскольку первый элемент массива имеет индекс 0, обращение к 542-ому элементу записывается, как n=m[541]. Это может раздражать. Поэтому, задаёшь массив из n+1 элементов, а индекс 0 не используешь, или хранишь в нём дополнительную информацию.

Массив int m[1000] в памяти непрерывен и занимает 4000 байт для 32-х разрядных систем. Поэтому, его можно удобно использовать для всяких буферов обмена, юзая буковку "m" в качестве указателя. Наименьший структурный элемент массива int - 4 байта, char - 1 байт. Это важно для указательной арифметики. Поэтому, если ты используешь имена массивов не из инлайн ассемблера, если он есть, правила довольно строги. Именно поэтому, ты никогда не промажешь.

Что такое многомерный массив? Например, m[10][20][30]. Очень важно представлять себе его обозримую структуру. Допустим, у нас есть комната 42кв.м. Но не стандартная, а в виде коридора, шириной 1 метр. Соответственно, длина её буде 42 метра. Разделим эту комнату перегородками - участками в 1кв.м. Получим 42-е ячейки. В каждой будем хранить по одной целой табуретке разных цветов и размеров. Это табуреточный массив. Назовём его int m[42]. Табуретки ведь целые :).

Рядом с нашей, на этаже находятся ещё 3 однокомнатные, такие же, как и наша, квартиры - двумерный массив int m[4][42]. В подъезде 5 этажей - трёхмерный массив int m[5][4][42]. В доме 6 подъездов - четырёхмерный массив int m[6][5][4][42]. И так далее. При этом, базовым структурным элементом нашего многомерного массива, по-прежнему, является 1кв.м., а одна непрерывная запись не может превышать 42-е табуретки подряд. Всё остальное - [0-5][0-4][0-3] - предназначено для упорядочивания общей структуры и является указателем на конкретный подмассив m[42]. Таким образом, многомерный массив, это много раз одномерный массив. И не многомерный он, а многорядный. А одномерный массив, это много раз переменная. Копирайт мой :)). Собственно и всё. А ни какая не многомерная реальность - чушь :).

Проще говоря, не существует никакого массива m[42], но только набор отдельных переменных m[0], .., m[6],.., m[24],.., m[41]. Именно поэтому, в большинстве компиляторов нельзя выполнить операцию int *a; int m[42]; a=&m; без предупреждений, потому, что не существует никакой переменной m равно, как и массива m[42], но только переменные m[0], .., m[6],.., m[24],.., m[41]. Дошло? :)

Если быть совсем точным, переменной m не существует, но существует указатель на массив с этим именем. Который хоть и не переменная для хранения адресов переменных, по Ритчи, но всё равно указатель от этого ничуть не менее. Поэтому, int *a; int m[42][33][9]; a=&m; вполне ликвидна, в том числе и согласно Завета. Не ликвидна она только у задротов, написавших Pelle C, MSVC, GCC, TCC и прочего.

Мне известны всего два компилятора, оба за авторством David Lindauer, которые полностью соответствуют тому, что написал Великий Ритчи во втором издании Завета, относительно ссылок на функции и массивы. Но ты не должен обращать на них внимания. Работая за колбасу, ты всё равно будешь иметь дело только с MSVC.

Кстати, David Lindauer разработчик с богатым опытом. Вот цитата из его биографии: We added touch screen controls to our products back in the 80's. It is a fact that Steve Jobs saw the results of that work while buying manufacturing equipment for his plant.. Что как бы намекает. :)

С многомерным массивом то же самое, только индексы отдельных переменных длиннее. Единственное, что можно предполагать, что проиндексированные таким образом переменные будут в точности следовать друг за другом в отведённом им участке памяти. Собственно, за этим массив и создаётся.

Теперь, легко понять структуру. Структура, это разнотипный массив, к элементам которого обращаются по имени. Копирайт мой :)).

Зачем вообще придуманы массивы, кроме возможности обратиться по индексу? Допустим, для решения задачи надо 1000 однотипных переменных, следующих строго друг за другом. Описывать их в виде int a1,a2,a3,...,a1000; крайне утомительно и нет гарантий того, как именно они лягут в памяти. Поэтому и возникла форма m[n]. Твой препод тебе об этом не расскажет.

Из приведенного описания никак не следует аксиома физической непрерывности многомерных массивов. Вспомним структуру многоквартирного дома. Первичные записи m[42], в общем случае, могут располагаться в памяти в произвольном порядке. Хотя, массивы m[6][5][4][42] и m[5040] заполняют адресное пространство, всегда, абсолютно одинаково. Проверим, непрерывны ли многомерные массивы в памяти. А заодно, утрём Андрюшке Столярову нос. Покажем ему, блин, как это с помощью простых звёздочек делается.

//(c)TereshkovOE

//dim2.c tcc 9.27

//sites.google.com/site/excelmidi

extern int printf();

int main(void) {

int a,b,c,n;

int *mm;

int m[5][4][3];

n=0;

for (a=0;a<5;a++){

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

for (c=0;c<3;c++){

(m[a][b][c])=n;n++;}}}

mm=**m;

for (n=0;n<60;n++){

printf("%d %d\n", mm,*mm);

mm++;}

return 0;

}

Запускаем. Напряжённо всматриваемся в вывод программы. Перепроверяем всё ещё раз.

И с этой минуты, считаем непрерывность многомерных массивов в компиляторе tcc 9.27 доказанной. Скромно надеемся, что в будущем ничего не изменится :). Обращаем особое внимание на шаг приращения в правой и в левой колонке. Кстати, Pelle C безумно строгий, в плане соответствия синтаксиса, компилятор. Но и единственный с адекватными сообщениями об ошибках. Очень, очень рекомендую. Повторим ещё раз, первичным структурным элементом массива char является байт, а int - четыре байта.

Самое время послать нафиг Андрюшку Столярова, а вместе с ним и его долбаный хак. Для этого заменим строчку mm=**m;, как и положено, на mm=&m[0][0][0];. Задолбал. Аминь!

Между прочим, на зависть всем Великим Практикам, особо умные, в роде меня, просто взглянув на эту картинку, уже могут абсолютно точно сказать почему падает программа при попытке записи за пределами локального массива. И много чего ещё. Великим Практикам этого не дано.:)

С массивами можно проделать массу интересных вещей. Например, проверить позволяет ли ваш компилятор выходить за их пределы в + и в -. Что при этом возвращается? И в буквенном эквиваленте. Эдакий дампер памяти и внутрифункциевый самоотладчик. Есть ли разница для локальных и глобальных массивов? Элементарно, Ватсон! Дерзайте :).

Очень важно инициализировать локальные массивы перед началом работы с ними.

//dim3.c (c)Tereshkov_OE

//Pelle C

//sites.google.com/site/excelmidi

extern int printf();

int main(void) {

int a,b,c,n;

int m[10][10][10];

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

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

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

printf("%d ", (m[a][b][c]));

}}}

printf("\n\n");

printf("G_o_t_o_v_o_!");

printf("\n\n");

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

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

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

m[a][b][c]=0;}}}

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

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

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

printf("%d ", (m[a][b][c]));

}}}

printf("\n\n");

printf("G_o_t_o_v_o_!");

printf("\n\n");

n=0;

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

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

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

(m[a][b][c])=n;n++;}}}

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

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

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

printf("%d ", (m[a][b][c]));

}}}

}

Кроме того, программка иллюстрирует диалектическую двойственность бытия. :) Если сделать массив int m[10][10][10]; глобальным, он проинициализируется нулями сам. Что может стать причиной задержки старта при значительных размерах массива.

Если фишка самоинициализации не нужна, отключить её не возможно, ибо выполняется она на уровне операционной системы. Как мило. :) Запихнуть же локальный массив 400к и выше на стек так просто тебе не удастся. Придётся возиться с линкером. Ибо, как мы уже договорились, хороший компилятор интеллектом не обладает. А компиляторы у нас все "хорошие", без исключения.

Самое время ещё раз вернуться к нашим баранам. Так «Куда же указывает выражение (m+1) в массиве int m[10][20][30];». Тот, кто уже запускал dim6.c легко может сказать,что m указывает сюда [0][0][0], m+1 сюда [1][0][0], m+9 вот сюда [9][0][0], а m+1425 хер его знает куда, не смотря на то, что в массиве int m[10][20][30]; 6000 доступных элементов. Зачем Андрюшка Столяров с кентами, вообще, затрагивали этот вопрос, без предоставления объяснений? - хер их знает. От безделья и скудости ума, видимо.

Подведём печальный итог. При использовании имён массивов в указательной арифметике, язык Си оперирует многомерными массивами, как одномерными. При этом, количество доступных элементов в таком типа одномерном массиве определяется первым индексом после имени массива, всегда. А размер одного элемента в байтах - произведением всех последующих индексов, умноженном на размер базового типа массива. И кто бы мог подумать, бля!

Конгениально! Точнее, конгенитально. Это даже, не высокий порог вхождения - это банальный долбое#изм, когда требуется написать с десяток прикидочных программ, покрутить дисассемблер, прежде, чем понять, как та или иная языковая конструкция на самом деле работает. И так ли это на самом деле изначально задумывалось, или снова очередной дефект?

Много бумаги измарано по поводу идеального кода. Особенно, о себе кричащих именах массивов, структур, переменных. Чрезвычайно умиляет тот факт, что в русскоязычных переизданиях, посвящённых "злободневной" проблеме идеальности, все примеры названий почему-то оставлены на английском языке. Типа, а чё?

А ничё! - чё через плечо. Тупые папуасы так и не смогли прикрутить к своим компиляторам русский. И не собирались прикручивать. Ибо, пофиг им на всё это, просто пофиг. Доморощенные воятели, типа Андрюшки Столярова, при всей их офигенности, до высокого уровня понимания принципов написания компиляторов так и не дошли. И не дойдут. И никогда не дойдут.

Приятное исключение 1С. Но оно нас не касается. И что же делать? :) Извечный русский вопрос.

Если тебе, как и мне, надоело выковыривать из сознания английские эквиваленты, хороший выход транслит. Скачай подходящую программку или напиши её сам и больше не заморачивайся. Как вариант, комплексный премутатор, который транслитит русскоязычный исходник сам.

В сети есть пример русскоязычного типа-компилятора на базе GCC. Но чудак в своих изысканиях зашёл так далеко, что наиновейшая версия не воспринимает даже слово void, что есть bad :).

Написание такого премутатора русскоязычных имён переменных в транслит по ходу компиляции было бы делом пары вечеров, если не пары часов, но возня с кодировками :(. Дядюшка Билл прос(та)рался. Короче, не сейчас :).

Казалось бы. Но не всё так однозначно. В МГУ от студентов требуют только так: 3.2. В именах должны использоваться только английские слова. Транслитерации русских слов не допускаются.

Например, имя функции f является недопустимым. Имена функций calculate_square_root и calc_sqrt допустимы, но последнее предпочтительнее, так как короче и использует общепринятые сокращения. Имя функции vychislenie_kornya недопустимо, так как использует русские слова.

Приятно было встретиться ещё раз. Надеюсь, кому-то помогло. Расширились горизонты, развеялся туман и всё такое? :) Пока-пока.

(с) Терешков Олег Евгеньевич. Октябрь 2019.

Продолжим? Теперь, ты знаешь о массивах всё. Ну, кроме того, что массив m[0] определить не возможно. Что будучи переменными, элементы массива не могут принимать на себя аргументы функции. И много ещё чего.

int printf ();

int add(int a, int b, int c){

return (a+b+c);}

int main(void){

int d,e,f; d=e=f=5;

printf("%d", add(d,e,f));

return 0;

то же самое, но не работает

int printf ();

int add(int a[0], int a[2], int a[3]){

int a[3];

return (a[0]+a[2]+a[3]);}

int main(void){

int d,e,f; d=e=f=5;

printf("%d", add(d,e,f));

return 0;

А ещё, переменные и массивы не могут иметь одинаковые имена.

int main(void) {

int a,b,c,n,m;

int *mm;

int m[5][4][3];

m=4; - не катит

Голое имя массива в большинстве компиляторов не может использоваться, как указатель на массив - вызывает ошибку или предупреждение. По версии Ритчи, всё это - int *mm; int m[10][20][30]; mm=&m[0][0][0]; mm=&m; mm=*m; mm=m; mm=&(m+0+0+0); - одно и тоже. Но в мире есть всего два-три компилятора, способных отработать всё это многообразие без приключений.

Но если функция должна получить указатель на массив, голое имя массива подходит и даже очень. Интересно, можно ли передать ссылку на массив транзитом через 3 последовательных вызова функции из функции?

//try.c (c)Tereshkov_OE

//Pelle C

//sites.google.com/site/excelmidi

extern int printf();

int cc (int line[]){

return (line[0]+line[1]+line[2]);}

int bb (int line[]){

return cc(line);}

int aa (int line[]){

return bb(line);}

int main(void){

int line[3];

line[0]=3;line[1]=3;line[2]=3;

printf("%d ", aa (line));

return 0;

}

Ты всё понял? Точно? А я нет. :) Собственно, этот пример обман зрения и ничего не доказывает. Просто иллюстрирует идею высокого порога вхождения в Си. Переделаем его в более очевидный вариант. Старичок Ритчи нервно курит в сторонке. :)

//try2.c (c)Tereshkov_OE

//Pelle C

//sites.google.com/site/excelmidi

extern int printf();

int cc (int my[]){

my[0]=5;my[1]=11;my[2]=16;

return (my[0]+my[1]+my[2]);}

int bb (int myy[]){

return cc(myy);}

int aa (int myyy[]){

return bb(myyy);}

int main(void){

int line[3];

//line[0]=line[1]=line[2]=3;

line[0]=3;line[1]=3;line[2]=3;

printf("%d %d %d\n", line[0],line[1],line[2]);

printf("%d\n", aa (line));

printf("%d %d %d\n", line[0],line[1],line[2]);

return 0;

Такая мистическая конструкция

int *strp_i;

strp_i=(char*)strp_i+7;

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

с вероятностью 100% не останется не замеченной твоим компилятором, если он не cc386, а в PelleC/Lcc32/GCC и вовсе вызовет ошибку компиляции, причём, в том и другом, и в третьем случае ошибочными будут признаны разные строки, что при недостатке фантазии потребует перехода в инлайн ассемблер.

Написанный мной перекодировщик, уместившийся в 100 строках кода, после компиляции в MSVC или TCC работал неправильно, а после PCC, Lcc32, GCC и cc386 - правильно. В PelleC/Lcc32/GCC без индивидуальных допиливаний не компилировался, вообще.

100 строк кода, слегка отличного от "Hello world"! Поэтому, сколько иметь при себе компиляторов? - вопрос далеко не праздный и не риторический. :) Вопрос, скорее, в том - до каких пор ОНИ будут НАС иметь?

Отсюда, прямиком дефекацируется парадигма функционального программирования. Компиляторы сплошь и рядом тупые.

Написаны такими же тупыми. Лучше не станут. Разобрать 5000 строк сплошного кода, нафаршированного бесконечными указателями и goto, им не по силам, особенно в Си.

Поэтому, давайте бить программу на файлы, файлы - на функции, использовать локальные переменные, распределённую компиляцию. Но в общем случае и это не помогает. Ракеты по-прежнему падают.

А чтобы никто не догадался, что и они, и написанные ими компиляторы тупые, т.е. заявленные ими цели и задачи выходят далеко за пределы их выдающихся умений и возможностей, будем во всю глотку на всех перекрёстках орать о вреде глобальных переменных и оператора goto. Не программистский пакет под нас, а мы под него.

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

Вот уже программа побита на файлы, разделена на бесконечные функции и вдруг, в ответ на строку (char*)strp_i=(char*)strp_i+7; компилятор OrangeC, без единого намёка на уведомление об ошибке, демонстрирует полный игнор моего светлого гения. Работа уже проделана, тупорылость компилятора не прошибаема - остаётся только инлайн асм.

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

Вот и получается, что программировать приходится не на Си, а на локальной версии конкретного компилятора. Как и 30-40 лет назад, до введения всяких стандартов. 1000 раз прав Великий Практик. Средняя программа среднего вуза рассчитана на середнячка. А средний компилятор только на то, чтобы этот середнячок до седых волос с увлечением и очень восхищённо компилировал примеры из папки examples.

Хозяин - барин. Не нравится? - пиши свой. Почему так? Официально, Ритчи писал для себя всего лишь утилиту. Бессистемно писал. Беспланово. Что ему требовалось в данный момент, то в неё и включал. Что-то, как способы обращения к массивам, продублировано в ста вариантах. Что-то не включено совсем.

Запрограммировать адекватно получившийся обезьянник, как оказалось, не в состоянии никто, включая самого Ритчи. С введением новых стандартов, зоопарк только разросся. Поэтому, без опыта в этом многотрудном деле никак. Опыт = высокий порог вхождения. Другие языки ещё хуже.

Космогоническому языку Ада повезло гораздо меньше, чем Си. Всё, что я по нему видел, в лучшем случае, можно назвать спецификацией. А вот такого мануала, какой создали для своего любимца Керниган и Ритчи, для Ада за сорок лет так и не родилось.

Что же делать? Если ты никогда не программировал, но нацелен на быстрый результат, выбирай Purebasic. Если ты хочешь научиться приводить типы так, чтобы от зубов отскакивало, выбирай Pelle C. Если хочешь в дальнейшем получить работу, выбирай MSVC. Для страждущих невыносимой лёгкости бытия, TCC и cc386.

Лично я выбираю для себя наиболее адекватный, простой, очевидный и везде рабочий вариант mm=&m[0][0][0]; и буду всегда и везде его придерживаться. И пофиг мне на звездочки Андрюшки Столярова.

В заключение, анекдот, рассказанный самим Андгюшкой по секрету всему свету. Училась у него как-то на курсе молоденькая девочка. И считал её Андрюшка дурой-дурой. И такой конченой дурой, что страшно удивился, когда та, вопреки его стараниям, всё-таки получила диплом и распределилась на соседнюю кафедру. Там стала изучать руби, питон, лисп. Да так ловко, что вскоре защитилась и всё у неё встало хорошо, и начала она преподавать сама.

Мораль той басни такова. Если поверить Андрюшке, на кафедре МГУ сможет работать любая дура или дурак. И не только, но и защититься. Великой российской науке слава! Если не поверить Андрюшке, эта дура вовсе и не дура, а та самая, особа умная, которую Андрюшка ставил на место, а может быть даже и раком. Но не поддалась она. И только вывернулась из-под Андрюшки, так сразу и расцвела. Андрюшке Столярову позор!

Как выяснилось, тема, вынесенная в заголовок статьи, достаточно болезненная для господ педагогов. Вот откровение от Сани Чернова. "Кроме того, я не вижу ничего страшного в том, чтобы сказать «пока надо так писать, все поймете потом». Выскочек, которые, очевидно, самоутверждаясь, будут выступать с вопросами «а можно ли без #include» можно и нужно давить в зародыше. Ничего хорошего дискуссия с ними во время занятия не принесет." Сань, а до/после занятия? А такие свойства человеческой души, как любознательность, любопытство? Где угодно, только не в МГУ?

Видишь, ты уже не особо умный, а порядком поднадоевшая выскочка. Саня считает, что тебя надо давить. А это есть bad. И в первую очередь, для тебя. Не надо раздрачивать преподавателей, #include <stdio.h> для них святое. :). Иначе, что же они будут и преподавать? Чему учить? Если хочешь, чтобы препод тебя зауважал, спроси у них лучше, как выйти из тела функции по goto и не завалить стек при этом.

Спроси-спроси. :) А у Сашки и Андрюшки обязательно спроси. В Purebasic это стандартная фича. Я так думаю, что всё зависит от того куда именно рвануть, из какой функции подряд ты по goto выходишь и в каком именно компиляторе это рвание когтей происходит, может и не пустить. :) Но не уточняй. И про стек, тоже, сразу не говори. :)

Перед стариной Ритчи такой вопрос не стоял. Перед комитетом ANSI C, тоже. Перед твоим преподом и подавно. Но ты-то не лыком шит. Теперь вспомни, что значит это слово по-английски. Вот о нём-то, родимом, собственно и речь.