[英]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
我們可以看到
str1
和str1[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 str1
與sizeof &str1[0]
。
不太重要的是:顯示指針地址時,請使用%p
代替%d
。
數組名是一個常量指針,指向數組的起始地址。
str [i] = [i] str = *(str + i)
因此,&str [0] =&(*(str + 0))=&* str = str;
因此很明顯第一個元素的地址與數組名(數組的基地址)相同。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.