Выполнение функций 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-й строке (очевидно, я не могу писать на постоянном массиве символов) . Тогда почему printf () строки № 6 не выполняется ?

4 ответов


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

в этом случае он segfaulting, прежде чем буфер будет сброшен и напечатан.

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

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)

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

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

const char *name = "Vikram";

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

добавлять

-Wa,-ahlms=myfile.lst

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

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

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

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

char name[] = "Vikram";

производит

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

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


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

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

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

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


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

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