当前位置:  开发笔记 > 编程语言 > 正文

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

如何解决《如何将std::string转换为constchar*或char*?》经验,为你挑选了7个好方法。

如何将std::stringa 转换为a char*或a const char*



1> Johannes Sch..:

如果你只是想传递一个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;

编辑:请注意,上述内容不是安全例外.如果new呼叫和delete呼叫之间有任何内容抛出,您将泄漏内存,因为没有任何内容会delete自动为您调用.有两种直接的方法可以解决这个问题.

提高:: scoped_array

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

std::string str;
boost::scoped_array 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 ::矢量

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

std::string str;
std::vector writable(str.begin(), str.end());
writable.push_back('\0');

// get the char* using &writable[0] or &*writable.begin()


你可以,但strdup不是ac或c ++标准函数,它来自posix :)
只需使用char*result = strdup(str.c_str());
std :: copy是执行此操作的c ++方式,无需获取字符串指针.我尽量避免使用C函数.
我通常更喜欢的是std :: vector writable(str.begin(),str.end()); writable.push_back( '\ 0'); char*c =&writable [0];
从C++ 17开始,[`std :: string :: data()`](http://en.cppreference.com/w/cpp/string/basic_string/data)现在返回一个`CharT*`而不是一个`const CharT*`.更新这个答案可能是个好主意:)
您还可以使用以下内容构造向量:vector writable(str.c_str(),str.size()+ 1);
绝对使用std :: vector.分配char数组不是异常安全的.
我建议它同样是"C++方式",只需将std :: string就地变异,因为它的公共接口暴露了`char&operator []`:这是指定且保证的行为.
你应该在`new char [..]`语句旁边添加一条注释"DO not DO THIS".我怀疑很多人只是复制/粘贴代码示例而不阅读附带的文本.
@Finster,这完全没问题,也许我会在真正的代码中使用它以及匆忙时:)我只是喜欢使用`std :: copy`,当我有时间制作好的代码.让我们不要忘记这是一个区别(虽然不太可能有真正的字符串 - 但谁知道他将要存储什么).`std :: copy`也将复制嵌入的空字节,直到`std :: string`结束.
如果`std :: string`包含嵌入的空字符,涉及`strcpy()`或`strdup()`的解决方案可能会失败.

2> Tony Delroy..:

鉴于......

std::string x = "hello";

从`string`获取`char*`或`const char*`

如何获取有效的字符指针,同时x保留在范围内并且不会进一步修改

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

const char* p_c_str = x.c_str();
const char* p_data  = x.data();
char* p_writable_data = x.data(); // for non-const x from C++17 
const char* p_x0    = &x[0];

      char* p_x0_rw = &x[0];  // compiles iff x is not const...

所有上述指针都将保持相同的值 - 缓冲区中第一个字符的地址.即使是空字符串也有"缓冲区中的第一个字符",因为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()]

仅适用于非const指针p_writable_data:

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

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

C++ 03中,事情要复杂得多(突出显示关键差异):

std::string

返回x.data()到字符串的内部缓冲区,标准不需要以NUL结束(即可能const char*后跟未初始化或垃圾值,其中意外访问具有未定义的行为).

['h', 'e', 'l', 'l', 'o']字符可以安全阅读,即x.size()通过x[0]

对于空字符串,你可以保证一些非空指针,可以安全地添加0(欢呼!),但你不应该取消引用该指针.

x[x.size() - 1]

对于空字符串,这具有未定义的行为(21.3.4)

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

否则,按照f(x.data(), ...):

对于non,x.data() const这会产生非x const指针; 你可以覆盖字符串内容

char*

返回值x.c_str()的ASCIIZ(NUL终止)表示(即['h','e','l','l','o','\ 0']).

虽然几乎没有实现选择这样做,在C++ 03标准措辞,以允许字符串实现自由创造一个独特的NULL结尾的缓冲区 上飞,从潜在非NUL终止缓冲"暴露"的const char*x.data()

&x[0] + 1个字符可以安全阅读.

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

获取外部法律指数的后果

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

这些指针什么时候失效?

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

另请参见如何使字符指针有效,即使在string叶子范围之后或在下面进一步修改 ....

那么,哪个更好用呢?

从C++ 11开始,x用于ASCIIZ数据和.c_str()"二进制"数据(下面进一步说明).

在C++ 03,使用.data(),除非肯定.c_str()是足够的,而喜欢.data().data(),因为它是为空字符串安全....

...尝试&x[0]在适当的时候理解程序,或者你可能会犯其他错误......

保证的ASCII NUL'\ 0'字符data()被许多函数用作表示相关和安全访问数据结束的标记值.这适用于C++ -只有类似功能的说.c_str()和类似共享与-C的功能fstream::fstream(const char* filename, ...),和strchr().

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

使用.c_str()与其他程序员沟通,阅读数据不是ASCIIZ的源代码(相反,你使用字符串来存储数据块(有时甚至不是真正的文本)),或者你将它传递给另一个将它视为"二进制"数据块的函数.这可以是确保其他程序员的代码更改继续正确处理数据的重要见解.

仅限C++ 03:您的.data()实现可能需要进行一些额外的内存分配和/或数据复制才能准备NUL终止缓冲区

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

如何在字母.c_str()范围或进一步修改后使字符指针有效

您需要内容复制x string到外部的新内存区域x.这个外部缓冲区可能在很多地方,例如另一个x或字符数组变量,它可能会或可能没有不同的生命周期,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 old_x(x.data(), x.data() + x.size());       // without the NUL
std::vector 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);

想要a std::string xchar*从a生成的其他原因const char*

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

给"C"代码访问C++ string的文本,如string

printf("x is '%s'", x.c_str());文本复制到函数调用者指定的缓冲区(例如x)或用于设备I/O的易失性存储器(例如strncpy(callers_buffer, callers_buffer_size, x.c_str()))

for (const char* p = x.c_str(); *p; ++p) *p_device = *p;文本追加到已经包含一些ASCIIZ文本的字符数组(例如x) - 注意不要超出缓冲区(在很多情况下你可能需要使用strcat(other_buffer, x.c_str()))

返回一个strncatconst char*一个函数(可能是出于历史原因 - 客户端使用您现有的API - 或者对于C兼容性,您不想返回一个char*,但确实想要将您std::string的数据复制到调用者的某个地方)

注意不要返回一个指针,该指针在指针指向的局部string变量已经离开范围之后可能被调用者解除引用

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


好一个.想要char*(非const)的另一个原因是使用MPI广播.如果您不必来回复制,它看起来更好.我个人会提供一个字符串char*const getter.const指针,但可编辑的字符串.虽然它可能搞乱了从const char*到string的隐式转换......

3> Mark Ransom..:

使用.c_str()方法const char *.

您可以使用&mystring[0]获取char *指针,但有几个问题:您不一定会得到一个零终止字符串,并且您将无法更改字符串的大小.您尤其要注意不要在字符串末尾添加字符,否则会出现缓冲区溢出(以及可能的崩溃).

在C++ 11之前,无法保证所有字符都是同一个连续缓冲区的一部分,但实际上所有已知的实现std::string方式都是这样的.看"&s [0]"是否指向std :: string中的连续字符?.

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


从技术上讲,std :: string存储只在C++ 0x中是连续的.

4> Pixelchemist..:

C++ 17

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

charT* data() noexcept;

返回:指针p,使得[0,size()]中的每个i的p + i ==&operator.


CharT const *std::basic_string

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

CharT *std::basic_string

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

C++ 11

CharT const *std::basic_string
std::string str = { "..." };
str.c_str();
CharT *std::basic_string

从C++ 11开始,标准说:

    对象中的char状basic_string对象应连续存储.也就是说,任何basic_string对象s,身份&*(s.begin() + n) == &*s.begin() + n应持的所有值n这样0 <= n < s.size().


    const_reference operator[](size_type pos) const;
    reference operator[](size_type pos);

    返回:*(begin() + pos)if pos < size(),否则引用CharT具有值的类型的对象CharT(); 参考值不得修改.


    const charT* c_str() const noexcept;
    const charT* data() const noexcept;

    返回:指针p,p + i == &operator[](i)对于每个iin [0,size()].

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

1.使用C++ 11的连续存储

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

简单而简短

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

缺点

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

2.使用 std::vector

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

简单

自动内存处理

动态

缺点

需要字符串副本

3.使用std::arrayif N是编译时间常量(并且足够小)

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

简单

堆栈内存处理

缺点

静态的

需要字符串副本

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

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

内存占用少

自动删除

简单

缺点

需要字符串副本

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

比矢量或数组少的功能

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

最大'控制'

精读

需要字符串副本

最大责任/易错性

复杂



5> Alessandro T..:

我正在使用一个带有很多函数的API作为输入a 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(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一个现有字符串的深层唯一副本(不可复制).


我会避免这个命名约定.`std`使用的`c_str()`是"C-string"的缩写而不是"const string",而`str()`总是返回`std :: basic_string`,而不是`char*`(例如`的std :: stringstream的:: STR()`)

6> devsaw..:

看看这个:

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

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


嗨,你发布的内容已经被多次说过了,有更多的细节,在这个5岁的问题的其他答案中.可以回答较早的问题,但仅限于添加新信息.否则,它只是噪音.
就个人而言,我很欣赏它的简洁性.

7> cegprakash..:
char* result = strcpy((char*)malloc(str.length()+1), str.c_str());


是的,功能是基本的,但你扭曲和弯曲他们看起来像一碗意大利面或一个班轮弗兰肯斯坦的怪物:)
strcpy(),malloc(),length()和c_str()是基本函数,并没有什么难的.只需分配内存和复制.
__是的函数是基本的但是___你还记得你何时开始处理编程语言?一些更多的解释,它将真正帮助新手学习为什么例如与[这个答案]不同或更好(http://stackoverflow.com/a/16505452/3569208):)
@cegprakash:每当有malloc()时,也必须有free().否则代码泄漏内存,答案中的解决方案也是如此.在没有至少暗示所需的释放的情况下分配内存对于这些问题是不好的做法.
推荐阅读
pan2502851807
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有