对 char * 的 NULL 赋值的说明

发布于 2025-01-20 14:03:32 字数 1512 浏览 3 评论 0 原文

我遇到了一个不可移植的 C 代码示例,其中 char 指针是可变参数 C 函数的参数。该示例如下图所示。蓝色突出显示的部分不一定清晰,并且看起来是错误的。特别是,我有两个问题:

  1. 假设系统上的 NULL 是 32 位 int 0,当编译器遇到 char 时,编译器不会隐式地将 32 位 - int 转换为 64 位 0 *字符串= NULL。如果不是,那么我们是否说像 char *string = NULL 这样的每个表达式都是不可移植的,并且必须始终用像 char *string = (char *)NULL< 这样的显式强制转换来替换/code> 用于可移植 C?

  2. 如果 NULL 是 32 位 int 0,并且 char *string 是 64 位,那么为什么 printf 会像蓝色突出显示中建议的那样用完要打印的位。 printf 不应该获得完整的 64 位,因为它传递的是字符串而不是 NULL。

不合规代码示例(NULL)

C 标准允许 NULL 为整型常量或指针常量。虽然将 NULL 作为参数传递给具有固定数量参数的函数将导致 NULL 被转换为适当的指针类型,但当它作为可变参数传递时,如果 sizeof(NULL) != ,则不会发生这种情况sizeof(void *)。这是可能的,有几个原因:

  • 在 NULL 为整数常量的平台上,指针和整数可能具有不同的大小
  • 平台上可能有不同大小的不同指针类型。在这种情况下,如果 NULL 是 void 指针,则它与 char 指针的大小相同(C11 第 6.2.5 节,第 28 段),其大小可能与所需的指针类型不同。

在任一此类平台上,以下代码将具有[未定义的行为][1]:

char* 字符串 = NULL;
printf("%s %d\n", 字符串, 1);

在具有 32 位 int 和 64 位指针的系统上,printf() 可能会将 NULL 解释为指针的高位,将第三个参数 1 解释为低位的指针。在这种情况下,printf() 将打印一个值为 0x00000001 的指针,然后尝试读取 %d 转换说明符的附加参数,未提供。

合规解决方案(NULL)

此兼容解决方案避免将 NULL 发送到 printf()

char* 字符串 = NULL;
printf("%s %d\n", (字符串 ? 字符串 : "null"), 1);

(来源)

I came across an example of non-portable C code where a char pointer is an argument to a variadic C function. The example is described in the image below. The part highlighted in blue is not necessarily clear, and appears wrong. In particular, I have two questions:

  1. Assuming that NULL was 32-bit int 0 on a system, wouldn't compiler do an implicit cast of 32-bit - int to 64-bit 0 when it encounters char *string = NULL. If not, then are we saying that each expression like char *string = NULL is non-portable and must be always replaced with an explicit cast like char *string = (char *)NULL for portable C?

  2. If NULL was 32-bit int 0, and char *string was 64-bit then why would printf run out of bits to print like it is suggested in the blue highlight. Shouldn't printf get full 64 bits as it was passed string and not NULL.

Noncompliant Code Example (NULL)

The C Standard allows NULL to be either an integer constant or a pointer constant. While passing NULL as an argument to a function with a fixed number of arguments will cause NULL to be cast to the appropriate pointer type, when it is passed as a variadic argument, this will not happen if sizeof(NULL) != sizeof(void *). This is possible for several reasons:

  • Pointers and ints may have different sizes on a platform where NULL is an integer constant
  • The platform may have different pointer types with different sizes on a platform. In that case, if NULL is a void pointer, it is the same size as a pointer to char (C11 section 6.2.5, paragraph 28), which might be sized differently than the required pointer type.

On either such platform, the following code will have [undefined behavior][1]:

char* string = NULL;
printf("%s %d\n", string, 1);

On a system with 32-bit int and 64-bit pointers, printf() may interpret the NULL as high-order bits of the pointer and the third argument 1 as the low-order bits of the pointer. In this case, printf() will print a pointer with the value 0x00000001 and then attempt to read an additional argument for the %d conversion specifier, which was not provided.

Compliant Solution (NULL)

This compliant solution avoids sending NULL to printf():

char* string = NULL;
printf("%s %d\n", (string ? string : "null"), 1);

(Source)

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

抠脚大汉 2025-01-27 14:03:32

引用的文章是错误的,应忽略。

  1. 假设 NULL 在系统上是 32 位 int 0,当编译器遇到 char *string = NULL 时,编译器不会隐式将 32 位 - int 转换为 64 位 0 .

赋值会自动将右操作数转换为左操作数的类型。因此 char *string = NULL 会将 NULL 值转换为 char *,而不是“64 位 0”。

如果不是,那么我们是否说像 char *string = NULL 这样的每个表达式都是不可移植的,并且必须始终用像 char *string = (char * )NULL 用于可移植 C?

不,char *string = NULL 是可移植的 C 代码;它严格符合

  • 如果 NULL 是 32 位 int 0,并且 char *string 是 64 位,那么为什么 printf 会像蓝色突出显示中建议的那样用完要打印的位。 printf 不应该获得完整的 64 位,因为它传递的是字符串而不是 NULL。
  • 引用的代码 char* string = NULL; 后跟 printf("%s %d\n", string, 1); 不传递 NULL printf。它将 string 传递给 printf,之前的赋值将 NULL 转换为 char *。因此,向 printf 传递了一个具有空指针值的 char *。这不会在解释 printf 的变量参数时造成任何问题。 (但是,为 %s 转换传递空指针是不正确的。)

    如果调用是 printf("%s", NULL);,则有问题。与变量参数函数的 ... 部分相对应的参数不会自动转换为参数类型。它们由默认参数提升处理,这主要将窄整数类型提升为int并将float提升为double ,但它们不会将 int 转换为任何类型的指针。因此,如果 NULL 定义为 0,则 printf("%s", NULL); 传递一个 int > 其中需要 char *,这可能会导致对参数的各种误解。

    因此,切勿使用 NULL 宏作为具有可变参数列表的函数的直接参数。使用从 NULL 分配的指针变量是可以的。

    The referenced article is wrong and should be disregarded.

    1. Assuming that NULL was 32-bit int 0 on a system, wouldn't compiler do an implicit cast of 32-bit - int to 64-bit 0 when it encounters char *string = NULL.

    An assignment automatically converts the right operand to the type of the left operand. So char *string = NULL will convert the NULL value to char *, not to “64-bit 0”.

    If not, then are we saying that each expression like char *string = NULL is non-portable and must be always replaced with an explicit cast like char *string = (char *)NULL for portable C?

    No, char *string = NULL is portable C code; it is strictly conforming.

    1. If NULL was 32-bit int 0, and char *string was 64-bit then why would printf run out of bits to print like it is suggested in the blue highlight. Shouldn't printf get full 64 bits as it was passed string and not NULL.

    The code referenced, char* string = NULL; followed by printf("%s %d\n", string, 1);, does not pass NULL to printf. It passes string to printf, and the prior assignment converts NULL to char *. So printf is passed a char * that has the value of a null pointer. This will not cause any problem in interpreting the variable arguments to printf. (It is, however, improper to pass a null pointer for the %s conversion.)

    If the call were instead printf("%s", NULL);, then there is a problem. Arguments corresponding to the ... part of a variable-argument function are not automatically converted to a parameter type. They are processed by the default argument promotions, which largely promote narrow integer types to int and promote float to double, but they will not convert an int to any type of pointer. Thus, if NULL is defined as 0, then printf("%s", NULL); passes an int where a char * is expected, and this may cause various misinterpretations of the arguments.

    In consequence, never use the NULL macro as a direct argument to a function with a variable argument list. Using a pointer variable that has been assigned from NULL is okay.

    ~没有更多了~
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文