2014 dxdy logo

Научный форум dxdy

Математика, Физика, Computer Science, Machine Learning, LaTeX, Механика и Техника, Химия,
Биология и Медицина, Экономика и Финансовая Математика, Гуманитарные науки




Начать новую тему Ответить на тему На страницу 1, 2, 3  След.
 
 Экзотическое применение конструкций Си/Си++
Сообщение03.01.2008, 15:29 


21/03/06
1545
Москва
Предлагаю собрать в этой теме коллекцию экзотических, не очевидных, остроумных конструкций языков Си/Си++.

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

Данная тема может служить хорошим пособием "как не надо писать программы", но одновременно позволит почувствовать глубину, и, может быть, кому-то лучше понять данные языки.

У меня имеются две такие конструкции:

"Оператор передергивания":
Код:
int i;

++i--;

Кстати, в Си++ при соотв. перегрузке операторов ++ и -- может иметь практический смысл.

А вот эта конструкция мне встретилась то ли в Подбельском, то ли в Кернигане и Ричи:
Код:
int a[10];

5[a] = (int)(a + 5);

Оч. полезная конструкция - на ней мы тестируем тех, кто приходит на вакансию программиста :). Вопрос - а что в ней вообще происходит :).

 Профиль  
                  
 
 
Сообщение10.01.2008, 09:22 
Экс-модератор


17/06/06
5004
Вот такую штуку в книжке какой-то видел:

Код:
int flags=5; /* ну или что-то-там */
printf("-xon\n" + ((flags & 0x4) != 0));


Мы объясняем, что это значит, или даём подумать?
Я, кажется, немного понимаю вашу последнюю конструкцию :D

 Профиль  
                  
 
 
Сообщение10.01.2008, 12:13 
Заслуженный участник


31/12/05
1480
Код:
send(to, from, count)
register short *to, *from;
register count;
{
    register n = (count + 7) / 8;
    switch (count % 8) {
    case 0: do { *to = *from++;
    case 7:      *to = *from++;
    case 6:      *to = *from++;
    case 5:      *to = *from++;
    case 4:      *to = *from++;
    case 3:      *to = *from++;
    case 2:      *to = *from++;
    case 1:      *to = *from++;
               } while (--n > 0);
    }
}

http://en.wikipedia.org/wiki/Duff%27s_device

 Профиль  
                  
 
 
Сообщение11.01.2008, 00:24 
Экс-модератор
Аватара пользователя


23/12/05
12047
e2e4 писал(а):
Вопрос - а что в ней вообще происходит

Если мы тут, толпой покумекав, правильно решили, то идёт присвоение пятому элементу массива его адреса.

 Профиль  
                  
 
 
Сообщение11.01.2008, 02:02 


21/03/06
1545
Москва
AD писал(а):
Мы объясняем, что это значит, или даём подумать?

Конечно, даем подумать!

Код:
int flags=5; /* ну или что-то-там */
printf("-xon\n" + ((flags & 0x4) != 0));

Рискну предположить, что данный код выводит в stdout или "-xon\n", или "xon\n", в зависимости от состояния 2-го (считая с нуля, конечно) бита переменной flags.
Естественно, что функция printf в качестве первого аргумента ожидает const char *s, т.е. указатель на константу типа char. Т.к. само значение указателя (в отличие указываемой переменной) может изменяться, то операция его суммирования с целым типом - определена и законна. Результат - опять таки указатель на константу, только по следующему адресу относительно первоначального, если значение выражения ((flags & 0x4) != 0) - истина.
Кстати, последнее не совсем верно. Я лично не знаю компилятора, где бы результат логической операции, в случае ее истинности, представлялся бы значением, отличным от 1. Но стандарт, насколько мне известно, не ограничивает компилятор в этом - все что не ноль - то истина. Так что не исключена неработоспособность Вашего примера в некоторых компиляторах или при применении хитрой оптимизации.

tolstopuz, красиво, красиво, ничего не скажешь. Пока не прочитал Вики, не понял изюминки. Вообще-то, не уверен в правильной интерпритации операторов case, вложенных в более глубокий блок относительно своего оператора switch... Я бы запретил такие вольности :)
Кстати, мне еще не понятна конструкция:
Код:
send(to, from, count)
register short *to, *from;
register count;
{...}

Понятно, что старая нотация описания ф-ии, но что за тип имеет переменная count????

photon писал(а):
Если мы тут, толпой покумекав, правильно решили, то идёт присвоение пятому элементу массива его адреса.

Совершенно верно, в правой части равенства - подсказка - использование арифметической операции с указателем. Надо только твердо знать, что имя массива - это константный указатель на нулевой элемент массива, и применить коммунитативность операции сложения:
a[b] = *(a+b) = *(b+a) = b[a]

 Профиль  
                  
 
 
Сообщение11.01.2008, 02:34 
Заслуженный участник


31/12/05
1480
e2e4 писал(а):
Вообще-то, не уверен в правильной интерпритации операторов case, вложенных в более глубокий блок относительно своего оператора switch... Я бы запретил такие вольности :)
В приличных языках высокого уровня их уже запретили, а Си - это такой переносимый ассемблер. Вон в Chicken Scheme делают сборку мусора на стеке через alloca с отрицательным аргументом, и хоть бы что :)
http://home.pipeline.com/~hbaker1/CheneyMTA.html
e2e4 писал(а):
Понятно, что старая нотация описания ф-ии, но что за тип имеет переменная count????
Слово int можно пропускать, если есть какие-нибудь модификаторы. На самом деле в register short тоже пропущено int.

 Профиль  
                  
 
 
Сообщение11.01.2008, 05:46 
Заслуженный участник
Аватара пользователя


17/10/05
3709
:evil:
e2e4 писал(а):
Кстати, последнее не совсем верно. ‹…› Но стандарт, насколько мне известно, не ограничивает компилятор в этом - все что не ноль - то истина.

Вы, мне кажется, смешиваете два разных требования стандарта. По стандарту, всё, что не 0 воспринимается на входе как Истина. Но все операции, выдающие логическое значение, по стандарту обязаны выдавать 0/1. Конкретно, в данном случае, см. С-89 пп. 3.3.9 и 3.3.8

Мне, кстати, больше нравится такая форма (ещё менее очевидная):
Код:
printf("-xon\n" + (flags & 0x4 || 0));


e2e4 писал(а):
Вообще-то, не уверен в правильной интерпритации операторов case, вложенных в более глубокий блок относительно своего оператора switch

Согласно стандарту языка, (1) case 1: — это метка (специального вида), (2) на самом деле, switch () {} — это два оператора, switch() и составной {}, тоже относится и к do {} while(); и (3) переход внутрь составного оператора на метку разрешён. Соответственно, конструкция разрешена стандартами и С, и С++.

На мой взгляд (в отличии от высказанного в Википедии), изюминка конструкции состоит не в раскрытии цикла, а именно в пересечении switch и do - while. Duff на эту тему писал…

tolstopuz писал(а):
В приличных языках высокого уровня их уже запретили

Представления о приличиях у людей разные. Разные культуры, знаете ли. Мне так не один из прямых потомков С (С++, Java, C#) не представляется приличным. Например, потому, что уже к середине 70х было общеизвестно, что рамочной структуре программы должен соответствовать синтаксис языка, а не костыли фигурных скобок, которые все забывают.

 Профиль  
                  
 
 
Сообщение11.01.2008, 18:20 
Экс-модератор


17/06/06
5004
e2e4 писал(а):
Рискну предположить, что данный код выводит в stdout или "-xon\n", или "xon\n", в зависимости от состояния 2-го (считая с нуля, конечно) бита переменной flags.
Меня разгадали :D

photon писал(а):
Если мы тут, толпой покумекав, правильно решили, то идёт присвоение пятому элементу массива его адреса.
Что меня смущает - а какого типа у нас указатель "5"? Может быть, в записи 5[a] к нему прибавляется величина a, умноженная на sizeof типа 5? И тот же вопрос про сумму a+5. Хотя чего я спрашиваю, надо просто попробовать же и всё ...

Добавлено спустя 45 минут 23 секунды:

Программка
Код:
#include <stdio.h>
int a[10];
int main(void) {
   5[a]=(int)(a+5);
   printf("%d\n",a);
   printf("%d\n",5[a]);
   printf("%d\n",a[5]);
   return 0;
}
выдала
Код:
134518176
134518196
134518196
Таким образом, действительно 5[a]=a[5], а вот насчет a+5 - при сложении указателя и числа к указателю прибавляется число умножить на размер его типа.

 Профиль  
                  
 
 
Сообщение11.01.2008, 20:10 
Заслуженный участник
Аватара пользователя


17/10/05
3709
:evil:
Уже упоминавшийся стандарт C-89, п. 3.3.2.1 прямо говорит, что x[y] эквивалентно (*(x+(y)), при этом одно из выражений должно быть указателем, а другое — целым.

e2e4 писал(а):
Оч. полезная конструкция - на ней мы тестируем тех, кто приходит на вакансию программиста

Ну, не знаю. Вы компиляторы разрабатываете? Если нет, то зачем знание этих деталей стандарта? Вы полагаете, что такой код следует писать?

 Профиль  
                  
 
 
Сообщение12.01.2008, 01:50 


21/03/06
1545
Москва
AD писал(а):
Меня разгадали

Функиця printf вообще славится заморочками, поэтому я привык особенно внимательно относиться к ее применению :).

незваный гость писал(а):
Представления о приличиях у людей разные. Разные культуры, знаете ли. Мне так не один из прямых потомков С (С++, Java, C#) не представляется приличным. Например, потому, что уже к середине 70х было общеизвестно, что рамочной структуре программы должен соответствовать синтаксис языка, а не костыли фигурных скобок, которые все забывают.

По-моему, мы уже дискутировали с Вами на похожую тему :). Ни в коем случае не ставя под сомнение Ваше мнение и опыт, все-таки хотелось бы спросить: а какой язык на текущий момент лучше, чем Си/Си++ подходит для написания embedded-программ? К таким программам применяются четыре основных требования (как я для себя формулирую): маленькая ресурсоемкость, быстрота выполнения, переносимость, и возможность использования наиболее полно ресурсов целевой системы (например, я не представляю, как приспосабливаются такие языки как Паскаль и Ява к Гарвардской архитектуре процессора, не теряя при этом преимуществ использования указанной организации памяти и т.п.).

незваный гость писал(а):
Ну, не знаю. Вы компиляторы разрабатываете? Если нет, то зачем знание этих деталей стандарта? Вы полагаете, что такой код следует писать?

Нет, мы разрабатываем системы управления электрическими машинами (инверторы), САУ и т.п. Но если из пяти человек, пришедших на вакансию программиста, ровно пять не знают основ работы с указателями, распределением памяти и т.п.?
Конструкция 5[a] - это лишь начало разговора. Если человек пытается ее анализировать, употребляет фразу "имя массива - это указатель на нулевой элемент массива" - то с ним уже есть о чем говорить.
И, кстати, если человек в принципе догадывается, что значит эта конструкция, но молчит, не смея высказать свое предположение, то тут проверяются его коммуникационные способности и способ решения возникающих проблем.
А так, по большому счету, без этой конструкции не только можно, ну и нужно жить :).

 Профиль  
                  
 
 
Сообщение12.01.2008, 06:28 
Заслуженный участник
Аватара пользователя


17/10/05
3709
:evil:
e2e4 писал(а):
все-таки хотелось бы спросить: а какой язык на текущий момент лучше, чем Си/Си++ подходит для написания embedded-программ


Вы просите песен? Их есть у меня!

Я предъявляю несколько иные требования к языку разработки, поэтому на Ваш вопрос мне ответить даже легче. Modula-2 покроет полностью Ваши запросы, и ещё немного останется. Мой опыт её применения был весьма успешен.

Для меня важнее другие аспекты языка: (1) Наличие компилятора для моего процессора для меня существенно важнее переносимости программ. Те встроенные системы, с которыми сталкивался я, были очень консервативными в железной части. (2) Возможность увязки с ядром ОС и/или отладчиком. (3) Наличие людей, знающих язык и имеющих опыт работы с ним. И им Modula-2 не удовлетворяет.

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

Кстати, о каких же приличных языках Вы говорили (в свете Вашего высказывания)? Ни Java, ни С# Вам, видимо, не подойдут. А С++ Duff's Device отлично работает. (Кстати, рекомендую ссылку — это рассказ Тома Дафа из первых рук.)

Добавлено спустя 2 часа 45 минут 29 секунд:

e2e4 писал(а):
Но если из пяти человек, пришедших на вакансию программиста, ровно пять не знают основ работы с указателями, распределением памяти и т.п.?


Я тоже сталкивался с этим. Правда, я предлагал более простой тест: написать программу копирования строки :) . Коллекция насекомых и их объяснений была просто потрясающая. Даже если писали более или менее правильно, дальше было интересно — как человек реагирует на вопросы типа переполнения буфера и т.п. В общем, тест себя оправдал (на мой взгляд).

Post scriptum: наши точки зрения, видимо, очень близки там, где дело доходит до принятия практических решений. А оценка приличный/неприличный — да ну её.

 Профиль  
                  
 
 
Сообщение13.01.2008, 03:10 


21/03/06
1545
Москва
незваный гость писал(а):
Для меня важнее другие аспекты языка: (1) Наличие компилятора для моего процессора для меня существенно важнее переносимости программ.

Ну это подразумевается, на мой взгляд... О какой переносимости может идти речь, если под целевую систему нет компилятора? :)

незваный гость писал(а):
Наличие людей, знающих язык и имеющих опыт работы с ним. И им Modula-2 не удовлетворяет.

Как и 90% остальных языков кроме Си и Паскаля :).

незваный гость писал(а):
Нельзя жить в обществе и быть свободным от общества. Точно так же нельзя работать в компании, которая Вам не принадлежит, и иметь полную свободу выбора языка. Но считать его приличным? Извините!

К счастью для меня, я имею полную свободу в выборе языка (пока). Но это частный случай.
А приличным Си я тоже уже давно перестал считать. Просто особой альтернативы (по приведенным Вами же аргументам) нет для моих задач.
Но именно благодаря его (Си) "неприличности" и возможны всякие неочевидный конструкции, которым посвящена данная тема.

незваный гость писал(а):
...я предлагал более простой тест: написать программу копирования строки...

А использовать библиотечные функции можно было? :D :D :D

незваный гость писал(а):
дальше было интересно — как человек реагирует на вопросы типа переполнения буфера и т.п.

Кстати, я вот тоже на этом месте задумался: а как должна реагировать функция копирования строки на переполнение целевого массива? Она либо о его длине ничего не знает, либо копирует $n$ байтов, $n$ передается ей как аргумент... Разве возможны другие варианты?
Или Вы имели ввиду, что в функции копирования строки должен создаваться промежуточный буфер и т.п.?

незваный гость писал(а):
В общем, тест себя оправдал (на мой взгляд)

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

незваный гость писал(а):
Post scriptum: наши точки зрения, видимо, очень близки там, где дело доходит до принятия практических решений. А оценка приличный/неприличный — да ну её.

Полностью согласен! Споры в данном вопросе - бесконечны :).

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

Рамочная структура - я так понимаю структура с вложенными блоками?
Чем плохи фигурные скобки?
И как принципиально можно их избежать? (ясно что begin... end - это то же самое).

:idea: Мне кажется, следующее упражнение будет довольно близко к содержанию темы:
Если мне не изменяет память, то кто-то когда-то высказал полушуточное мнение, что язык программирования только тогда может называться языком программирования, когда на нем возможно написание программы, печатающей свой собственный текст.

Найти в интернете такую программу на Си не представляется сложным. Гораздо интереснее попробовать придумать ее самому. Кто возьмется?

 Профиль  
                  
 
 
Сообщение13.01.2008, 05:11 
Заслуженный участник
Аватара пользователя


17/10/05
3709
:evil:
e2e4 писал(а):
О какой переносимости может идти речь, если под целевую систему нет компилятора? Smile

Переносимость куда более сложное понятие, чем наличие компилятора. Например, когда Unix (написанный на С) переносили на 32 битную архитектуру, буквально grep'ом искали в исходняках строки 16 и 15. А Вы про четвёртое измерение…

С другой стороны, компилятор недолго и написать, было бы желание. Можно ведь использовать готовый кодогенератор, или сделать front-end существующего языка (как много лет был С++).

e2e4 писал(а):
А использовать библиотечные функции можно было?

Кстати, я вот тоже на этом месте задумался: а как должна реагировать функция копирования строки на переполнение целевого массива? Она либо о его длине ничего не знает, либо копирует $n$ байтов, $n$ передается ей как аргумент... Разве возможны другие варианты?

Библиотечные функции — нельзя. А что можно делать — это часть вопроса. Понимает ли человек, как передаётся выходной буфер, как передаётся входной (кто сказал, что он не может быть переполнен — а выход за границы памяти чреват обвалом). В общем, очень многие аспекты…

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

А это возможно (100% надёжно)? Есть ведь масса нюансов — например, знаковые или беззнаковые указатели, с каким типом совместимы указатели, и т.п.

e2e4 писал(а):
Рамочная структура - я так понимаю структура с вложенными блоками?

Под рамочной структурой я в данном случае понимаю то, что нормой для тела цикла, ветки оператора ветвления является более, чем одно предложение. с этой точки зрения синтаксис, предлагающий группировать гораздо удобнее. Сравните:
Код:
if (x > 5) {           if x > 5 then
  dothis();              dothis();
  dothat();              dothat();
} else {               else
  sleep();               sleep();
  sleepagain();          sleepagain();
}                      end

В первом случае Вам нужны три пары скобок: () для условия ветвления и {} для группировки операторов. Во втором случае, пары if-then, then-else, else-end сами являются таковыми скобками. Их невозможно забыть. Обратите также внимание, что программа стала намного более читаемой — в ней меньше условностей (а использование скобок в С — это условность).

Использование end — это некоторая условность. Апологеты структурного программирования предлагали для каждого оператора свою закрывающую скобку (fi, endif, чтобы назвать некоторые варианты). Это — неважно. Важно иное: формообразующим элементом является группа операторов, а всякий элемент имеет открывающее и замыкающее ключевое слово. Составной оператор вовсе становится ненужным.

Теперь сравните:
Код:
while (x < 5);         while x < 5 do
{
  doit(x);               doit(x);
  ++x;                   ++x;
}                      end

Во втором случае эта малозаметная ошибка просто невозможна. И опять, while-do и do-end образуют рамки.

e2e4 писал(а):
Чем плохи фигурные скобки?

(1) Тем, что их можно не писать и (2) тем, что они визуально засоряют программу.

 Профиль  
                  
 
 
Сообщение13.01.2008, 05:40 
Заслуженный участник


31/12/05
1480
незваный гость писал(а):
Код:
if (x > 5) {           if x > 5 then
  dothis();              dothis();
  dothat();              dothat();
} else {               else
  sleep();               sleep();
  sleepagain();          sleepagain();
}                      end
А так?
Код:
if x > 5:
  dothis()
  dothat()
else:
  sleep()
  sleepagain()

 Профиль  
                  
 
 
Сообщение13.01.2008, 06:01 
Заслуженный участник
Аватара пользователя


17/10/05
3709
:evil:
Так тоже очень даже не плохо. Я полагаю, это Python, мой излюбленный на данный момент язык. Но малопригодный для встроенных систем.

Обратите внимание: (1) после двоеточия идет список предложений, (2) рамочная структура образована отступами, (3) if и : образуют рамку условия.

Я могу ещё вспомнить Clarion: там в качестве заключителя использовалась точка, и принято было писать так:
Код:
if x > 0
  while y > 0
    something
. .
. Т.е., точка ставилась вровень с конструкцией, которую закрывала, но при этом обычным делом было многоточие

 Профиль  
                  
Показать сообщения за:  Поле сортировки  
Начать новую тему Ответить на тему  [ Сообщений: 38 ]  На страницу 1, 2, 3  След.

Модераторы: Karan, Toucan, PAV, maxal, Супермодераторы



Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей


Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете добавлять вложения

Найти:
Powered by phpBB © 2000, 2002, 2005, 2007 phpBB Group