【问题标题】:C Programming: Why printf Format Specifier %s Is Invalid For The Number "123"?C 编程:为什么 printf 格式说明符 %s 对于数字“123”无效?
【发布时间】:2020-08-13 11:15:19
【问题描述】:

我是一个 C 编程新手。

通过其他问题的答案,我可以理解“字符”一词可能指向不同的元素;例如字母和数字。

我将“字符串”视为字符。

那么,请看下面几行:

#include <stdio.h>

main()
{
    int numbers = 123;
    printf("%c%s\n", "N", numbers); 
}

当我尝试编译此代码时,显示错误消息并且编译失败。

教科书“The C Programming Language”第 13 页如下所述关于 printf 格式说明符;

%c代表人物”

%s为字符串”。

第一个问题:

虽然“N”是字符,为什么%c说明符无效?

第二个问题:

虽然变量“numbers”的值为“123”,但它们是数字,包含在字符中。

我认为字符等于“字符串”。

那我虽然用%s来表示“123”,但是为什么无效呢?

我知道我的代码会起作用,如果我像下面这样修改它们:

#include <stdio.h>

main()
{
    int numbers = 123;
    printf("%s%d\n", "N", numbers); 
}

但我想了解为什么上面的代码无效。

【问题讨论】:

  • "N" 不是一个字符。它是一个字符串字面量,由一个字符(以及终止的空字符,它是隐式的)组成。
  • 欢迎来到 SO。请不要添加大量不相关的标签。这被认为是垃圾邮件。如果您对 C 标准函数有疑问,则无需添加 bashunix 标签。
  • main() 在 C 中已经有一段时间无效了。这是你教科书中的时代错误之一。你应该总是写int main(void)
  • @CaptainCookie 你应该使用int main(void)。省略void 可能适用于大多数实现,但不能保证有效。省略 int 会导致隐含的 int 返回值,但它的形式不好。
  • @CaptainCookie 用(void) 参数列表声明一个函数只是意味着该函数不接受任何参数。由于遗留原因,使用空的() 参数列表声明函数意味着未指定参数。永远不要在函数声明中使用空的 () 参数列表。

标签: c printf


【解决方案1】:

“虽然"N"是一个字符,但为什么%c说明符无效?”

"N" 不是字符常量。它是一个字符串文字。 'N' 是一个字符常量,与 %c 匹配。单引号和双引号的区别很重要。

此外,说明符永远不会无效。只有参数类型可以。

“那我虽然用%s代替"123",但是为什么无效呢?”

您没有使用"123",它是一个字符串文字并发出char *。你使用123123 是一个 integer 值,存储在 int numbers 类型的对象中。 %s 格式说明符不允许使用 int 参数,它需要 char * 类型的参数,而不是 int

这就是区别。

【讨论】:

  • 我明白了为什么我的代码不起作用,并且由于您的清晰描述,单引号和双引号之间的区别很重要。谢谢!
【解决方案2】:

说明

printf("%c%s\n", "N", numbers); 

包含两个错误。

  1. 格式说明符%c 期望传递单个字符。我们在 C 中定义字符常量的方式是使用单引号 'N'
    通过使用双引号 "N",您正在使用 C 提供的一种 语法糖方式来表示字符串,即由字符串终止符'\0'

    关闭的字符数组
  2. 123 是一个整数,就像您正确编写的 %d 格式说明符一样

说到最后一个错误,你给出了原创解释

虽然变量“numbers”的值为“123”,但它们是数字,包含在字符中。

我认为字符等于“字符串”。

那我虽然用%s来表示“123”,但是为什么无效呢?

虽然这显然是一个错误的解释,但我觉得它有趣并且值得解释。

123 是一个字符串,即一个字符序列吗?是的!但它是源代码中的一个字符串。编译器将解析这个字符序列,识别一个整数,并确保这个值存储在一个包含整数的内存位置中。在大多数现代计算机中,它将转换为 0/1 序列,表示 4 字节中的数字 123。

0b01111011

源代码的角度来看,"123" 不一样吗?嗯,不。该数字用双引号括起来,因此编译器会将其解释为一个实际的字符串,作为一个以 nul 结尾的字符序列存储在内存中

--------------------------
| '1' | '2' | '3' | '\0' |
--------------------------

并在内存中存储代表这些数字的 ASCII 值(假设使用 ASCII 编码)

-----------------------------
| 0x31 | 0x32 | 0x33 | 0x00 |
-----------------------------

【讨论】:

  • 我知道 123 对我来说是字符,但对计算机来说是整数。除了解决方案,我想尽可能从技术方面了解原因,所以你的回答很有帮助。谢谢!
  • 我需要从通常的类似语言的角度来填补一台计算机和我的一台计算机之间的解释差距。
  • @CaptainCookie 让我们开始说char 类型也是一个整数:一个仅使用一个字节的整数,因此它可以表示 256 个值。汤姆说你是 char 文字 'N' 被提升为类型 int (在大多数平台上为 4 个字节)。暂时不要专注于它。只需关注这样一个事实,即使用 %c 告诉程序将其值解释为 ascii 表中的可打印字符;使用 %d 程序只会打印它的整数值。
  • @captaincookie 知道字符存储为整数可能很有用,因为您可以对它们进行代数检查。这是一个太宽泛的话题,无法发表评论;所以我只写一个例子。利用字母字符的 ASCII 表示,我们可以通过if (c &gt;= 'a' &amp;&amp; c &lt;= 'z') 等简单检查轻松检查输入 char 是否为小写字母字符。
  • 您好,通过大量社区支持和我的教科书逐步学习 C 编程,所以我可以理解您的 cmets 及其示例。我已经破解了您用 printf("%c %d\n", '0','0'); 解释的内容。如你所说,前者显示为0,后者显示为48。谢谢。
【解决方案3】:

我会直接回答你的问题。

第一个问题:

C 编程语言区分双引号和单引号。双引号将创建 string 文字,而单引号将创建 character 文字。所以,例如'N'character,而"N" 是一个string,只包含一个字符,即N。所以,%c 不起作用的原因是它需要一个字符, 但你给它的是 string "N".

第二个问题:

变量numbers 的值是NOT“123”。使用 C 语言的符号,“123”将是一个包含字符 '1'、'2' 和 '3' 的字符串,而变量 numbers 现在包含 integer 123,并且这就是为什么 %s 不适用于 numbers 的原因。虽然您可以将变量 123 的值转换为这样的字符串:

#include <stdio.h>

main()
{
    int numbers = 123;
    char numbers_string[4];
    sprintf(numbers_string, "%d", numbers); // conversion happens here
    printf("%s\n", numbers_string); 
}

此外,您的问题中“字符串”和“字符”这两种类型之间似乎存在混淆。重要的是要了解这两种类型是不同的。一个字符是一个单个字节,它(通常)包含你想要的字符的 ASCII 码。例如,字符 'A'(ASCII 码 = 65)。但是“字符串”是字符的集合,如"Apple",在C 语言中,“字符串”通常使用char* 来实现,它指向内存中按顺序存储字符的位置。 (这就是我使用字符数组来保存将numbers转换为“字符串”的结果的原因)

【讨论】:

  • 谢谢!您的示例代码有助于我理解事物。另外,我已经了解了如何通过 sprintf 进行转换。我想我可以从现在开始减少选择错误的格式说明符。
【解决方案4】:

虽然“N”是一个字符,为什么%c说明符无效?

"N" 是一个字符串,而不是单个字符 - 双引号表示字符串文字。 'N'(单引号)将是一个字符常量1,所以

printf( "%c\n", 'N' );

可以正常工作。要打印"N",您需要使用%s 转换说明符:

printf( "%s\n", "N" );

%s 转换说明符期望其对应参数的类型为 char *(指向 char 的指针),它会告诉 printf 打印从指定地址开始的字符序列,直到它看到字符串终结者。

表达式"N" 的类型为“char 的二元数组”(char [2]),数组的内容为{'N', 0}。在 C 中,字符串是一系列字符值,包括用于标记字符串结尾的 0 值终止符。字符串(包括字符串文字)存储在字符类型的数组中。

在大多数情况下,“T 的 N 元素数组”(T [N]) 类型的 表达式 将被转换(“衰减”)为“指向 @ 的指针”类型的表达式987654337@" (T *) 并且表达式的值将是数组第一个元素的地址。

在上面的printf语句中,表达式"N"char [2]类型转换为char *,表达式的值是第一个字符的地址。

虽然变量“numbers”的值为“123”,但它们是数字,包含在字符中。

我们来看numbers的声明:

int numbers = 123;

首先,numbers 变量的类型int,而不是char *。它包含什么值并不重要,它不是%s 期望的类型。其次,123 不是字符串——它没有被引号包围。相反,它是一个 整数 常量。

numbers 不是字符串 - 它不是存储字符串的正确类型,并且它不包含带有 0 终止符的字符序列。它是一个整数,存储十进制值123 的二进制表示。要将其打印出来(即在输出设备上生成该整数值的文本表示),您可以使用%d%i


  1. 在 C 中,像 'N' 这样的字符常量的类型为 int,而在 C++ 中,它们的类型为 char

【讨论】:

  • 您好!我已经学会了表达; char c_string[] = 但仍然不像char *。所以我理解你的建议不是100%。在了解它们之后,我会再次阅读您的建议。感谢您的支持!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-10-07
  • 2013-06-22
  • 2013-06-22
  • 2010-11-03
  • 2017-08-04
相关资源
最近更新 更多