Автор Тема: Найдите ошибку, если она есть.  (Прочитано 34204 раз)

Geniepro

  • Hero Member
  • *****
  • Сообщений: 1955
  • Знайте- истина в том, что повторено трижды подряд!
    • Просмотр профиля
Re: Найдите ошибку, если она есть.
« Ответ #30 : Сентябрь 28, 2016, 11:30:06 am »
tr[v].mp[x] = go();Очевидная проблема в этом месте, так как go() изменяет tr.
Если изменить на int tmp = go();
tr[v].mp[x] = tmp;
То всё нормально. Да, проблема в плохом понимании работы универсальных решений. И в наличии состояния, конечно  :D
Бинго! :-)
Не понимаю, как это могло привести к падению программы? Ну подумаешь, добавили к вектору новый элемент в конец вместе с изменением одного из имеющихся в нём элементов, на что это влияет?
Походу это просто какая-то ошибка в компиляторе.
to iterate is human, to recurse, divine

Салат «рекурсия»: помидоры, огурцы, салат…

kkkk

  • Full Member
  • ***
  • Сообщений: 135
    • Просмотр профиля
Re: Найдите ошибку, если она есть.
« Ответ #31 : Сентябрь 28, 2016, 11:34:17 am »
Очевидно, что очевидная проблема не столь очевидна, коль было столь длинное и долгое обсуждение с массой неверных гипотез. :-)
Очевидной она стала после использования средств контроля целостности памяти. Стало понятно, что обращение идёт не к тому, что изначально предполагалось, и оставалось только выяснить почему.

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

Теперь осталось ответить на второй вопрос - на вашем любимом ЯП возможна ли подобная ошибка?
В Си такой ошибки бы не было, потому что для него использовали бы массив, а не vector.

kkkk

  • Full Member
  • ***
  • Сообщений: 135
    • Просмотр профиля
Re: Найдите ошибку, если она есть.
« Ответ #32 : Сентябрь 28, 2016, 11:38:55 am »
Не понимаю, как это могло привести к падению программы?
1. сначала была выполнена  tr[v].mp[x] , которая ссылалась на массив, использованный в vector в текущем состоянии.
2. затем go() добавила новый элемент в tr, что потребовало выделения нового массива и освобожения старого
3.  наконец было выполнено присваивание tr[v].mp[x] = go() , которое произвело запись в освобождённую память

Geniepro

  • Hero Member
  • *****
  • Сообщений: 1955
  • Знайте- истина в том, что повторено трижды подряд!
    • Просмотр профиля
Re: Найдите ошибку, если она есть.
« Ответ #33 : Сентябрь 28, 2016, 11:45:45 am »
Короче говно этот ваш с++, что и требовалось доказать.
Пользуйтесь иммутабельными данными!!! Хаскеллем, короче!!!!!!
to iterate is human, to recurse, divine

Салат «рекурсия»: помидоры, огурцы, салат…

Geniepro

  • Hero Member
  • *****
  • Сообщений: 1955
  • Знайте- истина в том, что повторено трижды подряд!
    • Просмотр профиля
Re: Найдите ошибку, если она есть.
« Ответ #34 : Сентябрь 28, 2016, 11:47:23 am »
Не понимаю, как это могло привести к падению программы?
1. сначала была выполнена  tr[v].mp[x] , которая ссылалась на массив, использованный в vector в текущем состоянии.
2. затем go() добавила новый элемент в tr, что потребовало выделения нового массива и освобожения старого
3.  наконец было выполнено присваивание tr[v].mp[x] = go() , которое произвело запись в освобождённую память
А почему компилятор этого не отследил и не переназначил область памяти при присваивании на новую? о_О баг какой-то, концептуальный...
to iterate is human, to recurse, divine

Салат «рекурсия»: помидоры, огурцы, салат…

valexey_u

  • Hero Member
  • *****
  • Сообщений: 3013
    • Просмотр профиля
Re: Найдите ошибку, если она есть.
« Ответ #35 : Сентябрь 28, 2016, 11:51:34 am »
Очевидно, что очевидная проблема не столь очевидна, коль было столь длинное и долгое обсуждение с массой неверных гипотез. :-)
Очевидной она стала после использования средств контроля целостности памяти. Стало понятно, что обращение идёт не к тому, что изначально предполагалось, и оставалось только выяснить почему.

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

Дык и тут ровно то же самое :-) Смотри, фокус покажу:
переписываем main вот так:
int main()
{
    tr.reserve(100500);
    go();
    s = "01";
    add(0, 0, 1);
}

И ничего уже никуда не падает, память не портится :-)

Теперь осталось ответить на второй вопрос - на вашем любимом ЯП возможна ли подобная ошибка?
В Си такой ошибки бы не было, потому что для него использовали бы массив, а не vector.
Не массив, а указатель на массив. И смотрелось бы это так:
size_t len = 1;
bor* tr = malloc(len*sizeof(bor));

tr[v].mp[x] = go();
...
int go()
{
    bor* old = tr;
    len++;
    tr = malloc(len * sizeof(bor));
    memcpy(tr, old, (len-1)*sizeof(bor));
    free(old);
    bor b;
    init(&b);
    tr[len-1] = b;
    return len-1;
}
Y = λf.(λx.f (x x)) (λx.f (x x))

kkkk

  • Full Member
  • ***
  • Сообщений: 135
    • Просмотр профиля
Re: Найдите ошибку, если она есть.
« Ответ #36 : Сентябрь 28, 2016, 12:08:50 pm »
Дык и тут ровно то же самое :-) Смотри, фокус покажу:
переписываем main вот так:
int main()
{
    tr.reserve(100500);
    go();
    s = "01";
    add(0, 0, 1);
}

И ничего уже никуда не падает, память не портится :-)
О таком фокусе я знаю, но в общем случае резерва не требуется, в то время как в Си - он обязателен.

Не массив, а указатель на массив. И смотрелось бы это так:
size_t len = 1;
bor* tr = malloc(len*sizeof(bor));

tr[v].mp[x] = go();
...
int go()
{
    bor* old = tr;
    len++;
    tr = malloc(len * sizeof(bor));
    memcpy(tr, old, (len-1)*sizeof(bor));
    free(old);
    bor b;
    init(&b);
    tr[len-1] = b;
    return len-1;
}
В том-то и дело, что для Си это
  • Трудоёмко, поэтому чаще будет задействовано более простое решение - выделения столько памяти, сколько должно хватить для всех допустимых случаев, возможно, по формуле
  • Даже если реализовано автоувеличение размера, оно явное и очевидное, что делает проблему более понятной и менее вероятной

ilovb

  • Hero Member
  • *****
  • Сообщений: 2538
  • just another nazi test
    • Просмотр профиля
    • Oberon systems
Re: Найдите ошибку, если она есть.
« Ответ #37 : Сентябрь 28, 2016, 04:37:01 pm »
2. затем go() добавила новый элемент в tr, что потребовало выделения нового массива и освобожения старого

Жесть. Это ошибка где-то на уровне генов в с++.

Ну в общем то ничего нового.

valexey_u

  • Hero Member
  • *****
  • Сообщений: 3013
    • Просмотр профиля
Re: Найдите ошибку, если она есть.
« Ответ #38 : Сентябрь 28, 2016, 08:56:57 pm »
О таком фокусе я знаю, но в общем случае резерва не требуется, в то время как в Си - он обязателен.
Какие-то странные представления о Си.. Конечно же и в Си это не обязательно.

...
В том-то и дело, что для Си это
  • Трудоёмко, поэтому чаще будет задействовано более простое решение - выделения столько памяти, сколько должно хватить для всех допустимых случаев, возможно, по формуле
  • Даже если реализовано автоувеличение размера, оно явное и очевидное, что делает проблему более понятной и менее вероятной

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

На тему явного и очевидного, то в Си в реальном коде, это смотрелось бы примерно так, на самом деле.
g_array_index(ts, bor, v).mp[x] = go();
...
int go() {
        bor b;
        init(&b);
        g_array_append_val(ts, b);
        return ts->len-1;
}
И ровно с той же семантикой.

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

Ну, то есть по сути ты говоришь, что это не С++ плохой как язык и им не надо пользоваться, а это использование готовых либ плохо и ими не следует пользоваться в любом языке, следует максимально на каждый чих писать велосипед. Причем сколько мест использования подобного кода, столько велосипедов в проекте и должно быть. Так сказать, по велосипеду отдельному на каждое место использования :-)
Y = λf.(λx.f (x x)) (λx.f (x x))

valexey_u

  • Hero Member
  • *****
  • Сообщений: 3013
    • Просмотр профиля
Re: Найдите ошибку, если она есть.
« Ответ #39 : Сентябрь 28, 2016, 08:59:49 pm »
2. затем go() добавила новый элемент в tr, что потребовало выделения нового массива и освобожения старого

Жесть. Это ошибка где-то на уровне генов в с++.

Ну в общем то ничего нового.

То есть, переводя на обычный язык, получается вроде того, что "в чем тут проблема, как это лечить (на уровне языка, либы или же подходов) и из за чего это случается я не знаю, но в любом случае это лишнее подтверждение тому, что С++ -- говно" :-)

Покажите мне язык где подобная ошибка не может возникнуть в принципе!
Y = λf.(λx.f (x x)) (λx.f (x x))

kkkk

  • Full Member
  • ***
  • Сообщений: 135
    • Просмотр профиля
Re: Найдите ошибку, если она есть.
« Ответ #40 : Сентябрь 28, 2016, 10:54:08 pm »
О таком фокусе я знаю, но в общем случае резерва не требуется, в то время как в Си - он обязателен.
Какие-то странные представления о Си. Конечно же и в Си это не обязательно.
У меня действительно странные представления о Си - основаны на логике и опыте ежедневного использования, к счастью и к сожалению. В Си в невыделенной памяти  ничего работать не будет и это очевидно. Даже если программист перепил чаю и забыл выделить память, после первого же неудачного запуска он это исправит. В С++ с вектором в большинстве случаев можно не волноваться, Tefal думает за вас. Но иногда это выливается в непонимание, как работает программа и приходится тратить немало времени, чтобы выяснить причину проблемы. Речь об этом.
 

Цитировать
Выделить сразу памяти столько сколько будет необходимо в общем случае либо невозможно, либо слишком ресурсоёмко для подобных классов задачек как минимум.
В общем случае - нет, а в частном зачастую есть асимптотические формулы, позволяющие найти достаточно хорошее приближение сверху, и именно такое выделение будет самым эффективным по скорости. И памяти нужно в 1.5-2 раза меньше, чем с саморасширяющимся вектором(при удачных для вектора раскладах - одинаковое). К тому же, не надо во время вычисления обрабатывать отказ от выделения очередной порции памяти - хватит или нет ясно в самом начале.

Цитировать
На тему явного и очевидного, то в Си в реальном коде, это смотрелось бы примерно так, на самом деле.
g_array_index(ts, bor, v).mp[x] = go();
...
int go() {
        bor b;
        init(&b);
        g_array_append_val(ts, b);
        return ts->len-1;
}
И ровно с той же семантикой.
Даже при повторении семантики один в один, проблема в Си видна гораздо лучше, потому что в нём нет возможности для самописных структур и функций мимикрировать под массивы, а потому вводить в заблуждение.

Цитировать
Никто по сто раз писать один и тот же велосипед для банального динамического массива/вектора не будет ни в одном из языков. Все либо пишут свои либы, либо используют уже написанные.
А я реализовал нечто подобное, только коллеги это отвергли.

Цитировать
Ну, то есть по сути ты говоришь, что это не С++ плохой как язык и им не надо пользоваться, а это использование готовых либ плохо и ими не следует пользоваться в любом языке, следует максимально на каждый чих писать велосипед.
Нет, я этого не говорю, да и что такое плохой язык, я тоже плохо понимаю.

Цитировать
Причем сколько мест использования подобного кода, столько велосипедов в проекте и должно быть. Так сказать, по велосипеду отдельному на каждое место использования :-)
А это зависит от задачи, решаемой библиотекой или велосипедом. Для банального кода лучше велосипед по месту без запрета на переиспользование неподалёку. Нередко оказывается экономичней помнить базовые принципы и уметь их быстро воплощать, чем помнить и разбираться в нюансах редкоиспользуемой библиотеки с постоянным сверением с документацией.  Для нетривиального кода, например, компрессора данных - однозначно нужно использовать готовое решение, если оно достаточно хорошо для твоей задачи.
Есть ещё вопрос лицензий. Я, как пролетарий, сталкиваюсь с тем, что часто не могу использовать свой старый код. Чтобы переиспользовать нужно получить непростое разрешение на выкладывание в Open Source. Дело благородное, но также сопряжено с дополнительными затратами на оформление кода, соответствующее уровню компании, как в ней это видят. И вот для такой банальщины, как вектор,  проще всего оказывается наколбасить по месту либо использовать ещё более простое решение, и часто это работает.

Geniepro

  • Hero Member
  • *****
  • Сообщений: 1955
  • Знайте- истина в том, что повторено трижды подряд!
    • Просмотр профиля
Re: Найдите ошибку, если она есть.
« Ответ #41 : Сентябрь 29, 2016, 05:10:39 am »
2. затем go() добавила новый элемент в tr, что потребовало выделения нового массива и освобожения старого
Жесть. Это ошибка где-то на уровне генов в с++.

Ну в общем то ничего нового.
Вообще-то аналогичная проблема была бы возможна и в обероне, если бы кто-то удосужился в нём реализовать класс вектор. Впрочем, думаю, можно и обероновским массивом такое же провернуть.
to iterate is human, to recurse, divine

Салат «рекурсия»: помидоры, огурцы, салат…

Geniepro

  • Hero Member
  • *****
  • Сообщений: 1955
  • Знайте- истина в том, что повторено трижды подряд!
    • Просмотр профиля
Re: Найдите ошибку, если она есть.
« Ответ #42 : Сентябрь 29, 2016, 05:21:59 am »
Покажите мне язык где подобная ошибка не может возникнуть в принципе!
Ну хаскель же! даже если там работать с мутабельным вектором, просто так сделать аналогtr[v].mp[x] = go();не вышло бы из-за дизайна языка, пришлось бы делать аналогint tmp = go();
tr[v].mp[x] = tmp;
Но выглядел бы такой код на хаскелле страшно, это точно )))
to iterate is human, to recurse, divine

Салат «рекурсия»: помидоры, огурцы, салат…

kkkk

  • Full Member
  • ***
  • Сообщений: 135
    • Просмотр профиля
Re: Найдите ошибку, если она есть.
« Ответ #43 : Сентябрь 29, 2016, 10:58:56 am »
Вообще-то аналогичная проблема была бы возможна и в обероне, если бы кто-то удосужился в нём реализовать класс вектор. Впрочем, думаю, можно и обероновским массивом такое же провернуть.
Эта проблема невозможна в Обероне ни с вектором ни с массивом. Может я ошибаюсь, но тогда покажите как.

ilovb

  • Hero Member
  • *****
  • Сообщений: 2538
  • just another nazi test
    • Просмотр профиля
    • Oberon systems
Re: Найдите ошибку, если она есть.
« Ответ #44 : Сентябрь 29, 2016, 06:41:42 pm »
Geniepro, вот ты все мутабельность упоминаешь (как и все функциональщики).
Можно подумать что причиной таких фейлов является одна только мутабельность.

Откуда вообще возник этот миф будто изменение состояния это зло?
Это же чепуха голимая. И хаскелы всякие сокращают количество ошибок
совсем не потому, что в них "нет состояния"
А потому, что в них заложена более строгая модель вычислений.
Тут адепт бы долго нес херню про типы, монады и прочее трехомудье.
Тогда как все гораздо проще. В любом языке заложена некая модель вычислений.
К какой парадигме принадлежит эта модель, совершенно насрать.
Важно лишь то, насколько эта модель проста и согласована.
Хорошая модель не должна давать написать то, что может похоронить модель в рантайме.
Понятно, что есть динамика, от которой избавиться даже в теории нельзя.
Но речь о другом.
Если язык позволяет свободно обращаться к указателю, который может быть невалидным,
то это херовый язык с кривой моделью.
В правильной модели либо указатель должен быть всегда валидным,
либо язык не должен пропускать обращение к такому указателю без предварительной проверки
его валидности.
Ну все тупо же и очевидно. Если указатель может быть в двух состояниях (валид/инвалид),
то неопределенность должна быть как-то разрешена перед обращением к нему.
Если провести параллель с Обероном, то это охрана типа. Убери охрану
из языка и будешь свободно обращаться к отсутствующим полям записей.

Это свойство и нравится оберонщикам. Это и есть хорошая модель.

И все мы знаем, что даже в обероне в этой модели есть дырка.
Более того в большинстве языков есть NIL. И большинство
языков (включая оберон) позволяют обращаться к указателю без проверки на NIL.
И ведь очевидно, что это косяк в модели, который растащили по языкам все, кому не лень.
Даже Вирт.

Проблема в кривых моделях.

Состояния и мутабельность - это страшилки для дураков.