Могу ли я получить неконстантную строку C обратно из строки C ++? - PullRequest
23 голосов
/ 17 декабря 2009

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

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

Ответы [ 12 ]

24 голосов
/ 30 ноября 2010

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


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

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

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

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

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

12 голосов
/ 17 декабря 2009

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

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

11 голосов
/ 17 декабря 2009

Я думаю, что всегда есть strcpy.

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

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

9 голосов
/ 17 декабря 2009

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

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

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

5 голосов
/ 17 декабря 2009

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

// 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()

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

5 голосов
/ 17 декабря 2009

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

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

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

2 голосов
/ 28 декабря 2017

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

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

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

2 голосов
/ 17 декабря 2009

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

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

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

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

1 голос
/ 25 января 2014
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. Но убедитесь, что вы знаете, что делаете, поскольку действительно легко выйти за пределы, используя функции необработанных строк. Бывают моменты, когда это необходимо.

1 голос
/ 17 декабря 2009

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

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...