10

Answers to Why this program compiles fine in C & not in C++? explain that unlike the C language, the C++ language does not tolerate an initializer string for a char array that is not long enough to hold the terminating null character. Is there a way to specify an unterminated char array in C++ without bloating the string by a factor of four in the source code?

For example, in C and in C++, the following are equivalent:

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'};

Because the string "Hello from Stack Overflow" has length 25, these produce a 26-element char array, as if the following had been written:

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'};

In C only, a program can exclude the terminating null character, such as if the string's length is known out of band. (Look for "including the terminating null character if there is room" in chapter 6.7.9 of the C99 standard.)

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'};

But in C++, only the second is valid. If I know I will be manipulating the data with functions in the std::strn family, not the std::str family, is there a counterpart in the C++ language to the shorthand syntax of C?

My motivation differs from that of the other question about unterminated char arrays in C++. What motivates this is that several names of items in a game are stored in a two-dimensional char array. For example:

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'}
};

If there is no shorthand to declare an unterminated char array, then maximum-length names will have to be written character-by-character, which makes them less readable and less maintainable than to shorter names.

7
  • 3
    Why not string for you arrays? Commented Oct 22, 2015 at 16:38
  • 1
    @black The answer to your question depends on the answer to the following question: In popular implementations of C++, how much constant overhead does each std::string instance have? Commented Oct 22, 2015 at 16:46
  • 1
    Negligible in "general". string_view is (will be) generally lighter. Commented Oct 22, 2015 at 16:49
  • 2
    Why bother? You're wasting three bytes by zero padding "steel hammer" and gaining only one on palladium. Would an array of pointers to null terminated strings take more or less space? What do you intend to do with the handful of bytes you might save by omitting the terminators? Commented Oct 22, 2015 at 18:52
  • @AlanStokes It's not just the terminators. It's also that if the strings are of different lengths, you need to store a pointer to each. Commented Oct 6, 2020 at 16:12

2 Answers 2

0

It is possible but I would agree with Alan Stokes' "Why"

For example using C++: Can a macro expand "abc" into 'a', 'b', 'c'? It could be tweaked father to allow operate on any provided array and constrain it by length 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'},
};

but it could cost you more trouble in a long run...

Sign up to request clarification or add additional context in comments.

Comments

0

the disadvantage of c-style array is that it can not be passed to or returned from functions (pass or return char* can give you an array of char* but not array of char[] in the end), we can use std::array to get around this
the advantage of c-style array is that small string literal can be used to initialize larger char[] in a char[][]'s initialize list (as in your example), for std::array we use std::max to simulate this

#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;
        }
    }
}

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.