Как определить конец целочисленного массива при манипуляциях с целочисленным указателем? - PullRequest
9 голосов
/ 29 апреля 2010

Вот код:

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

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

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

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

Output: ABCDE

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

Ответы [ 10 ]

12 голосов
/ 29 апреля 2010

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

Лучший способ перебора последовательностей - использовать цикл for на основе Range, включенный в 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;
}

P.S Boost.Foreach эмулирует цикл for на основе диапазона в компиляторах C ++ 98

11 голосов
/ 29 апреля 2010

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

3 голосов
/ 29 апреля 2010

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

3 голосов
/ 29 апреля 2010

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

int myInt[] ={ 1, 2, 3, 4, 5, -1 };
int *myIntPtr = &myInt[0];
while( *myIntPtr >= 0 )
{
    cout<<*myIntPtr<<endl;
    myIntPtr++;
}
1 голос
/ 29 апреля 2010

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

"one\0two\0three\0" // there's an implicit '\0' appended in C/C++

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

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

1 голос
/ 29 апреля 2010

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

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

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

1 голос
/ 29 апреля 2010

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

0 голосов
/ 12 июня 2017

Общий способ создания указателя конца для любого массива заключается в следующем: Сначала определите количество элементов в массиве, используя 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++)

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

0 голосов
/ 29 апреля 2010
for(i=0; i < sizeof(myInt); i++ )
{
    cout<<*myIntPtr<<endl;
    myIntPtr++;
}

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

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

0 голосов
/ 29 апреля 2010

Используйте 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;
  }
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...