首先,检查这条规则 - 来自 C11 Standard#6.3.2.1p3 [强调添加]:
3 除了是sizeof运算符、_Alignof运算符或一元&运算符的操作数,或者是用于初始化数组的字符串字面量, 类型为“类型数组”的表达式转换为类型为“类型指针”类型的表达式,该类型指向数组对象的初始元素,并且不是左值。如果数组对象有注册存储类,则行为未定义。
来自String literals[强调添加]:
就地构造一个指定字符数组类型的未命名对象,在需要将字符串嵌入源代码时使用。
让我们先解码:
char * stringa = "a";
printf("***stringa[0] = %c\n", (*(char **)(void *)&stringa)[0]);
在此语句char * stringa = "a"; 中,字符串"a" 将转换为指向类型char 的指针,该类型指向字符串"a" 的初始元素。因此,在初始化之后,stringa 将指向字符串文字 "a" 的第一个元素。
&stringa 的类型为 char **。取消引用它将给出 char * 类型,它只是字符串 "a" 并将 [0] 应用于它会给出字符 'a'。
现在,让我们解码:
printf("***a[0] = %c\n", (*(char **)(void *)&"a")[0]);
因为,这里你给出一元运算符&,所以在这个表达式中,(*(char **)(void *)&"a")[0],字符串"a"将不转换为指向其初始元素的指针,&"a" 将给出const char (*)[2] 类型的指针,该指针将被强制转换为 char ** 类型。
取消引用此指针将在地址处给出值,该地址只不过是字符串"a",它会将其视为char 类型的指针(因为类型转换char **)并将[0] 应用于它。这意味着,它正在尝试执行类似 ((char *)0x0000000000000061)[0](0x61 是字符 'a' 的十六进制值)的操作,这会导致错误 EXC_BAD_ACCESS。
相反,你应该这样做
printf("***a[0] = %c\n", (*(const char (*)[2])(void *)&"a")[0]);
编辑:
OP 仍然很困惑。此编辑尝试以不同的方式解释表达式(在帖子中)。
来自 cmets:
操作:But you wrote ((const char ()[2])(void )&"a")[0] works! There are two dereferencing operations ( and [0]) going on here!
不确定您是否知道,但我认为,分享来自 C11 Standard#6.5.2.1p2 的 [] 运算符的定义很好:
下标运算符[]的定义是E1[E2]等同于(*((E1)+(E2)))。
表达式(*(char **)(void *)&stringa)[0]:
(*(char **)(void *)&stringa)[0]
| | |
| +----------------------+
| |
| this will result in
| type casting a pointer
| of type char ** to char **
|
|
This dereferencing
will be applied on result of &stringa
i.e. ( * ( &stringa ) )
and result in stringa
i.e. this
|
|
| &stringa (its type is char **)
| +-------+
| | 800 |---+
| +-------+ |
| |
+-------> stringa |
/ +-------+ (pointer stringa pointing to first char of string "a"
/ | 200 |---+ (type of stringa is char *)
| +-------+ |
now apply [0] 800 |
to it |
i.e. stringa[0]. +-------+
stringa[0] is +-> | a | 0 | (string literal - "a")
equivalent to | +-------+
*((stringa) + (0)) | 200 ---> address of "a"
i.e. |
*(200 + 0), |
add 0 to address 200 |
and dereference it. |
*(200 + 0) => *(200) |
dereferencing address |
200 will result in |
value at that address |
which is character |
'a', that means, |
*(200) result in -------+
表达式(*(char **)(void *)&"a")[0]:
(*(char **)(void *)&"a")[0]
| | |
| +------------------+
| |
| this will result in
| type casting a pointer
| of type const char (*)[2] to char **
|
|
this dereferencing will be
applied to pointer of type
char ** which is actually a
pointer of type char (*)[2]
i.e. *(&"a").
It will result in value at address 200
which is nothing but string "a"
but since we are type casting
&"a" with double pointer (char **)
so single dereference result
will be considered as pointer of
type char i.e. char *.
*(char **)(void *)&"a"
|
|
| &"a" (its type is const char (*)[2] because type of "a" is
| +-------+ const char [2] i.e. array of 2 characters)
| | 200 |---+
| +-------+ |
| |
| |
| |
| +-------+
+------------------> | a | 0 | (string literal - "a")
/ +-------+
/ 200 ---> address of "a"
|
|
The content at this location will be
treated as pointer (of type char *)
i.e. the hex of "a" (0x0061) [because the string has character `a` followed by null character]
will be treated as pointer.
Applying [0] to this pointer
i.e. (0x0061)[0], which is
equivalent to (* ((0x0061) + 0)).
(* ((0x0061) + 0)) => *(0x0061)
i.e. trying to dereference 0x0061
Hence, resulting in bad access error.
表达式(*(const char (*)[2])(void *)&"a")[0]:
(*(const char (*)[2])(void *)&"a")[0]
| | |
| +----------------------------+
| |
| this will result in
| type casting a pointer
| of type const char (*)[2] to const char (*)[2]
|
|
this dereferencing will be
applied to pointer of type
const char (*)[2]
i.e. *(&"a")
and result string "a"
whose type is const char [2]
|
|
| &"a" (its type is const char (*)[2] because type of "a" is
| +-------+ const char [2] i.e. array of 2 characters)
| | 200 |---+
| +-------+ |
| |
| |
| |
| +-------+
+------------------> | a | 0 | (string literal - "a")
/ +-------+
/ 200 ---> address of "a"
|
|
Apply [0] to "a"
i.e. "a"[0].
Now, scroll to the top of my post
and check string literal definition -
string literal constructs unnamed object of character array type.....
also, read rule 6.3.2.1p3
(which is applicable for an array of type) -
....an expression that has type 'array of type' is converted
to an expression with type 'pointer to type' that points to
the initial element of the array object. ....
So, "a" (in expression "a"[0]) will be converted to pointer
to initial element i.e. pointer to character `a` which is
nothing but address 200.
"a"[0] -> (* ((a) + (0))) -> (* ((200) + (0)))
-> (* (200)) -> 'a'
来自 cmets:
操作:there is no such thing as an object in C ....
不要将单词 object 与 C++ 或其他面向对象语言中的对象混淆。
这就是 C 标准定义对象的方式:
来自 C11 标准#3.15p1
1 个对象
执行环境中的数据存储区域,其内容可以表示值
例如- int x; --> x 是 int 类型的对象。
如果您还有其他问题,请告诉我。