首页 文章

如何将std :: string转换为const char *或char *?

提问于
浏览
800

如何将 std::string 转换为 char*const char*

8 回答

  • 963

    试试这个

    std::string s(reinterpret_cast<const char *>(Data), Size);
    
  • 175

    看看这个:

    string str1("stackoverflow");
    const char * str2 = str1.c_str();
    

    但请注意,这将返回 const char * . 对于 char * ,请使用 strcpy 将其复制到另一个 char 数组中 .

  • 33

    .c_str() 使用 .c_str() 方法 .

    你可以使用 &mystring[0] 获得一个 char * 指针,但是有一些问题's: you won' t必然会得到一个零终止的字符串,并且你赢得了't be able to change the string'的大小 . 您尤其要注意不要在字符串末尾添加字符,否则会出现缓冲区溢出(以及可能的崩溃) .

    在C 11之前,无法保证所有字符都是同一个连续缓冲区的一部分,但实际上所有已知的 std::string 实现都是这样的 . 见Does “&s[0]” point to contiguous characters in a std::string? .

    请注意,许多 string 成员函数将重新分配内部缓冲区并使您可能已保存的任何指针无效 . 最好立即使用它们然后丢弃 .

  • 7

    C 17

    C++17 (即将推出的标准)更改模板的概要 basic_string 添加 data() 的非常量重载:

    charT * data()noexcept;返回:指针p,使得[i,size()]中的每个i的p i ==&operator .


    来自std :: basic_string <CharT>的CharT const *

    std::string const cstr = { "..." };
    char const * p = cstr.data(); // or .c_str()
    

    来自std :: basic_string <CharT>的CharT *

    std::string str = { "..." };
    char * p = str.data();
    

    C 11

    来自std :: basic_string <CharT>的CharT const *

    std::string str = { "..." };
    str.c_str();
    

    来自std :: basic_string <CharT>的CharT *

    从C 11开始,标准说:

    basic_string对象中的类char对象应连续存储 . 也就是说,对于任何basic_string对象,标识&(s.begin()n)==& s.begin()n应该适用于n的所有值,使得0 <= n <s.size() . const_reference运算符[](size_type pos)const; reference operator [](size_type pos);返回:*(begin()pos)如果pos <size(),否则引用CharT类型的对象,其值为CharT();参考值不得修改 . const charT * c_str()const noexcept; const charT * data()const noexcept;返回:指针p,使得对于[0,size()]中的每个i,p i ==&operator [](i) .

    有可分割的方法来获取非const字符指针 .

    1.使用C 11的连续存储

    std::string foo{"text"};
    auto p = &*foo.begin();
    

    Pro

    • 简单而简短

    • 快速(仅涉及无复制的方法)

    Cons

    • 最终 '\0' 不得更改/不一定是非常量内存的一部分 .

    2.使用std :: vector <CharT>

    std::string foo{"text"};
    std::vector<char> fcv(foo.data(), foo.data()+foo.size()+1u);
    auto p = fcv.data();
    

    Pro

    • 简单

    • 自动内存处理

    • 动态

    Cons

    • 需要字符串副本

    3.如果N是编译时间常量(并且足够小),则使用std :: array <CharT,N>

    std::string foo{"text"};
    std::array<char, 5u> fca;
    std::copy(foo.data(), foo.data()+foo.size()+1u, fca.begin());
    

    Pro

    • 简单

    • 堆栈内存处理

    Cons

    • 静态

    • 需要字符串副本

    4.具有自动存储删除功能的原始内存分配

    std::string foo{ "text" };
    auto p = std::make_unique<char[]>(foo.size()+1u);
    std::copy(foo.data(), foo.data() + foo.size() + 1u, &p[0]);
    

    Pro

    • 内存占用量小

    • 自动删除

    • 简单

    Cons

    • 需要字符串副本

    • 静态(动态使用需要更多代码)

    • 比矢量或数组少的功能

    5.具有手动处理的原始内存分配

    std::string foo{ "text" };
    char * p = nullptr;
    try
    {
      p = new char[foo.size() + 1u];
      std::copy(foo.data(), foo.data() + foo.size() + 1u, p);
      // handle stuff with p
      delete[] p;
    }
    catch (...)
    {
      if (p) { delete[] p; }
      throw;
    }
    

    Pro

    • 最大'control'

    Con

    • 需要字符串副本

    • 错误的最大责任/易感性

    • 复杂

  • 9

    我正在使用一个带有很多函数的API作为输入 char* .

    我创建了一个小班来面对这类问题,我已经实现了RAII成语 .

    class DeepString
    {
            DeepString(const DeepString& other);
            DeepString& operator=(const DeepString& other);
            char* internal_; 
    
        public:
            explicit DeepString( const string& toCopy): 
                internal_(new char[toCopy.size()+1]) 
            {
                strcpy(internal_,toCopy.c_str());
            }
            ~DeepString() { delete[] internal_; }
            char* str() const { return internal_; }
            const char* c_str()  const { return internal_; }
    };
    

    你可以用它作为:

    void aFunctionAPI(char* input);
    
    //  other stuff
    
    aFunctionAPI("Foo"); //this call is not safe. if the function modified the 
                         //literal string the program will crash
    std::string myFoo("Foo");
    aFunctionAPI(myFoo.c_str()); //this is not compiling
    aFunctionAPI(const_cast<char*>(myFoo.c_str())); //this is not safe std::string 
                                                    //implement reference counting and 
                                                    //it may change the value of other
                                                    //strings as well.
    DeepString myDeepFoo(myFoo);
    aFunctionAPI(myFoo.str()); //this is fine
    

    我已经调用了类 DeepString ,因为它正在创建现有字符串的深层且唯一的副本( DeepString 不可复制) .

  • 7
    char* result = strcpy((char*)malloc(str.length()+1), str.c_str());
    
  • -4

    鉴于......

    std::string x = "hello";
    

    string获取char *const char *

    How to get a character pointer that's valid while x remains in scope and isn't modified further

    C++11 简化了事情;以下所有内容都允许访问相同的内部字符串缓冲区:

    const char* p_c_str = x.c_str();
    const char* p_data  = x.data();
    const char* p_x0    = &x[0];
    
          char* p_x0_rw = &x[0];  // compiles iff x is not const...
    

    所有上述指针都将保持相同的值 - 缓冲区中第一个字符的地址 . 即使空字符串也有"first character in the buffer",因为C 11保证在显式分配的字符串内容之后始终保留额外的NUL / 0终结符字符(例如 std::string("this\0that", 9) 将具有保存 "this\0that\0" 的缓冲区) .

    给出以上任何指针:

    char c = p[n];   // valid for n <= x.size()
                     // i.e. you can safely read the NUL at p[x.size()]
    

    仅适用于来自 &x[0] 的非 const 指针:

    p_x0_rw[n] = c;  // valid for n <= x.size() - 1
                     // i.e. don't overwrite the implementation maintained NUL
    

    在字符串的其他地方写一个NUL不会改变 stringsize() ; string 被允许包含任意数量的NUL--它们没有被 std::string 给予特殊处理(在C 03中也是如此) .

    C++03 中,事情要复杂得多(关键差异 highlighted ):

    • x.data()

    • const char* 返回到字符串的内部缓冲区 which wasn't required by the Standard to conclude with a NUL (即可能是 ['h', 'e', 'l', 'l', 'o'] 后跟未初始化或垃圾值,其中意外访问具有未定义的行为) .

    • x.size() 个字符可以安全阅读,即 x[0]x[x.size() - 1]

    • 对于空字符串,你're guaranteed some non-NULL pointer to which 0 can be safely added (hurray!), but you shouldn' t取消引用那个指针 .

    • &x[0]

    • for empty strings this has undefined behaviour (21.3.4)

    • 例如给 f(const char* p, size_t n) { if (n == 0) return; ...whatever... }x.empty() 时不能调用 f(&x[0], x.size()); - 只需使用 f(x.data(), ...) .

    • 否则,根据 x.data() 但是:

    • for non const x 这会产生非 const char* 指针;你可以覆盖字符串内容

    • x.c_str()

    • const char* 返回到值的ASCIIZ(NUL终止)表示(即['h','e','l','l','o','\0']) .

    • 尽管很少有任何实现选择这样做,但是C 03标准的措辞是允许字符串实现自由地创建 distinct NUL-terminated buffer ,从潜在的非NUL终止缓冲区"exposed"到 x.data()&x[0]

    • x.size() 1个字符可以安全阅读 .

    • 即使对于空字符串也保证安全(['\0']) .

    访问外部法律索引的后果

    无论您获得指针的方式如何,您都不能从指针进一步访问内存,而不是上面描述中保证的字符 . 尝试这样做有未定义的行为,即使对于读取,也存在非常真实的应用程序崩溃和垃圾结果的可能性,此外还有批量数据,堆栈损坏和/或写入的安全漏洞 .

    这些指针什么时候失效?

    如果调用 string 成员函数来修改 string 或保留更多容量,则任何上述方法预先返回的任何指针值都将失效 . 您可以再次使用这些方法来获取另一个指针 . (规则与 string 中的迭代器的规则相同) .

    另请参见如何在 x 保留范围或在下面进一步修改后使字符指针有效....

    那么,哪个更好用?

    从C 11开始,对于ASCIIZ数据使用 .c_str() ,对于"binary"数据使用 .data() (在下面进一步说明) .

    在C 03中,使用 .c_str() ,除非确定 .data() 足够,并且更喜欢 .data() 而不是 &x[0] ,因为它对于空字符串是安全的....

    ...尝试理解程序足够在适当的时候使用 data() ,否则你可能会犯其他错误......

    .c_str() 保证的ASCII NUL '\0'字符被许多函数用作表示相关和安全访问数据结束的标记值 . 这适用于C -only函数,例如 fstream::fstream(const char* filename, ...) 和共享C函数,如 strchr()printf() .

    鉴于C 03的 .c_str() 对返回缓冲区的保证是 .data() 的超集,你可以随时安全地使用 .c_str() ,但人们有时不这样做,因为:

    • 使用 .data() 与其他程序员进行通信,读取数据不是ASCIIZ的源代码(相反,你甚至真的是文本)),或者're passing it to another function that treats it as a block of 101245 data. This can be a crucial insight in ensuring that other programmers'代码更改继续正确处理数据 .
    • C 03:您的 string 实现可能需要进行一些额外的内存分配和/或数据复制才能准备NUL终止缓冲区

    作为进一步提示,如果函数的参数需要( constchar* 但不坚持获取 x.size() ,则该函数可能需要ASCIIZ输入,因此 .c_str() 是一个不错的选择(函数需要知道文本以某种方式终止的位置,因此,如果它不是一个单独的参数,它只能是一个像长度前缀或标记或一些固定的预期长度的约定 .

    如果x离开作用域或进一步修改后,如何使字符指针有效

    您需要 copy string x 的内容到 x 之外的新内存区域 . 此外部缓冲区可能位于许多位置,例如另一个 string 或字符数组变量,由于处于不同的范围(例如命名空间,全局,静态,堆,共享内存,内存映射),它可能会或可能不会具有与 x 不同的生命周期文件) .

    要将 std::string x 中的文本复制到独立的字符数组中:

    // USING ANOTHER STRING - AUTO MEMORY MANAGEMENT, EXCEPTION SAFE
    std::string old_x = x;
    // - old_x will not be affected by subsequent modifications to x...
    // - you can use `&old_x[0]` to get a writable char* to old_x's textual content
    // - you can use resize() to reduce/expand the string
    //   - resizing isn't possible from within a function passed only the char* address
    
    std::string old_x = x.c_str(); // old_x will terminate early if x embeds NUL
    // Copies ASCIIZ data but could be less efficient as it needs to scan memory to
    // find the NUL terminator indicating string length before allocating that amount
    // of memory to copy into, or more efficient if it ends up allocating/copying a
    // lot less content.
    // Example, x == "ab\0cd" -> old_x == "ab".
    
    // USING A VECTOR OF CHAR - AUTO, EXCEPTION SAFE, HINTS AT BINARY CONTENT, GUARANTEED CONTIGUOUS EVEN IN C++03
    std::vector<char> old_x(x.data(), x.data() + x.size());       // without the NUL
    std::vector<char> old_x(x.c_str(), x.c_str() + x.size() + 1);  // with the NUL
    
    // USING STACK WHERE MAXIMUM SIZE OF x IS KNOWN TO BE COMPILE-TIME CONSTANT "N"
    // (a bit dangerous, as "known" things are sometimes wrong and often become wrong)
    char y[N + 1];
    strcpy(y, x.c_str());
    
    // USING STACK WHERE UNEXPECTEDLY LONG x IS TRUNCATED (e.g. Hello\0->Hel\0)
    char y[N + 1];
    strncpy(y, x.c_str(), N);  // copy at most N, zero-padding if shorter
    y[N] = '\0';               // ensure NUL terminated
    
    // USING THE STACK TO HANDLE x OF UNKNOWN (BUT SANE) LENGTH
    char* y = alloca(x.size() + 1);
    strcpy(y, x.c_str());
    
    // USING THE STACK TO HANDLE x OF UNKNOWN LENGTH (NON-STANDARD GCC EXTENSION)
    char y[x.size() + 1];
    strcpy(y, x.c_str());
    
    // USING new/delete HEAP MEMORY, MANUAL DEALLOC, NO INHERENT EXCEPTION SAFETY
    char* y = new char[x.size() + 1];
    strcpy(y, x.c_str());
    //     or as a one-liner: char* y = strcpy(new char[x.size() + 1], x.c_str());
    // use y...
    delete[] y; // make sure no break, return, throw or branching bypasses this
    
    // USING new/delete HEAP MEMORY, SMART POINTER DEALLOCATION, EXCEPTION SAFE
    // see boost shared_array usage in Johannes Schaub's answer
    
    // USING malloc/free HEAP MEMORY, MANUAL DEALLOC, NO INHERENT EXCEPTION SAFETY
    char* y = strdup(x.c_str());
    // use y...
    free(y);
    

    想要从字符串生成char *或const char *的其他原因

    所以,上面你已经看到了如何获得( constchar* ,以及如何制作独立于原始 string 的文本副本,但是你能用它做什么呢?一个随机的例子......

    • 给"C"代码访问C string 的文本,如 printf("x is '%s'", x.c_str());

    • copy x 's text to a buffer specified by your function' s来电(例如 strncpy(callers_buffer, callers_buffer_size, x.c_str()) ),或用于设备I / O的易失性存储器(例如 for (const char* p = x.c_str(); *p; ++p) *p_device = *p;

    • x 的文本附加到已包含某些ASCIIZ文本的字符数组(例如 strcat(other_buffer, x.c_str()) ) - 注意不要超出缓冲区(在许多情况下,您可能需要使用 strncat

    • 从函数返回 const char*char* (可能是出于历史原因 - 客户端's using your existing API - or for C compatibility you don' t想要返回 std::string ,但是想要复制 string 的数据在某个地方为来电者)

    • 注意不要在指针指向的本地 string 变量已经离开范围之后返回可能被调用者取消引用的指针

    • 为不同的 std::string 实现编译/链接的一些具有共享对象的项目(例如STLport和编译器本机)可以将数据作为ASCIIZ传递以避免冲突

  • 18

    如果您只想将std::string传递给需要 const char* 的函数,则可以使用

    std::string str;
    const char * c = str.c_str();
    

    如果你想获得一个可写的副本,比如 char * ,你可以这样做:

    std::string str;
    char * writable = new char[str.size() + 1];
    std::copy(str.begin(), str.end(), writable);
    writable[str.size()] = '\0'; // don't forget the terminating 0
    
    // don't forget to free the string after finished using it
    delete[] writable;
    

    Edit :请注意,上述内容并非异常安全 . 如果 new 调用和 delete 调用之间的任何内容抛出,则会泄漏内存,因为没有任何内容会自动为您调用 delete . 有两种直接的方法可以解决这个问题 .

    boost :: scoped_array

    boost::scoped_array将在超出范围时为您删除内存:

    std::string str;
    boost::scoped_array<char> writable(new char[str.size() + 1]);
    std::copy(str.begin(), str.end(), writable.get());
    writable[str.size()] = '\0'; // don't forget the terminating 0
    
    // get the char* using writable.get()
    
    // memory is automatically freed if the smart pointer goes 
    // out of scope
    

    std :: vector

    这是标准方式(不需要任何外部库) . 您使用std::vector,它可以为您完全管理内存 .

    std::string str;
    std::vector<char> writable(str.begin(), str.end());
    writable.push_back('\0');
    
    // get the char* using &writable[0] or &*writable.begin()
    

相关问题