Блог пользователя Fefer_Ivan

Автор Fefer_Ivan, 15 лет назад, По-русски

Доброе утро/день/вечер.

Только что во время дебага заметил, что написал

bool check(int x, int y){

    return 0 <= x && x < n && 0 <= y < m;

}

Программа компилировался, но работала, естественно, не правильно.

Другой подобный баг встречается чаще.

int getAns(...){

    int ans = 0;

    {

        ///Вычисление ответа

    }

     //здесь должен быть return ans; но он забыт.

}

Это тоже компилируется.

Другой более интересный пример, о котором я только слышал от Дмитрия Матова (Nerevar) :

int f = f(x1, y1, z1) + f(x2, y2, z2) + (x3, y3, z3);

Нет, я не забыл f перед третьей скобкой. Это тоже компилиться и работает.

Буквально сегодня из-за другой подобной ошибки мой напарник получал разные ответы при компиляции в g++ и  в Visual Studio.

А какие вы знаете подобные вещи в С++?

Теги c++
  • Проголосовать: нравится
  • +14
  • Проголосовать: не нравится

15 лет назад, # |
  Проголосовать: нравится 0 Проголосовать: не нравится
#define period 2*3*5*7*11

...

answer = T % period;

...
15 лет назад, # |
  Проголосовать: нравится 0 Проголосовать: не нравится

я за это и люблю с++, что на нём можно скомпилировать всё, что угодно =)

сейчас я делаю одну программу на java и очень раздражаюсь тем, что строка while(true); не компилируется, приходится писать if (true) while(true);

про забытие return ans; вам напомнит гнутый компилятор (в смысле gnu c++) warning'ом, а для предотвращения других ошибок нужно максимально упрощать логические формулы, разбивая их в несколько if'ов, да и просто нужно быть внимательным к своему коду :)

  • 15 лет назад, # ^ |
      Проголосовать: нравится +16 Проголосовать: не нравится
    вот за что я люблю Java, так это за то что там такая хрень НЕ компилится.
  • 15 лет назад, # ^ |
      Проголосовать: нравится 0 Проголосовать: не нравится
    Visual C++ не скомпилирует забытый return вообще, а гнутый компилятор напоминает только с -Wall. По крайней мере, та версия, что в MinGW.
  • 15 лет назад, # ^ |
      Проголосовать: нравится 0 Проголосовать: не нравится
    Всё компилируется на PHP. В С++ компилируется всё кроме кроме кода на нуль-множестве))
15 лет назад, # |
  Проголосовать: нравится +1 Проголосовать: не нравится
some_map[some_value] = (int) some_map.size();
  • 15 лет назад, # ^ |
      Проголосовать: нравится 0 Проголосовать: не нравится

    Вот это опасно, да.

    То же самое было бы 

    int newVal = (int)some_map.size() + (rand() & 1);

    some_map[some_value] = newVal;

15 лет назад, # |
  Проголосовать: нравится +1 Проголосовать: не нравится
О чем уже начал говорить Максим забавные ошибки случаются в стл.

Писал декартво дерево

...
struct node{
...
node(){
...
y=rand();
,,,
}
...
};

vectr<node> tree;

int main(){
...
   tree.resize(n);
...
}

RE 7 ловил дня 4.

Из более часто встречающегося

if (a=5) // ну это скорее когда начинал писать
if (j<1<<i) // а вот это обнаружил недавно.

Еще вспомню рссказ Виталия Гольдштейн о написани Фибоначиевой кучи. Проблема закючалась примерно в следующем.

был вектор структурок содержащих списки и указатели на списки лежащие в других структурках того же или другого вектора. Веселье начиналось когда вектор решал перераспределить память и все указатели становились не действительными.

такую тему можно использовать для отбивания желания учить с++)
  • 15 лет назад, # ^ |
      Проголосовать: нравится 0 Проголосовать: не нравится
    А можно пояснить что не так с декартовым деревом было ?
    • 15 лет назад, # ^ |
        Проголосовать: нравится 0 Проголосовать: не нравится
      То, что констуктор только один раз вызвался ?
      • 15 лет назад, # ^ |
          Проголосовать: нравится 0 Проголосовать: не нравится
        Да. у всех вершин получался одинаковый ключ y, в результате получалась цепочка.
15 лет назад, # |
  Проголосовать: нравится 0 Проголосовать: не нравится
int a = 1;
if (a % 3 == 1) {
  ...
}
if (a & 3 == 1) {
  ...
}

Когда я впервые на это наткнулся, мне показалось очень нелогично, что
первый if сработает а второй нет.
  • 15 лет назад, # ^ |
      Проголосовать: нравится 0 Проголосовать: не нравится

    я не сразу догадался, что "==" имеет более высокий приоритет, чем "&" =)

    • 15 лет назад, # ^ |
        Проголосовать: нравится 0 Проголосовать: не нравится

      А битовые операции - они такие, да.

      Кстати, мой вижак(2008) выдает warning проверьте приоритеты, если есть что-то подобнок без скобок.

    • 15 лет назад, # ^ |
        Проголосовать: нравится 0 Проголосовать: не нравится
      Мешает догадаться еще и то, что правильное было бы сравнивать &3 и %4, а не &3 и %3 :о)
15 лет назад, # |
  Проголосовать: нравится 0 Проголосовать: не нравится
Не думаю, что кто-то так нубил кроме меня, но очень забавная ошибка:
int main( )
{ ... .
 ...
 return -1;
}
Любая посылка - RE1 :о) Я так однажны на -80 сдал задачу :о)
  • 15 лет назад, # ^ |
      Проголосовать: нравится +13 Проголосовать: не нравится
    В с++ кстати отсутствие return в функции main эквивалентно return 0; Так что можно этот return вообще не писать.
    • 15 лет назад, # ^ |
        Проголосовать: нравится 0 Проголосовать: не нравится
      Только если void main
      Если int main и не написать return, не гарантируется, что вернется 0. Может с каким-то компилятором всегда будет 0, но вообще я уверен, что это не всегда так.
      • 15 лет назад, # ^ |
          Проголосовать: нравится +17 Проголосовать: не нравится
        Хех, вот не веришь ты мне.. формы void main в языке c++ нет вообще. Процитирую стандарт:
        3.6.1/2 
        An implementation shall not predefine the main function. This function shall not be overloaded. It shall have a return type of type int, but otherwise its type is implementation-defined.

        3.6.1/5
        A return statement in main has the effect of leaving the main function (destroying any objects with automatic storage duration) and calling exit with the return value as the argument. If control reaches the end of main without encountering a return statement, the effect is that of executing return 0;
        • 15 лет назад, # ^ |
            Проголосовать: нравится 0 Проголосовать: не нравится
          Ага, а еще в стандарте есть export. И где он реализован?
          Я помню у нас как-то после глобального реплейса случилась такая веселая вещь как double main. MSVC ее спокойной схавала, а вот GNU  на сервере - нет
          • 15 лет назад, # ^ |
              Проголосовать: нравится +1 Проголосовать: не нравится
            Если про export вопрос не риторический, то экспорт шаблонов реализован в Comeau и Sun Studio как минимум. Другое дело, что это во-первых, слишком сложная штука для реализации, во-вторых, практически никому ненужная как сейчас уже оказалось, слишком много неявных проблем с ней.

            Насчет double main в MSVC.. В MSVC, как и в любом другом компиляторе есть расширения, позволяющие порой больше, чем разрешает стандарт, эти расширения можно отключить при желании ключами компиляции. И факт, что в MSVC компилируется double main не отменяет того факта, что при int main и отсутствии return, будет return 0; Такие мелочи в современных компиляторах реализованы в соответствии со стандартом, уж поверьте :)
            • 15 лет назад, # ^ |
                Проголосовать: нравится 0 Проголосовать: не нравится
              Кстати, если уж пошла такая пьянка.. то вот рассказ Герба Саттера о последнем заседании комитета по стандартизации. Там в том числе есть про то, что в новом стандарте все-таки решили убрать export templates, при чем по просьбе EDG - единственной компании, реализовавшей эту сомнительную фичу.
15 лет назад, # |
  Проголосовать: нравится 0 Проголосовать: не нравится

Весьма коварный случай:

if(x=='y') x=='1';

  • 15 лет назад, # ^ |
      Проголосовать: нравится 0 Проголосовать: не нравится
    не знаю как в вижаке но мингв с -wall точно обратит на это внимание.
15 лет назад, # |
  Проголосовать: нравится +3 Проголосовать: не нравится
Друг учит си. Спросил а можно ли писать что-то тпа a == b== c. Решил исследовать этот вопрос. Пришел к забавным реультатам.
1 = = 2 = = 0 по мнению Visual Studio 2008 равно true.
2 = = 2 = = 2 по ее де мнению равно false.

При этом компиляцию конструкции
if (a = = b = = c)
//сдели что-то
 не приводит даже к варнингу.
Даже на MinGW с -Wall.
Забавно.
  • 15 лет назад, # ^ |
      Проголосовать: нравится +9 Проголосовать: не нравится
    Нужно просто почитать хотя бы книгу по языку.
    1. a == b == c эквивалентно (a == b) == c
    2. Результат (a == b) имеет тип bool.
    3. При сравнении bool с int происходит integral promotion, то есть bool приводится к int: true приводится к 1, false к 0.

    1 == 2 - это false, int(false) == 0 - это true
    2 == 2 - это true, int(true) == 2 - это false    

    И что забавного?
    • 15 лет назад, # ^ |
        Проголосовать: нравится 0 Проголосовать: не нравится
      я это все прекрасно понимаю. я разве где-то написал что не могу это объяснить?
      • 15 лет назад, # ^ |
          Проголосовать: нравится +1 Проголосовать: не нравится
        Ну, ты уж извини, если что не так. Но по твоему сообщению выше тогда  непонятно, почему ты решил исследовать этот вопрос (а не сразу рассказать другу, что и как), почему результаты тебе показались забавными, и почему ты ожидаешь ворнинга в данном случае.
        • 15 лет назад, # ^ |
            Проголосовать: нравится 0 Проголосовать: не нравится
          Исслеовать решил потому что было интересно будет варнинг или нет.
          И вообще я если честно не был уверен что это в принципе скомпилируется.

          По поводу почему жду варнинга. Ну скорее всего здесь получается не то что имелось ввиду. По моему это отличный повод  об этом сказать.
15 лет назад, # |
  Проголосовать: нравится +4 Проголосовать: не нравится
Бывают и забавные, но правильные конструкции. Например, помнится Сергей Назаров показывал оператор стремления (читать как "пока n стремиться к нулю) :
while (n --> 0) {
    // some code
}
15 лет назад, # |
  Проголосовать: нравится 0 Проголосовать: не нравится
В общем есть такой код, компилирую его в VS2008:

string s = "a";
vector<string> v;
v.push_back(s);
int l = 0;
v.push_back(v[l]);
v.push_back(v.back());
for(int i = 0; i < v.size(); i++)
printf("%s", v[i].c_str());

Как вы думаете сколько буковок должно вывестись? До последнего дня я думал, что 3. Выводится только одна. Не знаю может это студийный оптимизатор так трудится, или же это предусмотрено стандартом, но такое поведение vector<string> лично мне кажется очень странным, учитывая что с остальными типами данный код работает нормально.
  • 15 лет назад, # ^ |
      Проголосовать: нравится 0 Проголосовать: не нравится

    дело в том, что так нельзя делать.

    ты используешь одну и ту же переменную v в выражении два раза. лучше такого избегать.

    смотри что происходит.

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

    потом ты пихаешь v[0] в вектор, в функцию push_back()

    передаётся этот элемент по ссылке, и находится он в нектором массиве в памяти длины 1, перед тем как добавить второй элемент в функции push_back происходит выделение памяти под новый массив длины 2, элемент v[0] копируется в начало этого нового массива, а память из под массива длины 1 освобождается. Затем она заполняется нулями по видимому, а ссылка которая была передана указывает на эту самую память которая по идее освобождена, и не может тобой использоваться и ты пихаешь этот пустой стринг во второй элемент нового массива.

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

    было что-то вроде h[Get(x)].cnt++;

    а функция Get(x) делала push_back в массив h.

    • 15 лет назад, # ^ |
        Проголосовать: нравится 0 Проголосовать: не нравится
      или может так h[x].moo = Get(y);
    • 15 лет назад, # ^ |
        Проголосовать: нравится 0 Проголосовать: не нравится
      Вы так все хорошо объяснили :)
      Дополню до кучи, что произойдет это только если произошел resize у вектора. В частности попробуйте запускать несколько раз - увидите от 1 до 3 букв 'a'.
      • 15 лет назад, # ^ |
          Проголосовать: нравится 0 Проголосовать: не нравится

        если перед пушбеками вставит v.reserve(100);

        то всё толжно сработать так как ожидал автор.

    • 15 лет назад, # ^ |
        Проголосовать: нравится 0 Проголосовать: не нравится
      >ты используешь одну и ту же переменную v в выражении два раза. лучше такого избегать.

      В данном случае ничего страшного быть не должно по хорошему. Но похоже, что происходит действительно так, передаваемая ссылка используется после инвалидации, но это тогда грубая ошибка авторов этой STL. Завтра гляну на исходники - расскажу.
  • 15 лет назад, # ^ |
      Проголосовать: нравится 0 Проголосовать: не нравится

    а кто писал scanf( "%d%d%d", n, x, y);?

    причём в студии сразу же crash, а вот писали мы как-то в линуксовой аудитории... парень ошибку искал минут 20)

  • 15 лет назад, # ^ |
      Проголосовать: нравится 0 Проголосовать: не нравится
    Очень странно. Если это так на самом деле, то это ошибка в STL, нет сейчас под рукой VS2008 к сожалению. Не поленился пересмотреть все STL, которые дома есть. Вот строчка кода из STL, которая в поставке с VC++ 2005:
    _Ty _Tmp = _Val; // in case _Val is in sequence
    В stlport:
    value_type __x_copy = __x; 
    То есть перед вставкой происходит копирование. В SGI STL удаление старого буфера вообще происходит после копирования. Завтра приду на работу, посмотрю, что там в 2008, но если это баг, то какой-то удивительно глупый.

    • 15 лет назад, # ^ |
        Проголосовать: нравится 0 Проголосовать: не нравится
      В общем не воспроизводится у меня никак эта ошибка. 2008 студия у меня с первым сервис паком. Я посмотрел на исходники вектора, они там переписали существенно по сравнению с 2005, но вроде с виду все должно быть хорошо: старый буфер удаляется после вставки. Собирал я с debug/release по умолчанию, потом еще с /Ox попробовал.
      Если есть желание разобраться в причине, то нужны: версия студии, ключи компиляции, полный код, на котором воспроизводится баг.
      • 15 лет назад, # ^ |
          Проголосовать: нравится 0 Проголосовать: не нравится
        Microsoft Visual Studio 2008
        Version 9.0.21022.8 RTM
        Microsoft .NET Framework
        Version 3.5 SP1

        Installed Edition: Professional

        Вот командная строка:
        /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_UNICODE" /D "UNICODE" /Gm /EHsc /RTC1 /MDd /Fo"Debug\\" /Fd"Debug\vc90.pdb" /W3 /nologo /c /ZI /TP /errorReport:prompt 

        Сделав дебаг я увидел, что косяк происходит после вызова
          _Ptr = _Umove(_Myfirst, _VEC_ITER_BASE(_Where),
        _Newvec); // copy prefix
        в функции 
        void _Insert_n(const_iterator _Where,
        size_type _Count, const _Ty& _Val)
        // insert _Count * _Val at _Where
        Самое интересное во все этом безобразии, то что простая обертка для string (struct S
        {
        string s;
        S(const string s): s(s) {}
        };) отрабатывает как и ожидается (выводит три буковки). А вот любой класс из стл работает аналогично string.
        • 15 лет назад, # ^ |
            Проголосовать: нравится 0 Проголосовать: не нравится
          Ну, в общем, в моей STL такой строчки нет вообще. Видимо, в первом сервис-паке или раньше исправили. 
      • 15 лет назад, # ^ |
          Проголосовать: нравится 0 Проголосовать: не нравится
        Ах да, забыл код:
        #include <vector>
        #include <string>
        #include <cstdio>

        using namespace std;

        int main()
        {

        vector<string> v;
        v.push_back("a");
        v.push_back(v[0]);
        v.push_back(v.back());
        for(int i = 0; i < v.size(); i++)
        printf("%s", v[i].c_str());

        return 0;
      • 15 лет назад, # ^ |
          Проголосовать: нравится 0 Проголосовать: не нравится
        Кстати по поводу переписывания исходников вектора. В 2005 студии по сравнению с 2003 есть странноватое новшество (видимо оно перешло и к более поздним версиям) - при очистке (clear) вектор не освобождает память. Самый простой способ почистить вектор с освобождением памяти - написать что-то такое: v.swap(vector<int>()).
        • 15 лет назад, # ^ |
            Проголосовать: нравится 0 Проголосовать: не нравится
          Это не новшество, это они привели в соответствие стандарту. По стандарту нет способов уменьшить capacity вектора.
  • 15 лет назад, # ^ |
      Проголосовать: нравится 0 Проголосовать: не нравится

    Проверил в Visual C++ 2010 Express Edition. 

    Выводит 3 "а".

15 лет назад, # |
  Проголосовать: нравится 0 Проголосовать: не нравится

опережу ненавистников C++  которые минусовали юзера alliumnsk за посты про яву и скажу что такие непоняки есть не только в C++

Integer a = 42;
Integer b = 42;
Integer c = 420;
Integer d = 420;
System.out.println(a==b); // true
System.out.println(c<=d); // true
System.out.println(c>=d); // true
System.out.println(c==d); // false

сделайте мне кучу минусов за правду.

  • 15 лет назад, # ^ |
      Проголосовать: нравится 0 Проголосовать: не нравится
    Это забавно :)
    Вообще дело в том, что Integer - это класс, и запись Integer a = 42 понимается компилятором как Integer a = Integer.valueOf(42), при этом а - объект на куче
    Для всех операторов сравнения (кроме == ) происходит автоматическое преобразование к примитивному типу (т.е. c <= d <=> c.intValue() <= d.intValue())
    Оператор == же для объектов имеет другой смысл - true, если они указывают на один объект на куче, false - иначе.
    Теперь понятно, почему c==d -> false, но почему a==b -> true ??
    Видимо, дело в том, что оператор Integer.valueOf() производит кеширование
    Экспериментально проверено, что для отрезка [-128, 127] для одинаковых чисел будут возвращаться одинаковые объекты.
    • 15 лет назад, # ^ |
        Проголосовать: нравится +1 Проголосовать: не нравится
      Зачем экспериментально...
      Вот код из класса Integer (jdk1.6):
          static final Integer cache[] = new Integer[-(-128) + 127 + 1];

          static {
              for(int i = 0; i < cache.length; i++)
              cache[i] = new Integer(i - 128);
          }
          }
  • 15 лет назад, # ^ |
      Проголосовать: нравится 0 Проголосовать: не нравится
    Вообще-то, все правильно, поскольку Integer - это объект, а все обращения к имени объекта в Java дают ссылку (адрес) этого объекта. Поэтому далее сравниваются именно адреса, а не значения объектов. Для сравнения значений надо использовать a.equls (b). 

    Если в примере выше заменить Integer на примитивный тип int, то все сравнения дадут true (для примитивов == и equls() равнозначны).

    Кстати, такой же результат будет и для строк:
    String a = "string";
    String b = new String ("string");
    System.out.println(a==b); // false
    System.out.println(a.equals (b)); // true
    • 15 лет назад, # ^ |
        Проголосовать: нравится 0 Проголосовать: не нравится
      Сравнение строк, объявленных таким образом работает корректно:
      String a = "string";
      String b = "string";
      System.out.println(a==b); //true
      А вот если замутить с new... то уже начинаются ссылки.
      String a = "string";
      String b = new String ("string"); //тут будет ссылка на объект
      System.out.println(a==b); // false
      System.out.println(a.equals (b)); // true
      А с интами, конечно, вообще цирк... кэширование от -127 до 128... :о)
      • 15 лет назад, # ^ |
          Проголосовать: нравится 0 Проголосовать: не нравится
        >String a = "string";

        >String b = new String ("string"); //тут будет ссылка >на объект

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

        а с new получаем другой объект,  оператор == ссылки сравнивает, а не объекты.

        • 15 лет назад, # ^ |
            Проголосовать: нравится 0 Проголосовать: не нравится

          >А вот если замутить с new... то уже

          > начинаются ссылки.

          основная мысль предыдущего ответа это то что ссылки не начинаются, они всегда ссылки, просто в первом примере у тебя чудом a и b на один и тот же объект указывают.

  • 15 лет назад, # ^ |
      Проголосовать: нравится 0 Проголосовать: не нравится
    Да посты не про яву (по которой измерений было меньше всего). Если бы я мерял C++ vs. FORTRAN они бы тоже меня заминусовали :)))

15 лет назад, # |
  Проголосовать: нравится 0 Проголосовать: не нравится
Кстати, помню один знакомый как-то потратил кучу времени в одной задаче. Дело в том, что в С++
                    a^=b^=a^=b;
 работает ( меняет a и b местами), а в Java нет.
  • 15 лет назад, # ^ |
      Проголосовать: нравится +1 Проголосовать: не нравится

     >> a^=b^=a^=b;

    А вот так писать не стоит. Это undefined behavior, т.к. присходит двойное присваивание  переменной b без операторов; и т.п..

    • 15 лет назад, # ^ |
        Проголосовать: нравится 0 Проголосовать: не нравится
      Ну, в  С это работает
    • 15 лет назад, # ^ |
        Проголосовать: нравится 0 Проголосовать: не нравится
      А почему здесь UB???
      Разве это не равносильно этому:
      a ^= (b ^= (a ^= b)); ?
      что, в свою очередь, разве не равносильно этому:
      a ^= b;
      b ^= a;
      a ^= b;
      ?
      • 15 лет назад, # ^ |
          Проголосовать: нравится +2 Проголосовать: не нравится
        Если мы говорим про встроенные типы (например, int), то не равносильно. В первом случае происходит множественное изменение скалярного объекта между точками следования (sequence points). Это UB. Когда же мы разносим это по трем инструкциям, то между ними добавляются sequence points, и все становится хорошо.
        Более подробно, например, здесь: http://en.wikipedia.org/wiki/Sequence_point
        На практике, конечно, почти всегда такое выражение будет работать, как и задумывалось, но я бы все-таки не советовал полагаться на то, что оптимизатор не сможет как-нибудь наоптимизировать :)
15 лет назад, # |
  Проголосовать: нравится 0 Проголосовать: не нравится
Спасибо!
Век живи - век учись :)