【问题标题】:Why does C's printf format string have both %c and %s?为什么C的printf格式字符串既有%c又有%s?
【发布时间】:2012-06-06 10:38:50
【问题描述】:

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

我知道%c 代表单个字符,%s 代表一个以空字符结尾的字符串,但是仅字符串表示还不够吗?

【问题讨论】:

  • 可能是为了区分以空字符结尾的字符串和一个字符。如果他们只有%s,那么每个字符也必须以空字符结尾。 char c = 'a';,在这种情况下,c 必须以 null 结尾。这是我的假设:)
  • @Mahesh:为什么不把它作为答案,尤其是当 这是答案
  • 嗯?如果你只想放一个字符怎么办,为什么需要在它后面附加一个 NUL 呢?还是我在这个问题中没有得到什么?
  • @Mahesh 你的假设就是我相信这个问题的答案
  • %c 需要 char 类型的数据 - 一个值原语。 %s 需要一个 char * 类型的数据,一个指针。尽管它们都适用于文本,但它们之间的差异在 C 中是巨大的。

标签: c string-formatting


【解决方案1】:

%s 表示打印所有字符,直到找到空值(将变量视为指针)。

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

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

从其他答案中窃取以另一种方式解释它。

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

char c = 'c';
printf('%.1s', &c);

【讨论】:

  • 这并不完全正确。对字符使用 %s 可能会做完全出乎意料的事情,因为它将字符解释为指向可能指向无效位置的字符的指针。
  • 为了清楚起见(因为很多人混淆了这些概念),字符串以空字符终止,不要与空指针混淆.
  • @KeithThompson 只是为了迷惑人们:空字符 '\0' 和空指针在内部都表示为 0 :)
  • @JuanMendes:不,它们都可以在源代码中表示为0。它们的内部表示可能完全不同;空字符通常为 8 位,而空指针通常为 32 或 64 位。此外,空指针的内部表示不一定必须全为零(尽管它很常见)。
  • @Keith 有问题的语言是 C,我从来没有听说过内部表示是 0 以外的任何东西。这就是为什么您可以使用以下任何一种。 if (pointer)if (pointer != 0 )if(pointer != NULL ) 另外,我认为使用 8、16、32 位的 0 并没有太大不同。 bytes.com/topic/c/answers/213647-null-c
【解决方案2】:

我用printf("%.1s", &c)printf("%c", c) 做了一个实验。 我使用下面的代码进行了测试,bash 的time 实用程序获取了运行时间。

    #include<stdio.h>
    int main(){
        char c = 'a';
        int i;
        for(i = 0; i < 40000000; i++){
            //printf("%.1s", &c); get a result of 4.3s
            //printf("%c", c); get a result of 0.67s
        }
        return 0;
    }

结果表明使用%c%.1s 快10 倍。因此,尽管 %s 可以完成 %c 的工作,但性能仍然需要 %c。

【讨论】:

    【解决方案3】:

    %c 需要一个字符,它是一个整数,并根据编码规则打印出来。

    %s 期望 pointer 指向包含 char 值的内存位置,并根据编码规则打印该位置中的字符,直到找到 0(null ) 字符。

    所以你会看到,这两种情况虽然看起来很相似,但它们并没有太多共同之处,因为一种适用于值,另一种适用于指针。一种是将特定整数值解释为 ascii char 的指令,另一种是逐个 char 迭代内存位置 char 的内容并解释它们直到遇到零值。

    【讨论】:

      【解决方案4】:

      由于没有人提供任何参考的答案,这里有一个来自pubs.opengroup.com 的 printf 规范,它类似于来自IBM 的格式定义

      %c

      int 参数应转换为无符号字符,并写入结果字节。

      %s

      参数应该是一个指向char数组的指针。数组中的字节应写入(但不包括)任何终止的空字节。如果指定了精度,则写入的字节数不得超过该数量。如果未指定精度或大于数组的大小,应用程序应确保该数组包含一个空字节。

      【讨论】:

        【解决方案5】:

        对于%s,我们需要提供字符串的地址,而不是它的值。

        对于%c,我们提供字符的值。

        如果我们使用%s 而不是%c,我们将如何在字符后提供'\0'

        【讨论】:

          【解决方案6】:

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

          这真的归结为数据类型。我在这里看到了答案,表明您可以提供指向 char 的指针,并提供一个

          "%.1s"
          

          这确实可能是真的。但答案在于 C 设计者试图为程序员提供灵活性,并且确实是一种(尽管很小)减少应用程序占用空间的方法。

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

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

          有趣的问题:-)

          【讨论】:

            【解决方案7】:

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

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

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

            明确一点:我并不是说你应该更喜欢%.1s 而不是%c。我只说 %c 不能被替换的原因与其他答案假装告诉的不同。这些其他答案在技术上是错误的。空终止不是%s 的问题。

            【讨论】:

            • @LuchianGrigore:不,我并不是说任何人都应该更喜欢那样,相反。我说%.1s 将具有仅打印一个甚至不能以空终止的字符的功能,因此空终止不是对“引入”%c 的有效反对。
            • @Als %.1s 会期待 charchar* 吗?
            • @LuchianGrigore,没人这么说。我的回答只是说明空终止不是正确的论点,但获取对象的地址是一个论点。
            • @JoshuaDrake,你读过我的回答吗?我发现投票反对这一点真的过分了。我并不是说任何人都应该使用%.1s 来打印字符。这不是问题。问题是%c 是否是绝对必要的。我对此给出肯定的回答,只是说明原因与其他答案中给出的原因不同。
            • @JoshuaDrake:-1 表示没有阅读答案。问题询问为什么 X 存在于 C 中,这个答案清楚地解释了 X 必须存在才能使用右值。如果不考虑右值,语言/库设计者可能会(并且可能应该)将其保留在 %.1s,因为它使 printf 更清晰(因此更具可读性)。如果没有 c,打印返回单个字符的函数的结果最终可能会非常复杂(特别是如果调用具有副作用且依赖于顺序的多个函数)。
            【解决方案8】:

            C 具有 %c%s 格式说明符,因为它们处理不同的类型。

            char 和字符串与 night 和 1 差不多。

            【讨论】:

            • 当您提到 %s 和 %c 完全不同时,请告诉我为什么 scanf(%s, &amp;ch)scanf(%c, &amp;ch) 语句返回相同的结果。考虑 ch 是一个类似于 char ch 的字符;
            【解决方案9】:

            printf 函数是一个可变参数函数,这意味着它具有可变数量的参数。在调用函数 (printf) 之前,参数被压入堆栈。为了让 printf 函数使用堆栈,它需要知道堆栈中的信息,格式字符串用于此目的。

            例如

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

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

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

            【讨论】:

            • 但是您的 sizeof 规范完全错误。首先,ch 被提升为int(或在某些平台上提升为unsigned),printfint 解释为一个字符。所以那个参数“在堆栈上”的大小是sizeof(int)。第二次你又错了,它是sizeof(char*),仅此而已。 sizeof(int) 可以(现在经常)与此不同。第三,不需要像“堆栈”这样的东西。这些参数的传递方式由平台 ABI 决定,一切都可能发生在寄存器中。
            • @AndersK:“技术上” Jens 是完全正确的。事实上,你不妨去掉“技术上”这个词;他说的很对。
            • “一点点不准确有时可以节省大量解释”——赫克托·休·芒罗
            【解决方案10】:

            %s 打印出字符,直到到达0(或'\0',同样的事情)。

            如果您只有 char x;,则使用 printf("%s", &amp;x); 打印它 - 您必须提供地址,因为 %s 需要 char* - 会产生意想不到的结果,因为&amp;x + 1 可能不是0

            所以你不能只打印一个字符,除非它是空终止的(非常低效)。

            编辑:正如其他人指出的那样,两者在 var args 参数中期望不同的东西 - 一个是指针,另一个是单个字符。但这种差异有些明显。

            【讨论】:

            • 你应该提到%s 接受一个指针,而%c 接受一个字符本身。
            • 而且更重要的是,如果可以获取地址,您只能打印一个字符。这将排除打印函数的返回值,例如,以及寄存器变量。
            • 您可以通过提供精度来避免空终止的问题。
            【解决方案11】:

            可能是为了区分以空字符结尾的字符串和一个字符。如果他们只有%s,那么每个字符也必须以空字符结尾。

            char c = 'a';
            

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

            【讨论】:

            • 不是一个真正的答案,因为%.1s可以避免这种情况@
            • @Merlin 我没这么说。可能,我的句子结构可能令人困惑。您可能知道,格式说明符%s 的格式字符串必须是null-terminated。如果语言中没有 %c,那么在我上面的示例中,标识符 c 必须以 null 结尾才能与格式说明符 %s 一起使用。
            • @mahesh 混淆答案比给出错误答案更糟糕......我不敢相信你在争论它。
            • -1 这是一个糟糕的答案...printf("%s", c) 不起作用的原因不是因为空终止,而是因为printf 期望%s 指向一个内存位置, 而c 是一个字符值。空终止 printf("%s", &amp;c) 不起作用的原因,但即使它起作用,拥有%c 标识符仍然 有意义,因为需要&amp;c与所有其他变量类型的打印方式不一致。所以只是说“这是因为空终止”是不正确的。
            • 我不明白 74 个人如何认为这是正确的答案,因为它没有提到您必须通过 char* 才能使用 %s 打印一个字符.只是重申 BlueRaja 所说的
            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2020-03-10
            • 2020-04-07
            • 2020-03-12
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多