未定义的行为?

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; 来指示成功被认为是很好的风格。