我被告知名称数组基本上只是对数组第一个元素的引用,但我无法完全理解这个问题。例如,来自以下代码段
char str1[] = "Hello";
printf("%d %d %s %c\n", &str1, &str1[0], str1, str1[0]);
// => -476114470 -476114470 Hello H
我们可以看到str1和str1 [0]共享相同的内存地址(-476114470),这表明,至少根据我对此的不了解,它们是相同的东西"。但是他们的实际印刷价值不同于"你好"和" H"因此。这怎么可能?
答案 0 :(得分:3)
// `str1` is an _array of char_
char str1[] = "Hello";
名称数组与C中数组的第一个元素(地址)的区别是什么?
数组是同一类型的几个元素。 (第一个元素的地址)是一个地址。
我被告知名称数组基本上只是对数组第一个元素的引用...
在某些上下文中,它们看起来是一样的,但这是转换的结果。第1行:数组str1
传递给printf()
,在此上下文中,数组被转换。 str1
从数组转换为第一个元素的地址和类型。
printf("%s\n", str1); // Prints "Hello\n"
第二行:传递第一个数组元素的地址。
printf("%s\n", &str1[0]); // Prints "Hello\n"
不出所料,他们打印的相同。
在这种情况下,你肯定会看到差异。
printf("size: %zu\n", sizeof str1); // Prints 6
printf("size: %zu\n", sizeof &str1[0]);// Size of a char * pointer
我们可以看到
str1
和str1[0]
共享相同的内存地址
printf("%d %d\n", &str1, &str1[0]);
这种探索线可能导致误导。 &str1
和&str1[0]
都是同一内存位置的地址,但具有不同的类型:array 6 of char地址与char
的地址。
作为非常不同的类型,它们甚至可以打印不同的地址编码,因为地址可能具有许多表示。 (虽然这种情况并不常见。)
注意:使用"%d"
打印对象指针的定义不明确,在"%p"
投射后使用(void *)
。
printf("%p %p\n", (void *) &str1, (void *) &str1[0]);
答案 1 :(得分:2)
这就是它在内存中的样子:
+-----+-----+-----+-----+-----+-----+
| 'H' | 'e' | 'l' | 'l' | 'o' | \0 |
+-----+-----+-----+-----+-----+-----+
^
+-- pointer to first element
|
+-- also: pointer to array
请特别注意
%c
和%s
处理相同类型 争论!%c
处理char
(即单个字符), 而%s
处理char *
(即指向数组的指针) 字符,希望以空值终止。)
如果您只有char x;
,请使用printf("%s", &x);
- 进行打印,因为%s
需要char*
- 会产生意外结果,因为&x + 1
可能不是0
。
此外,
int printf(const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt); // Initialize stdargs
const char *p = fmt; // Pointer into the format string
while (*p != '\0') {
char ch = *p++; // Get the next character of the format string
if (ch != '%') { // Not a format character? Just print it
putchar(ch);
continue;
}
char fmt = *p++; // Get the character following the %
if (fmt == 'd') {
int n = va_arg(ap, int);
print_number(n);
if (fmt == 'c') {
char c = va_arg(ap, char);
putchar(c);
} else if (fmt == 's') {
char *str = va_arg(ap, char *);
print_string(str);
} else {
// Unrecognized format character
}
}
va_end(ap);
return n;
}
考试结束后,让我们看一下指针算术,
#include <stdio.h>
int test() {
char str1[] = "Hello";
char const * ptr = str1;
while(*ptr != '\0') {
printf("%p --> %c\n", (void*)ptr, *(ptr++));
}
return 0;
}
int main(int argc, char * argv[]) {
test();
return 0;
}
输出:
0x7ffedff6132a --> H
0x7ffedff6132b --> e
0x7ffedff6132c --> l
0x7ffedff6132d --> l
0x7ffedff6132e --> o
答案 2 :(得分:0)
在您的打印声明中,尝试将str1[0]
替换为其内存地址&str1[0]
,并使用%s
代替%c
。您会注意到整个字符串也会被打印出来。
正如伊万所说:
您可以毫无问题地使用printf("%s\n", &str1[0]);
,因为str1
与第一个元素(&str1[0]
)的ADDRESS完全相同,而“%s”代表您的地址希望它开始打印字符串并打印,直到找到'\ 0'。以同样的方式,你也可以使用printf("%s\n", &str1[3]);
在屏幕上输出'lo'。
现在解释为什么str1
和&str1[0]
并不总是相同的事情:
在数组上使用sizeof
是一个例子,当数组没有变成指向其第一个元素的指针时。 sizeof str1
与sizeof &str1[0]
不同。
不太重要:显示指针地址时使用%p
而不是%d
。
答案 3 :(得分:0)
数组名是一个常量指针,指向数组的起始地址。
str [i] = [i] str = *(str + i)
所以,&amp; str [0] =&amp;(*(str + 0))=&amp; * str = str;
因此很清楚第一个元素的地址与数组名称(数组的基址)相同。