Передача массива в качестве параметра функции в C ++ - PullRequest
17 голосов
/ 15 января 2009

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

void doSomething(char charArray[])
{
    // if I want the array size
    int size = sizeof(charArray);
    // NO GOOD, will always get 4 (as in 4 bytes in the pointer)
}

У меня нет возможности узнать, насколько велик массив, поскольку у меня есть только указатель на массив.

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


РЕДАКТИРОВАТЬ: просто дополнение относительно решения. Если массив символов, в частности, был инициализирован так:

char charArray[] = "i am a string";

, тогда \0 уже добавлен в конец массива. В этом случае ответ (помеченный как принятый) работает, так сказать, «из коробки».

Ответы [ 12 ]

38 голосов
/ 15 января 2009

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

void doSomething(char charArray[], size_t size)
{
   // do stuff here
}

template<size_t N>
inline void doSomething(char (&charArray)[N])
{
    doSomething(charArray, N);
}

Этот метод используется функциями Microsoft CRT и шаблоном STLSoft array_proxy .

23 голосов
/ 15 января 2009

Без изменения подписи? Добавить элемент стража. В частности, для массивов символов это может быть '\0' с нулевым символом в конце, который используется для стандартных строк C.

void doSomething(char charArray[])
{
    char* p = charArray;
    for (; *p != '\0'; ++p)
    {
         // if '\0' happens to be valid data for your app, 
         // then you can (maybe) use some other value as
         // sentinel
    }
    int arraySize = p - charArray;

    // now we know the array size, so we can do some thing
}

Конечно, тогда сам ваш массив не может содержать элемент sentinel в качестве содержимого. Для других типов (то есть не-char) массивов это может быть любое значение, которое не является допустимым. Если такого значения не существует, этот метод не работает.

Кроме того, это требует сотрудничества на стороне вызывающего абонента. Вы действительно должны убедиться, что вызывающая сторона резервирует массив из arraySize + 1 элементов, а всегда устанавливает элемент стража.

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

6 голосов
/ 15 января 2009

В целом при работе с C или низкоуровневым C ++ вы можете подумать о переобучении своего мозга, чтобы никогда не рассматривать запись параметров массива в функцию, потому что компилятор C всегда будет обращаться с ними как с указателями. По сути, вводя эти квадратные скобки, вы обманываете себя, полагая, что передается реальный массив с информацией о размере. На самом деле, в C вы можете передавать только указатели. Функция

void foo(char a[])
{
    // Do something...
}

является, с точки зрения компилятора C, в точности эквивалентным:

void foo(char * a)
{
    // Do something
}

и, очевидно, указатель на символ nekkid не содержит информации о длине.

Если вы застряли в углу и не можете изменить сигнатуру функции, рассмотрите возможность использования префикса длины, как предложено выше. Непереносимым, но совместимым хаком является указание длины массива в поле size_t, расположенном перед массива, что-то вроде этого:

void foo(char * a)
{
    int cplusplus_len = reinterpret_cast<std::size_t *>(a)[-1];
    int c_len = ((size_t *)a)[-1];
}

Очевидно, что ваш вызывающий должен создать массивы соответствующим образом, прежде чем передавать их в foo.

Само собой разумеется, что это ужасный взлом, но этот трюк может выйти из неприятностей в крайнем случае.

6 голосов
/ 15 января 2009

Раньше это было довольно распространенное решение для передачи длины в первом элементе массива. Этот тип структуры часто называют BSTR (для «ОСНОВНОЙ строки»), хотя это также обозначает разные (но похожие) типы.

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

В приведенной ниже форме он также работает только для строк длиной <= 255. Однако это можно легко расширить, сохранив длину более одного байта. </p>

void doSomething(char* charArray)
{
    // Cast unnecessary but I prefer explicit type conversions.
    std::size_t length = static_cast<std::size_t>(static_cast<unsigned char>(charArray[0]));
    // … do something.
}
4 голосов
/ 15 января 2009

если он не определен, strlen () будет работать.

2 голосов
/ 15 января 2009

Это на самом деле больше C, чем C ++, в C ++ вы, вероятно, предпочитаете использовать std :: vector. Однако в C нет способа узнать размер массива. Компиляция позволит вам сделать sizeof, если массив был объявлен в текущей области, и только если он был явно объявлен с размером ( EDIT: и "с размером", я имею в виду, что он был либо объявляется с целочисленным размером, либо инициализируется при объявлении, в отличие от передачи в качестве параметра, спасибо за отрицательное голосование).

Распространенным решением в C является передача второго параметра, описывающего количество элементов в массиве.

EDIT:
Извините, пропустил часть о нежелании менять подпись метода. Тогда нет иного решения, кроме как описано другими также: если есть некоторые данные, которые не разрешены в массиве, они могут быть использованы в качестве терминатора (0 в C-строках, -1 также довольно распространено, но это зависит от вашего фактический тип данных, предполагая, что массив символов является гипотетическим)

2 голосов
/ 15 января 2009

Вы не можете определить размер только из charArray. Эта информация не передается в функцию автоматически.

Конечно, если это строка с нулевым символом в конце, вы можете использовать strlen(), но вы, вероятно, уже это учли!

Подумайте о передаче параметра std::vector<char> &, или пары указателей, или указателя с параметром размера.

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

Чтобы функция знала, сколько элементов в массиве было передано ей, вы должны сделать одну из двух вещей:

  1. передать в параметр размера
  2. Поместить информацию о размере в массив каким-либо образом.

Последнее можно сделать несколькими способами:

  • Завершить его с помощью NULL или некоторых другой страж, который не произойдет в нормальные данные.
  • сохранить количество элементов в первой записи, если массив содержит числа
  • сохранить указатель на последнюю запись, если массив содержит указатели
0 голосов
/ 07 июня 2014

попробуйте использовать strlen (charArray); используя заголовочный файл cstring. это произведет количество символов, включая пробелы, пока не достигнет закрытия ".

0 голосов
/ 16 января 2013

Чувак, у вас может быть глобальная переменная для хранения размера массива, который будет доступен во всей программе. По крайней мере, вы можете передать размер массива из функции main () в глобальную переменную, и вам даже не придется изменять сигнатуру метода, поскольку размер будет доступен глобально.

Пожалуйста, смотрите пример:

#include<...>
using namespace std;

int size; //global variable

//your code

void doSomething(char charArray[])
{
    //size available

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