为什么C的printf格式字符串同时包含%c和%s?

Bil*_*ina 70 c string-formatting

为什么C的printf格式字符串有%c%s

我知道它%c代表一个单个字符并%s表示一个以空字符结尾的字符串,但单独的字符串表示不足够吗?

Mah*_*esh 98

可能要区分空终止字符串和字符.如果他们只有%s,那么每个字符也必须为空终止.

char c = 'a';
Run Code Online (Sandbox Code Playgroud)

在上述情况下,c必须以null结尾.这是我的假设:)

  • -1这是一个糟糕的答案......'printf("%s",c)`不起作用的原因不是因为空终止,而是因为`printf`希望`%s`指向一个内存位置,而`c`是字符值.Null-termination*是*printf("%s",&c)`不起作用的原因,但即使它确实如此,有一个'%c`标识符*仍然*有意义,因为需要`&c`是与每个其他变量类型的打印方式不一致.所以只是说*"这是因为空终止"*是不正确的. (39认同)
  • 这不是一个真正的答案,因为`%.1s`可以避免这种情况 (13认同)
  • 我不知道74人是怎么认为这是正确的答案,因为它没有提到你必须传递一个`char*`甚至让一个字符用'%s`打印.重申BlueRaja所说的话 (9认同)
  • @mahesh混淆答案比给出错误答案更糟糕......我无法相信你在争论它. (7认同)
  • @Merlin我没有这么说.可能,我的句子形成可能令人困惑.您可能知道,格式说明符'%s`的格式字符串必须是**以null结尾**.如果语言中没有'%c`,那么在上面的例子中,标识符`c`必须为空终止才能与格式说明符'%s`一起使用. (3认同)

Luc*_*ore 73

%s打印出字符,直到它达到0(或'\0'同样的东西).

如果你有一个char x;,打印它printf("%s", &x);- 你必须提供地址,因为%s预期char* - 会产生意想不到的结果,&x + 1可能不会0.

所以你不能只打印一个字符,除非它是以null结尾的(非常无效).

编辑:正如其他人所指出的,两者期望var args参数中有不同的东西 - 一个是指针,另一个是一个char.但这种差异有点明显.

  • 你应该提到`%s`接受一个指针,而`%c`接受一个字符本身. (8认同)

Jen*_*edt 38

其他人提到单个字符必须为空终止的问题并不是真正的问题.这可以通过提供格式的精确度来解决%.1s.

在我看来更重要的是,对于%s任何形式,你必须提供指向一个或多个字符的指针.这意味着您将无法打印rvalues(计算表达式,函数返回等)或注册变量.

编辑:我对这个答案的反应非常生气,所以我可能会删除它,这真的不值得.似乎人们在没有阅读问题或知道如何理解问题的技术性的情况下对此作出反应.

为了说清楚:我不说,你应该更喜欢%.1s%c.我只是说为什么%c不能替换的原因不同于其他答案假装告诉.这些其他答案在技术上是错误的.空终止不是问题%s.

  • @LuchianGrigore:不,我不是说任何人都应该更喜欢这样,相反.我说`%.1s`将具有打印一个甚至不被空终止的字符的功能,因此空终止不是"引入"`%c`的有效异议. (5认同)
  • @Als会'%.1s`期待一个`char`或`char*`? (4认同)
  • @JoshuaDrake,你读过我的答案吗?我觉得这真的太过分了.我不是说任何人都应该使用`%.1s`来打印一个角色.这不是问题.问题是`%c`是否是绝对必要的.我对此给出了肯定的答案,只是说明原因与其他答案中给出的理由不同. (3认同)
  • @JoshuaDrake:-1因为没有看到答案.问题是为什么X存在于C中,这个答案清楚地解释了X必须存在才能使用rvalues.如果不考虑rvalues,语言/库设计者可能会(并且可能应该)将它留在%.1s,因为它使printf更清晰(adn因此更具可读性).如果没有c,打印返回单个字符的函数的结果可能会变得非常复杂(特别是如果调用具有副作用的多个函数并且依赖于顺序). (3认同)
  • @LuchianGrigore,没有人这么说.我的回答只是说空终止不是正确的参数,但是获取对象的地址是一个参数. (2认同)

And*_*rsK 28

printf函数是一个可变函数,意味着它具有可变数量的参数.在调用函数(printf)之前,会在堆栈上推送参数.为了使函数printf能够使用堆栈,它需要知道有关堆栈中的内容的信息,格式字符串用于此目的.

例如

printf( "%c", ch );    tells the function the argument 'ch' 
                       is to be interpreted as a character and sizeof(char)
Run Code Online (Sandbox Code Playgroud)

printf( "%s", s );   tells the function the argument 's' is a pointer 
                     to a null terminated string sizeof(char*)
Run Code Online (Sandbox Code Playgroud)

printf函数内部不可能以其他方式确定堆栈内容,例如区分'ch'和's',因为在C中运行时没有类型检查.

  • 但你的`sizeof`规格完全错了.首先,`ch`被提升为`int`(或者在某些平台上被提升为`unsigned`),`printf`将`int`解释为一个字符.所以参数"在堆栈上"的大小是`sizeof(int)`.对于第二个你又错了,它是`sizeof(char*)`而没有别的.`sizeof(int)`可以(现在经常)与之不同.第三,没有必要涉及"堆叠"之类的东西.如何传递这些参数是由平台ABI自行决定的,一切都可能在寄存器中发生. (9认同)
  • @AndersK:"技术上"Jens是完全正确的.事实上,你可以放弃"技术上"这个词; 他说得很对. (2认同)

Jua*_*des 13

%s 说打印所有字符,直到找到null(将变量视为指针).

%c 说只打印一个字符(将变量视为字符代码)

%s对于一个字符的使用不起作用,因为该字符将被视为一个指针,然后它将尝试打印该位置之后的所有字符,直到它找到一个空

从其他答案窃取,以不同的方式解释它.

如果您想使用打印字符%s,可以使用以下内容正确传递一个字符的地址,并防止它在屏幕上写垃圾,直到找到空值.

char c = 'c';
printf('%.1s', &c);
Run Code Online (Sandbox Code Playgroud)

  • @JuanMendes:不,它们都可以在源代码*中表示为*0.他们的内部表征可能完全不同; 空字符通常是8位,而空指针通常是32位或64位.此外,空指针的内部表示是*不一定是全位 - 零(尽管它很常见). (2认同)

Rol*_*dXu 7

因为%s,我们需要提供字符串的地址,而不是它的值.

因为%c,我们提供了字符的值.

如果我们使用了%s代替%c,我们将如何提供一个'\0'字符后?


tru*_*cks 5

我想为这个有趣的问题添加另一个观点.

真的,这归结为数据输入.我在这里看到的答案表明你可以提供一个指向char的指针,并提供一个

"%.1s"
Run Code Online (Sandbox Code Playgroud)

这确实可能是真的.但答案在于C设计师试图为程序员提供灵活性,实际上是一种(虽然很小)减少应用程序占用空间的方法.

有时程序员可能想运行一系列if-else语句或switch-case,其中需要简单地根据状态输出一个字符.为此,对字符进行硬编码确实可以减少内存中的实际空间,因为单个字符是8位而不是指针是32位或64位(对于64位计算机).指针将在内存中占用更多空间.

如果您希望通过使用实际字符与指向字符的指针来减小大小,那么有两种方法可以考虑在printf类型的运算符中执行此操作.一个是关闭.1s,但是例程应该如何确定你真正提供了一个char类型而不是一个指向char的指针或一个指向字符串的指针(chars数组)?这就是为什么他们选择"%c",因为它是不同的.

有趣的问题:-)