Модераторы: bsa

Поиск:

Ответ в темуСоздание новой темы Создание опроса
> Применение алгоритма find к массивам 
V
    Опции темы
NoviceF
Дата 24.9.2012, 21:25 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 313
Регистрация: 13.3.2012
Где: Ростов-на-Дону

Репутация: 2
Всего: 2



Вот у Липпмана в книжке есть такой пример с описанием:

Код

#include <algoritm>
#include <iostream>

int main()
{
   int search_value;
   int ia[ 6 ] = { 27, 210, 12, 47, 109, 83 };

   cout << "enter search value: ";
   cin >> search_value;

   int *presult = find( &ia[0], &ia[6], search_value );

   cout  << "The value " << search_value
        << ( presult == &ia[6]
            ? " is not present" : " is present" )
   << endl;
}


"Если возвращенный указатель равен адресу &ia[6] (который расположен за последним элементом массива), то поиск оказался безрезультатным, в противном случае значение найдено".

Но у меня как-то в памяти закрепилось, что обращение к памяти  "за последним элементом массива" не влечёт за собой ничего хорошего..

Или данный приём является нормальной практикой и не грозит неопределённым поведением?

Беглый поиск по интернетам и форуму мне не помог.
PM MAIL   Вверх
586
Дата 25.9.2012, 01:17 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Завсегдатай
Сообщений: 2243
Регистрация: 8.5.2006

Репутация: 8
Всего: 146



Ничего страшного не произойдёт, т. к. по этому индексу ничего не пишется и не читается.

То же самое и при использовании итераторов STL контейнеров:
Код
#include <vector>
#include <iostream>
#include <algorithm>
int main()
{
    std::vector<int> v;
    v.push_back(11);
    v.push_back(22);
    std::vector<int>::iterator it = std::find( v.begin(), v.end(), 33);
    if(it != v.end())
    {
        std::cout << *it << std::endl;
    }
    else
    {
        std::cout << *it << std::endl;  // сбой программы
    }
}

Итератор v.begin() ведёт себя как правильный указатель, и по нему можно прочитать значение, а вот чтение или запись с помощью итератора v.end() приведёт к ошибке.

Это сообщение отредактировал(а) 586 - 25.9.2012, 01:18
PM   Вверх
feodorv
Дата 25.9.2012, 01:41 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Комодератор
Сообщений: 2214
Регистрация: 30.7.2011

Репутация: 12
Всего: 45



Цитата(NoviceF @  24.9.2012,  22:25 Найти цитируемый пост)
обращение к памяти

Это было бы тогда так:
Код

int *finishPtr = &a[6];

*finishPtr = 0;
v = *finishPtr;

Вот разименовывание указателя *finishPtr и есть обращение к памяти. До разименовывания указатель представляет собой просто переменную, с целочисленным значением (по сути, номер байта в памяти процесса), 32-битную или 64-битную. Поэтому указателям можно присваивать произвольные значения (например, NULL), можно их сравнивать друг с другом и т.д.

В Вашем примере
Цитата(NoviceF @  24.9.2012,  22:25 Найти цитируемый пост)
find( &ia[0], &ia[6], search_value )

функция find просто так написана, что в случае неудачи с поиском возвращает значение указателя, лежащего за пределами передаваемого ей массива, но это не значит, что внутри find происходит обращение по этому некорректному адресу. Почему в случае неудачи не возвращается NULL - это надо спросить Липпмана.

Вы можете представить себе код функции find в таком виде:
Код

int *find( int *startPtr, int *finishPtr, int searchValue)
{
  int *ptr;

  for( ptr = startPtr; ptr < finishPtr; ptr++)
     if( *ptr == searchValue ) break;

  return ptr;
}

Функция find при этом упрощена до предела. Обращения за пределы массива нет, хотя в случае неудачи возвращается указатель, не указывающий внутрь массива.

Впрочем, здесь есть подводные камни. При вызове find значение аргумента finishPtr может не быть выровненной по границе четырёх (или восьми) байт, то есть ((unsigned int) finishPtr) % 4 != 0 (при этом полагаю, что startPtr - выровнена):
Код

char buf[128]; /* Думаем, что список целочисленных значений */
int bytes = recv( socket, buf, sizeof(buf));
if( bytes > 0 && find( (int *) buf, &buf[bytes], 0) != &buf[bytes] )
{
  /* success */
}
else
{
  ...
}

В этом коде уже заложена ошибка, связанная с тем, что число прочитанных из сокета (а это может быть и файл, и устройство, и всё что угодно) байт не соответствует целому числу ожидаемых интегеров, например, когда bytes равно 10. Тогда мало того, что find в случае неуспеха вернёт не тот указатель (&buf[12] вместо нами проверяемых &buf[10]), так ещё и может найти наше искомое значение в &buf[8], хотя значения buf[10] и buf[11] представляют собой мусор.

Это сообщение отредактировал(а) feodorv - 25.9.2012, 01:45


--------------------
Напильник, велосипед, грабли и костыли - основные инструменты программиста...
PM MAIL   Вверх
borisbn
Дата 25.9.2012, 06:25 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Завсегдатай
Сообщений: 4875
Регистрация: 6.2.2010
Где: Ростов-на-Дону

Репутация: 21
Всего: 135



> Почему в случае неудачи не возвращается NULL - это надо спросить Липпмана.
 smile  smile  smile 


--------------------
Женщины отличаются от программистов тем, что у них чары состоят из стрингов
PM MAIL Jabber   Вверх
NoviceF
Дата 25.9.2012, 08:11 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 313
Регистрация: 13.3.2012
Где: Ростов-на-Дону

Репутация: 2
Всего: 2



Цитата(586 @ 25.9.2012,  01:17)
Ничего страшного не произойдёт, т. к. по этому индексу ничего не пишется и не читается.

То же самое и при использовании итераторов STL контейнеров:
Код
#include <vector>
#include <iostream>
#include <algorithm>
int main()
{
    std::vector<int> v;
    v.push_back(11);
    v.push_back(22);
    std::vector<int>::iterator it = std::find( v.begin(), v.end(), 33);
    if(it != v.end())
    {
        std::cout << *it << std::endl;
    }
    else
    {
        std::cout << *it << std::endl;  // сбой программы
    }
}

Итератор v.begin() ведёт себя как правильный указатель, и по нему можно прочитать значение, а вот чтение или запись с помощью итератора v.end() приведёт к ошибке.

По поводу контейнеров STL вопросов не возникало, т.к. об итераторе, указывающем на область за последним элементом контейнера, написано везде и много, а вот такого обращения с массивами в книгах я не встречал, или не обращал внимания.

Вот что касается того, что речь идёт именно об индексе, который в действительности никак не влияет на память, этого я не увидел, почему-то сразу подумалось (сам додумал, чего в коде не написано smile ), что читаются данные по этому адресу, хотя, конечно, форма представления адресов массива сразу показалась мне странной, оказывается в этом и было всё дело, она использована именно для наглядности.

Спасибо.

Добавлено @ 08:19
feodorv,  спасибо за объяснения.

Это сообщение отредактировал(а) NoviceF - 25.9.2012, 08:22
PM MAIL   Вверх
borisbn
Дата 25.9.2012, 08:19 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Завсегдатай
Сообщений: 4875
Регистрация: 6.2.2010
Где: Ростов-на-Дону

Репутация: 21
Всего: 135



На самом деле find (впрочем, как и другие алгоритмы STL) работает не с итераторами, а с шаблонными параметрами, которые поддерживают операцию разыменования (*it), операцию ++ и сравнения друг с другом. Под эти требования замечательно подходят указатели, а итераторы просто имитируют поведение указателей.

и ещё. рекомендую делать не так
Цитата(NoviceF @  24.9.2012,  21:25 Найти цитируемый пост)
int *presult = find( &ia[0], &ia[6], search_value );

а так
Код
size_t ia_count = sizeof( ia ) / sizeof( ia[ 0 ] );
int * presult = find( ia, ia + ia_count, search_value );

if ( presult != ia + ia_count ) { OK } else { BAD }
думаю, понятно почему.


--------------------
Женщины отличаются от программистов тем, что у них чары состоят из стрингов
PM MAIL Jabber   Вверх
NoviceF
Дата 25.9.2012, 09:04 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 313
Регистрация: 13.3.2012
Где: Ростов-на-Дону

Репутация: 2
Всего: 2



Цитата(borisbn @ 25.9.2012,  08:19)
Код
size_t ia_count = sizeof( ia ) / sizeof( ia[ 0 ] );
int * presult = find( ia, ia + ia_count, search_value );

if ( presult != ia + ia_count ) { OK } else { BAD }
думаю, понятно почему.

Если бы для меня всё было так очевидно smile Это защита от того, что размер инта может быть разным на разных платформах, чтобы не обратиться к не существующему элементу массива по индексу?
PM MAIL   Вверх
borisbn
Дата 25.9.2012, 09:14 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Завсегдатай
Сообщений: 4875
Регистрация: 6.2.2010
Где: Ростов-на-Дону

Репутация: 21
Всего: 135



Цитата(NoviceF @  25.9.2012,  09:04 Найти цитируемый пост)
Это защита от того, что размер инта может быть разным на разных платформах

нет. это защита от того, что завтра ты изменишь размер массива


--------------------
Женщины отличаются от программистов тем, что у них чары состоят из стрингов
PM MAIL Jabber   Вверх
xvr
Дата 25.9.2012, 11:49 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Комодератор
Сообщений: 7046
Регистрация: 28.8.2007
Где: Дублин, Ирландия

Репутация: 35
Всего: 223



Стандарт С++, глава 5.7 Additive operators:
Цитата

5 When an expression that has integral type is added to or subtracted from a pointer, the result has the type
of the pointer operand. If the pointer operand points to an element of an array object, and the array is
large enough, the result points to an element offset from the original element such that the difference of
the subscripts of the resulting and original array elements equals the integral expression. In other words, if
the expression P points to the i-th element of an array object, the expressions (P)+N (equivalently, N+(P))
and (P)-N (where N has the value n) point to, respectively, the i + n-th and i − n-th elements of the array
object, provided they exist. Moreover, if the expression P points to the last element of an array object,
the expression (P)+1 points one past the last element of the array object, and if the expression Q points
one past the last element of an array object, the expression (Q)-1 points to the last element of the array
object. If both the pointer operand and the result point to elements of the same array object, or one past
the last element of the array object
, the evaluation shall not produce an overflow; otherwise, the behavior is
undefined.
Т.е. указатель, смотрящий сразу за границу массива вполне валиден, но не далее.

PM MAIL   Вверх
feodorv
Дата 25.9.2012, 13:48 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Комодератор
Сообщений: 2214
Регистрация: 30.7.2011

Репутация: 12
Всего: 45



Цитата(xvr @  25.9.2012,  12:49 Найти цитируемый пост)
Т.е. указатель, смотрящий сразу за границу массива вполне валиден, но не далее.

Чешу репу. Что значит "валидный указатель" в данном контексте?
Я понял отрывок так, что если вычислить указатель Q, равный P плюс несколько элементов массива, то получим указатель, значение которого больше значения P, даже если Q станет смотреть сразу за границу массива. Если же переборщить, то может произойти переполнение значения Q, и Q может стать меньше P.
Возможно, такое понимание является игрой моего воображения, поправьте, если чо...


--------------------
Напильник, велосипед, грабли и костыли - основные инструменты программиста...
PM MAIL   Вверх
xvr
Дата 25.9.2012, 15:58 (ссылка) |    (голосов:1) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Комодератор
Сообщений: 7046
Регистрация: 28.8.2007
Где: Дублин, Ирландия

Репутация: 35
Всего: 223



Цитата(feodorv @  25.9.2012,  13:48 Найти цитируемый пост)
Я понял отрывок так, что если вычислить указатель Q, равный P плюс несколько элементов массива, то получим указатель, значение которого больше значения P, даже если Q станет смотреть сразу за границу массива.

Насколько я понимаю, имелось в виду следующее. Если у нас есть массив типа int a[10];, то мы можем свободно использовать в адресной арифметике указателями от &a[0] до &a[10] включительно. При этом будут получаться указатели с вполне ожидаемым поведением (в том числе и ожидаемыми результатами их сравнения). Если же мы попытаемся вычислить &a[11] - то это уже UB

Добавлено через 1 минуту и 17 секунд
Цитата(feodorv @  25.9.2012,  13:48 Найти цитируемый пост)
Если же переборщить, то может произойти переполнение значения Q, и Q может стать меньше P.

Более того, он вполне может стать чем угодно, NULL например  smile 

PM MAIL   Вверх
bsa
Дата 25.9.2012, 17:27 (ссылка) |    (голосов:1) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Модератор
Сообщений: 9185
Регистрация: 6.4.2006
Где: Москва, Россия

Репутация: 85
Всего: 196



Проще говоря, на 16-ти битных платформах можно создать массив из 65535 чаров. При этом индекс 65535 валиден, но выходит за границу массива. Следующий же индекс должен быть 65536, но будет 0. А это уже валидный индекс внутри массива. В итоге, программа его использующая, будет работать не так, как ожидалось.
PM   Вверх
maxim1000
Дата 25.9.2012, 17:56 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Участник
Сообщений: 3334
Регистрация: 11.1.2003
Где: Киев

Репутация: 1
Всего: 110



Цитата(borisbn @  25.9.2012,  08:19 Найти цитируемый пост)
size_t ia_count = sizeof( ia ) / sizeof( ia[ 0 ] );
int * presult = find( ia, ia + ia_count, search_value );

в новом стандарте можно даже так:
Код

auto result_iterator=find(std::begin(ia),std::end(ia),search_value);




--------------------
qqq
PM WWW   Вверх
feodorv
Дата 26.9.2012, 01:18 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Комодератор
Сообщений: 2214
Регистрация: 30.7.2011

Репутация: 12
Всего: 45



xvr, bsa, спасибо!
Именно так я и понял))) Речь идёт именно о неожиданном переполнении значения указателя. Пока значение указателя не переполнилось, мы можем спокойно его сравнивать с другим указателем. Иначе - сюрприз.


Цитата(bsa @  25.9.2012,  18:27 Найти цитируемый пост)
 А это уже валидный индекс внутри массива.

Вот. Сочетание "валидный указатель", всё же, соотносится с понятием "разименовывание указателя", не?


Цитата(xvr @  25.9.2012,  16:58 Найти цитируемый пост)
Более того, он вполне может стать чем угодно, NULL например

Гм. В плоской модели памяти поведение указателя при сдвиге предсказуемо, совсем "чем угодно" он стать не может))) И в этом смысле (void *) 4 ничуть не лучше или хуже чем просто NULL)))


--------------------
Напильник, велосипед, грабли и костыли - основные инструменты программиста...
PM MAIL   Вверх
xvr
Дата 26.9.2012, 10:56 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Комодератор
Сообщений: 7046
Регистрация: 28.8.2007
Где: Дублин, Ирландия

Репутация: 35
Всего: 223



Цитата(feodorv @  26.9.2012,  01:18 Найти цитируемый пост)
Вот. Сочетание "валидный указатель", всё же, соотносится с понятием "разименовывание указателя", не?

Не. Например NULL - вполне себе валидный указатель, но разименовывать его явно нельзя  smile 

PM MAIL   Вверх
Ответ в темуСоздание новой темы Создание опроса
Правила форума "C/C++: Для новичков"
JackYF
bsa

Запрещается!

1. Публиковать ссылки на вскрытые компоненты

2. Обсуждать взлом компонентов и делиться вскрытыми компонентами

  • Действия модераторов можно обсудить здесь
  • С просьбами о написании курсовой, реферата и т.п. обращаться сюда
  • Вопросы по реализации алгоритмов рассматриваются здесь


Если Вам понравилась атмосфера форума, заходите к нам чаще! С уважением, JackYF, bsa.

 
0 Пользователей читают эту тему (0 Гостей и 0 Скрытых Пользователей)
0 Пользователей:
« Предыдущая тема | C/C++: Для новичков | Следующая тема »


 




[ Время генерации скрипта: 0.0628 ]   [ Использовано запросов: 22 ]   [ GZIP включён ]


Реклама на сайте     Информационное спонсорство

 
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности     Powered by Invision Power Board(R) 1.3 © 2003  IPS, Inc.