Объясните, почему я получаю ошибку ниже кода & hellip, почему он показывает ошибку сегментации? [Дубликат]

Я согласен с ответом от zacherates.

Но вы можете сделать вызов intern () в ваших нелиберальных строках.

Из примера zacherates:

// ... but they are not the same object
new String("test") == "test" ==> false 

Если вы ставите нелитеральное равенство строки, это правда

new String("test").intern() == "test" ==> true 
441
задан StoryTeller 22 December 2017 в 10:04
поделиться

12 ответов

Разница в том, что

char *s = "Hello world";

поместит "Hello world" в только для чтения части памяти и сделает s указателем на то, что делает любая операция записи в этой памяти незаконна.

Выполняя:

char s[] = "Hello world";

помещает литеральную строку в постоянную память и копирует строку в новую выделенную память в стеке. Таким образом, сделать

s[0] = 'J';

законным.

481
ответ дан nbro 17 August 2018 в 09:04
поделиться
  • 1
    Литеральная строка "Hello world" находится в «частях памяти только для чтения», в обоих примерах. Пример с массивом указывает там, пример с массивом копирует символы в элементы массива. – pmg 9 November 2009 в 23:42
  • 2
    pmg: во втором случае литеральная строка необязательно существует в памяти как единый непрерывный объект вообще - это всего лишь инициализатор, компилятор вполне мог бы испускать серию «загрузки немедленного байта». инструкции, содержащие значения символов, встроенные в них. – caf 9 November 2009 в 23:46
  • 3
    В примере массива char not обязательно помещается строка в стеке - если она появляется на уровне файла, она, скорее всего, будет в некотором виде инициализированном сегменте данных. – caf 9 November 2009 в 23:47
  • 4
    Я хотел бы указать, что char s = "xx" не имеет , чтобы быть в постоянной памяти (некоторые реализации не имеют MMU, например). Проект n1362 c1x просто утверждает, что изменение такого массива приводит к неопределенному поведению. Но +1 во всяком случае, так как полагаться на это поведение - глупо. – paxdiablo 10 November 2009 в 13:35
  • 5
    Я получаю чистую компиляцию файла, содержащего только char msg[] = "hello, world!";, строка заканчивается в разделе инициализированных данных. Когда объявлено, что char * const закончится в разделе данных только для чтения. GCC-4.5.3 – gcbenison 27 April 2012 в 16:44
char s[] = "Hello world";

Здесь s представляет собой массив символов, который может быть перезаписан, если мы хотим.

char *s = "hello";

Строковый литерал используется для создания этих символьных блоков где-то в памяти, на который указывает этот указатель s. Мы можем здесь переназначить объект, на который он указывает, изменив это, но пока он указывает на строковый литерал, блок символов, на которые он указывает, не может быть изменен.

4
ответ дан Bo Persson 17 August 2018 в 09:04
поделиться
  • 1
    @bo Persson Почему блок символов не может быть изменен во втором случае? – Pankaj Mahato 29 January 2014 в 20:28

Это объявление:

char s[] = "hello";

Создает объект one - массив char размером 6, называемый s, инициализированный значениями 'h', 'e', 'l', 'l', 'o', '\0'. Если этот массив выделен в памяти и как долго он живет, зависит от того, где появляется объявление. Если декларация находится в пределах функции, она будет жить до конца блока, в котором она объявлена, и почти наверняка будет выделена в стеке; если он находится вне функции, то вероятно будет храниться в «инициализированном сегменте данных», который загружается из исполняемого файла в записываемую память при запуске программы.

На с другой стороны, это объявление:

char *s ="hello";

Создает два объекта :

  • массив только для чтения из 6 char s, содержащий значения 'h', 'e', 'l', 'l', 'o', '\0', который не имеет имени и имеет продолжительность статического хранения (что означает, что он живет на весь срок службы программы); и
  • - переменная типа указателя на символ, называемая s, которая инициализируется с расположением первого символа в этом неназванном, доступном только для чтения массиве.

Неименованный доступный только для чтения массив обычно находится в сегменте «текст» программы, что означает, что он загружается с диска в постоянное запоминающее устройство вместе с самим кодом. Расположение переменной указателя s в памяти зависит от того, где появляется объявление (как в первом примере).

60
ответ дан caf 17 August 2018 в 09:04
поделиться
  • 1
    В обоих объявлениях для "привет" память выделяется в однократное время ?. И еще одна вещь char * p = "hello" здесь "привет" хранится в текстовом сегменте, как вы указали в своем ответе ... и как насчет char s [] = & quot; hello & quot; будет ли он также хранить сначала в части текстового сегмента, а во время выполнения он будет копироваться в стеке, как сказал Рикард в ответ. прошу пояснить этот момент. – Nishant Kumar 16 May 2012 в 16:00
  • 2
    @Nishant: В случае char s[] = "hello", "hello" является только инициализатором, сообщающим компилятору, как должен инициализироваться массив. Это может привести или не привести к соответствующей строке в текстовом сегменте - например, если s имеет статическую продолжительность хранения, то, скорее всего, единственный экземпляр "hello" будет находиться в инициализированном сегменте данных - объект s сам. Даже если s имеет автоматическую продолжительность хранения, ее можно инициализировать с помощью последовательности литералов, а не копии (например, movl $1819043176, -6(%ebp); movw $111, -2(%ebp)). – caf 17 May 2012 в 02:28
  • 3
    Спасибо кафе за ваше разъяснение. – Nishant Kumar 17 May 2012 в 05:55
  • 4
    Точнее, GCC 4.8 помещает его в .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".

14
ответ дан CB Bailey 17 August 2018 в 09:04
поделиться
  • 1
    Какой метод предпочтительнее использовать в функциях, если s не будет изменен, f (const char s []) или f (const char * s)? – psihodelia 8 November 2011 в 15:26
  • 2
    @psihodelia: В объявлении функции нет разницы. В обоих случаях s является указателем на const char. – CB Bailey 8 November 2011 в 16:20

C99 N1256 draft

Существует два совершенно разных применения литералов массива:

  1. Инициализировать char[]:
    char c[] = "abc";      
    
    Это «более волшебное» и описано при 6.7.8 / 14 «Инициализация»: массив типа символа может быть инициализирован литералом строки символов, необязательно заключенным в фигурные скобки. Последовательные символы символьного строкового литерала (включая завершающий нулевой символ, если есть место или массив неизвестного размера) инициализируют элементы массива. Таким образом, это просто сокращение для:
    char c[] = {'a', 'b', 'c', '\0'};
    
    Как и любой другой регулярный массив, c может быть изменен.
  2. Всюду: он генерирует: неназванный массив char . Каков тип строковые литералы в C и C ++? со статическим хранилищем, которое дает UB, если оно изменено. Итак, когда вы пишете:
    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";

определяет 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 
26
ответ дан Ciro Santilli 新疆改造中心 六四事件 法轮功 17 August 2018 в 09:04
поделиться
char *str = "Hello";

Вышеупомянутое устанавливает str для указания на буквальное значение «Hello», которое жестко закодировано в двоичном образе программы, которое помечено как доступное только для чтения в памяти, означает, что любое изменение в этом строковом литерале является незаконным и что вызовет ошибки сегментации.

char str[] = "Hello";

копирует строку в новую выделенную память в стеке. Таким образом, любое изменение в нем разрешено и законно.

means str[0] = 'M';

изменит str на «Mello».

Для получения более подробной информации, пожалуйста, перейдите к аналогичному вопросу:

Почему возникает ошибка сегментации при записи в строку, инициализированную символом «char * s»? но не «char s []»?

2
ответ дан Community 17 August 2018 в 09:04
поделиться

Учитывая объявления

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, чтобы указать на другой массив.

52
ответ дан John Bode 17 August 2018 в 09:04
поделиться
  • 1
    Я думаю, что гипотетическая карта памяти позволяет легко понять! – midnightBlue 9 November 2016 в 22:41

Просто добавьте: вы также получите разные значения для своих размеров.

printf("sizeof s[] = %zu\n", sizeof(s));  //6
printf("sizeof *s  = %zu\n", sizeof(s));  //4 or 8

Как уже упоминалось выше, для массива '\0' будет выделен конечный элемент.

3
ответ дан Jonathan Leffler 17 August 2018 в 09:04
поделиться

В случае:

char *x = "fred";

x является lvalue - ему может быть присвоено. Но в случае:

char x[] = "fred";

x не является lvalue, это rvalue - вы не можете назначить ему.

0
ответ дан Lee-Man 17 August 2018 в 09:04
поделиться
  • 1
    Технически x является немодифицируемым значением l. Однако почти во всех контекстах он будет оценивать указатель на свой первый элемент, а значение - значение 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() или аналогичный. Если массив выделен в глобальной области, конечно, не проблема.

134
ответ дан milleniumbug 17 August 2018 в 09:04
поделиться

В качестве дополнения учтите, что для целей только для чтения использование обоих одинаково, вы можете получить доступ к символу путем индексации либо с помощью формата [], либо *(<var> + <index>):

printf("%c", x[1]);     //Prints r

И:

printf("%c", *(x + 1)); //Prints r

Очевидно, что если вы попытаетесь выполнить

*(x + 1) = 'a';

, вы, вероятно, получите ошибку сегментации, поскольку вы пытаетесь получить доступ к постоянной памяти.

3
ответ дан Nick L. 17 August 2018 в 09:04
поделиться
  • 1
    Это ничем не отличается от 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

0
ответ дан user 17 August 2018 в 09:04
поделиться
  • 1
    Это все правильно, но это не имеет никакого отношения к вопросу. И что касается вашего предположения о компиляторе C ++, вопрос помечен как C, а не как C ++. – Fabio Turati 5 November 2015 в 18:29
  • 2
    Нет ничего плохого в char * s = & quot; const string & quot ;; – Paul Smith 7 September 2017 в 11:31
Другие вопросы по тегам:

Похожие вопросы: