Почему ниже printf вызывает ошибку сегментации?
#include <stdio.h>
int main()
{
int *intp = {1,2,3,4,5};
printf("%d", *intp);
return 0;
}
Проверить это на onlinegdb.com
Я изучал материал в Интернете и пробовал код в онлайн-компиляторах и получил это. Но даже если это опасно, почему это так?
Попробуйте int intp[].





В вашем случае вы пытаетесь инициализировать указатель с помощью заключенного в скобки списка инициализаторов int, что является недопустимым.
int *intp = {1,2,3,4,5};
Если вы попытаетесь скомпилировать свой код с включенными правильными предупреждениями, вы увидите предупреждающие сообщения компилятора, например
source_file.c: In function ‘main’: source_file.c:9:18: warning: initialization makes pointer from integer without a cast [-Wint-conversion] int *intp = {1,2,3,4,5}; ^ source_file.c:9:18: note: (near initialization for ‘intp’) source_file.c:9:20: warning: excess elements in scalar initializer int *intp = {1,2,3,4,5}; ^ source_file.c:9:20: note: (near initialization for ‘intp’) source_file.c:9:22: warning: excess elements in scalar initializer int *intp = {1,2,3,4,5}; ^ source_file.c:9:22: note: (near initialization for ‘intp’) source_file.c:9:24: warning: excess elements in scalar initializer int *intp = {1,2,3,4,5}; ^ source_file.c:9:24: note: (near initialization for ‘intp’) source_file.c:9:26: warning: excess elements in scalar initializer int *intp = {1,2,3,4,5};
Этот оператор в вашем коде является нарушением ограничения и не означает ничего значимого. Для скаляра инициализатор должен быть единственным выражением: как указано в C11, глава §6.7.9
The initializer for a scalar shall be a single expression, optionally enclosed in braces. [...]
Таким образом, список, заключенный в фигурные скобки, не является подходящим инициализатором для скаляра.
Вы можете изменить указатель на массив и инициализировать его с помощью оператора инициализатора, но не указателем.
Позже, когда вы пытаетесь разыменовать, вы, по сути, пытаетесь разыменовать память недействителен, которая вызывает неопределенное поведение.
Измените свой код на что-то вроде
int intp[] = {1,2,3,4,5};
сделает свою работу.
Это действительно недействительный int *intp = {1,2,3,4,5}? Поскольку удаление printfне выдает ошибок.
Ага, пытаюсь их понять. Но тогда каков конечный результат int *intp = {1,2,3,4,5}? Ничего такого? Компилятор просто игнорирует строку из-за предупреждений или меняет значение адреса intp на что-то, и если да, то на что?
[.. продолжение из последнего комментария] Почему компилятор выдает предупреждения, а не ошибку времени компиляции? Особенно когда это: int *intp; ntp = {1,2,3,4,5}; выдает ошибку на 2-й строке. С какой целью разрешить int *intp = {1,2,3,4,5} с предупреждениями?
Потому что вы может создаете указатель из целого числа без приведения, а у вас может есть скалярный инициализатор с лишними элементами. Но выполнение как правило любого из них указывает на ошибку, и существуют предупреждения, чтобы проверить, что программист не сделал это намеренно для какой-то цели, которую компилятор не может определить.
@paddy в скалярном инициализаторе не может быть лишних элементов, в стандарте C
@paddy Я считаю, что мой первый вопрос остался без ответа: (1) «Но каков же конечный результат int *intp = {1,2,3,4,5}? Ничего? Компилятор просто игнорирует строку из-за предупреждений или меняет значение адреса intp на что-то, и если да, то на что? "
@paddy Также я не понял, что вы имеете в виду, говоря «вы можете создать указатель из целого числа без приведения». Любой пример? Также что вы имеете в виду под «у вас может быть скалярный инициализатор с лишними элементами». Это неопределенное поведение: char a[3] = {'h','e','l','l','o'};.
@ M.M можете ли вы ответить на Вопрос 1 и 2 выше: (2) Почему компилятор выдает предупреждения, а не ошибку времени компиляции? Особенно когда это: int *intp; ntp = {1,2,3,4,5}; выдает ошибку на 2-й строке. Какова цель разрешения int *intp = {1,2,3,4,5} с предупреждениями? Стоит ли мне задать для них новый вопрос?
@anir массив - это нет скалярного типа.
Арифметические типы и типы указателей вместе называются скалярными типами. Типы массивов и структур вместе называются агрегатными типами.
оххх хорошо, но я все еще не понимаю, были ли ответы на мои вопросы: Q1 и 2 квартал
@anir (1) включает самый высокий уровень предупреждений и обрабатывает все предупреждения как ошибки - ваш код не будет компилироваться. (Да, это параметры компилятора, и они существуют по какой-то причине - для использования) (2) То же самое.
Re «Инициализатором для скаляра должно быть одно выражение»: это забавно, потому что инициализатор - это почти единственное выражение в C, которое не может быть выражение, как определено в грамматике. Потому что, конечно же, 1, 2, 3, 4, 5 - это выражение; он имеет значение 5 с использованием оператора запятой. Согласно грамматике, инициализатор на самом деле является присваивание-выражение, который обходит оператор запятой.
@anir: после отображения предупреждений большинство компиляторов, если они продолжат компиляцию, игнорируют лишние значения и преобразуют первое значение из целого числа в указатель. Делают ли они это на самом деле или нет, стандарт C.
@EricPostpischil Пробовал это: int *intp = {1,2,3,4,5}; printf("%u",intp);. Он печатает 1, подтверждающий то, что вы сказали: «игнорировать лишние значения и преобразовать первое значение из целого числа в указатель». (что несколько удивительно неочевидно) и расположение адреса 1 находится вне доступного сегмента, поэтому это вызывает ошибку сегментации. Итак, я чувствую, что в первой строке int *intp = {1,2,3,4,5}; нет ничего плохого (хотя то, что он делает [назначение 1 на intp], определенно неочевидно), просто это расположение адреса 1 просто недоступно, вызывая ошибку сегментирования. Правильно? @Sourav
[... продолжение из последнего комментария] Предупреждения, которые мы получаем, не имеют ничего общего с ошибкой сегментации. С int *intp = 5; printf("%d",*intp); я все еще получаю как ошибку сегментации, так и «предупреждение: инициализация делает указатель из целого числа без приведения». Хотя предупреждение не имеет ничего общего с ошибкой сегментации. Правильно?
@anir, вам следует узнать об этом у разработчиков gcc. Для меня это не имеет особого смысла.
Итак, вы говорите нам, что ваш компилятор не выдавал предупреждения?