Я согласен с ответом от zacherates.
Но вы можете сделать вызов intern () в ваших нелиберальных строках.
Из примера zacherates:
// ... but they are not the same object
new String("test") == "test" ==> false
Если вы ставите нелитеральное равенство строки, это правда
new String("test").intern() == "test" ==> true
Разница в том, что
char *s = "Hello world";
поместит "Hello world"
в только для чтения части памяти и сделает s
указателем на то, что делает любая операция записи в этой памяти незаконна.
Выполняя:
char s[] = "Hello world";
помещает литеральную строку в постоянную память и копирует строку в новую выделенную память в стеке. Таким образом, сделать
s[0] = 'J';
законным.
char s[] = "Hello world";
Здесь s
представляет собой массив символов, который может быть перезаписан, если мы хотим.
char *s = "hello";
Строковый литерал используется для создания этих символьных блоков где-то в памяти, на который указывает этот указатель s
. Мы можем здесь переназначить объект, на который он указывает, изменив это, но пока он указывает на строковый литерал, блок символов, на которые он указывает, не может быть изменен.
Это объявление:
char s[] = "hello";
Создает объект one - массив char
размером 6, называемый s
, инициализированный значениями 'h', 'e', 'l', 'l', 'o', '\0'
. Если этот массив выделен в памяти и как долго он живет, зависит от того, где появляется объявление. Если декларация находится в пределах функции, она будет жить до конца блока, в котором она объявлена, и почти наверняка будет выделена в стеке; если он находится вне функции, то вероятно будет храниться в «инициализированном сегменте данных», который загружается из исполняемого файла в записываемую память при запуске программы.
На с другой стороны, это объявление:
char *s ="hello";
Создает два объекта :
char
s, содержащий значения 'h', 'e', 'l', 'l', 'o', '\0'
, который не имеет имени и имеет продолжительность статического хранения (что означает, что он живет на весь срок службы программы); и s
, которая инициализируется с расположением первого символа в этом неназванном, доступном только для чтения массиве. Неименованный доступный только для чтения массив обычно находится в сегменте «текст» программы, что означает, что он загружается с диска в постоянное запоминающее устройство вместе с самим кодом. Расположение переменной указателя s
в памяти зависит от того, где появляется объявление (как в первом примере).
char s[] = "hello"
, "hello"
является только инициализатором, сообщающим компилятору, как должен инициализироваться массив. Это может привести или не привести к соответствующей строке в текстовом сегменте - например, если s
имеет статическую продолжительность хранения, то, скорее всего, единственный экземпляр "hello"
будет находиться в инициализированном сегменте данных - объект s
сам. Даже если s
имеет автоматическую продолжительность хранения, ее можно инициализировать с помощью последовательности литералов, а не копии (например, movl $1819043176, -6(%ebp); movw $111, -2(%ebp)
).
– caf
17 May 2012 в 02:28
.rodata
, который скрипт компоновщика затем выгружается в тот же сегмент, что и .text
. См. мой ответ .
– Ciro Santilli 新疆改造中心 六四事件 法轮功
5 June 2015 в 07:33
char s[] = "hello";
объявляет s
как массив из char
, который достаточно длинный, чтобы удерживать инициализатор (5 + 1 char
s) и инициализирует массив, копируя члены данного строкового литерала в array.
char *s = "hello";
объявляет s
указателем на один или несколько (в данном случае больше) char
s и указывает его непосредственно в фиксированном (только для чтения) месте, содержащем литерал "hello"
.
s
является указателем на const char
.
– CB Bailey
8 November 2011 в 16:20
C99 N1256 draft
Существует два совершенно разных применения литералов массива:
char[]
: char c[] = "abc";
Это «более волшебное» и описано при 6.7.8 / 14 «Инициализация»: массив типа символа может быть инициализирован литералом строки символов, необязательно заключенным в фигурные скобки. Последовательные символы символьного строкового литерала (включая завершающий нулевой символ, если есть место или массив неизвестного размера) инициализируют элементы массива. Таким образом, это просто сокращение для: char c[] = {'a', 'b', 'c', '\0'};
Как и любой другой регулярный массив, c
может быть изменен. char *c = "abc";
Это похоже на: /* __unnamed is magic because modifying it gives UB. */
static char __unnamed[] = "abc";
char *c = __unnamed;
Обратите внимание на неявный листинг с char[]
на char *
, который всегда законным. Затем, если вы измените c[0]
, вы также измените __unnamed
, который является UB. Это описано в разделе 6.4.5 «Строковые литералы»: 5 В фазе 7 перевода байт или код нулевого значения добавляется к каждой многобайтовой последовательности символов, которая получается из строкового литерала или литералов. Последовательность многобайтовых символов затем используется для инициализации массива статической продолжительности хранения и длины, достаточной для того, чтобы содержать последовательность. Для символьных строковых литералов элементы массива имеют тип char и инициализируются отдельными байтами многобайтовой последовательности символов [...] 6 Не определено, являются ли эти массивы различными, если их элементы имеют соответствующие значения. Если программа пытается изменить такой массив, поведение не определено. 6.7.8 / 32 «Инициализация» дает прямой пример:
ПРИМЕР 8: Объявление
char s[] = "abc", t[3] = "abc";
определяет «простые» объекты массива char
s
иt
, элементы которых инициализируются символами строковых символов.Это объявление идентично
char s[] = { 'a', 'b', 'c', '\0' }, t[] = { 'a', 'b', 'c' };
Содержимое массивов изменяемый. С другой стороны, объявление
char *p = "abc";
определяет
blockquote>p
с типом «указатель на символ» и инициализирует его, указывая на объект с типом «массив символов» с длиной 4, элементы которого инициализируются с символьным строковым литералом. Если попытается использоватьp
для изменения содержимого массива, поведение не определено.GCC 4.8 x86-64 Реализация ELF
Программа:
#include <stdio.h> int main() { char *s = "abc"; printf("%s\n", s); return 0; }
Скомпилировать и декомпилировать:
gcc -ggdb -std=c99 -c main.c objdump -Sr main.o
Выход содержит:
char *s = "abc"; 8: 48 c7 45 f8 00 00 00 movq $0x0,-0x8(%rbp) f: 00 c: R_X86_64_32S .rodata
Заключение: GCC хранит
char*
в секции.rodata
, а не в.text
.Если мы сделаем то же самое для
char[]
:char s[] = "abc";
, получим:
17: c7 45 f0 61 62 63 00 movl $0x636261,-0x10(%rbp)
, поэтому он получает хранится в стеке (относительно
%rbp
).Обратите внимание, однако, что сценарий компоновщика по умолчанию помещает
.rodata
и.text
в тот же сегмент, который имеет исполнение, но не имеет права на запись. Это можно наблюдать с помощьюreadelf -l a.out
, который содержит:
Section to Segment mapping: Segment Sections... 02 .text .rodata
char *str = "Hello";
Вышеупомянутое устанавливает str для указания на буквальное значение «Hello», которое жестко закодировано в двоичном образе программы, которое помечено как доступное только для чтения в памяти, означает, что любое изменение в этом строковом литерале является незаконным и что вызовет ошибки сегментации.
char str[] = "Hello";
копирует строку в новую выделенную память в стеке. Таким образом, любое изменение в нем разрешено и законно.
means str[0] = 'M';
изменит str на «Mello».
Для получения более подробной информации, пожалуйста, перейдите к аналогичному вопросу:
Учитывая объявления
char *s0 = "hello world";
char s1[] = "hello world";
, предположим следующую гипотетическую карту памяти:
0x01 0x02 0x03 0x04 0x00008000: 'h' 'e' 'l' 'l' 0x00008004: 'o' ' ' 'w' 'o' 0x00008008: 'r' 'l' 'd' 0x00 ... s0: 0x00010000: 0x00 0x00 0x80 0x00 s1: 0x00010004: 'h' 'e' 'l' 'l' 0x00010008: 'o' ' ' 'w' 'o' 0x0001000C: 'r' 'l' 'd' 0x00
Строковый литерал "hello world"
представляет собой 12-элементный массив из char
( const char
в C ++) со статической продолжительностью хранения, что означает, что память для него выделяется при запуске программы и остается выделенной до тех пор, пока программа не завершится. Попытка изменить содержимое строкового литерала вызывает неопределенное поведение.
Линия
char *s0 = "hello world";
определяет s0
как указатель на char
с длительностью автоматического хранения (это означает, что переменная s0
существует только для области, в которой она объявлено) и копирует адрес строкового литерала (0x00008000
в этом примере) к нему. Обратите внимание, что поскольку s0
указывает на строковый литерал, он не должен использоваться в качестве аргумента для любой функции, которая попытается ее модифицировать (например, strtok()
, strcat()
, strcpy()
и т. Д.).
Строка
char s1[] = "hello world";
определяет s1
как 12-элементный массив из char
(длина берется из строкового литерала) с длительностью автоматического хранения и копирует содержимого литерала массиву. Как видно из карты памяти, у нас есть две копии строки "hello world"
; разница в том, что вы можете изменить строку, содержащуюся в s1
.
s0
и s1
взаимозаменяемы в большинстве контекстов; вот исключения:
sizeof s0 == sizeof (char*)
sizeof s1 == 12
type of &s0 == char **
type of &s1 == char (*)[12] // pointer to a 12-element array of char
Вы можете переназначить переменную s0
, чтобы указать на другой строковый литерал или на другую переменную. Вы не можете переназначить переменную s1
, чтобы указать на другой массив.
Просто добавьте: вы также получите разные значения для своих размеров.
printf("sizeof s[] = %zu\n", sizeof(s)); //6
printf("sizeof *s = %zu\n", sizeof(s)); //4 or 8
Как уже упоминалось выше, для массива '\0'
будет выделен конечный элемент.
В случае:
char *x = "fred";
x является lvalue - ему может быть присвоено. Но в случае:
char x[] = "fred";
x не является lvalue, это rvalue - вы не можете назначить ему.
x
является немодифицируемым значением l. Однако почти во всех контекстах он будет оценивать указатель на свой первый элемент, а значение i> - значение r.
– caf
10 November 2009 в 00:02
Во-первых, в аргументах функций они в точности эквивалентны:
void foo(char *x);
void foo(char x[]); // exactly the same in all respects
В других контекстах char *
выделяет указатель, а char []
выделяет массив. Где строка идет в первом случае, спросите вы? Компилятор тайно выделяет статический анонимный массив для хранения строкового литерала. Итак:
char *x = "Foo";
// is approximately equivalent to:
static const char __secret_anonymous_array[] = "Foo";
char *x = (char *) __secret_anonymous_array;
Обратите внимание, что вы не должны пытаться модифицировать содержимое этого анонимного массива с помощью этого указателя; эффекты не определены (часто это означает сбой):
x[1] = 'O'; // BAD. DON'T DO THIS.
Использование синтаксиса массива напрямую выделяет его в новую память. Таким образом, модификация безопасна:
char x[] = "Foo";
x[1] = 'O'; // No problem.
Однако массив работает только до тех пор, пока его область охвата, поэтому, если вы делаете это в функции, не возвращайте и не пропадаете указатель на этот массив - сделайте скопируйте вместо этого strdup()
или аналогичный. Если массив выделен в глобальной области, конечно, не проблема.
В качестве дополнения учтите, что для целей только для чтения использование обоих одинаково, вы можете получить доступ к символу путем индексации либо с помощью формата []
, либо *(<var> + <index>)
:
printf("%c", x[1]); //Prints r
И:
printf("%c", *(x + 1)); //Prints r
Очевидно, что если вы попытаетесь выполнить
*(x + 1) = 'a';
, вы, вероятно, получите ошибку сегментации, поскольку вы пытаетесь получить доступ к постоянной памяти.
x[1] = 'a';
, который также будет segfault (в зависимости от платформы, конечно).
– glglgl
15 July 2016 в 10:07
В свете комментариев здесь должно быть очевидно, что: char * s = "hello"; Это плохая идея, и ее следует использовать в очень узком пространстве.
Это может быть хорошей возможностью указать, что «const correctness» - это «хорошая вещь». Когда бы и где бы вы ни находились, используйте ключевое слово «const» для защиты своего кода, от «расслабленных» абонентов или программистов, которые обычно наиболее «расслаблены», когда указатели вступают в игру.
Достаточно мелодрамы, здесь что можно достичь, когда украшающие указатели с «const». (Примечание: каждый должен читать декларации указателей справа налево.) Вот три разных способа защитить себя при игре с указателями:
const DBJ* p means "p points to a DBJ that is const"
- то есть объект DBJ не может быть изменено с помощью p.
DBJ* const p means "p is a const pointer to a DBJ"
- то есть вы можете изменить объект DBJ через p, но вы не можете изменить сам указатель p.
const DBJ* const p means "p is a const pointer to a const DBJ"
- то есть вы не можете изменить сам указатель p, и вы не можете изменить объект DBJ через p.
Ошибки, связанные с попытками мутаций const-ant, попадают во время компиляции. [Const].
(Предполагается, что вы используете компилятор C ++, конечно?)
- DBJ
"Hello world"
находится в «частях памяти только для чтения», в обоих примерах. Пример с массивом указывает там, пример с массивом копирует символы в элементы массива. – pmg 9 November 2009 в 23:42char msg[] = "hello, world!";
, строка заканчивается в разделе инициализированных данных. Когда объявлено, чтоchar * const
закончится в разделе данных только для чтения. GCC-4.5.3 – gcbenison 27 April 2012 в 16:44