sizeof应用于数组类型
c11标准说尺寸,
“当应用于具有数组类型的操作数时,结果是数组中的总字节数”
- (6.5.3.4,子弹4)。
脚注(103)说:
“当应用于声明具有数组或函数类型的参数时,sizeof运算符会产生调整后的(指针)类型的大小”。
我从中可以看出,当应用于数组类型时,sizeof给出了数组的大小(元素的数量x元素的大小),但是应用于声明为具有数组类型的参数,它给出了指针的大小。
我的问题:
由于脚注,如何使数组类型的对象不会产生指针的大小?
在不知情的情况下,我觉得在某些情况下我无法信任sizeof运算符。
谢谢。
编辑:我想我应该澄清我的担忧,如果定义了“int a [4]”,那么我从响应中看到a == 4 * sizeof(int)的sizeof,但是sizeof(a + 0)呢? 看来sizeof(a + 1)必须被评估为指针。 我担心除了函数调用之外的情况,其中数组衰减到指针。
响应您的更新(关注sizeof(foo+1)
类型情况:
是的,应用于array_name + int
sizeof &(array_name[int]);
等效于sizeof &(array_name[int]);
,基于数组,在这些情况下衰减为指针。 同样, arr_name + 1
数组中获取实际值,您不要写入arr_name + 1
,而是编写*(arr_name + 1)
。
因此,考虑到脚注, sizeof
何时会产生实际的数组大小(以字节为单位)? 为此,请查看标准对于衰减为指针的数组的说法:
除非它是sizeof运算符或一元&运算符的操作数,或者是用于初始化数组的字符串文字,否则将类型为”array of type”的表达式转换为类型为”指针的表达式type”指向数组对象的初始元素,而不是左值。
含义:
- 直接在数组变量上使用
sizeof
(sizeof array_var
) - 取消引用指向数组的指针(
sizeof *(&array_var)
) 注意:这也适用于将此指针传递给另一个函数的数组,但并不总是最好的方法(参见下面的示例) - 字符串文字(如
char foo[] = "foobar";
的Rvaluechar foo[] = "foobar";
=>sizeof("foobar");
)
在所有其他情况下(AFAIK),数组衰减为指针, sizeof
将产生指针的大小:
- 算术on array =>指针算术(
sizeof (array_var +1 )
) - 将数组传递给函数(衰减成指针)
- …
将数组传递给函数
因此,使用一元&
运算符,可以将指向数组的指针传递给函数,但很少这样做。 不过,这是一个例子:
void pointer_to_array(char (*ptr)[]);//pointer to array type void error_p_to_arr(char (*ptr)[]); int main ( void ) { char str[] = "some random string";//array of 18 bytes printf( "Sizeof array %zu\n", sizeof str ); pointer_to_array(&str); return 0; } //we need to specify the exact type, including size! //replace 18 with 10, you're fine, but use 20 and you're in trouble void pointer_to_array(char (*ptr)[18]) { printf( "sizeof argument: %zu\nsizeof array %zu", sizeof ptr,//4 or 8 sizeof *ptr//18!! YaY ); } //if we don't specify the array size here void error_p_to_arr(char (*ptr)[]) { printf( "sizeof argument: %zu\nsizeof array %zu", sizeof ptr,//4 or 8 sizeof *ptr//ERROR! ); }
后一个sizeof *ptr
将导致错误( “将sizeof’无效应用于不完整类型’char []’” )。 因为这种传递数组的方式非常容易出错(必须在任何地方定义正确的大小),所以简单地让数组衰减并传递第二个参数会更常见:
void common_usage(const char *ptr, size_t arr_len); int main ( void ) { char str[] = "some random string"; common_usage(str, sizeof str/sizeof *str); return 0; }
它看起来更清洁,它更常见,更容易维护。
见这里的例子
引用的关键点是“ 声明具有数组类型的参数 ”和“ 调整后的(指针)类型 ”。 这是指“数组类型”的函数参数被调整为指针类型的事实。 一旦进行了调整,类型就是指针,其大小必须是指针的大小。 它不可能是其他任何东西。 这是它的工作原理:
void foo(int p[42]);
被调整为
void foo(int* p);
这两个函数声明是等效的。 所以p
的类型是int*
。 和sizeof(int*)
始终是指针的大小。
但是,在不同的上下文中,没有类型调整:
int a[42]; // no adjustment. a is an array of size 42; sizeof(a); // gives the size of type int[42]
这里,a的类型确实是“size 42 array of int”。 sizeof运算符可以访问此(编译时)信息,因此可以为该类型提供正确的大小。
请注意,这与数组衰减有关,在某些情况下,数组可以“衰减”为指向其第一个元素的指针。 那个衰变是允许你用数组参数调用foo
的:
int a[26]; foo(a); // foo(int*): a decays to int* int* p = a; // same phenomenon
因此, 调整会更改函数签名,而decay允许您将数组传递给需要指针的函数。
更新关于您的更新,二进制算术运算符的应用是数组衰减到指向其第一个元素的指针的众多情况之一。 例如
#include int main(void) { int a[42]; printf("sizeof(a) = %zu\n", sizeof(a)); printf("sizeof(a+1) = %zu\n", sizeof(a+1)); return 0; }
输出:
sizeof(a) = 168 sizeof(a+1) = 8
脚注适用于(函数)参数。
例如
void foo(int param_arr[32]) { int local_arr[32]; }
param_arr
是函数的一个参数 – 虽然看起来像一个数组,但它实际上是一个指针(一个int *
)。 因此sizeof param_arr
产生int *
的大小。
local_arr
不是参数。 因此sizeof产生该数组的大小。
拥有一个不产生指针大小的数组类型的对象很简单:不要在函数参数上执行:
const int foo[32]; printf("hey, foo is %zu bytes\n", sizeof foo);
不会打印sizeof (int *)
。
这是正常用法,你引用的文本指出,当一个数组传递给一个函数时,它会衰减到一个指针,即使该函数的prototype指定了一个数组大小。
所以这:
static void printsize(int x[100]) { printf("the argument is %zu bytes\n", sizeof x); } int main(void) { const int foo[100]; printsize(foo); return 0; }
将打印sizeof (int *)
。
只是为了澄清您在下面的代码可能会有所帮助:
void func(int a[]) { sizeof(a) is not equal to sizeof(int) * 10 but equal to sizeof(pointer) Because int a[] is adjusted to int * } int main() { int a[10]; int *p = a; //Initialize //sizeof(a) = sizeof(int) * 10 //sizeof(p) = sizeof(pointer) func(a); }
当数组衰减成指针时,当一个数组传递给函数作为参数时,我们可以将它说明为将数组声明为数组,如下所示,
void function (char a[]) { ... }
现在上面的声明被编译器不同地解释为指针声明,因为函数实际上接收到指向类型为T的arrya的指针,如下所示:
void function(char *a) { ... }
因此,编译器假装声明为指针(类型为char *
)和sizeof
的数组参数实际上将给出指针的大小而不是数组的大小。
例:
void function (char a[10]) { int i = sizeof(a); printf("%d\n", i); }
输出实际上是4
而不是10
。