Передача массива в качестве параметра функции в C++

В 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 уже добавлен в конец массива. В этом случае ответ (помеченный как принятый) работает, так сказать, "из коробки".

На самом деле здесь есть довольно большое недоразумение. Синтаксис в вашем примере не имеет смысла: функция принимает неизвестное количество данных в стеке. Компилятор фактически игнорирует ваш исходный синтаксис и дает функции сигнатуру void doSomething(char* charArray). Никакая массивность не сохраняется, если вы вызываете ее с помощью char array[10]; doSomething(array);. См. c-faq.com/aryptr/aryptrparam.html

kfsone 02.06.2013 21:53
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
21
1
97 562
12
Перейти к ответу Данный вопрос помечен как решенный

Ответы 12

Ответ принят как подходящий

Без изменения подписи? Добавьте дозорный элемент. В частности, для массивов символов это может быть '\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, а всегда устанавливает элемент дозорного.

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

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

Brian 15.01.2009 17:16

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

Да, это идеальный ответ для массивов символов, как в вопросе, но он не будет работать для других типов данных.

Reunanen 15.01.2009 00:35

Не считая того факта, что запись doSomething(NULL); совершенно легальна. компилятор меняет отпечаток OP на char* charArray :) c-faq.com/aryptr/aryptrparam.html

kfsone 02.06.2013 22:01

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

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

Рассмотрите возможность передачи параметра std::vector<char> &, или пары указателей, или указателя плюс параметр размера.

Я думаю, что Юваль ограничен сигнатурой функции.

Calyth 15.01.2009 00:45

@finnw Это потому, что он вообще не передает массив, а просто передает на него указатель.

kfsone 02.06.2013 22:00

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

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

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

-1. (a) Массивы, объявленные без размеров, определяют свои размеры из их инициализаторов, и к этому размеру можно получить доступ с помощью sizeof (). (b) Использование вместо этого шаблона функции (согласно ответу Джоша Келли) делает позволяет вам определить размер массива, переданного функции.

j_random_hacker 15.01.2009 15:53

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

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

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

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

Даже компилятор соглашается, что ему нужно изменить отпечаток ... c-faq.com/aryptr/aryptrparam.html

kfsone 02.06.2013 22:00

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

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]);

Johannes Schaub - litb 18.01.2009 08:24

В общем, при работе с 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.

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

Разве это не неопределенное поведение?

José D. 20.06.2015 21:54

Вы гарантированно получите 4 на 32-битном ПК, и это правильный ответ. по причине объясненной здесь и здесь. Короткий ответ: на самом деле вы проверяете размер указателя, а не массива, потому что «массив неявно преобразуется или распадается в указатель. Указатель, увы, не сохраняет размерность массива; он не даже сказать вам, что рассматриваемая переменная является массивом ".

Теперь, когда вы используете C++, boost :: array - лучший выбор, чем необработанные массивы. Поскольку это объект, теперь вы не потеряете информацию о размерах.

Думаю, ты справишься:

size_t size = sizeof(array)/sizeof(array[0]);

PS: Я тоже считаю, что название этой темы неверное.

Нет, все утверждения в ОП верны. Большая проблема заключается в том, что в определении void doSomething(char charArray[]) {/*...*/} параметр charArray на самом деле не является массивом.

aschepler 19.11.2012 22:02

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

См. Пример:

#include<...>
using namespace std;

int size; //global variable

//your code

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

}

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

Другие вопросы по теме