【问题标题】:Conversion type in printf (with no casting operator)printf 中的转换类型(没有强制转换运算符)
【发布时间】:2019-12-30 15:43:30
【问题描述】:

我正在尝试运行以下代码来演示转换的工作原理。

#include <stdio.h>
int main()
{
    int x=5;
    int y=2;
    float z;
    printf("x=%d\n", x);
    printf("y=%d\n", y);

    //Doing division as it is
    z= x/y;
    printf("With no preliminar assignment: (z=x/y)\n");
    printf("\"z\" is: %d (integer)  or %lf (double) or %f(float)\n\n\n", z, z, z);

    //Doing assignment before division
    printf("With the assignment: (z=x)\n");
    z=x;
    printf("\"z\" before division is:");
    printf("%d (integer)  or %lf (double) or %f(float)\n", z, z, z);

    //Doing division after assignment
    z/=y;
    printf("\"z\" after division with assignment: (z=z/y)");
    printf(" is: %d (integer)  or %lf (double) or %f(float)\n", z, z, z);

    return 0;
}

输出是:

x=5
y=2
With no preliminar assignment: (z=x/y)
"z" is: 0 (integer)  or 0.000000 (double) or 0.000000(float)


With the assignment: (z=x)
"z" before division is:0 (integer)  or 0.000000 (double) or 0.000000(float)
"z" after division with assignment: (z=z/y) is: 0 (integer)  or 0.000000 (double) or 0.000000(float)

这让我产生了疑问,也是因为我在大学的教授写了以下代码:

int x=5;
int y=2;
float z;

int main (void) {
    z = x / y;
    return 0;
}

对他来说,z 将假定为 2.0(这在逻辑上是正确的,但在 printf 中不正确)。

所以我的最后一个问题是,为什么所有 printf 的结果都是 0? (我不想使用类型转换运算符)

【问题讨论】:

  • 参数类型必须与格式字符串匹配。如果他们没有,你必须投。否则它将是未定义的行为。
  • 第 12 行怎么会给出 0.000000 作为双重结果?请提供正确的输出
  • 我不修改 OUTPUT,自己尝试一下,你会得到我的结果 @kalpajagrawalla。所以我的问题仍然存在。
  • 不,我们没有得到您的结果。对于“没有初步分配”,我得到“z”是:73896(整数)或 2.000000(双精度)或 2.000000(浮点数)”。有一条规则是参数的 type 必须匹配 conversion specifier 才能定义行为。当您为转换规范 %d 传递 float 参数 (z) 时,您就违反了此规则。最后,这弄乱了将参数传递给printf 的工作方式。这意味着printf 得到了错误的数据。它在不同的实现上表现不同。这意味着打印的内容并未向您显示z 中的内容。
  • 正确:“最后”应该是“至少”。

标签: c casting type-conversion printf


【解决方案1】:

printf(),和许多可变参数函数一样,使用格式字符串告诉函数调用堆栈上期望哪些类型的参数,但这并不意味着基于这些参数执行任何转换格式字符串

无论如何,任何体面的编译器都会警告您格式/参数类型不匹配,这意味着您故意忽略它们..

您可能混淆了参数提升和格式说明符。

在可变参数函数中,未显式定义的参数(由 ... 标识的参数)按照 C 标准 ISO/IEC 9899:201x, §6.5.2.2 函数调用中所述的标准方式进行处理。在第 6 小段中,我们读到:

如果表示被调用函数的表达式有一个类型 不包括原型,整数提升在 每个参数和类型为 float 的参数都被提升为 double这些被称为默认参数提升

如果 参数的数量不等于参数的数量, 行为未定义。如果函数是用一个类型定义的 包括一个原型,并且原型以省略号结尾 (, ...) 或提升后的参数类型不是 与参数的类型兼容,行为是 不明确的。如果函数定义的类型不 包括原型,以及提升后的参数类型 与升级后的参数不兼容, 行为未定义,但以下情况除外:

  • 升职 type 是有符号整数类型,另一个提升的类型是 对应的无符号整数类型,其值可表示为 两种类型;

  • 这两种类型都是指向字符类型或 void 的合格或不合格 > 版本的指针。

因此,如果您想获得有意义的输出,您必须明确转换参数,如下所示:

printf(" is: %d (integer)  or %lf (double) or %f(float)\n", (int)z, z, z);

请注意,只有到 int 的转换到期,其他 2 个条目留给默认提升,这会将 float 转换为 double,满足函数基于格式字符串的期望。如果没有显式转换,编译器会将变量 zfloat 提升到 double,根据默认参数提升规则,而 printf() 代码将尝试将其作为 int 访问。

代码中值全为零的问题是由于参数大小不匹配int/double 破坏了堆栈展开。

【讨论】:

  • 我没有使用 cmd 行 "gcc -Wall ecc...." 但现在我看到了你所指的并且是真的(我正在学习 C,从现在开始它会变成我在编译阶段的第一个动作,所以谢谢)。正如您所建议的,我在所有 printf func 中的第一个“z”(称为 %d)之前添加了“(int)”以指定整数转换。所以我还有一个问题,printf() 在所有情况下都没有输出 z 的“真实”值(%d,%fl,%f),因为由于 %d(格式)之间的不匹配,它处于未定义的行为状态type) 和 z (float var.)?
  • 如果您的意思是在显式演员之前,我在答案中给出了解释。更深入一点是,从调用堆栈中提取所需参数的代码使用参数类型的维度(sizeof)。如果在您的系统中sizeof(int)==4sizeof(double)==8,该函数读取堆栈上double 的前4 个字节,并将其解释为int。如果他们是0,你明白了。然后以错误的偏移量访问了以下参数。一团糟。
【解决方案2】:

为什么我的 printf 结果都是 0?

您对printf 格式字符串中格式化指令的重要性理解不完整。 最重要的是它们向printf 传达了它们相应参数的数据类型,printf 不知道这些数据类型。它们还指定了如何格式化值的详细信息,但这对于如何解释参数列表的问题来说是次要的。如果指令与实际参数类型不正确对应,则结果行为完全未定义。当然printf 在这种情况下会产生令人惊讶的输出,* 但原则上,任何事情都可能发生。

因此,这样的陈述......

   printf("\"z\" is: %d (integer)  or %lf (double) or %f(float)\n\n\n", z, z, z);

...如果转换指令为相同的值指定不同的、不兼容的类型,则总是不正确。纠正它的一种方法是将参数转换为正确的类型,如下所示:

    printf("\"z\" is: %d (integer)  or %lf (double) or %f(float)\n\n\n", (int) z, (double) z, z);

但这对于您的明显目的并不是特别有用。看来您真正感兴趣的所有转换都发生在每次分配给z 之前或期间,因此考虑随后将该结果转换为其他数据类型的结果不会获得任何收益。那么,这将是一个更好的修正:

    printf("\"z\" is: %f(float)\n\n\n", z);

或者,为了将赋值期间的转换效果与算术计算期间的转换效果分开,您可以对不同类型的变量执行不同的赋值。例如,

    int x = 5;
    int y = 2;
    int zi;
    double zd;
    float zf;

    // ...

    zi = x / y;
    zd = x / y;
    zf = x / y;

    // ...

    printf("\"z\" is: %d (integer)  or %lf (double) or %f(float)\n\n\n", zi, zd, zf);

*这是对提出的实际问题的答案。

【讨论】:

  • floats 被提升为doubles,因此无需将float 转换为double。还是你这样做是为了让事情更清楚?
  • 你说得很对,@alk,包括你对我意图的猜测。没有必要深入了解默认参数提升的细节来回答这个问题,而且我认为这样做不会改善相对于使用强制转换来明确表达与类型匹配要求的一致性的答案。
猜你喜欢
  • 2018-03-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-01-24
  • 1970-01-01
  • 2023-03-20
  • 1970-01-01
  • 2023-03-30
相关资源
最近更新 更多