Pull to refresh

Comments 79

UFO just landed and posted this here
Угу. Мужики привыкли приведение типов писать как (int)b, т.е. в данному случае: (a)b.
UFO just landed and posted this here
Приведение типов должно бесить по умолчанию. C-style cast вместо заметного static_cast<> — тем более
После какого-то ключа они начинают бесить компилятор.
когда я насчитал четвертый легконабираемый static_cast<> в одном выражении я его возненавидел (даже с code completion) и возжелал владеть слепым десятипальцевым.
UFO just landed and posted this here
Задачка конечно веселая. И очередной раз как бы намекает нам что гибкость С и особенно C++ нужно использовать во благо, а не писать нечитаемый код.
UFO just landed and posted this here
А вы если случайно ложку дёгтя уроните в бочку с мёдом, тоже вот такой вот статьей разразитесь, что дескать мёд какой-то невкусный получился? :)

Это я к чему? К тому, что программист в любом случае должен быть аккуратным, независимо от того, следит за его косяками язык или нет. И уж не соглашусь, что std::string a() — классика, бред это, по рукам давать программисту прутом железным, чтобы неповадно было.

«Программа делает то, что вы ей сказали делать, а не то, что хотели бы, чтобы она делала.»
Баг — это муха, упавшая в бочку мёда. Я по рукам себя не бью — вытаскиваю муху. Мёд ем.
Значит показалось :) Согласитесь, мёд вкусный и полезный? :)
Слово «подебачить » стало правильно понятым раза с 5го :)
Прикольный стиль повествования :)
Поначалу показалось, что перевод, западные статьи часто в подобном стиле написаны.
UFO just landed and posted this here
Честно говоря вот читал и ждал когда расскажут почему «1» а не «5»… Может я что-то все-таки не до конца понял?
очень просто, мы объявляем локальную переменную b, которая в scope main()'a используется вместо глобальной.
а так как b не инициализирована то туда попадает мусор
просто мне кажется что стоило и написать что в результате мусор.

А вот то что всегда один и тот же результат — «1» — вот это плохо.

Помню когда-то была даже история от кого-то из Microsoft о том, как они убирали кусок 100% неиспользуемого кода и продукт (вроде офис) валился. Разкоменчивали — работал. Как оказалось «мусор» брался именно с того куска, который не использовался, и на результате этого мусора была накручен используемый функционал.

Так что детерминированный «мусор» гораздо опасней случайного.

В исходном коде лучше бы написали:
printf(b==0?"zero":"non-zero"); 

или типа того
у меня была похожая ситуация, косяк был не в некорректной инициализации, а в том что я рано память высвобождал, а линки на неё были.
в Debug-сборке работало, а в Release валилось на ура, и я никак не мог догнать почему.
Логгирование памяти тогда помогло.
Можете рассказать поподробнее, как вы делали логирование памяти?
Результат всегда не определен, т.к. локальная переменная не была инициализирована. Более того, в дебаге и в релизе результат может быть разный. В общем, автор статьи просто С++ изучал по ускоренной методике и не знал о существовании старых давно изученных граблей ;)
Переменную не инициализировали, а на стеке, где она была размещена был мусор.
>Даже предупреждения не дождётесь — подумаешь, объявление неиспользуемой функции!

И это в том месте, где её быть не может! Между прочим, компилятор о большинстве таких грабель предупреждает.
О большинстве грабель предупреждает, но не о всех, которые приведены в тексте.
Нельзя определять вложенные функции. Объявлять можно.

// main.cpp

int foo(int bar) { return bar; };

int main() {

  extern int foo();

  return foo();

};


// foo.cpp

int foo() { return 0; };

Почему значение стековой (auto) переменной как правило будет детерминировано даже на системах без обнуления свободной памяти?

Глупый вопрос.
Перед выполнением main()'a исполняется код crt, который может оставлять на стеке что угодно.
В одной версии crt — на стеке по этому адресу лежит константа, в другой — переменная, которая содержит, например, версию ОСи, и т.д.
Может ли какая-нибудь правильная реализация на системах с обнулением памяти при каждом запуске выдавать разные значения?

легко, смотрите предыдущий ответ :)
даже если не используем crt, то предварительно выполняется системный код, например для WinNT одна из функций это LdrpInitializeProcess().
Хм… На Маке еще интереснее результат (copy/paste с самого верхнего примера):

$ ./test
32767

$ gcc -v
Using built-in specs.
Target: i686-apple-darwin10
Configured with: /var/tmp/gcc/gcc-5666.3~123/src/configure --disable-checking --enable-werror --prefix=/usr --mandir=/share/man --enable-languages=c,objc,c++,obj-c++ --program-transform-name=/^[cg][^.-]*$/s/$/-4.2/ --with-slibdir=/usr/lib --build=i686-apple-darwin10 --program-prefix=i686-apple-darwin10- --host=x86_64-apple-darwin10 --target=i686-apple-darwin10 --with-gxx-include-dir=/include/c++/4.2.1
Thread model: posix
gcc version 4.2.1 (Apple Inc. build 5666) (dot 3)
а что здесь интересного?
мусор он и есть мусор :)
Простите за дурацкий вопрос, но почему
std::string a("my string");
а не
std::string a = "my string";
Это могло бы убрать изрядную долю двусмысленности кода, по крайней мере объявлением неиспользуемой функции тут явно не пахнет
И заодно зающать конструктор копирования, когда это не надо! explicit нужно ставить в определение конструктора, тогда неоднозначностей будет меньше!
А почему Вы решили, что будет использован конструктор копирования? Компилятор не глупый :)
В данном нет, но в специально написанном классе запросто.
эти строки эквивалентны, если explicit нет
На компилятор надейся, а сам не плошай =)
Шилдт говорит, что в случае
myobj a = 123;
используется обычный конструктор с одним параметром соответствующего типа, так что конструктор копирования лишний раз вызываться не будет.
И верно говорит, т.к. умный компилятор не создает временного объекта для 123 ;)
В обоих случаях будет вызыван один и тот же конструктор std::string(const char*)
Можно предположить, что будет operator= (но его не будет — присваивать можно только к существующему объекту, инициализация — не присваивание и делается конструктором), но без копирующего конструктора в первом случае как обойтись?
Ну т.е не копирующего, а string(const char*)
100% нет тут конструктора копирования, это просто другая форма записи.
Какая разница какой конструктор будет вызван, когда они делают одно и тоже? В чем провинился copy ctor?
Ну и не надо explicit как мантру повторять, он нужен только там где он нужен и не более того. Есть много мест, где неявное преобразование является желаемым поведением.
Про copy ctor вопрос снимается, я Вас неправильно понял.
Да, конечно же конструктор в string не объявлен как explicit, и сам я всегда пишу равно. Но тогда бы и задачки не было. А скобочки по ошибке добавляют часто. Тем более, что:
    MyClass* c1 = new MyClass(); // всё супер!
    MyClass с2(); // а вот тут мы попали
Благо, в этом случае при использовании с2 будет ошибка
А зачем там вообще скобки во втором случае? Это какая-то нотация или привычка? У меня скобки ассоциируются с функциями, ну или в крайнем случае с приведением типов, но зачем ставить скобки, особенно пустые, около имени переменной?..
в конструкторах так не пишете —

class A
{
private:
    int a, b;
public:
    A(int a, int b) : _a(a), _b(b)
    {
    }
}
Я пишу так:
class A
{
  private:
    int a, b;

  public:
    A(int _a, int _b)
    {
      a = _a;
      b = _b;
    }
}
И тратите лишние такты) Списки инициализации не просто так придумали.
Если бы вместо int был какой-то пользовательский тип, может и потратилось бы, а так — очень сомневаюсь. И к счастью, в моем случае читабельность кода важнее экономии одной-двух процессорных инструкций.
Вот не вижу особой разницы, зато потом будет меньше желания накосячить.
Но так не инициализировать константы.
Вообще-то, запись
class A
{
private:
    int a, b;
public:
    A(int a, int b) : a(a), b(b)
    {
    }
}

прекрасно работает. А использование идентификаторов с подчеркиванием в начале — запрещается Стандартом, так как такие идентификаторы зарезервированы для разработчиков языка/компилятора/стандартных библиотек.
Ога а подчеркивание в конце не так удобно визуально, поэтому лучше уж m_a писать.
вы не совсем правы

17.4.3.2.1 Global names [lib.global.names]

Certain sets of names and function signatures are always reserved to the implementation:
  • Each name that contains a double underscore (_ _) or begins with an underscore followed by an uppercase letter (2.11) is reserved to the implementation for any use.
  • Each name that begins with an underscore is reserved to the implementation for use as a name in the global namespace.


то есть _a вполне корректно внутри класса, но только не в global namespace'e
а вот _A уже нельзя юзать.
Да, кстати, а что такое "_a" в вашем примере?
А как должно быть?
я сейчас больше пишу на php, поэтому члены класса мне удобнее обзывать _name, хотя раньше юзал m_name
Красивыми граблями устлана дорога в C++
я бы предпочел, обойтись без этих «красивостей».
Иногда пятеро в монитор смотрят и балдеют, что же это такое, и почему, а как… хмммм… бывает весело но потом.
Да, увы. Вот и статья зи цикла: повеселиться. Вообще, задачка про a(b) показывалась куче народу на протяжении многих лет. Статистика печальная — без нулевой подсказки правильный ответ что такое a(b) дали единицы.
По мне так вполне очевидная вещь.
Может потому что я читал стандарт Си и писал свой компилятор :)
> Уже натерпевшиеся от своего любимого языка, но ещё не прошерстившие всех бизонов gcc, почувствуют подвох — и правильно

Без подвохаи те, кто писал синтаксический анализатор С/C++ и знают, что a(b) многим чем может быть. На С всё просто — там сканер заглядывает в таблицу символов. В С++ ситуация сложнее из-за скопов и шаблонов. Из-за последних пришлось придумывать typename, чтобы в теле шаблонизированной функции подсказывать что является типом.
Странный сумбур: вы с одном проблемы перескакиваете на другую) По теме, каждый уважающий себя С++ программист прочел всего Саттера и Майерса и на подобном его уже не словишь ;)
О сколько нам открытий чудных
Готовят просвещенья дух…

мб хватит постов в стиле «смотрите как оказывается оно поступает»?
И стилистика и суть поста — отвратительна. Автор, со своими «подсказочками», «мусипусичками» и прочей ерундой идите преподавать в детский сад.
И стилистика и изложение решений с многократными подсказочками сильно напоминает эту книгу
www.ozon.ru/context/detail/id/1908029/

сейчас под рукой нет, чтобы проверить
Хорошо, что вспомнили и эту книжку. Были ещё Майерс, Саттер, а в статье упоминается Александреску. Но только данная статья полностью моя — основная задачка ниоткуда не содрана, но ближе всего будут тексты стандарта, глава 7. И особенно оттуда — Ambiguity resolution.

Вот примеры оттуда (C++ Standard):
struct S {
   S (int);
};

void foo ( double a )
{
   S x(int(a));
   S y((int)a);
   S z(int());
}
кто не глядя в стандарт скажет что есть: x, y, z? Это та же тема, что и в статье
много раз видел эту задачу
Ну и отвратительный же у вас стиль написания. Имейте хоть каплю уважения к читателям.
Уважаемый рецензент, разве я кому-то грубил? кому-то демонстрировал своё неуважение?
Да нет, никому не грубили. Просто перечитайте еще раз. Такое количество не литературных оборотов, каких-то присказок и просторечий просто невозможно. Из-за этого докопаться до смысла статьи очень трудно.
Извиняйте, если не понравился стиль. Я в тексте и у Шишкова присил прощения, намекая на текст, наверняка вам известный. Ещё, Александра Сергеевича уже цитировал один из рецензентов. Времена, конечно, изменились, и всё-таки разрешите припомнить нашего великого поэта ещё раз в контексте моего повествования:

А вижу, я винюсь пред вами,
Что уж и так мой бедный слог
Пестреть гораздо б меньше мог
Иноплеменными словами.
Не получается у меня 0 и -1 под FreeBSD:

%gcc -O0 test.c -o test
%./test
24
%gcc -O1 test.c -o test
%./test
-1
%gcc -O2 test.c -o test
%./test
-1
%gcc -O3 test.c -o test
%./test
-1
Скорее всего, другая версия gcc а точнее glib — от неё в стеке и остаётся мусор, который и показывает неинициализированная переменная. Кстати, у меня результат не зависит от оптимизации. Мусор, конечно, чаще всего вполне определённый — поэтому при перезапуске выдаётся всегда одно и то же значение…
Sign up to leave a comment.

Articles