Главное — чтоб самому нравилось

вторник, 19 апреля 2011 г.

Пуре Си

Однажды Никлаус Вирт приехал в Испанию и спросил:
— Вам нравится Паскаль?
— Си! Си! — закричали испанцы.
(анекдот с бородой)



Язык Си очень старый. Шутка ли — разработан в начале 70-х уже давно прошедшего столетия. Но в тоже время остаётся вечно молодым, так как занимает свою уникальную нишу куда другие языки предпочитаю не соваться. К тому же этот язык очень компактный, подробное описание стандарта новейшего диалекта — C99, составляет всего около 170-и страниц, и это с примерами, между прочим. Сравните 170 страниц с книжками Страуструпа, где он поверхностно описывает концепции C++ буквально всего-то на 700 страницах.

Но тем не менее, синтаксис языка содержит несколько довольно непривычных глазу конструкций, от которых даже бывалые зубры могу воскликнуть: “WTF?!!”. Итак, кто в теме проверяйте себя.

int foo(a, b, c)
int a, c;
float b;
{
printf(
"a = %d, b = %0.2f, c = %d\n", a, b, c);
}

int main()
{
foo(3, 23.45, 5);
return 0;
}

Желающие могут проследовать по ссылке на codepad и покрутить всё самостоятельно, нажав fork. Такое странное объявление типов передаваемых аргументах пришло к нам из старинных диалектов языка Си, ещё со времён Кирилла и Мефодия Кернигана и Ритчи. В словаре напротив такой конструкции было бы написано “устар.”. Сейчас так уже никто не пишет, но такой код можно встретить, например, в старинных реализациях TCP/IP.

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

Зато следующие синтаксические фишки, наоборот взяты из стандарта C99. Это очень молодой стандарт, ему всего-то чуть больше десяти лет, и даже не все производители компиляторов решаются связываться со столь необстрелянным стандартом.

typedef struct
{
int a;
int b;
float c;
} s_t;

s_t my_struct1 = {.c = 3.0, .a = 2};
s_t my_struct2 = {.a = 4, .b = 2, .c = 4.2};



Инициализация полей структур при объявлении через точку. Поля можно инициализировать в произвольном порядки и не обязательно все сразу.

Причём структуры могут быть вложенными:

typedef struct
{
int a;
int b;
float c;
} s1_t;

typedef struct
{
s1_t a;
int b;
} s2_t;

s2_t my_struct = {.a.a = 4, .a.b = 3, .b = 2};



Похожая тема - это инициализация массивов.

int a[] = {[0] = 3, [2] = 7};



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

И тем более удивительно для Си выглядит вот такая инициализация массива.

int a[] = {[3 ... 5] = 3, [7] = 7};



Перед и после троеточия должны стоять пробелы.

Ну и чтобы никто не расслаблялся, комбинация всех перечисленных способов инициализации:

struct
{
int a;
int b[3];
} c[] = {[0].a = 1, [1].b[0 ... 2] = 2};



Что интересно, финт с пустыми скобочками для члена int b[] компилируется, но при выполнении происходит Segmentation fault :-).

Ну и последние две не очень известные фичи, тоже про статический Си, но уже на тему макросописательства.

#define Create_instance(_name, _a) \
struct \
{ \
int a; \
char name[]; \
} Instance_##_name = {_a, #_name};


Create_instance(abc, 1);
Create_instance(def, 2);

int main()
{
printf(
"Instance_abc.name = %s, Instance_abc.a = %d\n",
Instance_abc.name, Instance_abc.a);
printf(
"Instance_def.name = %s, Instance_def.a = %d\n",
Instance_def.name, Instance_def.a);

return 0;
}



Макрос Create_instance объявляет переменную имя которой склеивается c помощью токена ## из Instance_ и переданного аргумента макроса _name. В данном примере с помощью макросов объявляется две глобальные структуры. Instance_abc и Instance_def. Обе структуры сразу инициализируются. Поле a, переданным значением типа int, но это не так интересно, а вот поле name предствляющее из себя строку, для этих двух структур получит значинея “abc” и “def” соответсвенно. Для этого используется токен #, который следующее за ним слово заворачивает в кавычки. Надеюсь, я не слишком перемудрил, и аутпут прояснит это.

Instance_abc.name = abc, Instance_abc.a = 1
Instance_def.name = def, Instance_def.a = 2


Итак, сколько "WTF?!!"-ов насчитали вы?


PS. Всё это богатство не работает без специальных ухищрений в C++. Плюсовики, плачьте!

Комментариев нет:

Отправить комментарий