#include<stdio.h>
int main()
{
char *str1 = "computer";
printf ("%p\n", (void *) str1); // i
printf ("%p\n", (void *) &str1); // ii
printf ("%d\n", *str1); // iii
printf ("%p\n", (void *) *&str1); // iv
printf ("%p\n", (void *) &*str1); // v
return 0;
}
我知道&
是'''运算符的地址,而*
是'值在地址'运算符。
据我说,
i。这是char数组的地址,因为数组的名称代表第一个元素的地址。
ii。我不明白这一点,因为地址的地址似乎令人困惑。
iii。这应代表地址的值(即computer
),但格式说明符为%p
,并在输出中打印随机数。
iv。这应该是指地址的值。这再次令人困惑。
我在输出中得到随机值。我知道其中一些可能是因为未定义的行为。但如果有人能澄清这些含义,我将非常感激。
我也想说
printf ("%p\n", **str1);
printf ("%p\n", &&str1);
给出编译失败的错误。
答案 0 :(得分:3)
示例代码中唯一的数组是由字符串文字生成的数组:
"computer"
在大多数情况下, 1 C中的字符串文字指示编译器在内存中的某个位置生成(或者就好像它已经生成了)一个数组,该数组的内容用字符填充组成字符串文字。您不能在数组元素上写入 - 就好像数组的类型const char [N]
是适当的整数常量 N
- 但数组本身实际上具有类型{{1 }}。 (阵列可能是,并且在大多数现代编译器中尽可能地放置在物理上只读的存储器中,例如ROM或文本空间,尽管许多编译器具有可以影响这一点的指令。正如其他人指出的那样,规则有点对于C ++不同。整数常量 char [N]
由字符串文字中的字符数决定,并添加了终止N
'\0'
。)
通常,给定一个变量char
类型为“ T T ”(对于某些类型v
),您只需编写T
获取其地址。这是“指向 T 的数组 N ”类型的值。这适用于字符串文字: 2
&v
这里char (*p_a)[6] = &"hello";
是指向数组的指针。这和之间的区别是:
p_a
是char *p_c = "world";
是指向单p_c
的指针,而不是指向(整个)数组的指针。因此,char
是该数组中的第二 p_c[1]
,char
。但'o'
将是第一个数组p_a[1]
之后的整个6 - char
- 长数组,如果有这样的数组(没有一个数组,因此试图访问"hello"
1}}产生未定义的行为。)
从字面上讲,然后,问题的答案是“没有一个”。这些表达式都没有一次指向整个数组!
由于我们知道p_a[1]
由9个"computer"
s组成 - 八个可见的char
和一个终止char
- 我们可以将'\0'
中存储的值包含在内。 1}},它是指向这9个str1
中的第一个的指针,并将其(通过强制转换和/或赋值)转换为正确的类型char
,以获得“地址” (整个)数组“:
char (*)[9]
或:
char (*answer_1)[9] = (void *)str1;
例如。
然而,关于C中的指针和数组的问题是,出于所有实际目的,指向数组的第一个元素的指针“与指向<的指针一样好” em>整个数组。因此,只有普通((char (*)[9]) str1); /* answer2: produce the value, then ignore it */
,它是指向9 - str1
- 长数组的第一个元素的指针,与指向整个9 - char
的指针“一样好” - 长阵。
对于刚接触C的人来说,这一切听起来都很奇怪。很久以前,我制作了一个显示关键区别的图形;它可以找到here。差异的本质是“圆的大小”而不是“地址值”本身。
列表中的项目编号2(或者我应该说“ii”)char
使用的实际对象&str1
类型为“指向{{1}的指针” }”。 address-of运算符str1
为您提供指向该对象的指针:类型为“指向char
}的指针”或&
的值。值本身可以被视为指向变量char
的箭头,而char **
中存储的值本身可以被视为指向{{1}中的str1
的箭头}}
1 此处的主要例外是将字符串文字用作初始值设定项。例如:
str1
完全填充9 - c
- 长数组"computer"
,而不必在任何地方创建第二个数组。在这些情况下,您可以控制结果数组的实际char ch_array[9] = "computer";
- ness; char
不是ch_array
而是你可以写的内容,所以你可以更改这个数组中的字母,使其成为“catputer”,例如,如果你愿意的话
C标准还允许您使阵列中的一个元素“太短”,即无法保持终止const
。在这种情况下,终结器根本不存储在任何地方。在没有创建这样的对象的情况下,没有标准的方法可以做到这一点,尽管有一个提议无处可去,在字符串文字中使用最终的ch_array
转义来获得相同的结果。
请注意字符串连接:
const
不会在部分之间插入终止'\0'
:在连接之后添加终结符。并且,由于在此处生成的匿名数组上写入会产生未定义的行为,因此编译器也可以(并且很好)共享字符串文字,可能还有“尾部共享”, 3 这样:
\z
可能只生成一个数组,保留char *x = "com" "put" "er";
字符串,并使'\0'
指向该字符串中的char *s1 = "hello world", *s2 = "world";
。
2 早在20世纪80年代,很多编译器都错了。公平地说,当时没有C的标准:最初的ANSI C标准在1989年12月出现,所以直到1990年才有可能适用于编译器编写者的任何官方规则。
3 “尾巴分享”不是一个艺术术语,只是我在这里试图描述这个过程的短语。
答案 1 :(得分:1)
让我们按顺序采取这些:
str
返回指向数组中第一个字符的指针&str
返回指向指针的指针(即指向变量str
所在的位置)*str
取消引用指针,并返回指向的值,换句话说,它返回字符串中的第一个字符(因此它根本没有指针)*&str
返回1,因为运算符的取消引用和地址相互抵消&*str
与上述相同,地址和解除引用相互抵消第4点和第5点之间的差异是表达式的计算顺序,但结果是相同的。
在您使用char
指针的情况下,&#34;正确&#34;方式是1。
至于**str
和&&str
的错误,因为*str
会产生char
,而且不是类型,您可以取消引用。具有双符号的那个是因为它是在一元表达式而不是二进制表达式中应用的逻辑和运算符&&
。
虽然不相关点:字符串文字是字符的常量数组,其大小等于字符串中的字符数加上字符串终止符的字符数。这意味着有一个普通的非const指针是错误的,声明应该是
const char *str = "computer";
或可能(不知道它是否在C中有效)
char const *str = "computer";
答案 2 :(得分:1)
所有示例都会导致未定义的行为,因为格式说明符为printf
的{{1}}需要%d
,而不是指针。
作为stdcall points out,数据指针的正确格式说明符(虽然仅用于指向字符的指针和signed int
- 指针,严格来说,其余部分必须强制转换为其中任何一个){{3}} 1}},它将以实现定义的格式打印指针。
这是void
类型的右值:%p
投放到char**
并打印&str1
这些是void*
类型的左值:%p
char*
使用str1
打印字符串,使用*&str1
打印指针值
这是%s
类型的右值:%p
与上述相同
这是char*
类型的左值:&*str1
使用char
打印
所有*str1
指向同一个对象。
请注意,尽管字符串文字由于历史原因而具有类型%c
,但它们是可以共享的常量:尝试更改它们会导致UB。
答案 3 :(得分:0)
printf(“\ n%d”,* str1); // iii
不要打印随机数,而是打印第一个字符的ascii值
* str1实际上是第一个char'c'== 99
的值使用*(str1 + i)访问str1的元素i
答案 4 :(得分:0)
printf ("%d", str1); // i
这是您的“字符串”的地址,其中存储了char
数组(相当于&str[0]
)。 str1
的类型是char*
,它与%d
说明符不匹配,但大多数实现都会将地址输出为整数。
printf ("\n%d", &str1); // ii
str1
指向char
中str1
的第&str[0]
个地址(即&str1
),因此str1
指向&str1 -> str1 -> "computer"
printf ("\n%d", *str1); // iii
:< / p>
str1[0]
这相当于'c'
%d
。由于您指定了ansi
说明符,因此值输出为'c'
的{{1}}值。
printf ("\n%d", *&str1); // iv
printf ("\n%d", &*str1); // v
嗯,*&a
和&*a
在大多数(所有?)案例中等同于a
,所以在这里你只是得到第一个行为。
printf ("\n%d", **str1);
str1
是指针,因此您可以取消引用它以获取'c'
,但*str1
为'c'
并且您无法取消引用char
。
printf ("\n%d", &&str1);
&str1
是str1
的地址,但它不是变量,因此它没有任何地址,因此无法&&str1
(str1
的地址不存在)。
正如其他答案所提到的,输出指针的正确方法是%p
。您获得所有案例的随机值(通常除iii
除外),因为str1
并不总是具有相同的地址。