Перемещение массива с указателем на массив

Я думал о том, как перемещаться по массиву без итератора int, и мне любопытно, если мой ответ хороший /плохой ,

Первый цикл for просто заполняет каждый элемент массива.

Второй цикл цикла проходит через массив. Указатель int, curr, изначально указывает к первому адресу массива, а затем продолжает кратковременно удерживать адрес каждого элемента массива. Указатель int, ptrLastElement, содержит адрес последнего элемента массива arr.

Условие проверки - это, по существу, разность адреса, содержащегося в ptrLastElement, а адрес, содержащийся в curr. Если разница меньше 0, то нам не хватало элементов для отображения.

Мои главные вопросы:

  1. Будет ли эта идея когда-нибудь полезной?
  2. Это ужасная идея?
  3. Я сделал что-то невероятно глупое?
  4. Как я могу улучшить это и /или сделать его более общим?

main.cpp

#include <iostream>

int main()
{
    const size_t SIZE = 10;
    int arr[SIZE];
    int * ptrLastElement = arr + (SIZE-1);

    // Populate the array, arr, using int iterator
    for (int i = 0; i < SIZE; ++i)
    {
        arr[i] = i;
    }

    // Traversing arr using a pointer
    for (int * curr = arr; (ptrLastElement - curr) >= 0 ; ++curr)
    {
        std::cout << curr << "\t" << *curr << std::endl;
    }

    return 0;
}
8 голосов | спросил Classiest 18 MonEurope/Moscow2017-12-18T10:57:29+03:00Europe/Moscow12bEurope/MoscowMon, 18 Dec 2017 10:57:29 +0300 2017, 10:57:29

2 ответа


7

Это не так страшно, но код может быть улучшен. Вот некоторые идеи о том, как это сделать.

Минимизировать работу внутри цикла

Если мы переопределим ptrLastElement примерно так:

const int *end = &arr[SIZE];

Поскольку указатель начинается с начала массива, цикл for может выглядеть следующим образом:

for (int * curr = arr; curr != end; ++curr)

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

Однако современный C ++ редко использует указатели, поэтому я бы, вероятно, не использовал и не советовал кому бы то ни было использовать такую ​​конструкцию. Там мало пользы, и использование указателей может привести к трудным для поиска ошибкам.

Не используйте std::endl, если '\n' будет делать

Использование std::endl испускает \n и смывает поток. Если вам действительно не нужен поток, вы можете повысить производительность кода, просто испустив '\n' вместо того, чтобы использовать потенциально более дорогостоящий вычислительный ---- +: = 9 =:. + ----

Omit std::endl

Когда программа C или C ++ достигает конца return 0, компилятор автоматически сгенерирует код для возврата 0, поэтому нет необходимости поставить main явно в конце return 0;.

Примечание: , когда я делаю это предложение, он почти всегда сопровождается одним из двух видов комментариев: «Я этого не знал». или «Это плохой совет!» Мое обоснование заключается в том, что безопасно и полезно полагаться на поведение компилятора, явно поддерживаемое стандартом. Для C, поскольку C99; см. раздел 5.1.2.2.3 стандарта ISO /IEC 9899: 1999:

  

[...] возврат от начального вызова к функции main эквивалентен вызову main со значением, возвращаемым функцией exit в качестве аргумента; достигнув main, который завершает функцию } возвращает значение 0.

Для C ++, начиная с первого стандарта в 1998 году; см. раздел ИСО /МЭК 14882: 1998 раздел 3.6.1:

  

Если элемент управления достигнет конца main без столкновения с оператором return, эффект будет выполняться при возврате 0;

Все версии обоих стандартов с тех пор (C99 и C ++ 98) поддерживают ту же идею. Мы полагаемся на автоматически сгенерированные функции-члены в C ++, и немногие люди пишут явные выражения main в конце return;. Причины против упущения, похоже, сводятся к " это выглядит странно ". Если, как и мне, вам любопытно обосновать изменение стандарта C прочитайте этот вопрос . Также обратите внимание, что в начале 1990-х годов это считалось «неряшливой практикой», поскольку в то время это было неопределенное поведение (хотя и широко поддерживаемое).

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

ответил Edward 18 MonEurope/Moscow2017-12-18T11:21:14+03:00Europe/Moscow12bEurope/MoscowMon, 18 Dec 2017 11:21:14 +0300 2017, 11:21:14
5

Заблуждение

По крайней мере, в сообществе C ++ индекс не считается итератором. По факту, использование std::iterator_traits<> on int приведет к ошибке компиляции , Хотя указатель итератор, но не каждый итератор является указателем.

С

Обычно люди предпочитают итераторы, если они легко доступны и упрощают проблему. IIRC даже в C люди предпочитают \ $ [начало, конец) \ $ диапазонов, например. один прошлый конец массива. Это специально разрешено в стандарте C идти один за концом массива, но не дальше.

Применение приведенных выше концепций даст вам то, что Эдвард рассмотрел на этом посту.

итераторы

Можно получить #include <iterator>, чтобы получить доступ к std::begin() и std::end(). Обычно итераторы сопрягаются с #include <algorithm>, чтобы обеспечить доступ к алгоритмам более высокого уровня. Как правило, следует использовать алгоритмы более высокого уровня, если им не нужно управлять алгоритмами, отбирающими у них.

Алгоритмы

std::copy(std::begin(arr), std::end(arr),
          std::ostream_iterator<int>{std::cout, " "});

Теперь это не делает то же самое, что и в вашем сообщении (оно не печатает адрес), но это то, что я использую ежедневно. В моей среде IDE есть специальные ярлыки для этого, особенно для std::begin /std::end, а для std::ostream_iterator<int>.


Цикл заполнения массива можно переписать с помощью std::iota (из <numeric>):

std::iota(std::begin(arr), std::end(arr), 0);

Почему стандартные алгоритмы и итераторы?

Комбинация делает ваш код очень гибким. Если вы решите изменить тип на std::vector<int> или std::list<int> (пожалуйста, не используйте список, если для этого не существует серьезной причины), ваш код будет работать, как ожидалось.

constexpr

const это хорошо, но constexpr будет лучше в этом случае. Эта функция специально разработана для этих случаев использования. В принципе, просто добавьте expr в конец const.

Путь вместе

Со всем вышесказанным код будет выглядеть следующим образом:

#include <iostream>
#include <iterator>
#include <algorithm>
#include <numeric>
#include <array>

int main()
{
    constexpr size_t size = 10;
    std::array<int, size> arr;

    std::iota(std::begin(arr), std::end(arr), 0);
    std::copy(std::begin(arr), std::end(arr),
              std::ostream_iterator<int>{std::cout, " "});
}

Дальнейшее использование стандартной библиотеки

C ++ 11 имеет <array>, который находится внутри простого массива, например. у него нет других нестатических элементов, выравнивание также выполняется правильно. Синтаксис:

std::array<int, size> arr;

Преимущества, которые он дает:

  • Нет разложения в указатель

  • Сохраните их размеры (это constexpr)

  • mimics std::vector '

  • Операции
  • могут быть применены к std::array

Недостатки:

  • Не распадается на указатель (потребуются функции шаблона, которые могут привести к раздуванию бинарных файлов)

  • несколько более подробный

ответил Incomputable 18 MonEurope/Moscow2017-12-18T11:57:29+03:00Europe/Moscow12bEurope/MoscowMon, 18 Dec 2017 11:57:29 +0300 2017, 11:57:29

Похожие вопросы

Популярные теги

security × 330linux × 316macos × 2827 × 268performance × 244command-line × 241sql-server × 235joomla-3.x × 222java × 189c++ × 186windows × 180cisco × 168bash × 158c# × 142gmail × 139arduino-uno × 139javascript × 134ssh × 133seo × 132mysql × 132