После прочтения this, я понимаю, что следующая программа должна вызывать UB. Я прав?Неопределенное поведение?
int main(void)
{
char *ptr = "ABCD";
ptr = 'A';
printf("%c\n", ptr);
}
Спасибо.
После прочтения this, я понимаю, что следующая программа должна вызывать UB. Я прав?Неопределенное поведение?
int main(void)
{
char *ptr = "ABCD";
ptr = 'A';
printf("%c\n", ptr);
}
Спасибо.
Вы правы, опубликованный код вызывает неоднозначное поведение несколькими способами.
Возможно, вы не хотите работать в компании, где такой код считается ОК.
Когда я компилировать код с моими опциями по умолчанию clang
, я получаю 4 предупреждения:
clang -O2 -funsigned-char -std=c11 -Weverything -Wno-padded -Wno-shorten-64-to-32 -Wno-mis\
sing-prototypes -Wno-vla -Wno-missing-noreturn -Wno-sign-conversion -Wno-missing-variable-\
declarations -Wno-unused-parameter -Wwrite-strings -lm -o ub2 ub2.c
ub2.c:3:11: warning: initializing 'char *' with an expression of type 'const char [5]' dis\
cards qualifiers [-Wincompatible-pointer-types-discards-qualifiers]
char *ptr = "ABCD";
^ ~~~~~~
ub2.c:4:9: warning: incompatible integer to pointer conversion assigning to 'char *' from \
'int' [-Wint-conversion]
ptr = 'A';
^~~~
ub2.c:5:5: warning: implicitly declaring library function 'printf' with type 'int (const c\
har *, ...)'
printf("%c\n", ptr);
^
ub2.c:5:5: note: include the header <stdio.h> or explicitly provide a declaration for 'pri\
ntf'
ub2.c:5:20: warning: format specifies type 'int' but the argument has type 'char *' [-Wfor\
mat]
printf("%c\n", ptr);
~~ ^~~
%s
4 warnings generated.
3 из этих предупреждений указывают на возможное неопределенное поведение:
назначая целое значение указателю 'A'
вызывает неопределенное поведение, за исключением целочисленной константы 0
, которая преобразуется в нулевой указатель. Некоторые системы будут инициировать исключение для некоторых значений, некоторые просто сохранят целочисленное значение в местоположении указателя, на который нельзя положиться. вы увидите код ptr = (char*)0x0040006C;
, который может правильно компилироваться в конкретной системе, для которой он настроен, но он работает только для конкретных комбинаций компилятора/цели.
Передача указателя для спецификатора преобразования %c
явно описывается как вызов неопределенного поведения в стандарте C.Указатели могут быть переданы в printf
иначе, чем int
значений, например, в другом наборе регистров, поэтому printf
не получит значение указателя для печати в качестве символа. Это значение не может быть 'A'
в любом случае, даже если правильно откинуто как (int)ptr
.
, вызывающий printf
без соответствующего объявления в области, вызывает неопределенное поведение. Неявный прототип, выведенный компилятором из переданных аргументов, может быть несовместимым с конвенцией о вызовах varargs, используемой printf
. Вы должны указать <stdio.h>
или хотя бы предоставить действительный прототип перед вызовом printf
.
В меньшем примечании, есть еще несколько замечаний:
"ABCD"
является строковым. Его нельзя писать. Для совместимости с тоннами устаревшего кода стандарт C (relunctantly) дает ему тип char[5]
, где он действительно должен быть const char[5]
. Это объясняет, почему вы не получаете предупреждение по умолчанию на char *ptr = "ABCD";
, но разумно разрешить компилятору быть более строгим, чем стандарт, и предупредить программиста об этом. const
правильность может потребовать значительных изменений в крупных проектах, но предотвратит потенциальное неопределенное поведение и улучшит способность компиляторов оптимизировать код.
возвращение 0
из main()
подразумевается, так как C99, но считается COOD стиль иметь явное return 0;
, чтобы указать успех.
Мне нравится, когда компилятор говорит «Что такое printf? не включайте прототип, но имейте в виду, что вы используете его плохо, потому что на самом деле я знаю, что такое printf " – Stargateur
c-string - это серия символов. Завершить с помощью char '\0'
.
char *ptr = "ABCD";
Создать массив из 5 символов. {'A', 'B', 'C', 'D', '\0'}
где-то в статической памяти и назначить адрес первого символа (ака. 'A'
) на ptr.
Когда вы
ptr = 'A';
присваивается значение из 'A'
в ptr
, это не является допустимым указателем или с-строка так de-referencing указатель причина неопределенное поведение.
Плюс, C - это типизированный язык, в который вы ничего не можете поместить. Это не гарантирует, что после назначения 'A'
на ptr
. То, что ptr
будет равно 'A'
. ptr
имеет тип char *
, 'A'
имеет тип char
. Вы не можете смешивать эти типы.
char *ptr = 'A';
if (ptr == 'A') // this is undefined behavior
*ptr; // same here
Здесь вы должны написать, если хотите, делать то, что вы пытаетесь.
char c = 'A';
char *ptr = &c; // here ptr is not a valid c-string, it's just a pointer for one char
printf("%c", *ptr);
Но указатель ** не ** разыменовывается. –
Даже если @WeatherVane верен, на некоторых реализациях просто установка указателя на недопустимый адрес может вызвать UB. –
@WeatherVane Это лучше сейчас? Я думаю, что это было наиболее важно объяснить, потому что держать недопустимый указатель действительно очень плохо. – Stargateur
Здесь ptr указывает на местоположение в разделе раздела данных (только для чтения) программы. Вы модифицировали ptr = 'A'; Здесь ptr указывает на значение ascii i.e 65.So ptr указывает на значение в точке 65, следовательно, неопределенное поведение
_Да, вы правы. Но, вероятно, не было необходимости задавать вопрос для этого, поскольку есть много подобных вопросов и ответов с объяснением. – ameyCU
Отказался от интервью, сказав, что это UB. Я чувствовал, что интервьюер никогда не слышал, что такое УБ. В любом случае, удалит этот пост. – babon
@PhilipCouling На моей машине нет ошибок. Просто предупреждение о возможном некорректном преобразовании указателя. Я использую GCC 6.2.1. Не могли бы вы объяснить больше? – babon