名称数组与C中数组的第一个元素之间的区别是什么?

时间:2018-05-13 21:45:32

标签: c

我被告知名称数组基本上只是对数组第一个元素的引用,但我无法完全理解这个问题。例如,来自以下代码段

char str1[] = "Hello";
printf("%d %d %s %c\n", &str1, &str1[0], str1, str1[0]);
// => -476114470 -476114470 Hello H

我们可以看到str1和str1 [0]共享相同的内存地址(-476114470),这表明,至少根据我对此的不了解,它们是相同的东西"。但是他们的实际印刷价值不同于"你好"和" H"因此。这怎么可能?

4 个答案:

答案 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
  

我们可以看到str1str1[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 str1sizeof &str1[0]不同。

不太重要:显示指针地址时使用%p而不是%d

答案 3 :(得分:0)

数组名是一个常量指针,指向数组的起始地址。

str [i] = [i] str = *(str + i)

所以,&amp; str [0] =&amp;(*(str + 0))=&amp; * str = str;

因此很清楚第一个元素的地址与数组名称(数组的基址)相同。