Компактная запись указателя с двойными - PullRequest
0 голосов
/ 15 января 2010

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

while (*ptr != '\0') {
   do something
}

Теперь есть двойной или int эквивалент?

#define ARRAY_SIZE 10
double someArray[ARRAY_SIZE] = {0};
double *ptr = someArray;
// then not sure what to do here?  I guess I am looking for an equivalent of the above while loop, but don't want to just do:
for (int i = 0; i < ARRAY_SIZE); *ptr++) 
cout << *ptr;

спасибо!

Ответы [ 7 ]

4 голосов
/ 15 января 2010

Если я вас правильно понимаю, вы хотите перебрать массив и остановиться, когда * ptr имеет определенное значение. Это не всегда возможно. Для символьного массива (строки) общепринятым условием является "строка с нулевым окончанием"; то есть у него будет 0 байт ('\ 0') в конце. Вы можете добавить такого стража в массив int или double-value (если вы можете выделить «специальное» значение, которое иначе не будет использоваться), но это не общепринятый метод.

Кстати, ваш цикл for, вероятно, не тот, который вы хотите:

for (int i = 0; i < ARRAY_SIZE); *ptr++)

Если вы хотите выполнить итерацию по массиву, вам нужно увеличить указатель (ptr ++), а не значение, на которое он указывает (* ptr ++).

1 голос
/ 15 января 2010

(Здесь нет ничего нового, чего не сказано в других ответах, но я попытаюсь кратко представить объяснения и примеры, не теряя ясности.)

Циклическая проверка массива char для '\0' работает, потому что строки в стиле C имеют соглашение, что int, double и большинство других типов не имеют: нулевое завершение. (Этот символ '\0' называется «null» или «NUL», но редко «NULL», чтобы избежать путаницы с макросом с таким именем.)

Так как массивы int и double не имеют этого соглашения, вы должны использовать что-то еще. Вот самые простые альтернативы:

// pass arrays with their size
void ex1(double const* data, int size) {
  for (int n = 0; n < size; ++n) {
    use(data[n]);
  }
}

// use a container class which has a size() method
void ex2(vector<double> const& v) {
  for (int n = 0; n < v.size(); ++n) {
    use(data[n]);
  }
  // or:
  for (vector<double>::const_iterator i = v.begin(); i != v.end(); ++i) {
    use(*i);
  }
  // or, sometimes a slight tweak:
  for (vector<double>::const_iterator i = v.begin(), end = v.end();
       i != end; ++i
  ) {
    use(*i);
  }
}

// pass an iterator range, once you are familiar with iterators
void ex3(double const* begin, int const* end) {
  for (double const* i = begin; i != end; ++i) {
    use(*i);
  }
}

И как вы можете их использовать:

void ex4() {
  double data[] = {3, 5, 42}; // if you don't want to specify the size, then use
  int length = len(data);  // this special len function to get the array length
  // len defined below

  ex1(data, length);   // easy to pass with size now
  ex2(vector<double>(data, data + length));   // easy to create a container too
  ex3(data, data + length);
    // notice the container creation takes a similar iterator range

  double buncha_zeros[42] = {}; // or even if you specify the length
  length = len(buncha_zeros); // you still don't have to repeat yourself
}

template<class T, int N>
N len(T (&)[N]) {
  return N;
}
// note: this exists in boost in a better, more general form as boost::size

Замените "double" на "int" или почти на любой другой тип, и все здесь работает так же.

1 голос
/ 15 января 2010

В C строка заканчивается символом '\ 0' по определению. Наличие специального значения (в данном случае символа '\ 0') указывает на то, что конец последовательности иногда называется использованием часового. Нередко использование часовых в других структурах данных, но это ни в коем случае не универсально.

Если у вас есть набор целых или двойных чисел, оканчивающихся на известное значение, вы можете использовать технику для обхода этой последовательности, аналогично обходу по символам в строке, пока вы не нажмете «\ 0». Подвох в том, что вы должны убедиться, что последовательность, которую он корректно завершил с помощью дозорного, и ваш дозорный не может быть значением, которое вы можете найти в последовательности как фактический фрагмент данных.

Общие значения дозорного:

  • 0 (ноль)
  • -1 * * +1010
  • MAX_INT

но вы можете использовать все, что имеет смысл для вашего приложения. Скажем, ваше приложение имеет дело только с неотрицательными двойными числами, вы можете использовать отрицательное значение в качестве часового:

double *ptr = someArray;

while (*ptr >= 0.0) {
    cout << *ptr;
    ++ptr;
}

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

Недостатки:

  • у вас есть значение, которое не может быть законно использовано в качестве данных (приведенный выше пример является большой проблемой, если вы решите, что должны обрабатывать отрицательные значения)
  • вы должны приложить все усилия, чтобы ваши массивы были правильно завершены
  • при выделении пространства для массива вы должны убедиться, что не забыли выделить элемент для стража.
  • Вы должны пройтись по массиву, чтобы выяснить, насколько он велик

Альтернативой использованию дозорного элемента является перенос размера массива либо путем работы только с массивами определенного размера, известными во время компиляции, либо путем передачи размера массива в качестве параметра (или упаковки его в структуру вместе с массив или указатель на массив).

1 голос
/ 15 января 2010

Не совсем понятно, что вы ищете.

Структура первого цикла (версия "символьного массива") определяется тем фактом, что вы не работаете с абстрактным символьным массивом, а скорее работаете с C-строкой , находящейся в этом массив. C-строка - это последовательность символов (в массиве), оканчивающаяся нулевым символом. Этот нулевой символ завершает ваш первый цикл.

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

double *ptr = someArray;
while (*ptr != 0.0)
  cout << *ptr++;    

Но обычно это не так. Массивы с нулевым символом в конце double не очень часто используются на практике.

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

double *ptr;
for (unsigned n = ARRAY_SIZE, ptr = someArray; n > 0; --n, ++ptr)
  cout << *ptr;

или

for (double *ptr = someArray, *ptr_end = ptr + ARRAY_SIZE; ptr != ptr_end; ++ptr)
  cout << *ptr;

или

double *ptr;
for (unsigned i = 0, ptr = someArray; i < ARRAY_SIZE; ++i, ++ptr)
  cout << *ptr;

и т. Д.

0 голосов
/ 15 января 2010

Для строк в стиле C соглашение состоит в том, что строка заканчивается на 0. Это означает, что вы не можете иметь 0 в строке в стиле C.

Если у вас есть значение double, которое вам не нужно, вы можете сохранить его в конце массива и проверить его. Например, вы можете использовать 0 или NaN (не число). Посмотрите, как использовать NaN в C или в C ++ . Если вы используете не-NaN-число в качестве сторожа, вы должны прочитать Подводные камни при проверке вычислений с плавающей запятой перед сравнением чисел с плавающей запятой на равенство.

Конечно, поскольку вы используете C ++ и не хотите запоминать размер вашего массива, вам следует подумать об использовании std::vector<double>.

0 голосов
/ 15 января 2010

Да, точно такая же запись указателя с массивами значений типа double.

Вот исправленная версия вашей программы:

#include <iostream>

#define ARRAY_SIZE 10

int main(void) {
    double someArray[ARRAY_SIZE] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
    double *ptr = someArray;

    for (int i = 0; i < ARRAY_SIZE; i++, *ptr++) 
        std::cout << *ptr;
}

Вывод этой программы:

0123456789
0 голосов
/ 15 января 2010

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...