Выполнение printf() и ошибка сегментации

#include<stdio.h>

int main()
{
    char *name = "Vikram";
    printf("%s",name);
    name[1]='s';
    printf("%s",name);
    return 0;
}

На терминале нет выхода, и вы можете получить ошибку сегментации. Но когда я запускаю его в GDB, я получаю следующее -

Program received signal SIGSEGV, Segmentation fault.
0x0000000000400525 in main () at seg2.c:7
7       name[1]='s';
(gdb) 

Это означает, что программа получает SEG-ошибку на 7-й строке (очевидно, я не могу писать в постоянном массиве char). Тогда почему printf() строки 6 не выполняется?

Ответы

Ответ 1

Это связано с буферизацией потока stdout. Если вы не выполняете fflush(stdout) или вы печатаете новую строку "\n", вывод может быть буферизирован.

В этом случае он будет прерван до сброса и печати буфера.

Вместо этого вы можете попробовать:

printf("%s",name);
fflush(stdout);        //  Flush the stream.
name[1]='s';           //  Segfault here (undefined behavior)

или

printf("%s\n",name);   //  Flush the stream with '\n'
name[1]='s';           //  Segfault here (undefined behavior)

Ответ 2

Сначала вы должны закончить ваши printfs "\n" (или, по крайней мере, последним). Но это не связано с segfault.

Когда компилятор компилирует ваш код, он разбивает двоичный файл на несколько разделов. Некоторые из них доступны только для чтения, а другие - для записи. Запись в раздел только для чтения может вызвать segfault. Строковые литералы обычно помещаются в раздел только для чтения (gcc должен помещать его в ".rodata" ). Имя указателя указывает на этот раздел ro. Поэтому вы должны использовать

const char *name = "Vikram";

В моем ответе я использовал несколько "may" "should". Поведение зависит от настроек вашей ОС, компилятора и компиляции (компоновщик script определяет разделы).

Добавление

-Wa,-ahlms=myfile.lst

в командной строке gcc создается файл myfile.lst сгенерированным кодом ассемблера. Вверху вы можете видеть

    .section .rodata
.LC0:
    .string "Vikram"

Что показывает, что строка находится в Викраме.

Тот же код с использованием (должен находиться в глобальной области, иначе gcc может хранить его в стеке, обратите внимание, что это массив, а не указатель)

char name[] = "Vikram";

производит

    .data
    .type name, @object
    .size name, 7
name:
    .string "Vikram"

Синтаксис немного отличается, но посмотрите, как он теперь находится в разделе .data, который является read-write. Кстати, этот пример работает.

Ответ 3

Причина, по которой вы получаете ошибку сегментации, состоит в том, что строковые литералы C читаются только в соответствии со стандартом C, и вы пытаетесь написать 's' над вторым элементом литерала массива "Vikram".

Причина, по которой вы не получаете выход, заключается в том, что ваша программа буферизует свой вывод и сбой, прежде чем у него появится возможность сбросить свой буфер. Цель библиотеки stdio в дополнение к обеспечению дружественных функций форматирования, таких как printf (3), заключается в уменьшении накладных расходов операций ввода-вывода за счет буферизации данных в буферах в памяти и только при необходимости промывки, и только время от времени выполнение ввода а не постоянно. Фактический ввод и вывод в общем случае не будут происходить в тот момент, когда вы вызываете функцию stdio, но только при заполнении выходного буфера (или входной буфер пуст).

Вещи немного отличаются, если объект FILE установлен так, что он постоянно сбрасывается (например, stderr), но в целом этот смысл.

Если вы отлаживаете, лучше использовать fprintf для stderr, чтобы убедиться, что ваши отладочные распечатки будут сброшены до сбоя.

Ответ 4

По умолчанию, когда stdout подключен к терминалу, поток буферизируется по строке. На практике в вашем примере отсутствие '\n' (или явного потока потока) заключается в том, почему вы не печатаете символы.

Но в теории поведение undefined не ограничено (из стандартного поведения [...], для которого этот международный стандарт не налагает никаких требований "), и segfault может произойти еще до того, как произойдет поведение undefined, например перед первым вызовом printf!