当前位置: 首页 > 知识库问答 >
问题:

打印(“%d”,x)如何工作/解释?

阎功
2023-03-14

我想知道< code>printf("%d ",x)到底是怎么解释的。我只知道编译器会保留一个内存来存放< code>'%',< code>'%d',< code>'\0',并将它的地址返回给< code>printf,然后根据我们指定的格式说明符打印第二个参数< code>x。

因此,如果我使用例如int x=5; char*p="%d"; printf(p, x),它是否与printf("%d", x)相同?

共有2个答案

吉栋
2023-03-14

C的一个有点奇怪的方面是,printf函数虽然是一个不寻常的函数,但仍然是一个非常多的函数,所以所有通常的规则都适用。对于任何函数 f(),如果你有一个带有复杂参数的调用,比如

f(complicatedargument1, arg2, arg3);

您可以随时将其更改为

sometype tmp = complicatedargument1;
f(tmp, arg2, arg3);

如果你还没有学过,你一定会在某个时候学的:C语言(和大多数编程语言)中的表达式非常非常通用。所以你不能只拥有

a + b

你可以有

(arbitrarily-complicated expression with many subexpressions) + (arbitrarily-complicated expression with many subexpressions)

您不仅可以拥有

f(a, b, c)

你也可以拥有

f((complicated subexpression), (another complicated subexpression), (yet another complicated subexpression))

(如果你想变得非常疯狂,事实证明你也可以用一个复杂的子表达式替换f。)

因为表达是如此的笼统,所以你第二个问题的答案只能是“是”。

所以原则上写没有错

char *p = "%d";
int x = 5;
printf(p, x);

这确实会像你写的那样

printf("%d", x);

要非常清楚:这是有效的,因为printf的第一个参数是一个字符串。绝大多数情况下,它是一个常量字符串,但没有理由它不可能是“string”类型的其他表达式,例如变量p。(脚注:C实际上没有“字符串”类型,但这是一个单独的主题,我宁愿现在不讨论。

然而,在实践中,有一两个差异,与printf是奇特的事实有关。

一个问题是,由于在调用printf时可能会犯很多容易的错误,因此优秀的编译器会尝试通过检查其中一些错误来帮助您。如果您不小心写了

int x = 5;
printf("%f", x);

这行不通,gcc很有帮助地说:“警告:格式'%f'需要'double'类型的参数,但参数2的类型为'int'”。这是编译器所做的非常特殊的事情,但通常只有当printf的第一个参数是字符串文字时,它才能这样做。如果你要写

int x = 5;
char *p = "%f"
printf(p, x);

编译器可能无法为您执行此检查。

另一个问题是打印F可能是危险的。偶尔,您可能正在做一些奇特的事情,您想在不同情况下使用不同格式的字符串,如下所示:

int x = 123;
char *p;
if(somecondition)
     p = "%x\n";
else p = "%d\n";
printf(p, x);

根据somecondition,这将以十进制或十六进制打印x。这很好,但如果你真的疯了,写下:

int x = 123;
char fmt[20];
printf("What format would you like to use? ");
fgets(fmt, 20, stdin);
printf(fmt, x);

现在用户实际上可以输入< code>printf将要使用的格式字符串了!这个也可以,你可能想现在输入它,然后玩玩它。

…如果你感觉很冒险,你确实输入了它,在使用%d%x

嗯,%nprintf的一个非常特殊的格式说明符,它可以让您找出到目前为止打印了多少个字符。但是%n的相应参数必须是指向int的指针。所以在这种情况下,它会错误地将x的值解释为指针,这意味着它试图写入位置123之类的东西,这是非法的,并导致崩溃。

所以,这里的底线是使用常量字符串文字以外的东西作为printf的第一个参数是有风险的,因为编译器将无法检查错误。如果格式字符串最终来自不受控制的东西,比如用户输入,这可能是非常危险的。

所以一般来说,最好不要充分利用这种自由。也就是说,最好将大部分或所有printf格式字符串保留为常量字符串文字。如果你在那里使用任意表达式,要小心,因为编译器无法检查它。除非你绝对确定你可以信任你的用户,否则不要让他们指定你的程序将使用的printf格式,因为这会让他们崩溃,甚至可能破坏你的程序。

因此,一些样式指南要求< code>printf的第一个参数是一个字符串。也见这个问题。

艾和通
2023-03-14

printf(“%d”,x)是如何解释的?

OP的解释是对代码的一种潜在的部分描述。

然而,C和C通常是编译的,而不是解释的。今天的编译器可以检查 printf(“%d”,x) 并发出像 print_an_integer(x); 这样的代码。

在OP的示例中,2个代码在功能上是相同的,但编译器可能无法识别优化潜力,如上面所述的第一个代码。

int x=5;
// In C++, should be `const char *p="%d";`
char *p="%d";
printf(p,x);
// same as ????
printf("%d",x);

相反,处理传递给printf()的格式字符串,寻找要打印和打印说明符的字符。打印说明符反过来获取下一个参数并相应地处理它们。

如果格式字符串和参数不匹配,如< code>printf("%f ",x)或< code > p = " % dprintf(p,5.0),结果是未定义的行为。

...具体如何...

编译器在生成发出的代码方面可以有很大的自由度。它需要满足虚拟机的等效功能需求,而不是OP的解释。任何准确的解释都取决于编译器和代码。

 类似资料:
  • 我需要在服务器端区分本地和远程CUPS打印作业。我认为可以使用打印作业的原始主机,但我无法找到可靠获取其值的方法,最接近的是IPP作业属性之一“作业原始主机名”,但当我用“ipptool”检查从一个主机打印到另一个主机的暂停作业时,它是空的。“lpq”报告与“localhost”相同的作业,因此也没有多大帮助。 这是用于虚拟OS X打印机的自定义CUPS后端部分,但如果您知道如何获取此信息,这可能

  • 本文向大家介绍Java如何调用TSC打印机进行打印详解,包括了Java如何调用TSC打印机进行打印详解的使用技巧和注意事项,需要的朋友参考一下 前言 最近项目中用到了打印机,最开始的完全不懂,现在弄好了,所以做了总结,该篇包括后台的调用打印(两种方式)跟前端的js的打印,但是只有IE现在支持打印,而且如果想远程连接打印机,二维码的生成和直接由打印机的命令进行操作,就要把修改浏览器的安全配置,下面再

  • 我编写的代码获取数组的元素,并遍历数组以给出所有排列。但我需要它只显示一定数量的排列: 最后的代码是只给出9个元素的6个排列(换句话说,打印总362880个输出中的前60480个排列)。为简单起见,我正在使用数组中的4个元素,并且我得到了所有24个排列以打印出来。但是我需要代码可以用于任意数量的排列。例如,如果我需要它打印出1-perMutation,代码应该打印前4个排列——ABCD、ABDC、

  • 我试图在远程服务器上使用pgAdmin中的psql控制台和sql转储文件恢复架构。我收到以下错误: 由于缺乏超级用户权限,我似乎无法打印目录列表。 是否有方法在psql控制台中标识或打印当前工作目录?默认目录是什么?

  • 问题内容: 搜索过,但没有找到满意的答案。 我知道没有可移植的方式来打印pthread_t。 您如何在您的应用程序中做到这一点? 更新: 实际上,我不需要pthread_t,但是需要一些小的数字ID,以便在调试消息中标识不同的线程。 在我的系统(64位RHEL 5.3)上,它被定义为unsigned long int,因此它的数量很大,仅打印它就在调试行中占据了宝贵的位置。 gdb 如何 分配 短

  • 我正在尝试在删除第一个实例“c”“C”“d”或“D”的情况下打印aLine。但是,我仅限于使用String的indexOf、charAt、长度、compareTo、toUpperCase、toLowerCase、trim、equals、equalsIgnoreCase和子字符串方法。假设您可以输入任何您喜欢的字符串。 我已经实现了删除“c”。但是,如果“c”之前有“d”,它仍然会删除“c”。我已经