c中的printf()是否只允许("")作为第一个参数?

时间:2018-03-23 18:52:24

标签: c printf

根据printf的定义,它说第一个参数应该是一个数组,即char*后跟省略号...,即之后的变量参数。如果我写printf(3+"helloWorld"); //Output is "loWorld"。根据定义不应该给出错误?

这是printf的定义:

#include <libioP.h>
#include <stdarg.h>
#include <stdio.h>

#undef printf

/* Write formatted output to stdout from the format string FORMAT. */
/* VARARGS1 */
int __printf(const char *format, ...) {
    va_list arg;
    int done;

    va_start(arg, format);
    done = vfprintf(stdout, format, arg);
    va_end (arg);

    return done;
 }

 #undef _IO_printf
 ldbl_strong_alias(__printf, printf);
 /* This is for libg++.  */
 ldbl_strong_alias(__printf, _IO_printf);

8 个答案:

答案 0 :(得分:5)

这不是错误。

如果将"helloWorld"传递给printf,则字符串文字将转换为指向第一个字符的指针。

如果你传递3+"helloWorld",你就会在指向第一个字符的指针上加3,这会产生指向第4个字符的指针。这仍然是指向字符串的有效指针,它不是定义的整个字符串。

答案 1 :(得分:3)

来自Pointer Arithmetic

  

如果指针P指向索引为I的数组的元素,则

     
    

P + N和N + P是指针,它们指向索引为I + N的同一数组的元素
      P-N是一个指针,它指向索引为{tt | I-N}}

的同一数组的元素   
     

仅当原始指针和结果指针都指向同一数组的元素或指向该数组末尾的元素时,才定义行为。 ....

字符串文字的类型为char[N],其中N是字符串的大小(包括空终止符)。

来自C标准#6.3.2.1p3 [强调我的]

  

3除非它是sizeof运算符,_Alignof运算符或一元&运算符的操作数,或者是用于初始化数组的字符串文字,否则类型为“ array of type”的表达式'会转换为类型为“ pointer to type”的表达式,该表达式指向数组对象的初始元素,而不是左值。如果数组对象具有寄存器存储类,则该行为是不确定的。

所以,在表达式中

3+"helloWorld"

"helloWorld"(类型为char [11](字符数组),将转换为指向字符的指针,它指向数组对象的初始元素。

这意味着,表达式是:

3 + P
where P is pointer to initial element of "helloWorld" string

  ----------------------------------------------
  | h | e | l | l | o | W | o | r | l | d | \0 |
  ----------------------------------------------
    ^
    |
    P (pointer pointing to initial element of array)

3添加到指针P时,结果指针将指向第4个 字符:

  ----------------------------------------------
  | h | e | l | l | o | W | o | r | l | d | \0 |
  ----------------------------------------------
                ^
                |
                P (after adding 3 the resulting pointer pointing to 4th element of array)

此结果表达式3+"helloWorld"的指针将传递给printf()。请注意,printf()的第一个参数不是数组,而是指向以空值结尾的字符串的指针,表达式3+"helloWorld"的结果是指向4th字符串的"helloWorld"元素的指针。因此,您将获得输出"loWorld"

答案 2 :(得分:2)

3+"helloWorld"char *类型(在printf转换后)。这完全合法。在C中,字符串文字的类型为char []。当作为函数的参数传递时,char []将转换为指向它的第一个元素(数组到指针转换规则)的指针。因此,"helloWorld"将转换为指向元素h的指针。 3+"helloWorld"将指向数组"helloWorld"的4 th elemet。

答案 3 :(得分:2)

dbush和haccks的答案简明扼要,我为支持dbush提供的赏金而拒绝了hacks的答案。

我唯一没有说的是问题标题的表达方式让我想知道OP是否会认为这也会产生错误:

char sometext[] = {'h', 'e', 'l', 'l', 'o', '\0'};
printf (sometext);

因为根本不涉及字符串文字。 OP需要了解,永远不要认为带有char *参数的函数调用可以“仅允许将字符串文字用作[that]参数”。

dbush和haccks的答案通过提及将字符串文字转换为char *(以及如何向其添加整数来求值)来暗示这一点,但是我觉得值得明确指出char *的任何东西,即使不是从字符串文字转换为 的东西。

答案 4 :(得分:1)

printf的第一个参数声明为const char *format。这意味着printf应该传递一个指向char的指针,并且该指针指向的字符不会被printf更改。第一个参数还有其他限制:

  • 它应指向正确的C字符串,即以空字节结尾的字符数组。
  • 它可能包含转换说明符,必须正确构造转换说明符,并且必须将相应的参数作为额外的参数传递给printf,并带有从格式字符串派生的预期类型和顺序。

传递诸如"helloWorld"之类的字符串常量作为format参数是调用printf的最常见方法。字符串常量是char的数组,该数组以空字节结尾,程序不应该对其进行修改。将它们传递给期望指向char指针的函数将导致指向其第一个字节的指针被传递,就像C语言中的所有数组一样。

表达式"helloWorld" + 33 + "helloWorld"的计算结果是指向字符串第4个字​​节的指针。它等效于表达式3 + &("helloWorld"[0])&("helloWorld"[3])或简单地&"helloWorld"[3]。实际上,它也等效于&3["helloWorld"],但此最新形式仅用于病理混淆。

printf不使用format参数之前的字节,因此传递3 + "helloWorld"等同于传递"loWorld"并产生相同的输出。

答案 5 :(得分:1)

printf(3+"helloWorld"); //Output is "loWorld"



不会出现错误,因为C语言字符串中的字符串是字符数组,而数组名称给出了数组的基地址。

如果是 printf(3+“ helloWorld”)
3+“ helloWorld” 给出字符数组的第四个元素的地址,即String
这仍然是指向字符串即char *

的有效指针
printf only allow taking a char* as the first argument

答案 6 :(得分:0)

更确切地说,printf()函数的第一个参数不是字符数组,而是指向字符数组的指针。它们之间的差异与VB世界中byvalbyref之间的差异相同。 可以使用(++和-)或应用算术运算(+和-)来递增和递减指针。

在您的情况下,您正在传递指向“ helloWorld”的指针,该指针增加了3,因此它指向字符数组“ helloWorld”的第四个元素。

让我们用asm伪代码简化此过程


MOV EAX, offset ("helloWorld")
ADD EAX, 3
PUSH EAX
CALL printf

您可能认为3+"hello word"在3和“ hello world”之间进行了级联,但在C中则进行了级联。最简单的方法是sprintf(buff, "%d%s",3,"hello wrord");

答案 7 :(得分:-1)

这很奇怪,但没有错。通过执行 services.AddMvc().AddRazorPagesOptions(options => { options.Conventions.AddPageRoute("/details", "{datestart}/{dateend}/{client?}"); }); ,您可以将指针移到其他位置。

初始化3 +时,同样的事情起作用:

char *

char *str1 = "Hello"; char *str2 = 2 + str1; 现在等于str2