Как определить конец целочисленного массива при манипулировании целочисленным указателем?

вот код:

int myInt[] ={ 1, 2, 3, 4, 5 };
int *myIntPtr = &myInt[0];
while( *myIntPtr != NULL )
{
    cout<<*myIntPtr<<endl;
    myIntPtr++;
}

Output: 12345....<junks>..........

для массива символов: (поскольку у нас есть нулевой символ в конце, нет проблем при итерации)

char myChar[] ={ 'A', 'B', 'C', 'D', 'E', '' };
char *myCharPtr = &myChar[0];
while( *myCharPtr != NULL )
{
    cout<<*myCharPtr<<endl;
    myCharPtr++;
}

Output: ABCDE

мой вопрос в том, так как мы говорим, чтобы добавить нулевой символ в качестве конца строк, мы исключаем такие проблемы! Если в случае правила добавить 0 в конец целочисленного массива, мы могли бы избежать этой проблемы. Что скажешь?

10 ответов


соглашение C-строк заключается в том, что символ* заканчивается символом '\0'. Для array или любого другого контейнера C++ существуют другие идиомы, которые могут быть применены. Далее следует мои предпочтения

лучший способ итерации по последовательностям-использовать основанный на диапазоне for-loop, включенный в C++0x

int my_array[] = {1, 2, 3, 4, 5};
for(int& x : my_array)
{
  cout<<x<<endl;
}

Если ваш компилятор еще не предоставил это, используйте итераторы

for(int* it = std::begin(array); it!=std::end(array); ++it)
{
  cout<<*it<<endl;
}

и если вы не можете использовать ни std:: begin/end

for(int* it = &array[0]; it!=&array[sizeof(array)]; ++it)
{
  cout<<*it<<endl;
}

П. Ускорения.Еогеасп эмулирует Диапазон на основе for-loop на компиляторах C++98


в C++ лучшим решением является использование std:: vector, а не массива. векторы переносят свои размеры с собой. Проблема с использованием нуля (или любого другого значения) в качестве конечного маркера заключается в том, что, конечно, он не может отображаться в другом месте массива. Это не столько проблема для строк, поскольку мы редко хотим печатать символ с нулевым кодом, но это проблема при использовании массивов ints.


Как насчет использования sizeof? http://www.cppreference.com/wiki/keywords/sizeof


вы можете, конечно, решить самостоятельно значение "sentinel" для хранения в конце массива целых чисел. Если целые числа всегда должны быть неотрицательными, например, можно использовать -1 в качестве значения sentinel, которое отмечает конец массива.

int myInt[] ={ 1, 2, 3, 4, 5, -1 };
int *myIntPtr = &myInt[0];
while( *myIntPtr >= 0 )
{
    cout<<*myIntPtr<<endl;
    myIntPtr++;
}

значение char 0 имеет особое значение, стандартизированное конвенцией и практикой. Значение int 0 не имеет, поэтому это не может быть общим правилом. Если это работает в вашем конкретном случае, вы можете пойти с ним. Однако в целом лучше просто отслеживать длину целых массивов отдельно, так как это работает универсально. Или использовать std::vector или подобный контейнер, который обрабатывает эту работу для вас.


стандарт ASCII и Unicode определяет символ со значением 0 как нулевой символ, а не маркер конца массива/строки. Это только соглашение C/C++, что строки завершаются этим символом. Паскаль использует другую нотацию. Кроме того, символ NULL не обязательно указывает конец массива, содержащего строку. Существует несколько функций Win32 API, которые используют строки с двойным нулевым завершением (диалог открытия файла для одного), например это:

"onetwothree" // there's an implicit '' appended in C/C++

это допустимый код C / C++, нулевой символ не означает конец массива.

чтобы адаптировать эту идею нулевого значения к целочисленным массивам, вам нужно пожертвовать одним из ваших целочисленных значений. Если ваши данные состоят из подмножества набора целых чисел, это не проблема, но если ваши данные могут состоять из любого целочисленного значения, то нет способа определить, является ли данное целое число маркером конца массива или допустимым значением. В этом последнем случае нужна дополнительная информация о количестве элементов в массиве, вручную или автоматически через std:: vector.


во-первых, мы не "добавляем нулевой символ" в конце строки. Нет такой вещи, как "нулевой символ". Мы добавляем ноль символ, который иногда называют "нулевым символом". Но!--0--> не имеет абсолютно никакого отношения к этому. NULL обычно используется в контексте указателя, а не в характере или целого контекста. Ваши сравнения, как *myCharPtr != NULL или *myIntPtr != NULL будет компилироваться (из-за пути NULL определена в C++), но практически не имеет смысла. Если вы ищете нулевой символ в массиве, вы можете проверить его как *myCharPtr != '' или *myCharPtr != 0 или просто *myCharPtr, а не *myCharPtr != NULL.

во-вторых, нулевой символ называется нулевым символом по причине: он равен целому нулю. В конце концов, тип символов в C++ - это простой целочисленный тип. Единственная причина, по которой мы можем использовать нулевой символ как нечто особенное в контексте строки, заключается в том, что его значение зарезервировано для этой конкретной цели. В общем случае в целочисленном контексте резервирование нуля для эта цель явно невозможна по очевидным причинам: ноль так же полезен, как и любое другое целочисленное значение. Тем не менее, если в вашем конкретном приложении integer zero можно использовать в качестве зарезервированного значения, не стесняйтесь использовать его таким образом. Или вы можете использовать любое другое целочисленное значение для этой цели. Но в общем случае, ссылаясь на вопрос, который вы задаете в названии, есть ни чтобы определить конец массива. Это ваша ответственность, чтобы знать, где конец (зная общее количество элементы или пометив конец зарезервированным значением по вашему выбору или каким-либо другим способом). Невозможно определить конец массива даже со строками, потому что все, на что вы можете надеяться, это найти конец строка, что не обязательно является концом массива, в котором хранится эта строка.

если вы явно добавили ноль в конец вашего целочисленного массива, ваш первый цикл с радостью остановится на нем. По какой-то причине вы явно добавили в конце Ваш массив символов (и второй цикл останавливается), но вы не добавили ноль в конце вашего массива целых чисел (и первый цикл не останавливается). Вы задаетесь вопросом, почему ваш первый цикл не остановился на нуле? Потому что ты не положил туда ноль. Это так просто.


используйте std:: vector, как говорит Нил.

или сделайте это способом итератора:

int myInt[] ={ 100, 200, 300, 400, 500 };
int *myIntPtr = &myInt[0];
int *myIntPtr_end = myIntPtr + 5;
while(myIntPtr != myIntPtr_end)
  {
  cout<<*myIntPtr<<endl;
  ++myIntPtr;
  }

for(i=0; i < sizeof(myInt); i++ )
{
    cout<<*myIntPtr<<endl;
    myIntPtr++;
}

Если вы предлагаете свой код, в котором манипулируется myIntPtr, не имеет представления о размере куска, на который он указывает, вам либо нужно решить для магического значения в вашем массиве int, либо реструктурировать код так, чтобы sizeof(myInt) также доступна.

стандартные функции библиотеки C используют последний подход: всякий раз, когда вам нужно передать буферную область через указатель, вы должны передать им ее размер в том же вызове.


общий способ создания конечного указателя для любого массива заключается в следующем: Сначала определите количество элементов в массиве, используя sizeof(array)/sizeof(array[0]). Обратите внимание, что sizeof появляется дважды, потому что он возвращает размер в байтах. Таким образом, для статического массива это размер массива, разделенный на размер элемента в массиве. Тогда конечным указателем на массив является array+number_of_elements. Так что это должно сработать:

int myInt[]={1, 2, 3, 4, 5};
int myIntNumElements = sizeof(myInt) / sizeof(myInt[0]);
int *myIntEnd = myInt + myIntNumElelents;

for (int *myIntPtr = myInt; myInt != myIntEnd; myIntPtr++)
  {
    cout << *myIntPtr << endl;
  }

а теперь для некоторых предостережений:

  • конец указатель указывает на местоположение сразу после окончания массива! Так что *myIntPtr возвращает мусор, а не значение последнего элемента в массиве.
  • это хорошо только для регулярных, статических массивов! для контейнеров, используйте begin и end функции-члены и итераторы.
  • этот подход будет работать с любой версией c++. Однако, если вы используете C++-11 или более позднюю версию, рекомендуется использовать std::begin и std::end функции в инструкции for следующим образом:

    for (int *myIntPtr = std::begin(myInt); myIntPtr != std::end(myIntPtr); myIntPtr++)

  • этот метод предназначен для рассмотрения в дополнение к другим ответам. Какой из них лучше-это вопрос контекста.