我遇到了这个问题:
#include<stdio.h>
int main()
{
char str[25]="Catch me, if u can!";
printf("%s\n",&str+2);
return 0;
}
谁能解释我的意思&str+2
?这是如何运作的?
&str
返回类型的指针char (*)[25]
。这意味着,该指针指向的内存大小将是sizeof(str)
,即 25。根据正常的指针算法,将 2 加到该值将产生一个类型的指针,该指针将指向数组char (*)[25]
末尾后 25 个字节。str
打印它很可能是未定义的行为。
str
是一个类型为“25 个字符的数组”的表达式。在大多数情况下,数组会自动转换为指向其第一个元素的指针。因此, instr+2
被str
转换为指向char
数组中第一个的指针,并且当添加 2 时,结果是指向char
数组中第三个的指针。
但是,根据 C 2011 6.3.2.1 3,当数组类型的表达式是一元运算符sizeof
,的操作数,或者是用于初始化数组的字符串文字时,不会发生这种自动转换。因此, in ,不会转换为指针;它仍然是一个 25 个字符的数组。然后是一个指向 25 个字符的数组的指针。将 2 添加到此时,从概念上讲,结果是指向 25 个字符数组的数组的第三个元素的指针。_Alignof
&
&str+2
str
&str
但是,指针运算仅在数组内部定义,直到数组末尾的虚构元素。(此外,为了指针算术,单个对象被视为具有一个元素的数组。)由于我们只有一个 25 字符数组而不是 25 字符数组数组,因此没有第三个元素可以指向,并且&str+2
是未定义的。
首先,您在那里所做的最多会导致未定义的行为。
str
是一个符号,它与字符串开始的堆栈上的内存地址有关。&str
在堆栈符号上无效,使用它将导致未定义的行为。您可能想要的是str + 2
指向字符串中第三个字节的指针,因此
printf("%s\n",str+2);
将打印"tch me, if u can!\n"
当你定义你的数组时:
char str[25] = "Catch me, if you can!";
你得到的是堆栈上保留的 25 个字节的空间,其中包含以下内容:
@Address @Address
0xbfa7fc13 0xbfa7fc2C
| |
V V
[C][a][t][c][h][ ][m][e][,][ ][i][f][ ][y][o][u][ ][c][a][n][!][\0][\0][\0][\0]
查看str的地址:
printf("%#x, %#x\n", str, &str);
你会看到0xbfa7fc13
,0xbfa7fc13
。相同的值,因为数组从它的地址开始。当您将 2 添加到 str 时,您将指向字符串的下方:
0xbfa7fc13 + 2 =
0xbfa7fc15
|
V
[t][c][h][ ][m][e][,][ ][i][f][ ][y][o][u][ ][c][a][n][!][\0][\0][\0][\0]
所以打印:
printf("%s\n", str+2);
只会把绳子往下扔一点。但是,如果您将 2 添加到 的地址str
,那么它将自动为您 * 按您要添加的任何大小的大小:
&str + 2 ==> &str + 2 * sizeof(str) ==> &str + 50 ==> 0xbfa7fc45
如您所见,该地址已超出数组在堆栈上的位置,因此打印:
printf("%s\n",&str+2);
'\0'
如果幸运的话,只会吐出数组后 50 字节的任何垃圾,直到找到 a 为止。这是未定义的行为,所以它几乎可以做任何事情。