在scanf()和printf()中,字符串变量名前要加上&符号吗?

3

我正在使用的编译器是Dev C++ 5.11。TDM-GCC 4.9.2 32位调试版本。使用C99模式。

1.

 char str1[100], str2[100];
 scanf("%s %s", &str1, &str2);
 printf("%s %s", &str1, &str2);

2.

 char str1[100], str2[100];
 scanf("%s %s", &str1, &str2);
 printf("%s %s", str1, str2);

3.

 char str1[100], str2[100];
 scanf("%s %s", str1, str2);
 printf("%s %s", str1, str2);

每段代码都有效。为什么?我非常困惑。

如果你想要一个可能展示不同行为的样例,可以比较 scanf("%s %s", &str1 + 1, &str2 + 1)(这个肯定有未定义行为)和 scanf("%s %s", str1 + 1, str2 + 1)(只有当用户输入过多数据时才会有未定义行为)。我将把为什么潜在差异更加明显留给你自己思考。 - Peter
4个回答

5
需要记住的第一件事是,数组自然而然地会“衰变”为指向它们第一个元素的指针。也就是说,在您的示例中,str1&str1 [0]是相等的。
需要记住的第二件事是,数组的指针和指向其第一个元素的指针将指向相同的位置,但它们在语义上是不同的,因为它们是不同的类型。再次以数组str1为例:当您执行&str1时,您将得到类型为char (*) [100]的内容,而仅使用纯文本str1(或其等效物&str[0])时,您将得到类型为char *的内容。这些是非常不同的类型。
最后需要记住的是,无论读取还是打印字符串,scanfprintf都需要指向char(即char *)的指针。有关详细信息,请参见例如scanf(和家族成员)printf(和家族成员)参考。
所有这些意味着,您在问题中只有第3种选择是正确的。

2
  • str1 提供了一个指向第一个元素的字符指针 char*,该元素将分配到数组中的第一个地址。
  • &str1 给出了整个数组的数组指针,类型为 char(*)[100]
  • 数组的地址始终与第一个元素的地址相同。
  • 使用 %s 格式说明符告诉 printf 将传递的指针视为 char*。因此,无论传递哪种指针类型,它都会被转换为这种类型。
  • 恰好,这种从 char(*)[100]char* 的指针转换必须产生完全相同的地址,因此代码无论使用哪种指针类型都可以工作。

1
虽然你说的一切都是正确的,但值得一提的是,建议使用 OP 的情况 3,因为传递的类型实际上是函数所期望的(即对应于 %s 格式的参数应传递 char * 而不是 char(*)[100])。 - Peter
@Peter 确实。好的编译器可以解析 printf 格式字符串,如果传递的类型与预期类型不对应,则会相应地发出警告。 - Lundin
一些程序员也会抱怨,有些时候他们是有道理的。 ;) - Peter

1
在C语言中,任何数组类型的表达式都会被隐式转换为指向该数组第一个元素的指针,除非它具有引用运算符。
scanf("%s %s", &str1, &str2);

scanf("%s %s", &str1[100], &str2[100]);

数组的地址始终与第一个元素的地址相同。


不,它不会被“隐式转换”为 &str[0] ,因为 &str[]&str[0] 是完全不同的类型,但是指向相同的对象。 - 0___________

0

因为字符串是字符数组,所以该数组的地址就是它的第一个元素的地址。

它们只是不同的类型。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接