在C++中正确使用memset和memcpy来初始化字符数组

5
我想使用字符指针的数据初始化字符数组。我写了以下代码:
(请原谅我在结构体中所做的一切,实际上这段代码应该适用于更大的东西,因此这个结构体和它的使用看起来有些奇怪)
#include <iostream>
#include <string>

struct ABC 
{
    char a;
    char b;
    char c[16];
};

int main(int argc, char const *argv[])
{
    struct ABC** abc;
    std::string _r = "Ritwik";
    const char* r = _r.c_str();

    if (_r.length() <= sizeof((*abc)->c))
    {
        int padding = sizeof((*abc)->c) - _r.length();

        std::cout<<"Size of `c` variable is : "<<sizeof((*abc)->c)<<std::endl;
        std::cout<<"Value of padding is calculated to be : "<<padding<<std::endl;

        char segment_listing[ sizeof((*abc)->c)]; 

        std::cout<<"sizeof segment_listing is "<<sizeof(segment_listing)<<std::endl;

        memcpy(segment_listing, r, _r.length());
        memset( (segment_listing + _r.length()), ' ', padding);

        std::cout<<segment_listing<<std::endl;

    }
    return 0;
}

然而,当我运行我的代码时,在字符串末尾出现了奇怪的字符:
(rh4dev01:~/rough) rghosh> ./crptr
Size of `c` variable is : 16
Value of padding is calculated to be : 10
sizeof segment_listing is 16
Ritwik          °×
(rh4dev01:~/rough) rghosh> ./crptr
Size of `c` variable is : 16
Value of padding is calculated to be : 10
sizeof segment_listing is 16
Ritwik           Ñ
(rh4dev01:~/rough) rghosh> ./crptr
Size of `c` variable is : 16
Value of padding is calculated to be : 10
sizeof segment_listing is 16
Ritwik          g
(rh4dev01:~/rough) rghosh> ./crptr
Size of `c` variable is : 16
Value of padding is calculated to be : 10
sizeof segment_listing is 16
Ritwik          pô
(rh4dev01:~/rough) rghosh> ./crptr
Size of `c` variable is : 16
Value of padding is calculated to be : 10
sizeof segment_listing is 16
Ritwik
(rh4dev01:~/rough) rghosh> ./crptr
Size of `c` variable is : 16
Value of padding is calculated to be : 10
sizeof segment_listing is 16
Ritwik          àå
(rh4dev01:~/rough) rghosh> ./crptr
Size of `c` variable is : 16
Value of padding is calculated to be : 10
sizeof segment_listing is 16
Ritwik           »
(rh4dev01:~/rough) rghosh> ./crptr
Size of `c` variable is : 16
Value of padding is calculated to be : 10
sizeof segment_listing is 16
Ritwik          pZ

请问您能解释这是为什么吗?因为我只在打印长度为16的字符数组,难道不应该只打印出16个字符吗?那两个(有时候为零,有时候为一个)字符来自哪里呢?更重要的是,我的填充是否会破坏任何内存(不属于我的字符数组)?

2
假设您使用 std::cout,我认为您的问题是指 C++ 而不是 C(两者有区别)。正确使用 memcpymemset 或 C 风格数组的方法是根本不用。此外,您究竟想要实现什么? - Grizzly
@JohnDibling 这很糟糕,因为我在混合使用 C 和 C++? - Chani
1
请注意,使用名称_r不是个好主意,因为标准将以下划线开头的名称保留在全局命名空间中供实现使用。 - Billy ONeal
4个回答

2

您的字符串需要以NUL结尾。

    memcpy(segment_listing, r, _r.length());
    memset( (segment_listing + _r.length()), ' ', padding-1);
    segment_listing[_r.length() + padding - 1] = '\0';

也许您最好使用snprintf(),它会为您添加终止符:
    snprintf(segment_listing, sizeof(segment_listing), "%-*s",
             (int)sizeof(segment_listing)-1, r);

谢谢。但是我可以问一下,当数组没有以null结尾时会发生什么变化?gcc知道我只声明了一个大小为16的数组。它不应该在打印16个字符后停止吗?另外,请问我是否正在破坏任何内存(在当前实现中,而不是在您的修改之后)? - Chani
NUL终止符是必需的,这样cout就知道何时停止从传递给输出例程的指针读取。 - jxh
@user315052 我实现了你的建议,但现在每次都出现段错误。这是gdb输出:#0 0x0088b761 在 /usr/lib/libstdc++.so.6 中的 __gnu_cxx::__exchange_and_add () (gdb) frame 1 #1 0x00871fe2 在 /usr/lib/libstdc++.so.6 中的 std::basic_string<char, std::char_traits<char>, std::allocator<char> >::~basic_string () (gdb) frame 2 #2 0x08048c63 在 crptr.cpp 的 main (argc=1, argv=0xbfe86dd4) 中:35 35 return 0; - Chani
@TaylorBrandstetter提到strncpy是一个危险的函数,最好避免使用。 - Maxim Egorushkin
@Maxim 哎呀,你说得对;我想的是 strcpy_s(segment_listing, 16, r);。这些字符串函数太多了,有时候我还是会搞混。 - Taylor Brandstetter
显示剩余2条评论

2
一个 C 字符串以一个0字节结尾,而你没有在任何地方考虑到这一点。你需要使用值为0来终止你的字符串,并且在所有的计算中都要考虑到这个额外的字节。

2
segment_listing中没有空字符。

-1
const int SIZE = 16;  //or 17 if you want 16 + null char
//pre-initialise array - then will automatically be null terminated
char segment_listing[SIZE] = {0};

你可以使用 memset() 函数来模拟它。危险在于如果你覆盖了缓冲区的最后一个字符,你就会失去终止符。更安全的做法是显式地将其放置在那里。 - jxh
你需要使用 constexpr,而不是 const - BatchyX
在C++中,数组的大小必须是所谓的常量表达式,即在编译时已知的表达式。常量表达式可以包含文字常量、运算符、constexpr常量以及将constexpr函数应用于常量表达式的结果。const int变量不能成为常量表达式的一部分,因为它可以被初始化为在编译时不知道的值(比如说time()),所以你的代码是无效的。在你的情况下,你使用常量表达式进行初始化,因此你可以将SIZE声明为constexpr int,这样它就是有效的。 - BatchyX
@user619818:大多数编译器支持变长数组作为扩展功能,但这不是有效的C++11语法。 - BatchyX
只需 constexpr int SIWE = 16; - BatchyX
显示剩余3条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接