如何从 C/C++ 函数返回 const char *?

Saq*_*Ali 2 c++ arrays arduino char

我有以下 C++ 函数,它从闪存读取字符串并返回它。我想避免使用 String 类,因为这是在 Arduino 上,并且我被告知 Arduino 的 String 类有问题

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;
    }
}
Run Code Online (Sandbox Code Playgroud)

这个功能似乎有效。但调用方法返回一个空字符串。我是否不允许返回指向该函数堆栈上的字符数组的指针?我应该如何编写这个函数的最佳/最惯用的方式是什么,以便调用函数接收我想要传递给它的值?

pad*_*ddy 7

这些评论可能会让您误入歧途,或者充其量会让您感到困惑。让我用一些选项来分解它。

首先,问题正如您所说:当函数从堆栈弹出到调用者时,您要返回的地址的数组不再存在。忽略此结果会导致未定义的行为。

以下是一些选项以及一些讨论:

  1. 调用者拥有缓冲区

    void readFromFlashMemory(char *s, size_t len)
    
    Run Code Online (Sandbox Code Playgroud)

    优点是调用者可以选择如何分配内存,这在嵌入式环境中很有用。

    请注意,为了方便起见,您也可以选择s从此函数返回,或者传达一些额外的含义。

    对我来说,如果我在 Arduino 等嵌入式环境中工作,这将是我 100% 的偏好。

  2. 使用std::string,std::vector或类似的

    std::string readFromFlashMemory()
    
    Run Code Online (Sandbox Code Playgroud)

    如果您不关心分配开销和其他潜在问题(例如随着时间的推移碎片),您可能会这样做。

  3. 自己分配内存

    char* readFromFlashMemory()
    
    Run Code Online (Sandbox Code Playgroud)

    如果您想确保分配的内存大小完全正确,那么您可能会先读入本地缓冲区,然后分配内存并复制。std::string与处理堆内存的其他解决方案相同的内存注意事项。

    这种形式还具有噩梦般的特性,即调用者负责管理返回的指针并最终调用delete[]. 这是非常不可取的。这也是令人痛苦的普遍现象。:(

  4. 如果绝对必须的话,返回动态分配的内存的更好方法

    std::unique_ptr<char[]> readFromFlashMemory()
    
    Run Code Online (Sandbox Code Playgroud)

    与 #3 相同,但指针是安全管理的。需要 C++11 或更高版本。

  5. 使用静态缓冲区

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

    一般都会皱起眉头。特别是因为这种类型的模式会在多线程环境中导致严重的问题。

    人们大多选择这种方法,因为他们很懒并且想要一个简单的界面。我想一个好处是调用者不必知道可接受的缓冲区大小。

  6. 使用内部缓冲区创建您自己的类

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

    这是一个更加冗长的解决方案,并且可能开始听起来像是过度设计。但它确实结合了#1 和#5 的最佳部分。也就是说,您获得缓冲区的堆栈分配,您不需要知道大小,并且函数本身不需要额外的参数。

    如果您确实想要一个静态缓冲区,那么您可以只定义该类的一个静态实例,但区别在于代码中对此的明确意图。

  • 5b) `静态 thread_local char s[FLASH_STOP_ADDR-FLASH_START_ADDR+2];`:-) (2认同)