C 标准是否保证执行 printf("%.*s", 0, NULL) 是安全的?
Is it guaranteed by the C standard to be safe to do printf("%.*s", 0, NULL)?
我有一个重复的打印,它打印了一堆非空终止的 char*
,就像这样:
int len1, len2, len3, len4;
char *str1, *str2, *str3, *str4;
get_vals(message, val1, &str1, &len1);
get_vals(message, val2, &str2, &len2);
get_vals(message, val3, &str3, &len3);
get_vals(message, val4, &str4, &len4);
printf("%.*s %.*s %.*s %.*s", len1, str1, len2, str2, len3, str3, len4, str4);
其中 get_vals
将 char *
指针设置为内存中的值,并将 len
设置为文本的长度。传入的char *
有可能被设置为NULL
,如果是这样,长度字段将被设置为0
。看起来这个打印已经发生了很多次并且没有出现段错误,我假设由于长度说明符是 0
并且因此可能没有解除引用。然而,这总是安全的吗?也许是 OS 或 libc 版本依赖?像这样进行安全检查是否值得:
printf("%.*s %.*s %.*s %.*s",
len1, str1 ? str1 : "",
len2, str2 ? str2 : "",
len3, str3 ? str3 : "",
len4, str4 ? str4 : "");
当您编写 printf("%.*s", len1, str1)
时,其中 len1
为零且 str1
为空指针,您正在使用 s
说明符并设置 precision 到 0。我查看了 N1570 的 section 7.21.6 的相关部分。在记录 s
说明符时,它说:
the argument shall be a pointer to the initial
element of an array of character type. Characters from the array are
written up to (but not including) the terminating null character. If the
precision is specified, no more than that many bytes are written.
因此,从技术上讲,仅查看该引用的第一部分,您确实需要提供指向数组的指针而不是提供空指针。所以你的代码没有遵循标准的那一部分。
但是,您将精度设置为 0,因此引用的第二部分告诉我们 printf
函数实际上不会将该数组中的任何字符写入输出。这对我来说意味着它也不会尝试读取任何字符:读取数组末尾是不安全的,因此 printf
实现不应该这样做。所以你的代码可能会在实践中工作,很难想象它会失败的情况。我在实践中能想到的最大问题是静态分析器或验证器可能会抱怨您的代码。
Is it guaranteed by the C standard to be safe to do printf("%.*s", 0, NULL)
?
不,不完全是。
NULL
是一个 实现定义的空指针常量 。它的类型可能是 void *
、int
、long
、long long
、unsigned
和其他一些整数类型。它可能具有与 char *
或 void*
相同的大小和 ...
参数兼容,但未指定。传递 NULL
以匹配 "%s"
会产生 未定义行为的风险 。
最好问一下:
Is it guaranteed by the C standard to be safe to do printf("%.*s", 0, (char *) NULL)
?
在这种情况下,
的回答很适用:技术 未定义的行为 ,但通常没问题。 [虽然我不遵守“这对我来说意味着它不会尝试读取任何字符”,因为库实现不需要遵循用户代码的规则——它们会作弊。]
为了高便携性,请不要使用 printf("%.*s", 0, NULL)
这两个原因。
选择:
printf("%.*s", 0, "");
我有一个重复的打印,它打印了一堆非空终止的 char*
,就像这样:
int len1, len2, len3, len4;
char *str1, *str2, *str3, *str4;
get_vals(message, val1, &str1, &len1);
get_vals(message, val2, &str2, &len2);
get_vals(message, val3, &str3, &len3);
get_vals(message, val4, &str4, &len4);
printf("%.*s %.*s %.*s %.*s", len1, str1, len2, str2, len3, str3, len4, str4);
其中 get_vals
将 char *
指针设置为内存中的值,并将 len
设置为文本的长度。传入的char *
有可能被设置为NULL
,如果是这样,长度字段将被设置为0
。看起来这个打印已经发生了很多次并且没有出现段错误,我假设由于长度说明符是 0
并且因此可能没有解除引用。然而,这总是安全的吗?也许是 OS 或 libc 版本依赖?像这样进行安全检查是否值得:
printf("%.*s %.*s %.*s %.*s",
len1, str1 ? str1 : "",
len2, str2 ? str2 : "",
len3, str3 ? str3 : "",
len4, str4 ? str4 : "");
当您编写 printf("%.*s", len1, str1)
时,其中 len1
为零且 str1
为空指针,您正在使用 s
说明符并设置 precision 到 0。我查看了 N1570 的 section 7.21.6 的相关部分。在记录 s
说明符时,它说:
the argument shall be a pointer to the initial element of an array of character type. Characters from the array are written up to (but not including) the terminating null character. If the precision is specified, no more than that many bytes are written.
因此,从技术上讲,仅查看该引用的第一部分,您确实需要提供指向数组的指针而不是提供空指针。所以你的代码没有遵循标准的那一部分。
但是,您将精度设置为 0,因此引用的第二部分告诉我们 printf
函数实际上不会将该数组中的任何字符写入输出。这对我来说意味着它也不会尝试读取任何字符:读取数组末尾是不安全的,因此 printf
实现不应该这样做。所以你的代码可能会在实践中工作,很难想象它会失败的情况。我在实践中能想到的最大问题是静态分析器或验证器可能会抱怨您的代码。
Is it guaranteed by the C standard to be safe to do
printf("%.*s", 0, NULL)
?
不,不完全是。
NULL
是一个 实现定义的空指针常量 。它的类型可能是 void *
、int
、long
、long long
、unsigned
和其他一些整数类型。它可能具有与 char *
或 void*
相同的大小和 ...
参数兼容,但未指定。传递 NULL
以匹配 "%s"
会产生 未定义行为的风险 。
最好问一下:
Is it guaranteed by the C standard to be safe to do
printf("%.*s", 0, (char *) NULL)
?
在这种情况下,
为了高便携性,请不要使用 printf("%.*s", 0, NULL)
这两个原因。
选择:
printf("%.*s", 0, "");