返回 C++ 中的字符串数组、长度和损坏

Lar*_*Bud -1 c++ visual-c++

我是一名 .NET 人员,正在深入研究 C++/微控制器领域。

以下是我正在尝试做的事情的模型。我有一个传入值的方法,并且根据该值我想返回一个不同的硬编码字符串数组。

当我单步执行此操作时,我的函数main()似乎可以工作,并且看起来我正在函数中返回正确的类型。我可以设置一个断点并查看files[0]是否是预期的字符串,files[1]是否是预期的字符串等。我可以单步执行循环并设置监视,files[i]然后我会看到适当的字符串。那里一切都很好。

那么两个问题:如何获取数组中有多少个元素?看了一百万个帖子,看来我做不到,对吧?我是否只需要一些在循环时需要检查的结束字符串(例如空字符串)?

其次,你会注意到我已经注释掉printf()了。当我启用该功能后,在单步执行后printf(),它会更改字符串的内容,但我不明白为什么要这样做。查看逐步执行之前/之后的图像以及手表的结果。我认为这可能是因为字符串没有以 a 隐式终止\0,但这没有效果。

#include <cstdlib>
#include <cstdio>

const char** GetSoundCategory(int category)
{
    const char* FILES1[3] = {
        "/myfile1.mp3",
        "/this_is_my_file.mp3",
        "/myfile9.mp3"
    };

    const char* FILES2[2] = {
        "/I love S.E.mp3",
         "/please_help_me.mp3"
    };   

    const char** ptr = nullptr;

    switch (category)
    {
        case 1:
            ptr = FILES1;
            break;
        case 2:
            ptr = FILES2;
            break;
    }
    return ptr;
}

int main()
{
    const char** files = GetSoundCategory(2);
    constexpr int number_of_files = 3;

    for (int i = 0; i < number_of_files; i++)
    {
        //  printf("%s\n", files[i]);
    }
}
Run Code Online (Sandbox Code Playgroud)

printf

在 printf 之前

printf

打印后

Rem*_*eau 5

您的代码以两种不同的方式表现出未定义的行为。

  1. GetSoundCategory()返回一个指向局部变量的指针,该局部变量在GetSoundCategory()退出时被释放,因此返回的指针处于悬空状态,在调用者有机会使用它之前指向无效内存。

    由于您的数组不会更改,因此您可以将它们移动到全局内存或静态内存中,以便它们在GetSoundCategory()退出后保持活动状态,例如:

    static const char* FILES1[3] = {
        "/myfile1.mp3",
        "/this_is_my_file.mp3",
        "/myfile9.mp3"
    };
    
    static const char* FILES2[2] = {
        "/I love S.E.mp3",
        "/please_help_me.mp3"
    };   
    
    const char** GetSoundCategory(int category)
    {
        ...
    }
    
    Run Code Online (Sandbox Code Playgroud)

    或者:

    const char** GetSoundCategory(int category)
    {
        static const char* FILES1[3] = {
            "/myfile1.mp3",
            "/this_is_my_file.mp3",
            "/myfile9.mp3"
        };
    
        static const char* FILES2[2] = {
            "/I love S.E.mp3",
             "/please_help_me.mp3"
        };   
    
        ...
    }
    
    Run Code Online (Sandbox Code Playgroud)
  2. GetSoundCategory()您的编码方式main()无法知道返回的数组中实际有多少个字符串。因此,当 时category == 2,循环最终超出范围,因为数组中只有 2 个字符串,但循环迭代了 3 次。

    要解决这个问题,您有几种选择。您可以:

    • 以空值终止数组,例如:

      const char** GetSoundCategory(int category)
      {
          static const char* FILES1[4] = {
              "/myfile1.mp3",
              "/this_is_my_file.mp3",
              "/myfile9.mp3",
              nullptr
          };
      
          static const char* FILES2[3] = {
              "/I love S.E.mp3",
              "/please_help_me.mp3",
              nullptr
          };   
      
          ...
      }
      
      ...
      
      const char** files = GetSoundCategory(2);
      
      for (int i = 0; files[i] != nullptr; i++)
      {
          printf("%s\n", files[i]);
      }
      
      Run Code Online (Sandbox Code Playgroud)
    • 返回数组长度以及数组指针。您可以使用输出参数来做到这一点,例如:

      const char** GetSoundCategory(int category, int *length)
      {
          ...
      
          *length = ...;
          return ...;
      }
      
      ...
      
      int number_of_files;
      const char** files = GetSoundCategory(2, &number_of_files);
      
      Run Code Online (Sandbox Code Playgroud)

      或者,返回一个struct包含数组指针和数组长度的简单值,例如:

      struct filesArray {
          const char** files;
          int number_of_files;
      };
      
      filesArray GetSoundCategory(int category)
      {
          ...
      
          filesArray fa;
          fa.files = ...;
          fa.number_of_files = ...;
      
          return fa;
      }
      
      ...
      
      filesArray fa = GetSoundCategory(2);
      
      for (int i = 0; i < fa.number_of_files; i++)
      {
          printf("%s\n", fa.files[i]);
      }
      
      Run Code Online (Sandbox Code Playgroud)

话虽如此,您确实应该在代码中放弃旧的基于 C 的习惯用法,并采用更现代的 C++ 习惯用法,例如std::vectorstd::arraystd::string(_view)等,例如:

#include <iostream>
#include <vector>
#include <array>
#include <string_view>

std::vector<std::string_view> GetSoundCategory(int category)
{
    static std::array<const char*, 3> FILES1 = {
        "/myfile1.mp3",
        "/this_is_my_file.mp3",
        "/myfile9.mp3"
    };

    static std::array<const char*, 2> FILES2 = {
        "/I love S.E.mp3",
        "/please_help_me.mp3"
    };   

    switch (category)
    {
        case 1:
            return {FILES1.begin(), FILES1.end()};
        case 2:
            return {FILES2.begin(), FILES2.end()};
    }

    return {};
}

int main()
{
    auto files = GetSoundCategory(2);

    for (auto file : files)
    {
        std::cout << file << std::endl;
    }
}
Run Code Online (Sandbox Code Playgroud)