Форум » C/C++ » Шутка - ложь, но в ней намек, добрым молодцам урок. » Ответить

Шутка - ложь, но в ней намек, добрым молодцам урок.

Сыроежка: Это не выдумка, а реальное сообщение, написанное на одном сайте: [quote]Решил я изучать C++. Но вот беда - перепробовал уже 9 книг - в каждой один и тот же первый пример - Hello world. И в каждой он не верен, если верить visual studio c++. [/quote] Перефразируя известное выражение, что если раз дали в морду, второй раз дали в морду, третий раз дали в морду, то может быть дело в морде?!

Ответов - 63 новых, стр: 1 2 3 4 All

Сыроежка: Еще одно забавное высказывание одного начинающего программиста: so i have been wanting to make a OS but can anyone give some help by telling me were to start and any other info that may help? Написано следующее: Мне захотелось создать операционную систему, поэтому не мог бы кто-нибудь сказать, с чего начать, а также предоставить другую информацию, которая может быть полезной. Это только по молодости можно строить такие наполеоновские планы тем более. не имея опыта программирования. Дополнение Похоже, это непреодолимая и бессмертная тема в программировании, как тема любви в художественной литературе, касательно начинающих программистов, которая не зависит ни от национальной, ни от территориальной принадлежности. Это, можно сказать, болезнь "переходного возраста" программистов, которой начинающие программисты должны переболеть, как многие из нас переболели в детстве "ветрянкой".. На днях опять вычитал на одном, уже российском форуме такое сообщение начинающего программиста (главное достоинство этого сообщения состоит в том, что на этот раз мне не требуется переводить его с иностранного языка на русский ): Хочу написать ос на c++ но не знаю как помогите и подскажите с чего начать. А ещё лучше если кинете мне статью или видео-урок по написанию ос на c++ Вы не находите, что это сообщение является почти идентичной копией первого сообщения на английском языке, учитывая то, что первое сообщение также было написано в разделе форума, посвященном языку C++?

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

PSP : Сыроежка пишет: Мне не понятен ни ход твоих мыслей, ни смысл сообщения в 4# посту. это нормально, я точно также не понимаю, что пишут другие. Супер!!!


Сыроежка: Если ваш труд оценивают по количеству набранных знаков, то вы можете включить в свой арсенал средств по увеличению объема кода следующий прием, используемый при объявлении переменных: [pre2]const unsigned const long const long const int x = 10;[/pre2] На первый взгляд эта конструкция кажется ошибочной, но на самом деле это есть объявление [pre2]const unsigned long long x = 10;[/pre2] Компилятор просто отбросит лишние квалификаторы const.

Сыроежка: Часто начинающие изучать С++ задают такой вопрос: чем отличается ++count от count++. Более опытные программисты им быстро разъясняют, чем прединкриметная операция отличается от постинкриментной. Естественно акцент делается на то, какой результат возвращают эти два выражения.. Но при этом обычно забывается еще один очень важный аспект, определяющий различие этих операций. И когда указываешь на него в виде примера записи выражения, то это порой удивляет даже опытных программистов. Так что это за аспект, о котором забывают, и как его более наглядно продемонстрировать? Все просто: разница между ++count и count++ состоит в том, что в первом случае вы можете указать любое четное число плюсиков, а во втором случае вы можете указать всего лишь только два плюсика! ++++++++++count; и только count++; Это справедливо для C++. Однако в C первое предложение выражения не будет компилироваться. В C вы можете использовать только два плюсика. ++count;

Сыроежка: А знаете ли вы, что в самом большом целочисленном типе С/С++ unsigned long long может разместиться ровно 20 факториалов? 0: 1 1: 1 2: 2 3: 6 4: 24 5: 120 6: 720 7: 5040 8: 40320 9: 362880 10: 3628800 11: 39916800 12: 479001600 13: 6227020800 14: 87178291200 15: 1307674368000 16: 20922789888000 17: 355687428096000 18: 6402373705728000 19: 121645100408832000 20: 2432902008176640000 Максимальное значение самого типа unsigned long long равно 18446744073709551615. Вот как соотносятся максимально вмещаемый факториал в объект данного типа и максимальное значение объекта данного типа: 2432902008176640000 18446744073709551615

Сыроежка: А для типа unsigned int можно вычислить лишь 12 факториалов. Вот они: 0: 1 1: 1 2: 2 3: 6 4: 24 5: 120 6: 720 7: 5040 8: 40320 9: 362880 10: 3628800 11: 39916800 12: 479001600 При этом максимальное значение объекта типа unsigned int равно 4294967295. Я это потому написал здесь, что часто начинающие программисты об этом совершенно не задумываются, когда используют вычисления факториалов. Вот пример размышления начинающего программиста, взятый с форума www.cplusplus.com Его задание выглядет так: Write a program that computes the value of e-x by using the formula: e-x= 1- x/1! + x2/2! - x3/3! + … То есть требуется вычислить ряд, в знаменателе каждого члена которого используется факториал. Далее этот навичок пишет цикл и рассуждает, сколько итераций должно быть в цикле, приходя к выводу, что самое подходящее число для итераций - это 30! Причем для хранения факториалов он использует даже тип не unsigned int, а просто int! int factorial=1; for(int i=1;i<=30;i++)//I don't know which number to chose because for high values when the compiler compiles i get values which say IDK..i thought 30 was the best choice..30! for instance is high enough)

Сыроежка: Интересно отметить, что оказывается количество факториалов, которые можно разместить в объекте типа unsigned int в точности совпадает с количеством факториалов, которые можно разместить в объекте типа int, то есть 12 факториалов. Вот значения факториалов для объекта типа int: 0: 1 1: 1 2: 2 3: 6 4: 24 5: 120 6: 720 7: 5040 8: 40320 9: 362880 10: 3628800 11: 39916800 12: 479001600 Максимальное значение объекта типа int равно 2147483647. Кстати сказать, то же самое справедливо и для типа long long, то есть количество значений факториалов, которые можно разместить в объекте этого типа совпадает с количеством значений факториалов, которые можно разместить в объекте типа unsigned long long, - ровно 20 факториалов. Я оговорился, сказав, что может бть всего лишь 20 и 12 значений факториалов для объектов соответственно типов unsigned long long/long long и unsigned int/int. На самом деле число факториалов на 1 больше, так как факториалы вычисляются, начиная с 0. То есть число фаториалов для этих типов 21 и 13. 20 и 12 - это максимальные значения неотрицательных чисел, факториалы которых могут уместиться соответственно в объектах указанных типов. Те, кому интересно посмотреть значения факториалов для больших значений чисел, могут использовать следующую программу, написанную мною на C#. Она выдает значения факториалов для чисел от 1 до 50. Вам достаточно будет изменить условие цикла в статическом методе Factorial, если вы хотите подсчитать факториал для больших значений. Эта программа использует класс BigInteger, определенный в пространстве имен System.Numerics, Чтобы его можно было бы использовать, вам надо добавить в проект ссылку на System.Numerics.dll. Вот текст программы: [pre2] using System; using System.Numerics; using System.Collections.Generic; namespace Factorial { class Program { static IEnumerable<string> Factorial() { uint i = 1; BigInteger value = 1; do { value *= i; yield return string.Format( "{0,4}: {1}", i, value ); } while ( i++ != 50 ); } static void Main(string[] args) { const int LINES_PER_SCREEN = 22; int i = 0; foreach (string next_value in Factorial()) { Console.WriteLine(next_value); if ( ( i = ++i % LINES_PER_SCREEN) == 0 ) { Console.Write("\nContinue (Enter - continue)? "); var c = Console.ReadKey(); if (c.Key != ConsoleKey.Enter) break; Console.WriteLine( "\n" ); } } Console.WriteLine(); } } } [/pre2] Ее вывод на консоль может выглядеть следующим образом: [pre2] 1: 1 2: 2 3: 6 4: 24 5: 120 6: 720 7: 5040 8: 40320 9: 362880 10: 3628800 11: 39916800 12: 479001600 13: 6227020800 14: 87178291200 15: 1307674368000 16: 20922789888000 17: 355687428096000 18: 6402373705728000 19: 121645100408832000 20: 2432902008176640000 21: 51090942171709440000 22: 1124000727777607680000 Continue (Enter - continue)? 23: 25852016738884976640000 24: 620448401733239439360000 25: 15511210043330985984000000 26: 403291461126605635584000000 27: 10888869450418352160768000000 28: 304888344611713860501504000000 29: 8841761993739701954543616000000 30: 265252859812191058636308480000000 31: 8222838654177922817725562880000000 32: 263130836933693530167218012160000000 33: 8683317618811886495518194401280000000 34: 295232799039604140847618609643520000000 35: 10333147966386144929666651337523200000000 36: 371993326789901217467999448150835200000000 37: 13763753091226345046315979581580902400000000 38: 523022617466601111760007224100074291200000000 39: 20397882081197443358640281739902897356800000000 40: 815915283247897734345611269596115894272000000000 41: 33452526613163807108170062053440751665152000000000 42: 1405006117752879898543142606244511569936384000000000 43: 60415263063373835637355132068513997507264512000000000 44: 2658271574788448768043625811014615890319638528000000000 Continue (Enter - continue)? 45: 119622220865480194561963161495657715064383733760000000000 46: 5502622159812088949850305428800254892961651752960000000000 47: 258623241511168180642964355153611979969197632389120000000000 48: 12413915592536072670862289047373375038521486354677760000000000 49: 608281864034267560872252163321295376887552831379210240000000000 50: 30414093201713378043612608166064768844377641568960512000000000000 Для продолжения нажмите любую клавишу . . . [/pre2]

Сыроежка: Следующий пример кода можно в шутку предворить крылатой фразой: "Что позволено Юпитеру, то не позволено быку.":) Если написать следующее объявление int *pi = &pi: То вы неминуемо получите ошибку компиляции. Нельзя неявно преобразовать тип int **, который имеет выражение &pi, в тип int *, который имеет сама переменная pi. Но вполне можно написать void *pv = &pv; и это объявление успешно скомпилирутеся. Первой реакцией неопытного С/С++ программиста может быть недоумение: "Как же так, ведь здесь тоже имеется два различных типа: void **. выражения &pv и void * переменной pv!" Как говорится, ларчик открывается просто. Указателю void * может быть присвоено значение указателя любого типа в том числе и void **. Никаких при этом приведений типов делать нет необходимости.

Сыроежка: Встретил такой вопрос на одном из форумов: Здравтвуйте, у меня такая проблема, не могу задать чтоб из побочной диагонали одномерного массива выбирался минимальный элемент. Наверное в мое время, когда я сам был школьником, нас неправильно учили! Мне стыдно признаться, но я до сих пор не знаю, что такое "побочная диагональ одномерного массива". Может быть кто-нибудь подскажет, как в одномерном пространстве изобразить диагональ?

Сыроежка: Есть опечатки, которые могут поразить своими последствиями даже опытного программиста. Вот одна из таких опечаток. Как известно в станлартном классе С++ std::string можно делать конкатенацию строк. Например: [pre2] std::string s = "Hello "; s += "World"; std:;cout << s << std::endl;[/pre2] Результатом работы этого фрагмента неполного кода программы будет вывод на консоль строки Hello World Ничего необычного здесь нет. Оператор += присоединяет операнд в правой части к строке, расположеннной слева от знака оператора. Теперь начинается забавное. Можно по невнимательности допустить ошибку и написать [pre2] std::string s = "Hello "; s =+ "World"; std:;cout << s << std::endl;[/pre2] То есть вместо перегруженного для класса std::string оператора += по ошибке написать =+. Первая реакция даже опытного программиста на такую опечатку будет утверждение, что код не будет компилироваться, так как такого оператора нет. Каково же будет его удивление, когда ему покажут, что код успешно компилируется и выводит на консоль строку World . "Что такое?!", - скажет опытный программист, - "Это скорей всего баг компилятора!" На самом деле он заблуждается. Никакого бага здесь нет. Код совершеннно корректный. Чтобы лучше было разобраться, что эта конструкция означает, желательно вставить пробел между знаками операторов = и +. s = +"World"; В правой части находится строковый литерал, который имеет тип константного символьного массива. Массивы в выражениях преобразуются к указателю на их первый элемент. А к указателям можно применять унарный оператор +! Согласно параграфу №7 раздела 5.3.1 Unary operators стандарта С++: 7 The operand of the unary + operator shall have arithmetic, unscoped enumeration, or pointer type and the result is the value of the argument. Integral promotion is performed on integral or enumeration operands. The type of the result is the type of the promoted operand. Для указателя он никакой роли не играет. Поэтому выражение s = +"World"; эквивалентно выражению s = "World"; без унарного оператора +. А это - уже знакомая всем запись присвоения символьного литерала объекту типа std::string. Замечу также, что в отличии от унарного оператора + унарный оператор - нельзя применять к указателям. Забавность этого примера состоит в том, что многие, даже опытные программисты, попадаются на этот трюк С++ с операторами =+, считая, что они обнаружили баг компилятора. Кстати сказать, такую опечатку легко пропустить, не заметив, а потом не один час потратить на поиск причины некорректной работы программы. Будьте внимательны!

Сыроежка: Вот очень простой и в то же время сложный вопрос, так как решение его не очевидно, то есть не сразу же приходит на ум. Допустим в программе на C++ объявлена структура, члены которой имеют арифметические типы или типы перечислений. [pre2]struct A { /* объявления членов структуры арифметических типов или типов перечислений */ };[/pre2] Являются ли два таких объявления объектов этой структуры, показанных ниже, эквивалентными, и может ли одно из них привести к ошибке компиляции, то есть при задании подобного объявления для некоторой сттруктуры, содержащей только члены, имеющие арифметические типы и типы перечислений, компилятор выдаст сообщение об ошибке? [pre2]A a1 = {}; A a2 = { 0 };[/pre2]

Сыроежка: Не удержался и решил привести пример самого абсурдного кода начинающего программиста, который на первый взгляд не должен компилироваться, но на самом деле с точки зрения синтаксиса он корректный. Забавно, что у автора кода программа действительно скомпилировалась и даже что-то там запросила ввести! Вот этот код. Привожу его целиком, чтобы можно было по достоинству оценить творческий порыв начинающих программистов [pre2]#include "stdafx.h" #include <iostream> #define PI 3.14159 using namespace std; void select() { cout << "Select r for radius or d for diameter: "; char choice; cin >> choice; } void calculation(void select(), char choice) { if (choice == 'r') cout << "Select a value for your radius: " << endl; double radius; cin >> radius; double circRadius; circRadius = 2 * PI * radius; if (choice == 'd') cout << "You have selected a diameter." << endl; double diameter; cin >> diameter; double circDiameter; circDiameter = 2 * PI * (diameter / 2); } int main(char choice, void calculation()) { select(); calculation(); return 0; }[/pre2] Забавно в этом примере то, что не сразу же бросается в глаза, как объявлены параметны функции main int main(char choice, void calculation()) Сначала, глядя на вызов функции calculation(); можно подумать, что код не должен компилироваться, так как количество аргументов не соответствует количеству параметров в объявлении функции void calculation(void select(), char choice) Поэтому если не обратить внимание на объявление main, то так и можно сказать, что код не будет компилироваться. Но дело в том, что объявленный параметр в main void calculation() скрывает объявление функции, и впредложении calculation(); вызывается не объявленная функция, а нечто, на что указывает этот параметр void calculation() в объявлении main. Поэтому, как это не парадоксально, код скомпилировался и даже попытался что-то сделать. Нет предела творческой фантазии человека! Вот ссылка на исходный пример

Сыроежка: Вот простой пример на сообразительность. При выполнении следующей функции [pre2] std::string ReplaceString( const std::string &input, const std::string &source, const std::string &dest ) { std::string result = input; unsigned int pos; while( true ) { pos = result.find( source ); if ( pos == std::string::npos ) break; result.replace( pos, source.size(), dest ); } return result; }[/pre2] возникло исключение std::out_of_range Найдите в коде причину возникновения этого исключения.

ололеша: 2Сыроежка Найдите в коде причину возникновения этого исключения. данный код будет кидать исключение только на тех платформах, где sizeof(unsigned int) != sizeof(string::size_type) (коим и является npos), например на x86-64. там где они равны, все будет работать нормально. меняем unsigned int pos на string::size_type pos и будет работать везде.

ололеша: Сыроежка пишет: Являются ли два таких объявления объектов этой структуры, показанных ниже, эквивалентными, и может ли одно из них привести к ошибке компиляции, то есть при задании подобного объявления для некоторой сттруктуры, содержащей только члены, имеющие арифметические типы и типы перечислений, компилятор выдаст сообщение об ошибке? A a1 = {}; A a2 = { 0 }; очевидно же, что если в структуре первый член будет являться перечислением, во второй строчке произойдет ошибка о невозможности неявного преобразования int к enum.

Сыроежка: ололеша пишет: данный код будет кидать исключение только на тех платформах, где sizeof(unsigned int) != sizeof(string::size_type) Все правильно. Данный пример демонстрирует, насколько важно использовать со стандартными контейнерами те имена типов, которые объявлены в каждом контейнере. Иначе можно столкнуться с подобной ситуацией, когда неожиданно ранее работающий код начинает вести себя непредвиденно. Смоделировать эту ситуацию легко и на пллатформе, для которой std::string::size_type определен как unsigned int. Для этого достаточно просто присвоить значение std::string::size_type переменной unsigned short. Вот пример такого кода [pre2] std::string s( "ABC" ); unsigned short n = s.find( "D" ); //std::string::size_type n = s.find( "D" ); if ( n != std::string::npos ) std::cout << "n != std::string::npos\n"; else std::cout << "n == std::string::npos\n";[/pre2] Если использовать предложение unsigned short n = s.find( "D" );, то на консоль будет выдано сообщение n != std::string::npos Если же использовать предложение std::string::size_type n = s.find( "D" );, которое закомментировано в примере, то на консоль будет выдано сообщение n == std::string::npos

Сыроежка: Вот еще одно простое задание на "освежение мозгов". Одинаков ли будет результат вывода на консоль двух выражений a - b и x - y в приведенном ниже коде [pre2] unsigned short a = 5; unsigned short b = 10; std::cout << a - b << std::endl; unsigned int x = 5; unsigned int y = 10; std::cout << x - y << std::endl;[/pre2]

ололеша: 2Сыроежка набор битов у результата будет одинаков, отображение разное. unsigned short после аддитивной операции (да и не только аддитивной) продвигается до типа int, unsigned int остается самим собой.

Сыроежка: Знаете ли вы, что MS VC++ позволяет использовать символ $ (знак доллара) в идентификаторе. Так что следующее предложение в MS VC++ будет успешно компилироваться. [pre2]char $ = '$';[/pre2] Не надо забывать, что стандард С++ разрешает в идентификаторах использовать implementation-defined символы.



полная версия страницы