当我们将其传递给char *时,%s在C中的行为*指向单个char

时间:2019-01-14 11:25:51

标签: c string pointers

这是代码-

 int main() {
     char c = 'A';
     char *p = &c;

     char *str = "Hello";

     printf("%s %s", p, str);
     return 0;
 }

输出如下

A????? Hello
A?*??? Hello
A?z??? Hello
A???? Hello
A????? Hello
A??y?? Hello
A?*?? Hello
A?Z??? Hello
A?*+?? Hello
A?l?? Hello
A?z?? Hello
A??i?? Hello
A?ʜ?? Hello

现在我知道%s试图寻找一个'\ 0'来停止打印,这就是为什么char指针 str 输出完美的原因。

但是我想知道的是,当我们将char *指向单个字符时,它会在实际的char之后打印一些垃圾值-如何决定每次4或5个垃圾值后停止输出?此安全检查如何工作?据了解,“?” 可能是字符串的一部分。

4 个答案:

答案 0 :(得分:6)

对于格式"%s",相应的参数必须指向有效的C字符串,这意味着它必须满足两个条件:(1)必须为char*类型(或{{1 }}),并且(2)它必须指向一个对象,该对象表示在对象边界内以const char*结尾的字符序列。

如果您这样做

'\0'

然后变量char c; char* ptr = &c; 仅满足第一个条件;它是一个ptr,但是它指向的对象仅包含一个字符,如果该字符不是char *,则char“序列”不会在对象的边界内终止。

因此,'\0'将访问对象外部的内存,从而产生未定义的行为。一种这样的行为可能是,它只是继续读取内存(即使它不是拥有的),直到遇到printf为止。这就是您可能正在观察的。

答案 1 :(得分:2)

printf中,转换说明符s [已加强调]

writes a character string 
     

该参数必须是指向字符数组的初始元素的指针。精度指定要写入的最大字节数。 如果未指定Precision,则写入每个字节,直到不包括第一个空终止符为止。如果使用l说明符,则参数必须是指向wchar_t数组的初始元素的指针,该数组将被转换为char数组,就像通过调用零初始化转换状态的wcrtomb一样。

在此声明中:

 printf("%s %s", p, str);

p指向char类型变量c的指针,并且您正在使用%s格式说明符。请注意,如果未使用转换说明符s指定精度,则它将写入字节,直到找到空终止符为止。 %s格式说明符,当写入p所指向的字节时,将从p所指向的内存开始写入字节,直到找到空终止符为止。这意味着,它可能会访问对象c拥有的内存以外的内存。读取程序不拥有的内存是未定义的行为,其中包括程序执行可能不正确(崩溃或无提示地生成不正确的结果),或者可能偶然地执行了程序员想要的操作。

您可以执行以下操作:

printf("%.1s %s", p, str);
         ^^
      precision of the conversion

使用%.1s,它将仅从p所指向的存储位置读取一个字符。

答案 2 :(得分:1)

这将产生不同的结果。主要是意料之外的结果。尝试在其他计算机(例如,不同的在线C编译器)上运行代码。您将能够看到不同的结果。永远都是意料之外的。

答案 3 :(得分:1)

这就是“不确定行为”的魔力。您无法预测将会发生什么或它将如何表现。 在不同的环境中,更有可能产生不同的结果,所以...