Могу ли я получить строку non-const C из строки С++?

Констант-правильность в С++ все еще дает мне головные боли. При работе с каким-то старым C-кодом мне нужно назначить поворот строкового объекта С++ в строку C и присвоить его переменной. Однако переменная char * и c_str() возвращает a const char []. Есть ли хороший способ обойти это без необходимости выполнять мою собственную функцию?

edit: Я также пытаюсь избежать вызова new. Я с радостью обменю несколько более сложный код на меньшее количество утечек памяти.

12 ответов

Я предполагаю, что всегда есть strcpy.

Или используйте строки char* в частях вашего кода на С++, которые должны взаимодействовать со старыми файлами.

Или реорганизовать существующий код для компиляции с компилятором С++, а затем использовать std:string.


Поскольку для меня необъяснимые причины никто не ответил так, как я сейчас это делаю, и поскольку другие вопросы сейчас закрываются, указывая на это, я добавлю это здесь, даже если приближение года слишком поздно будет означать, что оно зависает в самом низу кучи...

С С++ 03 std::string не гарантируется сохранение своих символов в непрерывной части памяти, а результат c_str() не должен указывать на внутренний буфер строки, поэтому единственный способ гарантированно работает:

std::vector<char> buffer(s.begin(), s.end());
foo(&buffer[0], buffer.size());
s.assign(buffer.begin(), buffer.end());
</char>

Поскольку не было известных реализаций, которые фактически использовали несмежную память, а поскольку std::string использовался многими, как если бы это было гарантировано, правила будут изменены для предстоящего стандарта, теперь гарантируя непрерывную память для своих символов. Поэтому в С++ 1x это должно работать:

foo(&s[0], s.size());

Однако необходимо соблюдать осторожность: результат &s[0] (как результат s.c_str(), BTW) гарантируется только до тех пор, пока не будет вызвана какая-либо функция-член, которая может измениться Струна. Поэтому вы не должны сохранять результат этих операций в любом месте. Как и в моих примерах, самое безопасное должно быть сделано с ними в конце полного выражения.


Существует важное различие, которое вам нужно сделать здесь: есть ли char*, которому вы хотите присвоить это "морально постоянное"? То есть, отбрасывает const -ness только техничность, и вы действительно будете обрабатывать строку как const? В этом случае вы можете использовать листинг - либо C-стиль, либо С++-стиль const_cast. Пока вы (и кто-либо еще, кто когда-либо поддерживает этот код), имеют дисциплину, чтобы относиться к этому char* как к const char*, все будет хорошо, но компилятор больше не будет смотреть вашу спину, так что если вы когда-нибудь рассматривайте его как не const, вы можете модифицировать буфер, на который опирается что-то еще в вашем коде.

Если ваш char* будет рассматриваться как не const, и вы намерены изменить то, на что он указывает, вы должны скопировать возвращенную строку, а не отбросить ее const -ness.


Всегда есть const_cast...

std::string s("hello world");
char *p = const_cast<char *="">(s.c_str());
</char>

Конечно, это в основном подрывает систему типов, но иногда это необходимо при интеграции со старым кодом.


Вы можете использовать метод copy:

len = myStr.copy(cStr, myStr.length());
cStr[len] = '\0';

Где myStr - ваша строка С++ и cStr a char * с размером не менее myStr.length() +1. Кроме того, len имеет тип size_t и необходим, поскольку копия не завершает нуль cStr.


Если вы можете позволить себе дополнительное размещение вместо рекомендуемого strcpy, я бы рассмотрел использование std::vector следующим образом:

// suppose you have your string:
std::string some_string("hello world");
// you can make a vector from it like this:
std::vector<char> some_buffer(some_string.begin(), some_string.end());
// suppose your C function is declared like this:
// some_c_function(char *buffer);
// you can just pass this vector to it like this:
some_c_function(&some_buffer[0]);
// if that function wants a buffer size as well,
// just give it some_buffer.size()
</char>

Для меня это немного больше, чем С++, чем strcpy. Взгляните на "Эффективный STL-элемент" Мейерса 16, чтобы получить более приятное объяснение, чем я мог бы предложить.


std::string vString;
vString.resize(256); // allocate some space, up to you
char* vStringPtr(&vString.front());
// assign the value to the string (by using a function that copies the value).
// don't exceed vString.size() here!
// now make sure you erase the extra capacity after the first encountered \0.
vString.erase(std::find(vString.begin(), vString.end(), 0), vString.end());
// and here you have the C++ string with the proper value and bounds.

Вот как вы превращаете строку С++ в строку C.. Но убедитесь, что вы знаете, что вы делаете, так как очень легко выйти за пределы, используя необработанные строковые функции. Бывают моменты, когда это необходимо.


Так как c_str() дает вам прямой доступ к структуре данных, вы, вероятно, не должны его бросать. Самый простой способ сделать это, не перераспределяя буфер, - просто использовать strdup.

char* tmpptr;
tmpptr = strdup(myStringVar.c_str();
oldfunction(tmpptr);
free tmpptr;

Это быстро, легко и правильно.


Если вы знаете, что std::string не изменится, будет выполняться отбор C-стиля.

std::string s("hello");
char *p = (char *)s.c_str();

Конечно, p указывает на некоторый буфер, управляемый std::string. Если std::string выходит за пределы области видимости или изменяется буфер (т.е. Записывается в), p, вероятно, будет недействительным.

Самым безопасным способом было бы скопировать строку, если рефакторинг кода не может быть и речи.


Если c_str() возвращает вам копию внутреннего буфера строкового объекта, вы можете просто использовать const_cast<>.

Однако, если c_str() предоставляет вам прямой доступ к внутреннему буферизующему объекту строкового объекта, сделайте явную копию вместо удаления константы.


Просто используйте const_cast<char*>(str.data())</char*>

Не чувствуйте себя плохо или странно, это отличный стиль для этого.

Он гарантированно работает на С++ 11. Тот факт, что он был квалифицирован вообще, является, пожалуй, ошибкой по оригинальному стандарту до него; в С++ 03 было возможно реализовать string как прерывистый список памяти, но никто никогда не делал этого. На Земле нет компилятора, который реализует string как нечто, кроме заразительного блока памяти, поэтому не стесняйтесь относиться к нему как таковой с полной уверенностью.


Неужели это трудно сделать самому?

#include <string>
#include <cstring>
char *convert(std::string str)
{
 size_t len = str.length();
 char *buf = new char[len + 1];
 memcpy(buf, str.data(), len);
 buf[len] = '\0';
 return buf;
}
char *convert(std::string str, char *buf, size_t len)
{
 memcpy(buf, str.data(), len - 1);
 buf[len - 1] = '\0';
 return buf;
}
// A crazy template solution to avoid passing in the array length
// but loses the ability to pass in a dynamically allocated buffer
template <size_t len="">
char *convert(std::string str, char (&buf)[len])
{
 memcpy(buf, str.data(), len - 1);
 buf[len - 1] = '\0';
 return buf;
}
</size_t></cstring></string>

Использование:

std::string str = "Hello";
// Use buffer we've allocated
char buf[10];
convert(str, buf);
// Use buffer allocated for us
char *buf = convert(str);
delete [] buf;
// Use dynamic buffer of known length
buf = new char[10];
convert(str, buf, 10);
delete [] buf;

licensed under cc by-sa 3.0 with attribution.