【问题标题】:Define a C++ char array without terminator定义一个没有终结符的 C++ char 数组
【发布时间】:2016-01-22 00:03:24
【问题描述】:

Why this program compiles fine in C & not in C++? 的答案解释说,与 C 语言不同,C++ 语言不允许 char 数组的初始值设定项字符串不足以容纳终止空字符。有没有办法在 C++ 中指定一个未终止的 char 数组,而不会使源代码中的字符串膨胀四倍?

例如,在 C 和 C++ 中,以下是等价的:

const char s[] = "Hello from Stack Overflow";
const char s[] = {'H','e','l','l','o',' ','f','r','o','m',' ','S','t','a','c','k',' ','O','v','e','r','f','l','o','w','\0'};

因为字符串 "Hello from Stack Overflow" 的长度为 25,所以它们会产生一个 26 元素的 char 数组,就像写了以下内容一样:

const char s[26] = "Hello from Stack Overflow";
const char s[26] = {'H','e','l','l','o',' ','f','r','o','m',' ','S','t','a','c','k',' ','O','v','e','r','f','l','o','w','\0'};

仅在 C 语言中,程序可以排除终止空字符,例如如果字符串的长度已知带外。 (在 C99 标准的第 6.7.9 章中查找“如果有空间,则包括终止的空字符”。)

const char s[25] = "Hello from Stack Overflow";
const char s[25] = {'H','e','l','l','o',' ','f','r','o','m',' ','S','t','a','c','k',' ','O','v','e','r','f','l','o','w'};

但在 C++ 中,只有第二个有效。如果我知道我将使用 std::strn 系列中的函数而不是 std::str 系列中的函数来操作数据,C++ 语言中是否存在与 C 的简写语法对应的部分?

我的动机与other question about unterminated char arrays in C++ 不同。其动机是游戏中的多个项目名称存储在二维char 数组中。例如:

const char item_names[][16] = {
    // most items omitted for brevity
    "steel hammer",
    {'p','a','l','l','a','d','i','u','m',' ','h','a','m','m','e','r'}
};

如果没有简写来声明未终止的char 数组,则必须逐个字符地写入最大长度名称,这使得它们的可读性和可维护性低于较短的名称。

【问题讨论】:

  • 为什么不给你的数组string
  • @black 您的问题的答案取决于以下问题的答案:在流行的 C++ 实现中,每个 std::string 实例有多少恒定开销?
  • 在“一般”中可以忽略不计。 string_view 通常(将会)更轻。
  • 何必呢?您通过零填充“钢锤”浪费了三个字节,而在钯上只获得了一个。指向空终止字符串的指针数组会占用更多或更少的空间吗?您打算如何处理通过省略终止符可能节省的少量字节?
  • @AlanStokes 不仅仅是终结者。另外,如果字符串的长度不同,则需要为每个字符串存储一个指针。

标签: c++ arrays string


【解决方案1】:

这是可能的,但我同意 Alan Stokes 的“为什么”

例如使用C++: Can a macro expand "abc" into 'a', 'b', 'c'? 可以对其进行调整,以允许对任何提供的数组进行操作并将其限制为 16 的长度。

#include <boost/preprocessor/repetition/repeat.hpp>
#include <boost/preprocessor/punctuation/comma_if.hpp>

template <unsigned int N>
constexpr char get_ch (char const (&s) [N], unsigned int i)
{
    return i >= N ? '\0' : s[i];
}

#define STRING_TO_CHARS_EXTRACT(z, n, data) \
    BOOST_PP_COMMA_IF(n) get_ch(data, n)

#define STRING_TO_CHARS(STRLEN, STR)  \
    BOOST_PP_REPEAT(STRLEN, STRING_TO_CHARS_EXTRACT, STR)


const char item_names[][16] = { // most items omitted for brevity
    "steel hammer",
    STRING_TO_CHARS(16, "palladium hammer"),
    //{'p','a','l','l','a','d','i','u','m',' ','h','a','m','m','e','r'},
};

但从长远来看,这可能会给您带来更多麻烦......

【讨论】:

    【解决方案2】:

    c 风格数组的缺点是它不能传递给函数或从函数返回(传递或返回 char* 可以给你一个 char* 的数组,但最后不能给你一个 char[] 的数组),我们可以使用 std::array 来解决这个问题
    c 样式数组的优点是小字符串文字可用于在 char[][] 的初始化列表中初始化较大的 char[](如您的示例所示),对于 std::array 我们使用 std::max模拟这个

    #include<array>
    #include<utility>
    #include<algorithm>
    #include<iostream>
    
    template<size_t string_length, typename T, T... ints, size_t string_literal_size>
    constexpr std::array<char, string_length> generate_string_impl(std::integer_sequence<T, ints...> int_seq, char const(&s)[string_literal_size])
    {
        return { {s[ints]...} };
    }
    
    template<size_t string_length, size_t string_literal_size>
    constexpr std::array<char, string_length> generate_string(char const(&s)[string_literal_size])
    {
        return generate_string_impl<string_length>(std::make_index_sequence<string_literal_size - 1>{}, s);
    }
    
    template<size_t ...string_literals_size>
    constexpr std::array < std::array<char, std::max({ string_literals_size... }) - 1 > , sizeof...(string_literals_size) > generate_array_of_strings(char const(&...s)[string_literals_size])
    {
        return { generate_string < std::max({ string_literals_size... }) - 1 > (s)... };
    }
    
    int main()
    {
        constexpr auto array_of_string1 = generate_array_of_strings("1234", "12345", "123456");
        //std::array<std::array<char,6>,3>{{
        //  {{ '1', '2', '3', '4', '\0', '\0' }},
        //  {{ '1', '2', '3', '4', '5', '\0' }},
        //  {{ '1', '2', '3', '4', '5', '6' }}
        //}}
    
        //std::array<std::array<char,6>,3> satisfies StandardLayoutType requirement
        char const(&array_of_string2)[std::size(array_of_string1)][6] = reinterpret_cast<char const(&)[std::size(array_of_string1)][6]>(array_of_string1);
        char const(*p_array_of_string2)[std::size(array_of_string1)][6] = reinterpret_cast<char const(*)[std::size(array_of_string1)][6]>(&array_of_string1);
    
        for (int i = 0; i != 3; ++i)
        {
            for (int j = 0; j != 6; ++j)
            {
                std::cout << i << "," << j << " " << array_of_string2[i][j] << " " << (*p_array_of_string2)[i][j] << std::endl;
            }
        }
    }
    

    【讨论】:

      猜你喜欢
      • 2015-02-11
      • 1970-01-01
      • 2012-04-20
      • 2010-10-29
      • 2021-06-05
      • 1970-01-01
      • 2012-01-10
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多