Создать модифицируемый строковый литерал в C ++ - PullRequest
5 голосов
/ 16 июня 2010

Можно ли создать модифицируемый строковый литерал в C ++? Например:

char* foo[] = {
    "foo",
    "foo"
};
char* afoo = foo[0];
afoo[2] = 'g'; // access violation

Это приводит к нарушению прав доступа, потому что "foo" расположены в постоянной памяти (я полагаю, раздел .rdata). Есть ли способ заставить "foo" в доступной для записи памяти (раздел .data)? Даже через прагму было бы приемлемо! (Компилятор Visual Studio)

Я знаю, что могу сделать strdup и ряд других вещей, чтобы обойти эту проблему, но я хочу знать конкретно, могу ли я сделать, как я просил. :)

Ответы [ 6 ]

9 голосов
/ 16 июня 2010

Поскольку это C ++, «лучшим» ответом будет использование строкового класса (std::string, QString, CString и т. Д. В зависимости от среды).вопрос, вы не должны изменять строковые литералы.Стандарт говорит, что это неопределенное поведение.Вам действительно нужно так или иначе продублировать строку, иначе вы пишете неверный C ++.

4 голосов
/ 16 июня 2010

Я думаю, что самое близкое, что вы можете сделать, это инициализировать простой char[] (не символ * []) ​​с литералом:

char foo[] = "foo";

Это все равно будет выполнять копирование в какой-то момент.

Единственный способ обойти это - использовать вызовы системного уровня, чтобы пометить страницу, на которой находится строковый литерал, как доступную для записи. В этот момент вы на самом деле говорите не о C или C ++, а о Windows (или любой другой системе, на которой вы работаете). Вероятно, это возможно в большинстве систем (если только данные не находятся в ПЗУ, как, например, во встроенной системе), но я точно не знаю деталей.

Да, и не забывайте, что в вашем примере:

char* foo[] = {
    "foo",
    "foo"
};

Поскольку в стандарте (C99 6.4.5 / 6 «Строковые литералы») говорится:

Не определено, различаются ли эти массивы при условии, что их элементы имеют соответствующие значения.

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

Вы могли бы даже иметь сценарий, где один строковый литерал существует «внутри» другого:

char* p1 = "some string";
char* p2 = "string";

p2 вполне может указывать на конец строки, на который указывает p1.

Таким образом, если вы начнете изменять строковые литералы с помощью какого-либо хака, который вы можете выполнить в системе, вы можете непреднамеренно изменить некоторые «другие» строки. Это одна из вещей, которую может привести неопределенное поведение.

2 голосов
/ 16 июня 2010

Если вы храните вашу строку в массиве, вы можете изменить ее.

Нет способа «правильно» записать в постоянную память.

Конечно, вы можете прекратить использование C-строк.

1 голос
/ 16 июня 2010

Вы можете создать многомерный массив символов:

#include <iostream>

int main(int argc, char** argv)
{
    char foo[][4] = {
        "foo",
        "bar"
    };
    char* afoo = foo[0];
    afoo[2] = 'g';
    std::cout << afoo << std::endl;
}

Более подробный способ определения массива:

char foo[][4] = {
    {'f', 'o', 'o', '\0'},
    {'b', 'a', 'r', '\0'}
};
1 голос
/ 16 июня 2010

Я бы этого не делал. Таким образом, я могу предоставить только неприятный уродливый взлом, который вы можете попробовать: Получите страницу с постоянным литералом и снимите защиту с этой страницы. См. Функцию VirtualProtect () для Win32. Однако, даже если это работает, это не будет гарантировать правильное поведение все время. Лучше не делай этого.

0 голосов
/ 16 июня 2010

Да.

   (char[]){"foo"}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...