Комментарии 78
Вах, какой C интересный. А на C++ implicit function declaration запрещены, насколько я помнимаю?
+3
а другие компиляторы ведут себя так-же?
+1
Microsoft ® 32-bit C/C++ Optimizing Compiler Version 16.00.30319.01 for 80x86
v.a = 1, v.b = 2
a = 1, b = 2
Microsoft ® C/C++ Optimizing Compiler Version 16.00.30319.01 for x64
«Программа выполнила недопустимую недопустимость» =)
Неправильно извлекаются параметры со стека.
v.a = 1, v.b = 2
a = 1, b = 2
Microsoft ® C/C++ Optimizing Compiler Version 16.00.30319.01 for x64
«Программа выполнила недопустимую недопустимость» =)
Неправильно извлекаются параметры со стека.
+1
У меня под рукой других нет, но мне тоже интересно, что у них выйдет на GNU/Linux
+1
Считаю, заключение должно быть в виде:
В С принято передавать функциям (и в свою очередь функции должны возврашать) указатели, а не забивать стек всяким хламом.
В С принято передавать функциям (и в свою очередь функции должны возврашать) указатели, а не забивать стек всяким хламом.
+4
Если мне не изменяет мой склероз, когда возвращаемый тип не указан компилятор считает что функция возвращает 'int', что явно отличается от реализации функции 'struct S'. И в зависимости от ABI может быть любое поведение, вплоть до порчи стека и невозврата из этой функции (например исключительная ситуация обращения по несуществующему адресу).
А если ещё отличается и механизм вызова (__attribute((__stdcall)) в GCC или __stdcall, __fastcall и т.п. в MSC)? Правда name mangling минимизирует вероятность таких ошибок, но при желании можно получить «результат».
Мораль проста и очевидна — прототипы функций необходимы. =)
А если ещё отличается и механизм вызова (__attribute((__stdcall)) в GCC или __stdcall, __fastcall и т.п. в MSC)? Правда name mangling минимизирует вероятность таких ошибок, но при желании можно получить «результат».
Мораль проста и очевидна — прототипы функций необходимы. =)
+4
Такое поведение на i386 идёт вразрез с принципом наименьшего удивления: какое мне, казалось бы, дело до возвращаемого значения, которым я даже не пользуюсь?
А прототипы — да, необходимы. Настолько, что gcc по-умолчанию даже не предупреждает об их отсутствии.
А прототипы — да, необходимы. Настолько, что gcc по-умолчанию даже не предупреждает об их отсутствии.
+1
Подобные эффекты можно получить (в зависимости от ABI, конечно), если передавать больше или меньше параметров, чем требует функция, независимо от возвращаемого значения (а в ранних реализациях C так и было, декларация функции описывала только возвращаемый тип). Думается, что ожидание какого-либо определённого поведения для вызова функции с неизвестным прототипом порочная практика =).
0
C такое C…
-5
Си требует от программиста умения соображать.
+1
Вы хотели сказать помнить много лишнего…
-5
Каждой задаче — свой язык разработки.
Ежу понятно что быстро набросать гуевое приложение под венду, если скорость работы его не важна, проще на C#.NET или подобном языке.
Однако если производительность имеет значение, то стоит взять Си, и иметь ввидуархитектуру машины «много лишнего», как вы выразились.
Ежу понятно что быстро набросать гуевое приложение под венду, если скорость работы его не важна, проще на C#.NET или подобном языке.
Однако если производительность имеет значение, то стоит взять Си, и иметь ввиду
+2
Однако если производительность имеет значение, то стоит взять Си
Если задача действительно сложна, и производительность реально имеет значение, то стоит выкинуть C/C++. И взять язык, позволяющий эффективнее заниматься оптимизацией. А не слежением за памятью или арифметикой указателей.
-1
язык, позволяющий эффективнее заниматься оптимизацией
имя, сестра, имя!
а арифметика указателей — это как раз применение знания особенностей архитектуры машины для написания более эффективного кода.
0
имя, сестра, имя!
Любой ЯВУ.
Где занимаешься задачей в терминах задачи, а не «байтов»/«указателей»/«шаблонов».
а арифметика указателей — это как раз применение знания особенностей архитектуры машины для написания более эффективного кода.
Это попытка считать себя умнее современных компиляторов. При этом значительно менее полезная по соотношению effort/result, чем высокоуровневая оптимизация алгоритма.
0
В любом языке программирования решать задачу вы будете в терминах языка программирования.
Я не вижу особой разницы между решением задачи в терминах языка А и решением ее же в терминах языка Б, немного меняется конечная модель, но по сути и там и там задача адаптируется к платформе разработки.
И да, я считаю, что задача, адаптированная к машине, будет в итоге скомпилирована (замечу. оптимизатор в Си тоже присутствует) в более эффективный код, чем адаптированная к некой другой платформе, логика которой отличается от логики машины.
Я не вижу особой разницы между решением задачи в терминах языка А и решением ее же в терминах языка Б, немного меняется конечная модель, но по сути и там и там задача адаптируется к платформе разработки.
И да, я считаю, что задача, адаптированная к машине, будет в итоге скомпилирована (замечу. оптимизатор в Си тоже присутствует) в более эффективный код, чем адаптированная к некой другой платформе, логика которой отличается от логики машины.
0
Я не вижу особой разницы между решением задачи в терминах языка А и решением ее же в терминах языка Б
За исключением того, что в терминах одного языка она может занимать избыточно много кода, основную часть которого будет составлять не описание алгоритма, а гоняние байтиков, игры с указателями и обезопашивание себя от игр с указателями, указание и приведение типов, и прочие интересные, увлекательные, глубокомысленные, тешащие самолюбие кодера, но для решения задачи совершенно не нужные вещи. За деревьями не будет виднhttp://feedproxy.google.com/~r/habrahabr/~3/U1LxAE96jiM/о леса.
А в терминах другого языка алгоритм будет описан компактно, потому что он достаточно высокоуровнев, чтобы не заставлять пользователя заниматься мурой.
Вот, наглядный пример: балансировка и вставка в red-black tree. Подумайте, почему на этой странице нет кода на C/C++.
И да, я считаю, что задача, адаптированная к машине, будет в итоге скомпилирована в более эффективный код
QuickSort на бейсике будет работать быстрее, чем сортировка пузырьком, написанная не то что на С с крутым оптимизатором — хоть на ассемблере. (Ну, опускаем «на достаточном большом объёме данных» и т.п. — мы тут достаточно взрослые люди, чтобы не придираться к мелочам). Потому что высокоуровневые оптимизации приносят больше эффекта, чем низкоуровневые. Низкоуровневые — это last resort, когда высокоуровнево уже больше никак не оптимизируешь.
Лет пять-десять назад были споры уровня «на ассемблере мой код будет быстрее, чем твой код на С/C++». Хватит, переболели — сейчас редко кто способен оптимизировать код руками так хорошо, как это делают современные компиляторы того же C/C++ и прочие фреймворки типа LLVM. Сейчас понемногу проходит мода и на машинно-ориентированные языки типа Fortran/C/C++ — потому что сформулированную высокоуровнево задачу компиляторы современных ЯВУ уже понемногу обучаются спускать до уровня адаптации к машине (после чего, может статься, компилироваться они будут с оптимизациями того же gcc). И слава богу.
0
Вы наверное меня не так поняли.
Я вроде бы нигде не утверждал, что кривой алгоритм на Си будет работать быстрее, чем хороший на языках более высокого уровня.
Я сказал лишь, что если концептуально алгоритм одинаков, то если его реализовать с учетом особенностей машины, он будет работать быстрее.
Я вроде бы нигде не утверждал, что кривой алгоритм на Си будет работать быстрее, чем хороший на языках более высокого уровня.
Я сказал лишь, что если концептуально алгоритм одинаков, то если его реализовать с учетом особенностей машины, он будет работать быстрее.
0
Сферический уже готовый алгоритм в вакууме — да. А вот если алгоритм ещё предстоит продумать и реализовать — то сделать это будет проще будет на ЯВУ.
0
«Алгоритмизация вообще» не зависит ни коим образом от целевого языка (я имею ввиду языки одной парадигмы — например процедурные). Когда я придумываю алгоритм решения задачи, я это делаю независимо от того языка, на котором буду его реализовывать.
0
Гипотеа Сепира-Уорфа в программировании проявляется как никуда нагляднее. А «иметь в виду языки одной парадигмы» вредно — слишком ограниченные программисты получаются.
0
как то вы все к словам цепляетесь.
Я не сказал, что следует иметь ввиду языки только одной парадигмы. Я сказал, что разница между языками одной парадигмы не на столько существенна, чтобы, желая разработать алгоритм решения задачи на C# например, я вдруг написал многократно лучший алгоритм, чем желая разработать его на Си.
Я не сказал, что следует иметь ввиду языки только одной парадигмы. Я сказал, что разница между языками одной парадигмы не на столько существенна, чтобы, желая разработать алгоритм решения задачи на C# например, я вдруг написал многократно лучший алгоритм, чем желая разработать его на Си.
0
But you will.
Размышляя об алгоритме, вы волей-неволей будете представлять его реализацию. И при этом, соответственно, даже в мышлении будете использовать средства, доступные в языке, и формируемые ими шаблоны проектирования. А в случае их ограничений, будете отвлекаться на их удовлетворение, а то и того хуже (человек слаб...) — на переадаптацию алгоритма, чтобы меньше было возиться с этими ограничениями.
Ну вот, например, наглядное сравнение сишника и питониста:
Там, где сишник думает о массиве данных, его размере и времени жизни, аллокации и деаллокации, питонист думает о списке, просто списке. (При этом если данные в списке должны храниться разнородные, питонист об этом даже не задумывается, а у сишника начинаются развлечения с указателями и приведениями типов). Простота ожидаемых структур данных повлияет на планируемую сложность алгоритма — думаете, нет?
Там, где питонист думает об использовании set или dict, сишник ни о чём не думает и тихо мечтает о C++ и STL. Там, где питонист, банально заменив квадратные скобки на круглые, превращает вычисление в ленивое, а список — в итератор, сишнику придётся размышлять, городить огород из коллбэков или нет. Там, где питонист думает о лямбда-функциях, частичном определении функций, замыканиях, сишник спит — в его алгоритмах таких кубиков не будет. Та же самая банальная сортировка, но компаратор для сортировки создаётся динамически, на основании каких-то вычислений… задумается ли вообще о такой возможности сишник? Или это там будет из области «высшей алгебры»?
Что-нибудь более практическое, с реальным примером? Мемоизация. Насколько сложно мемоизовать произвольную функцию в Си? Можно ли там вообще написать «мемоизатор общего вида» для функций, так что мемоизовать ещё одну функцию можно будет одной строчкой? В питоне что то, что другое — тривиальная задача; а значит, при продумывании алгоритма питонист сможет считать, что любая функция, в случае надобности, может быть мемоизована — у него и такой кубик есть — а что делать сишнику? А есть и более другие языки, в которых мемоизация может возникать автоматически…
Так что… как ни странно, вы вполне имеете все возможности для того, чтобы написать лучший алгоритм на ЯВУ более высокого уровня. Потому что, очевидно, у вас появляется возможность мыслить на более высоком уровне.
Размышляя об алгоритме, вы волей-неволей будете представлять его реализацию. И при этом, соответственно, даже в мышлении будете использовать средства, доступные в языке, и формируемые ими шаблоны проектирования. А в случае их ограничений, будете отвлекаться на их удовлетворение, а то и того хуже (человек слаб...) — на переадаптацию алгоритма, чтобы меньше было возиться с этими ограничениями.
Ну вот, например, наглядное сравнение сишника и питониста:
Там, где сишник думает о массиве данных, его размере и времени жизни, аллокации и деаллокации, питонист думает о списке, просто списке. (При этом если данные в списке должны храниться разнородные, питонист об этом даже не задумывается, а у сишника начинаются развлечения с указателями и приведениями типов). Простота ожидаемых структур данных повлияет на планируемую сложность алгоритма — думаете, нет?
Там, где питонист думает об использовании set или dict, сишник ни о чём не думает и тихо мечтает о C++ и STL. Там, где питонист, банально заменив квадратные скобки на круглые, превращает вычисление в ленивое, а список — в итератор, сишнику придётся размышлять, городить огород из коллбэков или нет. Там, где питонист думает о лямбда-функциях, частичном определении функций, замыканиях, сишник спит — в его алгоритмах таких кубиков не будет. Та же самая банальная сортировка, но компаратор для сортировки создаётся динамически, на основании каких-то вычислений… задумается ли вообще о такой возможности сишник? Или это там будет из области «высшей алгебры»?
Что-нибудь более практическое, с реальным примером? Мемоизация. Насколько сложно мемоизовать произвольную функцию в Си? Можно ли там вообще написать «мемоизатор общего вида» для функций, так что мемоизовать ещё одну функцию можно будет одной строчкой? В питоне что то, что другое — тривиальная задача; а значит, при продумывании алгоритма питонист сможет считать, что любая функция, в случае надобности, может быть мемоизована — у него и такой кубик есть — а что делать сишнику? А есть и более другие языки, в которых мемоизация может возникать автоматически…
Так что… как ни странно, вы вполне имеете все возможности для того, чтобы написать лучший алгоритм на ЯВУ более высокого уровня. Потому что, очевидно, у вас появляется возможность мыслить на более высоком уровне.
0
А так ли необходимы эти инструменты для реализации некоего алгоритма? оО
Возможно конечно мы разный круг задач решаем обычно, поэтому мне вот не приходит в голову ну ни одна практическая задача, которую решать на Си будет сложно из-за отсутствия каких-либо хитрых средств.
Возможно конечно мы разный круг задач решаем обычно, поэтому мне вот не приходит в голову ну ни одна практическая задача, которую решать на Си будет сложно из-за отсутствия каких-либо хитрых средств.
0
Вот как раз читаю статью fprog.ru/2009/issue3/dmitry-astapov-recursion-memoization-dynamic-programming/, где задачи динамического программирования решаются рекурсивно и с мемоизацией. Задача водосборов мне особенно понравилась — там алгоритм с мемоизацией явно вычислительно проще.
0
На днях закончу текущий проект и готов заключить своего рода пари — что я напишу на чистом C более быстрое решение любой задачи, чем ваше на питоне.
0
P.S. В разумных пределах, готов выделить на это сутки, не больше)
0
Не вижу особого смысла в сравнении компилируемого и интерпретируемого языка. А на компилируемых ЯВУ я пока не готов с вами соревноваться :)
Лучше сходите на projecteuler.net, посмотрите, хорошо ли берутся на чистом С задачки из разряда «решило 10000 человек или меньше». Тут может возникнуть проблема не уровня «более/менее быстрое решение», а уровня «решение есть/нет» :)
Лучше сходите на projecteuler.net, посмотрите, хорошо ли берутся на чистом С задачки из разряда «решило 10000 человек или меньше». Тут может возникнуть проблема не уровня «более/менее быстрое решение», а уровня «решение есть/нет» :)
0
Как будет свободное время, гляну обязательно.
До сих пор мне не встречалось ни одной задачи, не решаемой на чистом C.
Возможно это проблема опыта.
А интерпретируемые языки, безусловно, медленнее, поэтому мне было немного непонятно, почему вы привели в пример питон.
До сих пор мне не встречалось ни одной задачи, не решаемой на чистом C.
Возможно это проблема опыта.
А интерпретируемые языки, безусловно, медленнее, поэтому мне было немного непонятно, почему вы привели в пример питон.
0
До сих пор мне не встречалось ни одной задачи, не решаемой на чистом C.
Неправильная постановка вопроса. Согласно тезису Чёрча-Тьюринга, любой алгоритм в интуитивном понимании этого слова может быть представлен машиной Тьюринга; и соответственно, любой язык, на котором можно машину Тьюринга реализовать, способен реализовать и этот алгоритм.
Соответственно, вопрос не в том, решаема ли задача на чистом С (теоретически). Если она решаема на другом языке, то решаема и на С. Вопрос в том, насколько далеко ушёл язык от машины Тьюринга в плане комфорта :)
0
А интерпретируемые языки, безусловно, медленнее, поэтому мне было немного непонятно, почему вы привели в пример питон.
Исключительно как образец достаточно известного ЯВУ, на котором можно просто реализовывать все описанные мной высокоуровневые абстракции. Можно было привести в пример тот же Haskell — но там примеры были бы слегка сложнее в понимании.
0
P.S. Спасибо за дискуссию, в спорах рождается истина, мне было интересно расширить кругозор (мм, скорее наметить направления его расширения).
0
Ну и кстати, реализация мемоизации для произвольной функции без написания лишнего кода для каждой новой функции на Си мне пришла в голову после чуть более 15 секунд размышлений.
0
А расскажите, интересно.
В Python-е, к слову, мемоизация происходит примерно так:
— так выглядят немемоизованные функции.
— а так, соответственно, мемоизованные. Количество аргументов и типы определяет мемоизатор, после чего любое значение функции будет считаться самой функцией только один раз, дальше мемоизатор будет брать закэшированное значение. Это на случай, чтобы у нас с вами расхождения в ожиданиях или терминологии не возникло :)
В Python-е, к слову, мемоизация происходит примерно так:
def func1(a1, a2): ... some calculations return something1 def func2(b1, b2, b3, b4, b5, b6=v1, b7=v2): ... some calculations return something2
— так выглядят немемоизованные функции.
@memoize def func1(a1, a2): ... some calculations return something1 @memoize def func2(b1, b2, b3, b4, b5, b6=v1, b7=v2): ... some calculations return something2
— а так, соответственно, мемоизованные. Количество аргументов и типы определяет мемоизатор, после чего любое значение функции будет считаться самой функцией только один раз, дальше мемоизатор будет брать закэшированное значение. Это на случай, чтобы у нас с вами расхождения в ожиданиях или терминологии не возникло :)
0
Ну саму функцию, которая будет заниматься мемоизацией (кэшированием результатов) можно объявить как что-то вроде
void *func(void *f,...);
далее, внутри функции мы получаем значения всех параметров (кроме первого, это — сама мемоизируемая функция)), и проверяем (здесь разумеется надо использовать оптимизации для ускорения данной проверки), не было ли раньше вызовов этой функции с этими аргументами. Если было — возвращаем закэшированный ответ.
Иначе — вызываем функцию f (приводим ее к типу функции void *f(...)) со всеми переданными аргументами, запоминаем ее ответ и возвращаем его.
Здесь разумеется я очень грубо набросал и намеренно упустил много тонкостей.
1. Вы наверное скажете, что это неудобно, пользоваться исключительно функциями void *f(...).
Отвечу: пользоваться ими вовсе необязательно, достаточно собственно написать ту же одну строчку (ну да, чуть длиннее) для каждой конкретной функции. Строка эта — макрос. Например, нам нужна функция int func1(int x, float y).
Оригинальную функцию в этом случае объявляем как
void *func1_m(...), забирая аргументы, как в обычной функции с переменным числом аргументов, но зная, что это int и float. Возвращаем требуемый нам int, приводя к типу void *
Далее, пишем макрос:
#define func1(x,y) ((int)func(func1_m,x,y))
func здесь — наш мемоизатор
И пользуемся фукцией как совершенно обычной, при этом она окажется мемоизированной.
2. есть еще проблемы с типами и количеством аргументов и сооветственно с реализацией логики самого мемоизатора, но это тоже можно решить.
Безусловно, сам мемоизатор получится достаточно сложным, но я думаю, что реализация этого атрибута @memoize в питоне ничуть не проще :)
void *func(void *f,...);
далее, внутри функции мы получаем значения всех параметров (кроме первого, это — сама мемоизируемая функция)), и проверяем (здесь разумеется надо использовать оптимизации для ускорения данной проверки), не было ли раньше вызовов этой функции с этими аргументами. Если было — возвращаем закэшированный ответ.
Иначе — вызываем функцию f (приводим ее к типу функции void *f(...)) со всеми переданными аргументами, запоминаем ее ответ и возвращаем его.
Здесь разумеется я очень грубо набросал и намеренно упустил много тонкостей.
1. Вы наверное скажете, что это неудобно, пользоваться исключительно функциями void *f(...).
Отвечу: пользоваться ими вовсе необязательно, достаточно собственно написать ту же одну строчку (ну да, чуть длиннее) для каждой конкретной функции. Строка эта — макрос. Например, нам нужна функция int func1(int x, float y).
Оригинальную функцию в этом случае объявляем как
void *func1_m(...), забирая аргументы, как в обычной функции с переменным числом аргументов, но зная, что это int и float. Возвращаем требуемый нам int, приводя к типу void *
Далее, пишем макрос:
#define func1(x,y) ((int)func(func1_m,x,y))
func здесь — наш мемоизатор
И пользуемся фукцией как совершенно обычной, при этом она окажется мемоизированной.
2. есть еще проблемы с типами и количеством аргументов и сооветственно с реализацией логики самого мемоизатора, но это тоже можно решить.
Безусловно, сам мемоизатор получится достаточно сложным, но я думаю, что реализация этого атрибута @memoize в питоне ничуть не проще :)
0
Ошибся в макросе,
#define func1(x,y) ((int)func((void *)func1_m,x,y))
#define func1(x,y) ((int)func((void *)func1_m,x,y))
0
Безусловно, сам мемоизатор получится достаточно сложным, но я думаю, что реализация этого атрибута @memoize в питоне ничуть не проще :)
Я пользуюсь вот такой:
from decorator import decorator def getattr_(obj, name, default_thunk): "Similar to .setdefault in dictionaries." try: return getattr(obj, name) except AttributeError: default = default_thunk() setattr(obj, name, default) return default @decorator def memoize(func, *args): dic = getattr_(func, "memoize_dic", dict) if args in dic: return dic[args] else: result = func(*args) dic[args] = result return result
Она, естественно, поддерживает произвольное количество аргументов произвольных типов (с единственным ограничением: каждый аргумент должен быть hashable; иначе я бы сделал другую версию мемоизатора, чуть более длинную и слегка медленнее работающую, которая явно сериализует все аргументы), и произвольный тип возвращаемого результата. И это уже не «грубо набросанный пример», а «рабочий код».
0
Но вы всерьез уверены, что ваш вариант будет работать быстрее, чем мой? оО
0
Что вы имели в виду под словом «быстрее»? Мемоизатор? Он сам не интересен — интересно использование его в алгоритмах. Реализация на питоне? Тоже неинтересна, это уже особенности интерпретации/компиляции питона; если взять из приведённой сверху статьи мемоизатор для Haskell-а, то он будет работать сравнимо по производительности с вашим, но при этом будет намного проще и надёжнее в использовании (никаких кастингов, ага).
Интересно другое. У питонистов и хаскелистов есть уже готовые мемоизаторы (которые, в случае надобности, незначительными изменениями модифицируются как угодно — ну, например, для ограничения размера кэша, или для ограничения времени жизни закэшированного), которые можно накладывать на что попало — хоть на наивно определённую функцию вычисления чисел Фибоначчи или Аккермана, хоть на функцию SQL-запроса. И они эти мемоизаторы активно используют как компоненты более сложных алгоритмов (и именно благодаря тому, что мемоизаторы у них есть и легко используются, они и способны создавать эти «более сложные алгоритмы»).
У вас в С… ну, это ещё отлаживать и отлаживать :)
Интересно другое. У питонистов и хаскелистов есть уже готовые мемоизаторы (которые, в случае надобности, незначительными изменениями модифицируются как угодно — ну, например, для ограничения размера кэша, или для ограничения времени жизни закэшированного), которые можно накладывать на что попало — хоть на наивно определённую функцию вычисления чисел Фибоначчи или Аккермана, хоть на функцию SQL-запроса. И они эти мемоизаторы активно используют как компоненты более сложных алгоритмов (и именно благодаря тому, что мемоизаторы у них есть и легко используются, они и способны создавать эти «более сложные алгоритмы»).
У вас в С… ну, это ещё отлаживать и отлаживать :)
0
я уверен, что подобное решение для Си уже написано.
и отлажено, ага.
и кастинги != баги.
у вас я смотрю какое то совсем уж жуткое предубеждение к кастингам. как будто это вселенское зло.
и отлажено, ага.
и кастинги != баги.
у вас я смотрю какое то совсем уж жуткое предубеждение к кастингам. как будто это вселенское зло.
0
и кастинги != баги.
Полностью согласен.
(=) ≠ (⇒)
☺0
То, что кастинги обязательно приводят к багам, тоже неверно.
Это зависит скорее от опыта программиста.
Молотком можно гвозди забивать, а можно и пальцы ломать. Смотря кто пользоваться будет.
Впрочем, в некоторых случаях может быть нужно именно альтернативное применение молотка, но и тут нужен профессионал :)
Все зависит от задачи и от опыта исполнителя.
Это зависит скорее от опыта программиста.
Молотком можно гвозди забивать, а можно и пальцы ломать. Смотря кто пользоваться будет.
Впрочем, в некоторых случаях может быть нужно именно альтернативное применение молотка, но и тут нужен профессионал :)
Все зависит от задачи и от опыта исполнителя.
0
К слову сказать, на C++ (который 0x) мемоизатор может выглядеть так:
Использование предельно просто:
Лукап, понятно, в данном варианте — не самый оптимальный, но (как и в случае с питоном) можно легко сделать варианты мемоизаторов, которые будут делать это настолько оптимально, насколько захочет пользователь такого класса.
template<typename R, typename ... Args> std::function<R(Args ...)> memoize(R f(Args ...)) { auto l_f = [=](Args ... args) -> R { typedef std::tuple<Args ...> args_pack_t; typedef std::tuple<args_pack_t, R> memoize_val_t; static std::vector<memoize_val_t> values; args_pack_t args_pack(args ...); for (auto p = values.begin(); p != values.end(); ++ p) { if (std::get<0>(*p) == args_pack) return std::get<1>(*p); } R ret_val = f(args ...); values.push_back(memoize_val_t(args_pack, ret_val)); return ret_val; }; return l_f; }
Использование предельно просто:
int f(int a, double b) { return (a + 10) * b; } int main() { auto f_ = memoize(f); std::cout << f_(10, 20) << std::endl; std::cout << f_(10, 20) << std::endl; std::cout << f_(10, 40) << std::endl; return 0; }
Лукап, понятно, в данном варианте — не самый оптимальный, но (как и в случае с питоном) можно легко сделать варианты мемоизаторов, которые будут делать это настолько оптимально, насколько захочет пользователь такого класса.
+1
Если задача действительно сложна, и производительность реально имеет значение, то стоит выкинуть C/C++. И взять язык, позволяющий эффективнее заниматься оптимизацией. А не слежением за памятью или арифметикой указателей.
Любой ЯВУ.
Где занимаешься задачей в терминах задачи, а не «байтов»/«указателей»/«шаблонов».
Наверное именно исходя из этих соображений большинство игровых движков написано на ЯВЕ. Они кстати ещё и кросс-платформенными получаются забесплатно.
0
Так то оно так (ведь имеются в виду MMORPG-движки?)… Только вот какая картинка получится, если сравнить нагрузки, которые таки движки могут выдерживать?
0
Это был сарказм есличё :) ММОРПГ движки могут писать на ЯВЕ из соображений «должно работать долго и не течь», а не из соображений производительности. Они очень хорошо параллелятся, поэтому дешевле купить много железа, чем разрабатывать такую штуку на каком-нибудь С/С++ ради небольшого выигрыша в производительности.
А вот движки оффлайн-игрушек почему-то на ЯВЕ не пишут. Как не пишут почему-то Математики, Маткады, Матлабы, СаундФоржи, Кубейсы, ПинаклСтудии и СониВегасы.
А вот движки оффлайн-игрушек почему-то на ЯВЕ не пишут. Как не пишут почему-то Математики, Маткады, Матлабы, СаундФоржи, Кубейсы, ПинаклСтудии и СониВегасы.
0
Ну WoW явно не на яве написан…
0
Математики, Маткады, Матлабы
Не скажу за игрушечный Mathcad, но остальные два из них являют собой языки программирования сами. Поэтому частично написаны на себе. Сей почему-то не хватило.
0
Сарказм, увы, не распознал. :)
В плане MMORPG-движков (браузерных) — согласен. Был свидетелем, как один мой знакомый разрабатывал оный. Сначала пробовал на C++, потом (отчаявшись решить ряд вопросов в силу недостатка опыта в некоторых областях и очевидной сложности реализации некоторых аспектов именно на C++), переписал всё на java. Переписать получилось быстро и он остался доволен результатом (в целом), бо производительностью был не особо озабочен.
Про offline-игрушки — тоже согласен.
В плане MMORPG-движков (браузерных) — согласен. Был свидетелем, как один мой знакомый разрабатывал оный. Сначала пробовал на C++, потом (отчаявшись решить ряд вопросов в силу недостатка опыта в некоторых областях и очевидной сложности реализации некоторых аспектов именно на C++), переписал всё на java. Переписать получилось быстро и он остался доволен результатом (в целом), бо производительностью был не особо озабочен.
Про offline-игрушки — тоже согласен.
0
Однако если производительность имеет значение, то стоит взять Си, и иметь ввиду архитектуру машины «много лишнего», как вы выразились.
И да, кстати, а сильно производительности программы на Си помогает его слабая типизация? Или только создаёт проблемы (как в приведённом примере, но и не только)?
0
его слабая типизация дает гибкость в реализации решения задачи.
0
Это называется не словом «гибкость», а словом «хаки». Когда enum (казалось бы, совсем уж основа любого сложного типа) можно привести к int и обратно — это не гибкость, а усложняющий понимание хак (вызванный отсутствием средств удобной работы с тем же enum-ом — проверки границ диапазона и размера, например). Когда тот же enum нельзя использовать в качестве ключа для массива, и вместо этого используются какие-нибудь числа, которые конвертятся туда-сюда в enum с постоянныим перспективами вылезти за границы массива — это не «гибкость», а «на безрыбье». Когда указатель конвертится в int — это болезнь.
А гибкость дают, например, возможности создания анонимных функций, замыканий, или, ещё любопытнее, генерирование машинного кода в runtime. Но С/C++, к сожалению, такое и не снилось — они существуют в мире, где ещё не изобрели архитектуру фон Неймана.
А гибкость дают, например, возможности создания анонимных функций, замыканий, или, ещё любопытнее, генерирование машинного кода в runtime. Но С/C++, к сожалению, такое и не снилось — они существуют в мире, где ещё не изобрели архитектуру фон Неймана.
0
Мне совсем непонятно, почему «производительность» и «безопасность» для языка — взаимоисключающие понятия. btw, Опыта С/С++ и Java более чем достаточно.
0
Мне совсем непонятно, почему «производительность» и «безопасность» для языка — взаимоисключающие понятия.
Теоретически это не просто взаимоисключающие понятия, а скорее наоборот: реализация типобезопасного языка имеет все перспективы быть более производительной. чем реализация типоопасного языка. Хотя бы потому, что из строгости типов открываются возможности более агрессивных оптимизаций.
+1
Си требует от кодера заниматься всякой ненужной фигнёй.
-3
ANSI C таков что вы вообще можете вызвать f() с любым набором параметров
// a.c
#include
struct S {} a;
struct S f(void)
{
printf(«hello world\n»);
return a;
}
// b.c
#include
int main()
{
int a = 1, b = 2;
f(«what's up?»);
printf(«a = %d, b = %d\n», a, b);
return 0;
}
при этом возвращаемым значением будет int —
struct D {} d;
d = f(«what's up?»);
будет давать ошибку
error: incompatible types when assigning to type ‘struct D’ from type ‘int’
// a.c
#include
struct S {} a;
struct S f(void)
{
printf(«hello world\n»);
return a;
}
// b.c
#include
int main()
{
int a = 1, b = 2;
f(«what's up?»);
printf(«a = %d, b = %d\n», a, b);
return 0;
}
при этом возвращаемым значением будет int —
struct D {} d;
d = f(«what's up?»);
будет давать ошибку
error: incompatible types when assigning to type ‘struct D’ from type ‘int’
0
В данном случае переданные параметры совпадают с тем что ожидает вызываемая функция.
Речь о куда более тонком эффекте.
Речь о куда более тонком эффекте.
0
функция ожидает void а отнюдь не char *
+1
В данном — в моём.
Функции ожидающие меньше параметров чем им фактически передают отлично работают в С, пока ожидаемые параметры подходят по типу. В вашем случае char * безболезненно болтается на стеке. И если бы функция возвращала целое или указатель, всё работало бы на ура.
Функции ожидающие меньше параметров чем им фактически передают отлично работают в С, пока ожидаемые параметры подходят по типу. В вашем случае char * безболезненно болтается на стеке. И если бы функция возвращала целое или указатель, всё работало бы на ура.
0
ок, я имел в виду что в C передать внутрь можно все что угодно, также можно все что угодно вернуть — подводных и надводных камней при этом будет вагон и маленькая тележка. то что неверное определяемый тип возвращаемого значения может портить данные внутри вызываемой функции ( даже при правильных типов параметров ) всё таки логично — в момент формирования стека ( при вызове ) у компилятора неверное понимание его структуры.
0
Вроде бы это даже компилироваться не должно, поскольку в момент компиляции файла b.c функция f(struct S) не объявлена и не определена, т.к. отсутствует соответствующий инклуд.
+1
Пардон. Написал, не вчитавшись.
Однако такое компилироваться не должно. Если компилер это делает, то это неправильно.
Однако такое компилироваться не должно. Если компилер это делает, то это неправильно.
+1
Почему же не должно? Вполне соответствует стандарту:
«If the expression that denotes the called function has a type that does not include a
prototype, the integer promotions are performed on each argument, and arguments that
have type float are promoted to double.» Раздел 6.5.2.2, п. 6. В конце этого же пункта говориться об UB в случае несоответствия реальному прототипу функции, которая будет вызвана.
Ну а gcc по этому поводу ещё и предупреждение выдаёт.
«If the expression that denotes the called function has a type that does not include a
prototype, the integer promotions are performed on each argument, and arguments that
have type float are promoted to double.» Раздел 6.5.2.2, п. 6. В конце этого же пункта говориться об UB в случае несоответствия реальному прототипу функции, которая будет вызвана.
Ну а gcc по этому поводу ещё и предупреждение выдаёт.
0
«Соответствует стандарту» и «правильно с общечеловеческой точки зрения» — разные вещи. В чем глубокий смысл такого поведения?
0
Ну, общечеловеческая точка зрения — понятие весьма относительное. Глубокий смысл такого поведения раскрыть не смогу, поскольку мне он не известен, но рискну предположить (согласившись с вышеотписавшимися), что такое поведение — унаследовано от ранних реализаций C и оставлено из соображений backward compatibility. Плюс к этому добавляет определённую степень свободы (нет необходимости таскать прототипы функций), и, как следствие, определённое количество проблем.
0
0
Стандарт С89 допускал неявное объявление функций, а С99 — нет. Добавляем параметр -std=c99 и наслаждаемся ошибкой:
b.c: In function 'main':
b.c:8: warning: implicit declaration of function 'f'
b.c: In function 'main':
b.c:8: warning: implicit declaration of function 'f'
0
Зарегистрируйтесь на Хабре, чтобы оставить комментарий
О неявных объявлениях, обратной совместимости и ABI