未定义的行为?
Undefined behaviour?
看完this,我的理解是下面的程序应该调用UB。我说的对吗?
int main(void)
{
char *ptr = "ABCD";
ptr = 'A';
printf("%c\n", ptr);
}
谢谢。
C 字符串是一系列字符。由字符 '[=14=]'
.
终止
char *ptr = "ABCD";
创建一个包含 5 个字符的数组。 {'A', 'B', 'C', 'D', '[=15=]'}
静态内存中的某处并将第一个字符(又名 'A'
)的地址分配给 ptr.
当你做的时候
ptr = 'A';
您将 'A'
的 值 分配给 ptr
,它不是有效的指针或 c 字符串,因此 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);
这里ptr指向程序数据段(Read-only)段中的一个位置。
你修改了 ptr='A';这里 ptr 指向 ascii 值,即 65 。所以 ptr 指向 65 位置的值,因此是未定义的行为
你是对的,发布的代码确实以多种方式调用未定义的行为。
你可能不想在这样的代码被认为是好的公司工作。
当我使用默认 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 标准中被明确描述为调用未定义的行为。指针可能以不同于 int
值的方式传递给 printf
,例如在一组不同的寄存器中,因此 printf
不会收到 value 作为字符打印的指针。这个 value 无论如何都可能不是 'A'
,即使正确转换为 (int)ptr
.
在范围内没有正确声明的情况下调用 printf
会调用未定义的行为。编译器从传递的参数中推断出的隐式原型可能与 printf
使用的可变参数调用约定不兼容。在调用 printf
.
之前,您必须包含 <stdio.h>
或至少提供有效原型
在次要注意的地方,还有一些备注:
"ABCD"
是字符串文字。不得写入。为了与大量遗留代码兼容,C 标准(不情愿地)为它提供了 char[5]
类型,而实际上它应该是 const char[5]
。这解释了为什么在 char *ptr = "ABCD";
上默认情况下不会收到警告,但允许编译器比标准更严格并警告程序员这一点是明智的。 const
正确性可能需要在大型项目中进行大量更改,但会防止潜在的未定义行为并提高编译器优化代码的能力。
从 main()
返回 0
自 C99 以来是隐式的,但具有明确的 return 0;
来指示成功被认为是很好的风格。
看完this,我的理解是下面的程序应该调用UB。我说的对吗?
int main(void)
{
char *ptr = "ABCD";
ptr = 'A';
printf("%c\n", ptr);
}
谢谢。
C 字符串是一系列字符。由字符 '[=14=]'
.
char *ptr = "ABCD";
创建一个包含 5 个字符的数组。 {'A', 'B', 'C', 'D', '[=15=]'}
静态内存中的某处并将第一个字符(又名 'A'
)的地址分配给 ptr.
当你做的时候
ptr = 'A';
您将 'A'
的 值 分配给 ptr
,它不是有效的指针或 c 字符串,因此 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);
这里ptr指向程序数据段(Read-only)段中的一个位置。 你修改了 ptr='A';这里 ptr 指向 ascii 值,即 65 。所以 ptr 指向 65 位置的值,因此是未定义的行为
你是对的,发布的代码确实以多种方式调用未定义的行为。
你可能不想在这样的代码被认为是好的公司工作。
当我使用默认 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 标准中被明确描述为调用未定义的行为。指针可能以不同于int
值的方式传递给printf
,例如在一组不同的寄存器中,因此printf
不会收到 value 作为字符打印的指针。这个 value 无论如何都可能不是'A'
,即使正确转换为(int)ptr
.在范围内没有正确声明的情况下调用
printf
会调用未定义的行为。编译器从传递的参数中推断出的隐式原型可能与printf
使用的可变参数调用约定不兼容。在调用printf
. 之前,您必须包含
<stdio.h>
或至少提供有效原型
在次要注意的地方,还有一些备注:
"ABCD"
是字符串文字。不得写入。为了与大量遗留代码兼容,C 标准(不情愿地)为它提供了char[5]
类型,而实际上它应该是const char[5]
。这解释了为什么在char *ptr = "ABCD";
上默认情况下不会收到警告,但允许编译器比标准更严格并警告程序员这一点是明智的。const
正确性可能需要在大型项目中进行大量更改,但会防止潜在的未定义行为并提高编译器优化代码的能力。从
main()
返回0
自 C99 以来是隐式的,但具有明确的return 0;
来指示成功被认为是很好的风格。