[英]Why is a array typedef variable considered a pointer in C
當進行代碼審查,我發現程序員不小心通過一個typedef對象直接foo(foo)
當函數使用一個指針作為參數foo(&foo)
。 由於某種原因,只要typedef是數組而不是結構,它仍然可以工作。 有人可以解釋為什么嗎? 請參見以下代碼:
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
typedef uint8_t foo_t[3];
typedef uint8_t bar_t[2];
typedef struct __attribute__((packed))
{
foo_t foo;
bar_t bar;
} foobar_t;
void change_foo(foo_t foo)
{
foo_t bar = {1,2,3};
memcpy(foo, bar, sizeof(foo_t));
}
void change_foo_p(foo_t *foo)
{
foo_t bar = {1,2,3};
memcpy(foo, bar, sizeof(foo_t));
}
void change_foobar(foobar_t foobar)
{
foo_t bar = {1,2,3};
memcpy(foobar.foo, bar, sizeof(foo_t));
}
void change_foobar_p(foobar_t *foobar)
{
foo_t bar = {1,2,3};
memcpy(foobar->foo, bar, sizeof(foo_t));
}
int main()
{
printf("Hello, World!\n");
foobar_t foobar;
foobar_t foobar2;
foo_t foo;
foo_t foo2;
change_foo(foo);
change_foo_p(&foo2);
change_foobar(foobar);
change_foobar_p(&foobar2);
// Prints 1101 since the only method not working is change_foobar()
printf("%d%d%d%d\n", foo[0], foo2[0], foobar.foo[0], foobar2.foo[0]);
return 0;
}
在C語言中,數組通常會自動轉換為指向其第一個元素的指針。 C 2018 6.3.2.1 3說:
除非它是sizeof運算符的操作數或一元&運算符,或者是用於初始化數組的字符串文字,否則將類型為“ array of type ”的表達式轉換為類型為“ type to的指針 ”的表達式。指向數組對象的初始元素,並且不是左值。 如果數組對象具有寄存器存儲類,則該行為是不確定的。
因此,當類型foo_t
是三個uint8_t
的數組,並且x
是類型foo_t
的對象時,則在諸如function(x)
的函數調用中, x
被自動轉換,因此該函數調用等效於function(&x[0])
。
但是,在這種情況下,結果是x
成為指向uint8_t
。 參數必須仍然滿足在函數聲明中對參數類型的調用中有關匹配參數類型的規則。 如果聲明函數的參數具有指向foo_t
類型指針(因此它是指向三個uint8_t
的數組的uint8_t
),則編譯器應發出警告或錯誤,提示function(x)
正在將指針傳遞給uint8_t
。
在您包含在問題中的代碼中沒有這樣的調用,因此不清楚您在問什么。 memcpy
調用確實包含作為數組的參數,這些參數將轉換為指針。 允許將這些作為memcpy
參數,因為其參數被聲明為void *
或const void *
,並且指向任何類型對象的指針都可以轉換為void *
。
(此外,作為數組的參數聲明會自動調整為指向數組元素的指針。)
因為foo
和&foo
具有相同的值,但函數調用中的類型不同。
foo
的類型是foo_t
,它是uint8_t[2]
。 這是一種數組類型,因此在傳遞給函數時,它會衰減為指向數組第一個元素的指針 ( change_foo(foo)
等同於change_foo(&foo[0])
)。
&foo
的類型為foo_t*
,即uint8_t(*)[2]
,即指向整個數組的指針。 當指向整個數組時,指針中存儲的地址就是其第一個元素的第一個字節的地址,這毫不奇怪的是,它也是數組第一個元素的第一個字節的地址。
因此,調用change_foo(foo)
和change_foo_p(&foo)
編譯為同一機器代碼。 change_foo_p()
使用兩個不匹配的指針調用memcpy()
時,它會調用未定義的行為,但是由於它獲得的輸入地址與change_foo()
一樣,因此未定義的行為恰好是您想要的。
在C中,當您定義數組時,實際上存儲了數組的起始地址。
整數數組[25];
//存儲數組的起始地址。
例如:0x00ffabcd
array [10] == *(array + 10)//索引運算符用作指針算術。
請注意,在上面的示例中,編譯器知道指針類型,而指針算術知道應該從數組的起始地址跳轉到
10 * sizeof(int)
。
在某些情況下,例如將數組傳遞給memset時,可能需要將sizeof運算符的結果提供給指針算術運算。
我將留下答案,因為評論指出了為什么是錯誤的。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.