Как вернуть const char * из функции C / C ++? - PullRequest
0 голосов
/ 02 апреля 2020

У меня есть следующая функция C ++, которая читает строку из памяти fla sh и возвращает ее. Я хочу избежать использования класса String, потому что это на Arduino, и мне сообщили, что класс String для Arduino глючит .

const char* readFromFlashMemory()
{
    char s[FLASH_STOP_ADDR-FLASH_START_ADDR+2];
    memset(s, (byte)'\0', FLASH_STOP_ADDR-FLASH_START_ADDR+2);
    unsigned short int numChars = 0;

    for (int i = FLASH_START_ADDRESS; i <= FLASH_STOP_ADDRESS; i++)
    {
        byte b = EEPROM.read(i);
        if (b == (byte)'\0')
            return s;
        else
            s[numChars++] = (char)b;
    }
}

Эта функция работает , Но вызывающий метод возвращает пустую строку. Разве я не могу вернуть указатель на массив символов, который находится в стеке этой функции? Как лучше / лучше всего идиоматизировать c как мне написать эту функцию, чтобы вызывающая функция получила значение, которое я хочу передать ей?

1 Ответ

3 голосов
/ 02 апреля 2020

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

Во-первых, проблема в том, что вы говорите: массив, адрес которого вы возвращаете, больше не существует, когда функция извлекается из стека для вызывающей стороны. Игнорирование этого результата в Undefined Behavior.

Вот несколько вариантов, а также некоторые обсуждения:

  1. Вызывающая сторона владеет буфером

    void readFromFlashMemory(char *s, size_t len)
    

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

    Обратите внимание, что вы также можете выбрать s для этой функции для удобства или для передачи некоторого дополнительного значения.

    Для меня, если бы я работал во встроенной среде, такой как Arduino, это было бы моим предпочтением 100%.

  2. Использование std::string, std::vector или подобное

    std::string readFromFlashMemory()
    

    Вероятно, именно так вы и поступили бы, если бы вас не волновали накладные расходы и другие потенциальные проблемы, такие как фрагментация во времени.

  3. Выделите память самостоятельно

    char* readFromFlashMemory()
    

    Если вы хотите убедиться, что выделенная память точно соответствует размеру, то вы, вероятно, сначала прочитаете в локальный буфер, а затем выделите память и скопируете. Те же аспекты памяти, что и std::string или другие решения, связанные с кучей памяти.

    Эта форма также имеет свойство nightmari sh вызывающей стороны, которая отвечает за управление возвращаемым указателем и, в конечном итоге, вызывает delete[]. Это крайне нежелательно. Это также печально распространено. : (

  4. Лучший способ вернуть динамически выделенную память, если вам абсолютно необходимо

    std::unique_ptr<char[]> readFromFlashMemory()
    

    То же, что # 3, но указатель управляется безопасно. Требуется C + +11 или более поздняя версия.

  5. Использование буфера stati c

    const char* readFromFlashMemory()
    {
        static char s[FLASH_STOP_ADDR-FLASH_START_ADDR+2];
        // ...
        return s;
    }
    

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

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

  6. Создайте свой собственный класс с внутренним буфером

    class FlashReader
    {
    public:
        const char* Read();
    private:
        char buffer[FLASH_STOP_ADDR-FLASH_START_ADDR+2];
    };
    

    Это более подробное решение, которое может пахнуть как чрезмерное инженерство. Но оно сочетает в себе лучшее части как # 1, так и № 5. То есть, вы получаете выделение стека вашего буфера, вам не нужно знать размер, а сама функция не нуждается в дополнительных аргументах.

    Если вы сделал хочу иметь ati c buffer, тогда вы можете просто определить один экземпляр класса * stati c, но различие было бы ясным намерением этого в коде.

...