В 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, в частности, был инициализирован так:
char charArray[] = "i am a string";
тогда \0 уже добавлен в конец массива. В этом случае ответ (помеченный как принятый) работает, так сказать, "из коробки".





Без изменения подписи? Добавьте дозорный элемент. В частности, для массивов символов это может быть '\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
}
Конечно, тогда сам ваш массив не может содержать дозорный элемент в качестве содержимого. Для других типов массивов (например, не символьных) это может быть любое значение, не являющееся допустимым. Если такого значения не существует, этот метод не работает.
Более того, это требует сотрудничества со стороны вызывающего абонента. Вы действительно должны убедиться, что вызывающий объект резервирует массив элементов arraySize + 1, а всегда устанавливает элемент дозорного.
Однако, если вы действительно не можете изменить подпись, ваши возможности довольно ограничены.
Техника контрольных элементов работает даже с данными без недопустимых элементов, хотя в этом случае вам нужно будет экранировать данные, когда они поступают, и отменять экранирование, когда они выходят. Но в этот момент, вероятно, пора использовать вместо этого класс.
если он обнулен, strlen () будет работать.
Да, это идеальный ответ для массивов символов, как в вопросе, но он не будет работать для других типов данных.
Не считая того факта, что запись doSomething(NULL); совершенно легальна. компилятор меняет отпечаток OP на char* charArray :) c-faq.com/aryptr/aryptrparam.html
Вы не можете определить размер только по charArray. Эта информация не передается в функцию автоматически.
Конечно, если это строка с завершающим нулем, вы можете использовать strlen(), но вы, вероятно, уже об этом думали!
Рассмотрите возможность передачи параметра std::vector<char> &, или пары указателей, или указателя плюс параметр размера.
Я думаю, что Юваль ограничен сигнатурой функции.
@finnw Это потому, что он вообще не передает массив, а просто передает на него указатель.
На самом деле это больше C, чем C++, в C++ вы, вероятно, предпочтете использовать std :: vector. Однако в C нет способа узнать размер массива. Компиляция позволит вам сделать sizeof, если массив был объявлен в текущей области, и только если он был явно объявлен с размером (Обновлено: и «с размером», я имею в виду, что он был объявлен с целым размером, либо инициализируется при объявлении, а не передается в качестве параметра, спасибо за отрицательный голос).
Распространенным решением в C является передача второго параметра, описывающего количество элементов в массиве.
Обновлено:
Извините, пропустил часть о нежелании изменять подпись метода. Тогда нет решения, кроме как описано другими, если есть некоторые данные, которые не разрешены в массиве, их можно использовать в качестве терминатора (0 в C-строках, -1 также довольно часто, но это зависит от вашего фактический тип данных, если массив char является гипотетическим)
-1. (a) Массивы, объявленные без размеров, определяют свои размеры из их инициализаторов, и к этому размеру можно получить доступ с помощью sizeof (). (b) Использование вместо этого шаблона функции (согласно ответу Джоша Келли) делает позволяет вам определить размер массива, переданного функции.
Чтобы функция знала количество элементов в переданном ей массиве, вы должны сделать одно из двух:
Последнее можно сделать несколькими способами:
Даже компилятор соглашается, что ему нужно изменить отпечаток ... c-faq.com/aryptr/aryptrparam.html
Используйте шаблоны. Технически это не соответствует вашим критериям, поскольку меняет подпись, но код вызова изменять не нужно.
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 и шаблоном класса array_proxy STLSoft.
На самом деле раньше было довольно распространенным решением передать длину в первом элементе массива. Такой тип структуры часто называют BSTR (от «BASIC string»), хотя она также обозначает разные (но похожие) типы.
Преимущество по сравнению с принятым решением состоит в том, что определение длины с помощью дозорного устройства выполняется медленно для больших строк. Недостаток очевиден в том, что это довольно низкоуровневый взлом, который не уважает ни типы, ни структуру.
В приведенной ниже форме он также работает только для строк длиной <= 255. Однако это можно легко расширить, сохранив длину более чем в одном байте.
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.
}
у него есть небольшая проблема из-за странной обработки символов C++ и C. если он подписан, значения> 127 могут привести к огромным значениям size_t (циклический перенос) при преобразовании в size_t. вы можете исправить это, преобразовав сначала в символ без знака: static_cast <std :: size_t> ((unsigned char) charArray [0]);
В общем, при работе с C или низкоуровневым C++ вы можете подумать о том, чтобы переобучить свой мозг, чтобы никогда не рассматривать запись параметров массива в функцию, потому что компилятор C всегда будет рассматривать их как указатели. По сути, вводя эти квадратные скобки, вы обманываете себя, думая, что передается реальный массив с информацией о размере. На самом деле в C можно передавать только указатели. Функция
void foo(char a[])
{
// Do something...
}
с точки зрения компилятора C, в точности эквивалентен:
void foo(char * a)
{
// Do something
}
и очевидно, что указатель nekkid char не содержит информации о длине.
Если вы застряли в углу и не можете изменить сигнатуру функции, подумайте об использовании префикса длины, как предложено выше. Непереносимый, но совместимый прием - указать длину массива в поле size_t, расположенном в перед массиве, примерно так:
void foo(char * a)
{
int cplusplus_len = reinterpret_cast<std::size_t *>(a)[-1];
int c_len = ((size_t *)a)[-1];
}
Очевидно, ваш вызывающий должен создать массивы соответствующим образом, прежде чем передавать их в foo.
Излишне говорить, что это ужасный хакер, но этот трюк поможет в кратчайшие сроки избавиться от неприятностей.
Разве это не неопределенное поведение?
Вы гарантированно получите 4 на 32-битном ПК, и это правильный ответ. по причине объясненной здесь и здесь. Короткий ответ: на самом деле вы проверяете размер указателя, а не массива, потому что «массив неявно преобразуется или распадается в указатель. Указатель, увы, не сохраняет размерность массива; он не даже сказать вам, что рассматриваемая переменная является массивом ".
Теперь, когда вы используете C++, boost :: array - лучший выбор, чем необработанные массивы. Поскольку это объект, теперь вы не потеряете информацию о размерах.
Думаю, ты справишься:
size_t size = sizeof(array)/sizeof(array[0]);
PS: Я тоже считаю, что название этой темы неверное.
Нет, все утверждения в ОП верны. Большая проблема заключается в том, что в определении void doSomething(char charArray[]) {/*...*/} параметр charArray на самом деле не является массивом.
Чувак, у тебя может быть глобальная переменная для хранения размера массива, который будет доступен во всей программе. По крайней мере, вы можете передать размер массива из функции main () в глобальную переменную, и вам даже не придется менять сигнатуру метода, поскольку размер будет доступен глобально.
См. Пример:
#include<...>
using namespace std;
int size; //global variable
//your code
void doSomething(char charArray[])
{
//size available
}
попробуйте использовать strlen (charArray); используя файл заголовка cstring. это произведет количество символов, включая пробелы, пока не достигнет закрытия ".
На самом деле здесь есть довольно большое недоразумение. Синтаксис в вашем примере не имеет смысла: функция принимает неизвестное количество данных в стеке. Компилятор фактически игнорирует ваш исходный синтаксис и дает функции сигнатуру
void doSomething(char* charArray). Никакая массивность не сохраняется, если вы вызываете ее с помощьюchar array[10]; doSomething(array);. См. c-faq.com/aryptr/aryptrparam.html