Pure C
Материал из Lurkmore
По техническим причинам запрос «С#» перенаправляется сюда.
| Halt! Страница огорожена от Легиона. Хочешь высказаться? Добро пожаловать в обсуждение. |
C (Си) — язык программирования, разработанный придуманный по приколу в расовой пиндосской компании Bell Labs в начале 1970-х годов Деннисом Ритчи. Является на сегодняшний день фактически самым низкоуровневым из языков высокого уровня, и, как следствие, предоставляет достаточно гибкие возможности по использованию ресурсов компьютера благодаря повсеместному использованию указателей и операторов приведения типа. Но на том всё и заканчивается: ООП, динамика, метапрограммирование — всё это реализовали в родственниках и потомках. Cреди программистов носит неофициальный титул «кроссплатформенного ассемблера». Ответственность за корректную работу программы целиком и полностью лежит на программисте, за что Си ненавидим быдлокодерами и, что важно, их начальством. Хорошо мотивированного project manager'а, писавшего когда-то в патлатой молодости на Java, легко можно ввести в ступор, предъявив часть проекта на Си.
Быдлокод на Си обычно чуть более, чем полностью состоит из переполняющихся буферов и битья памяти, а также является излюбленной мишенью для экспериментов кулхацкеров.
Содержание |
Мёртвый язык
Многие почему-то считают, что Cи мёртв. Когда-то на нём писался практически весь софт, и понятие «быть программистом» однозначно включало в себя «знать Си». Сейчас, конечно же, это не так, и Си успешно вытеснили практически отовсюду. Но красноглазики не унывают, и до сих пор многие кошерные софтинки (зачастую с весьма навороченным пользовательским интерфейсом) пишутся на голом Си (Wireshark, например). Но из-за того, что практически единственным вменяемым для такого рода джедайства фреймворком является GTK, на подвиг отправляются исключительно закоренелые гномосеки. Си никогда не умрёт, пока есть микроконтроллеры и нужно писать драйвера.
Есть, конечно, некоторые проблемы. Бородатые олдфаги, единственные, кто умеет писать ядра операционок, драйвера и системные службы, демонстративно отказываются учить что-то ещё. На самом деле, любой уважающий себя сишник знает не только С++, но и ещё с десяток других языков, главным образом для того, чтобы их обсирать. А на Си они любят писать потому, что код в таком случае получается короче и прямее. Поэтому ядра и драйвера в их пространстве пишутся только на Си, и ни одного на Perl’е и, тем более, PHP. Это злостный цинизм и несправедливая конкуренция. А системные службы написаны на Си чуть менее, чем все. Кое-кто ехидно предполагает, что наличие хоть какого-нибудь вменяемого exceptioning’а в Си могло бы исключить появление BSOD в 90-х. Но всем похуй.
Си и объекты
В середине 80-х некто Страус Трупп (наст. имя Bjarne Stroustrup, не путать с Леви Страуссом) решил продвинуть дальше идею кросскомпилируемого ассемблера. Бородачи, не сподвигнувшиеся выучить ООП [1] (а некоторые из них про него и не слышали, искренне считая все технологии после 1981 г. унылым говном), разжигают холивар, который, как правило, сводится к необходимости описывать каждый чих в ООП с одной стороны и необходимость изобретать уйму велосипедов для хоть какой-нибудь объектности с другой. Однако, как показывает практика, там, где задача предрасполагает к объектности сама по себе, не использовать готовый инструментарий в подавляющем большинстве случаев глупо.
Плюсики в названии С++ (Си Плюс Плюс) обозначают наличие в нём объектно-ориентированного программирования, реализованного, правда, с учётом местной специфики. Вообще-то исконные правила ООП, в том числе и алгоритмические, предполагают полную инкапсуляцию объектов. Объект должен сам решать, что делать когда его что-то попросят, а не выставлять наружу публичные методы, которые дёргает всякий, кому не лень. Настоящий инкапсулированный объект должен принимать снаружи сообщение, причём не в контексте вызывающего объекта, а в своём собственном. Потом думать, хочет ли он это сделать, делать это и в ответ посылать сигнал о результате действия. Хотя многим нравится, но это уже тема отдельного холивара.
Любопытно, кроме C++ у Си есть ещё один обратно-совместимый родственник: Objective-C, в котором попытка реализовать объектно-ориентированное программирование до конца увенчалась успехом. Яблочники даже на нём пишут свои поделия (в том числе и интерфейс к iPhone. Но почему-то отклика в массах это не нашло.
Си отличается крайней шустростью (быстрее только ассемблер и за столетия допиленный до совершенства фортран), то есть, конечно, шустростью отличаются программисты. Гений всегда готов написать на Cи или асме так, что будет тормозить на любом самом быстром кластере. И Cи предоставляет ему в этом просто невероятное море возможностей. Например, возможность невозбранно выстрелить себе в ногу. Только для этого надо указать на участок памяти, где лежит нога, по смещению наложить структуру, пройтись по её полям и прямым преобразованием типов (динамического тут нету) передать данные функции «выстрелить». Если что-то произойдёт не так, дадут циферку с номером ошибки. Или не дадут, если функция void. Да, try-catch конструкций тут тоже нет. Ну, то есть, если вы, конечно, хотите, то есть long jump… и даже вроде как есть библиотеки с готовыми реализациями исключений а-ля C++. тысячи их, и все говно.
Лютая, бешеная ненависть
ТруЪ Си люто бешено любим многими, но также и люто бешено ненавидим еще бо́льшими. Похоже, что середины здесь нет и никогда не будет.
Для начала можно упомянуть тот факт, что в C (и во всех производных языках) 1/3 будет равно 0. То есть выражение 1/3==0 истинно, а вот 1/3.0==0 — нет. При этом == как оператор равенства и знак равенства как оператор присваивания до сих пор взрывает неподготовленный специальным образом мозг чуть менее, чем напрочь, по ходу и в наше время являясь источником массы трудноуловимых ошибок у умников, которые не искореняют и даже не читают варнинги.
Во вторую очередь он ненавидим професси-аналами. Они его люто, бешено ненавидят по целому ряду причин.
Во-первых, потому, что отладка в нем может быть сильнейшей еблей мозгов, особенно когда чего-то выходит из своих границ и естественно входит в границы чего-то другого. Сейчас (на самом деле давно уже) стало полегче, а в старые добрые DOS-овские времена запуск некошерного кода зачастую приводил к полному зависанию ящика с необходимостью нажимать «Any Key» и ненажимание кнопки «Save» (в общем-то, кнопки тогда были не везде и обычно надо было давить Ctrl+S, F2 или чего еще) до запуска оной некошерной программы каралось ее некошерным перенабиванием и перепрограммированием в кошерную.
Кроме выхода за границы недозволенного была еще такая штука, как утечка памяти. Это вообще не ловилось никакими отладчиками, и нужно было долго (иногда очень) и вдумчиво (иногда очень) вчитываться в текст такой некошерной программы, чтобы понять, куда эта блядская память течет[2]. Опать-таки теперь сильно полегчало. И не потому, что професи-аналы стали круче, а потому, что памяти стало в 9000 раз больше и сейчас никого ниибёт. А в старые DOS-овские времена любой професси-анал легко мог доказать, что 640К явно ни для чего не достаточно.
Следующая замечательная вещь — рекурсия. И если продвинутый професси-анал мог даже объяснить, как считается C(m, n) рекурсивной функцией, то отладить рекурсивную прогу из более, чем трех строчек было выше его професси-анальных возможностей. Хороший, годный компилятор умеет оптимизировать хвостовую рекурсию, но сам язык этого не гарантирует.
Ну и, наконец, указатели. Это полный, ну совсем полный, просто терминальный пиздец. И ежели кто сомневается в этом и считает, что рекурсия вставляет больше, примеры:
- double (*(*f)(double(*)(double)))(double) — указатель f на функцию, принимающую указатель на функцию, принимающую и возвращающую действительное число, возвращающую указатель на функцию, принимающую и возвращающую действительное число.
- int (**f)(char *с) — двойной указатель на функцию, принимающую строку и возвращающую целое число
- int *(*f)(char *с) — указатель на функцию, возвращающую указатель на целое
- библиотечная void (* signal(int __sig, void (* __func)(int))) (int) из signal.h возвращает указатель на функцию.
В стандарте предусматривается произвольная вложенность подобного матана, причем объясняется довольно просто. Наличие гибкого механизма использования указателей вообще и указателей на функции в частности, при грамотном подходе, позволяет реализовывать крайне элегантные технические решения, совершенно несопровождаемые в дальнейшем.
Такая лютая, бешеная ненависть к фундаментальным понятиям языка не могла пройти незамеченной всякими Майкрософтами. Венцом мелкософтовской ненависти к труЪ Си является появление С#, который на самом деле собственная версия Жабы.
Эзотерика
int i=8, a1, a2; for (a1=a2=1; i>2; a1=(a2+=a1)-a1) /* быдлокодеры думают что это мозгоебалка для школьника, а на самом деле а1=а2, а не быдло незабывает что такое += */ i--;
Вычисление i-го числа из ряда Фибоначчи с непредсказуемым поведением программы в итоге. из серии «я знаю что в циклах в С/С++ можно писать всякую эзотерическую поебень».
!(n & (n-1))
Проверка: является ли n степенью 2.
"abcd"[0]
Классика: достаем нулевой символ из строки. Нумерация элементов массива начинается с нуля.
printf("%s",sizeof('C')==sizeof(int)?"C":"C++");
Один из способов проверки С или С++, помимо идентификатора __cplusplus. Алсо, многие реализации С до C99 не признают однострочных комментариев // языка С++, что может быть использовано для различия двух братских языков.
#include <math.h> result = (use_cos ? cos : sin)(M_PI);
Выбор функции.
while( *dst++ = *src++ ) ;
Копирование строк.
m(f,a,s)char*s; {char c;return f&1?a!=*s++?m(f,a,s):s[11]:f&2?a!=*s++?1+m(f,a,s):1:f&4?a--? putchar(*s),m(f,a,s):a:f&8?*s?m(8,32,(c=m(1,*s++,"Arjan Kenter. \no$../.\""), m(4,m(2,*s++,"POCnWAUvBVxRsoqatKJurgXYyDQbzhLwkNjdMTGeIScHFmpliZEf"),&c),s)): 65:(m(8,34,"rgeQjPruaOnDaPeWrAaPnPrCnOrPaPnPjPrCaPrPnPrPaOrvaPndeOrAnOrPnOrP\ nOaPnPjPaOrPnPrPnPrPtPnPrAaPnBrnnsrnnBaPeOrCnPrOnCaPnOaPnPjPtPnAaPnPrPnPrCaPn\ BrAnxrAnVePrCnBjPrOnvrCnxrAnxrAnsrOnvjPrOnUrOnornnsrnnorOtCnCjPrCtPnCrnnirWtP\ nCjPrCaPnOtPrCnErAnOjPrOnvtPnnrCnNrnnRePjPrPtnrUnnrntPnbtPrAaPnCrnnOrPjPrRtPn\ CaPrWtCnKtPnOtPrBnCjPronCaPrVtPnOtOnAtnrxaPnCjPrqnnaPrtaOrsaPnCtPjPratPnnaPrA\ aPnAaPtPnnaPrvaPnnjPrKtPnWaOrWtOnnaPnWaPrCaPnntOjPrrtOnWanrOtPnCaPnBtCjPrYtOn\ UaOrPnVjPrwtnnxjPrMnBjPrTnUjP"),0);} main(){return m(0,75,"mIWltouQJGsBniKYvTxODAfbUcFzSpMwNCHEgrdLaPkyVRjXeqZh");}
#include <stdio.h> main (int t, int _, char *a) { return!0<t?t<3? main(-79,-13,a+main(-87,1-_,main(-86,0,a+1)+a)):1,t<_?main(t+1,_,a) :3,main(-94,-27+t,a)&&t==2?_<13?main(2,_+1,"%s %d %d\n"):9:16:t<0?t<-72? main(_,t,"@n'+,#'/*s{}w+/w#cdnr/+,{}r/*de}+,/*{*+,/w{%+,/w#q#n+,/#{l+,/n\ {n+,/+#n+,/# ;#q#n+,/+k#;*+,/'r :'d*'3,}{w+K w'K:'+}e#';dq#'l q#'+d'K#!\ /+k#;q#'r}eKK#}w'r}eKK{nl]'/#;#q#n'){)#}w'){){nl]'/+#n';d}rw' i;# ){nl]!\ /n{n#'; r{#w'r nc{nl]'/#{l,+'K {rw' iK{;[{nl]'/w#q#n'wk nw' iwk{KK{nl]!/\ w{%'l##w#' i; :{nl]'/*{q#'ld;r'}{nlwb!/*de}'c ;;{nl'-{}rw]'/+,}##'*}\ #nc,',#nw]'/+kd'+e}+;#'rdq#w! nr'/ ') }+}{rl#'{n' ')# }'+}##(!!/") :t<-50?_==*a?putchar(31[a]): main(-65,_,a+1): main((*a=='/')+t,_,a+1): 0<t?main(2,2,"%s") :*a=='/'||main(0,main(-61,*a, "!ek;dc i@bK'(q)-[w]*%n+r3#l,{}:\nuwloca-O;m .vpbks,fxntdCeghiry" ),a+1); }
Тоже программа. А в C++ и C99 вышеприведённые примеры работать не будут, ибо нет неявного int.
ОС на один лист? Легко! Поддерживает многозадачность, гуй (c мышкой). Имеется загрузчик программ, встроенный интерпретатор команд и текстовый просмотрщик.
#define G(n) int n(int t, int q, int d) #define X(p,t,s) (p>=t&&p<(t+s)&&(p-(t)&1023)<(s&1023)) #define U(m) *((signed char *)(m)) #define F if(!--q){ #define I(s) (int)main-(int)s #define P(s,c,k) for(h=0; h>>14==0; h+=129)Y(16*c+h/1024+Y(V+36))&128>>(h&7)?U(s+(h&15367))=k:k G (B) { Z; F D = E (Y (V), C = E (Y (V), Y (t + 4) + 3, 4, 0), 2, 0); Y (t + 12) = Y (t + 20) = i; Y (t + 24) = 1; Y (t + 28) = t; Y (t + 16) = 442890; Y (t + 28) = d = E (Y (V), s = D * 8 + 1664, 1, 0); for (p = 0; j < s; j++, p++) U (d + j) = i == D | j < p ? p--, 0 : (n = U (C + 512 + i++)) < ' ' ? p |= n * 56 - 497, 0 : n; } n = Y (Y (t + 4)) & 1; F U (Y (t + 28) + 1536) |= 62 & -n; M U (d + D) = X (D, Y (t + 12) + 26628, 412162) ? X (D, Y (t + 12) + 27653, 410112) ? 31 : 0 : U (d + D); for (; j < 12800; j += 8) P (d + 27653 + Y (t + 12) + ' ' * (j & ~511) + j % 512, U (Y (t + 28) + j / 8 + 64 * Y (t + 20)), 0); } F if (n) { D = Y (t + 28); if (d - 10) U (++Y (t + 24) + D + 1535) = d; else { for (i = D; i < D + 1600; i++) U (i) = U (i + 64); Y (t + 24) = 1; E (Y (V), i - 127, 3, 0); } } else Y (t + 20) += ((d >> 4) ^ (d >> 5)) - 3; } } G (_); G (o); G (main) { Z, k = K; if (!t) { Y (V) = V + 208 - (I (_)); L (209, 223) L (168, 0) L (212, 244) _((int) &s, 3, 0); for (; 1;) R n = Y (V - 12); if (C & ' ') { k++; k %= 3; if (k < 2) { Y (j) -= p; Y (j) += p += U (&D) * (1 - k * 1025); if (k) goto y; } else { for (C = V - 20; !i && D & 1 && n && (X (p, Y (n + 12), Y (n + 16)) ? j = n + 12, Y (C + 8) = Y (n + 8), Y (n + 8) = Y (V - 12), Y (V - 12) = n, 0 : n); C = n, n = Y (n + 8)); i = D & 1; j &= -i; } } else if (128 & ~D) { E (Y (n), n, 3, U (V + D % 64 + 131) ^ 32); n = Y (V - 12); y:C = 1 << 24; M U (C + D) = 125; o (n, 0, C); P (C + p - 8196, 88, 0); M U (Y (0x11028) + D) = U (C + D); } } } for (D = 720; D > -3888; D--) putchar (D > 0 ? " )!\320\234\360\256\370\256 0\230F .,mnbvcxz ;lkjhgfdsa \n][poiuytrewq =-0987654321" " \357\262 \337\337 \357\272 \337\337 ( )\"\343\312F\320!/ !\230 26!/\16 K>!/\16\332" " \4\16\251\0160\355&\2271\20\2300\355`x{0\355\347\2560 \237qpa%\231o!\230 \337\337\337" " , )\"K\240 \343\316qrpxzy\0 sRDh\16\313\212u\343\314qrzy !0( " [D] ^ 32 : Y (I (D))); return 0; } G (o) { Z; if (t) { C = Y (t + 12); j = Y (t + 16); o (Y (t + 8), 0, d); M U (d + D) = X (D, C, j) ? X (D, C + 1025, j - 2050) ? X (D, C + 2050, j - 3075) ? X (D, C + 2050, j - 4100) ? X (D, C + 4100, ((j & 1023) + 18424)) ? 176 : 24 : 20 : 28 : 0 : U (d + D); for (n = Y (t + 4); U (i + n); i++) P (d + Y (t + 12) + 5126 + i * 8, U (n + i), 31); E (Y (t), t, 2, d); } } G (_) { Z = Y (V + 24); F Y (V - 16) += t; D = Y (V - 16) - t; } F for (i = 124; i < 135; i++) D = D << 3 | Y (t + i) & 7; } if (q > 0) { for (; n = U (D + i); i++) if (n - U (t + i)) { D += _(D, 2, 0) + 1023 & ~511; i = ~0; } F if (Y (D)) { n = _(164, 1, 0); Y (n + 8) = Y (V - 12); Y (V - 12) = n; Y (n + 4) = i = n + 64; for (; j < 96; j++) Y (i + j) = Y (t + j); i = D + 512; j = i + Y (i + 32); for (; Y (j + 12) != Y (i + 24); j += 40); E (Y (n) = Y (j + 16) + i, n, 1, 0); } } } return D; }
Компилировать gcc-3.4, полученная программа при запуске сгенерирует ядро, которое нужно запустить grub'ом. Ещё нужен fs.tar. Инструкция по сборке и запуску.
См. также
- Ассемблер
- Perl
- Программист
- Песня Write in C.
Примечания
- ↑ ООП закладывалось еще в 60-е, когда его толком и реализовывать-то было не на чем, поэтому его и не замечали.
- ↑ Справедливости ради нужно отметить, что тем же самым страдают все остальные языки общего назначения, в которых не реализован механизм автоматической сборки мусора. А особо одарённые быдлокодеры умудряются устраивать memory leaks даже в мусоросборной Жабе путём формирования так называемых «островов» в памяти — коллекций связанных между собой объектов, которые, однако, уже не используются самой программой.
|
[ + ] Любой программист без словаря поймёт, что такое Pure C
|
||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|
|
|||||||||||
