簡體   English   中英

名稱數組和C中數組的第一個元素之間有什么區別?

[英]What's the difference between a name array and the first element of the array in C?

有人告訴我,名稱數組基本上只是對數組第一個元素的引用,但是我無法完全解決這個問題。 例如,來自以下代碼段

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

我們可以看到str1和str1 [0]共享相同的內存地址(-476114470),至少根據我對此的理解,這暗示它們是“同一件事”。 但是它們的實際印刷價值相應地是“ Hello”和“ H”。 這怎么可能?

// `str1` is an _array of char_
char str1[] = "Hello";

名稱數組和C中數組的第一個元素的(地址)之間有什么區別?

數組是相同類型的幾個元素。 第一個元素的(地址)是一個地址。


有人告訴我名稱數組基本上只是對數組第一個元素的引用...

某些情況下,它們看起來相同,但這是轉換的結果。 第一行:將數組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]都是相同存儲位置的地址,但是具有不同的類型: char數組6的地址與char地址。

作為一種非常不同的類型,它們甚至可以打印不同的地址編碼,因為地址可能具有許多表示形式。 (盡管這種情況並不常見)。

注意:使用"%d"打印對象指針的定義不明確,在(void *)強制轉換之后使用"%p"

printf("%p %p\n", (void *) &str1, (void *) &str1[0]);

這是在內存中的樣子:

+-----+-----+-----+-----+-----+-----+
| 'H' | 'e' | 'l' | 'l' | 'o' | \0  |
+-----+-----+-----+-----+-----+-----+
^
+-- pointer to first element
|
+-- also: pointer to array

請特別注意, %c%s 不會處理相同類型的參數! %c處理一個char (即單個字符),而%s處理一個char * (即指向字符數組的指針,希望該字符以null終止)。

如果您只有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

在您的打印語句中,嘗試將str1[0]替換為其內存地址&str1[0]並使用%s代替%c 您會注意到整個字符串也將被打印出來。

正如伊萬(Ivan)建議的那樣:

您可以使用printf("%s\\n", &str1[0]); 沒問題,因為str1與第一個元素的地址( &str1[0] )完全相同,並且“%s”接受您希望它開始打印字符串的地址,並且打印直到找到'\\ 0 '。 同樣,您也可以使用printf("%s\\n", &str1[3]); 它將在屏幕上輸出“ lo”。

現在解釋一下為什么str1&str1[0]並不總是同一件事:

在數組上使用sizeof是當數組未更改為指向其第一個元素的指針時的一種情況。 sizeof str1sizeof &str1[0]

不太重要的是:顯示指針地址時,請使用%p代替%d

數組名是一個常量指針,指向數組的起始地址。

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

因此,&str [0] =&(*(str + 0))=&* str = str;

因此很明顯第一個元素的地址與數組名(數組的基地址)相同。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM