使用sizeof来处理数组和指针的实验

22

针对该程序:

#include<stdio.h>
int main(void)
{

    int (*a)[2];
    int b[5];

    printf("sizeof(int) : %zu\n", sizeof(int)); 
    printf("sizeof(int*) : %zu\n", sizeof(int*));

    printf("sizeof(b) : %zu\n",sizeof(b));
    printf("sizeof((int*)b) : %zu\n",sizeof((int*)b));
    printf("sizeof(&b[0]) : %zu\n",sizeof(&b[0]));

    printf("sizeof(a) : %zu\n",sizeof(a));
    printf("sizeof(a[0]) : %zu\n",sizeof(a[0]));
    printf("sizeof(a[1]) : %zu\n",sizeof(a[1]));

    return 0;
}

输出结果为:

sizeof(int) : 4 -> Fact 1
sizeof(int*) : 8 -> Fact 2
sizeof(b) : 20 -> Case 1
sizeof((int*)b) : 8 -> Case 2
sizeof(&b[0]) : 8 -> Case 3
sizeof(a) : 8 -> Case 4
sizeof(a[0]) : 8 -> Case 5
sizeof(a[1]) : 8 -> Case 6

问题/观察(按照案例顺序):

  1. Case 1的输出结果为20,是因为b被声明为整数数组即int[]吗?根据Fact1确认,总块大小以字节返回。是这样吗?

  2. 我想是将b强制转换为int*使差异产生了。在这里,b被认为是指针。使用Fact2确认了这一点。是对还是错?

  3. &b[0]衰减为指针b。输出与Fact2相符。

  4. 预期这里的输出结果为16,但我得到了8作为输出。我得出结论,这是因为a毕竟是一个指针,而输出与Fact2相符。我的输出类似于问题2。

  5. a[0]是指针。输出与Fact2相符

  6. a[1]是指针。输出与Fact2相符

请回答这些问题并纠正我如果有任何错误观察。


7
表达式“&b[0]”不是在衰变成指针,它*就是一个指针。 - Some programmer dude
@JoachimPileborg:感谢您指出这个问题。这更多是我的术语问题。现在我明白了。但我可能不会编辑问题,因为我觉得这对其他人也有用。 - sjsam
@Olaf 我也对zd感到困惑。 - chux - Reinstate Monica
1
sjsam,详情:“衰减”被许多人用来描述C规范中详细说明的内容,即“...具有类型‘‘类型数组’’的表达式被转换为具有指向数组对象的初始元素的‘‘类型指针’’的表达式...”§6.3.2.1 3 &b[0]是一个指针,而不是一个数组。不需要“衰减”。 - chux - Reinstate Monica
1
@sjsam 不是的,因为&b[0]已经是一个指针了,取地址运算符会给你一个指向b[0]的指针。如果你将b作为参数传递,那么它就会衰变成一个指针。 - Some programmer dude
显示剩余6条评论
3个回答

16
请回答问题并纠正我观察到的任何错误。
1. Case 1的输出是20,因为b被声明为integers的数组,即int[]吗?Fact1已确认返回字节中的总块。是这样吗?
是的,结果显示了sizeof(int [5])。因此,从Fact1得出,大小为5 * 4。
2.我猜在这里将b强制转换为int*有所不同。这里将考虑b作为指针。我使用Fact2确认了这一点。对还是错?

没错。但需要补充的是:sizeof 只需要表达式的类型,它 不会 对表达式求值(获取值),除非它是 VLA 类型。(来自 C99 specs6.5.3.4 sizeof 运算符部分)

因为你对最终结果进行了强制类型转换,所以之后的任何事情都无关紧要。

  1. &b[0] 衰变成指针 b。输出与 Fact2 相符。

不是和否。 b[0] 的类型是 int,因此 &b[0] 的类型已经是 int * (记住 [...] 的优先级高于 &)。没有衰变。是的,输出与 Fact2 相符。

我原本期望在这里得到16,但实际输出结果是8。我得出结论,这是因为a最终是一个指针,并且输出与Fact2相符。我得到了类似于问题2的输出结果。
a作为int数组2的指针。因此打印的大小是指向int数组的指针的大小。
int (*a)[2];声明a为指向int数组2的指针。因此,您会得到指向数组的指针的大小。
要获得所需的结果(指向int的指针数组的大小),请使用:int *a[2];
int (*a)[2];

a           anonymous
+----+      +----+----+
| a  |----->|int |int |
+----+      +----+----+

int *b[2];

b  
+----+----+
|int*|int*|
+----+----+
b[0] b[1]
  1. a[0] 是指针。输出与 Fact2 相符。
  2. a[2] 是指针。输出与 Fact2 相符。

正如之前所述,a 是指向包含 2 个 int 的数组 2 的指针。因此,如果 indexa[index],则是一个包含 2 个 int 的数组 2。因此,a[0]a[1] 的类型是包含 2 个 int 的数组 2。因此,从 Fact 1 中输出的结果为 2*4
这可能与本答案无关,但 a 未初始化,在表达式中使用它会导致 未定义行为。虽然在 sizeof 中使用它是可以的。


为了理解输出结果,让我们分析sizeof的参数类型。
printf("sizeof(b) : %zu\n",sizeof(b));             // int [5]
printf("sizeof((int*)b) : %zu\n",sizeof((int*)b)); // int *
printf("sizeof(&b[0]) : %zu\n",sizeof(&b[0]));     // int *

printf("sizeof(a) : %zu\n",sizeof(a));             // int (*) [2]
printf("sizeof(a[0]) : %zu\n",sizeof(a[0]));       // int [2]
printf("sizeof(a[1]) : %zu\n",sizeof(a[1]));       // int [2]

一个便携式程序(不是绝对可靠)用于确认类型看起来像:

assert(sizeof(b) == sizeof(int [5]));
assert(sizeof((int*)b) == sizeof(int *));
assert(sizeof(&b[0]) == sizeof(int *));

assert(sizeof(a) == sizeof(int(*)[2]));
assert(sizeof(a[0]) == sizeof(int[2]));
assert(sizeof(a[1]) == sizeof(int[2]));

想要达到什么样的结果?我认为OP想更好地理解指针。 - StoryTeller - Unslander Monica
1
@StoryTeller 所谓期望结果,是指问题文本中所预期的结果。 - Mohit Jain
@StoryTeller:确实,特别是像 int(*a)[2] 这样棘手的声明。 - sjsam
事实证明,我忽视了 int (*a)[2] 是指向数组的指针这一事实。因此,情况 5 和 6 建立在错误的前提下。@dbush 的示例对此有所帮助。 - sjsam

13

sizeof 运算符是少数几个可以区分数组(假设它不是函数参数)和指针的东西之一。

  1. b 被识别为一个由 5 个元素组成的数组,每个元素为 4 个字节,因此 sizeof(b) 的结果为 20。
  2. 强制转换将数组转换为指针,类似于将其传递给函数。因此大小为 8。
  3. 这实际上并没有“衰减”为指针。它 就是 指针。您正在取一个 int 的地址,因此类型当然是 int *。回答您的评论,如果您将它传递给函数,则仍然不能准确地说表达式 &b[0] “衰减”为指针,因为它实际上是指针,而不是数组。
  4. 由于 a 是指向数组的指针,因此其大小是指针的大小,即 8。这与 int *c[2] 不同,后者是指针的数组,大小为 16。
  5. a[0] 不是一个指针,而是一个大小为 2 的数组。语法 a[0] 等效于 *(a + 0)。因此,由于 a 是指向数组的指针,对 a 进行解引用会得到一个数组。由于每个元素为 4 字节,因此其大小为 8。如果将 a 定义为 int (*a)[3],则 sizeof(a[0]) 的结果为 12。
  6. 与第 5 条类似,a[1] 是一个大小为 2 的数组。因此,sizeof(a[1]) 的结果为 8,因为它是一个由大小为 4 的 2 个元素组成的数组。

以下是使用 a 的示例:

int (*a)[2];
int d[3][2];

a=d;
d[0][0]=1;
d[0][1]=2;
d[1][0]=3;
d[1][1]=4;
d[2][0]=5;
d[3][1]=6;

printf("a00=%d\n",a[0][0]);
printf("a01=%d\n",a[0][1]);
printf("a10=%d\n",a[1][0]);
printf("a11=%d\n",a[1][1]);
printf("a20=%d\n",a[2][0]);
printf("a21=%d\n",a[3][1]);

输出:

a00=1
a01=2
a10=3
a11=4
a20=5
a21=6

当将一个二维数组传递给函数时,您也可以使用这种方法:

void f(int (*a)[2]) 
{
    ...
}

int main()
{
    int x[3][2];
    f(x);
}

1
很棒的例子,谢谢。 - sjsam
1
我正要回答你的问题,突然发现你设置了悬赏。你想要什么?是想要更多的答案,让更多人知道这些答案,还是想要讨论已有的内容? - Harry
1
@Harry 更多关于意识的内容。有一些微妙的点可能不会立即显现出来。特别是,由于值恰好相同,我最初错过了第5和第6点中发生的情况,但在仔细检查后发现了它们。如果您认为自己有更多要补充的内容,请随时添加您自己的答案。 - dbush
1
@dbush:非常感谢您为此问题提供了悬赏金:D。事实上,我在长达六年的时间里重新回到了C编程,并且这里的根本问题只是关于“指向数组”的误解,正如第5点和第6点所表明的那样。再次感谢您为这个论坛树立了榜样。致敬! - sjsam
@SumitTrehan:实际上,这个答案和被采纳的那个一样好,但是另一个答案有一个小图表,让我更容易地理解概念。 - sjsam
如果您记住数组指针实际上是不同的东西,那么就没有什么量子力学粒子在您观察时会发生变化的神秘或混淆。 - M.M

2

这里是有关该主题的一些个人研究。 我在四个不同的环境中运行了您的测试代码,其中两个是64位的,另外两个是32位的。
我使用了三种不同的编译器:llvm、gcc和mipsPro cc。
以下是注释的结果比较

// 64-bit environment - all compilers
sizeof(int) :     4 -> Fact 1       -32 bit int -> 4 bytes   
sizeof(int*) :    8 -> Fact 2       -this and other pointers in a 64-bit system are 8-bytes long
sizeof(b) :      20 -> Case 1       -array of 5 32 bit ints -> 20 bytes
sizeof((int*)b) : 8 -> Case 2
sizeof(&b[0]) :   8 -> Case 3
sizeof(a) :       8 -> Case 4
sizeof(a[0]) :    8 -> Case 5       -array of two 4 byte ints
sizeof(a[1]) :    8 -> Case 6       -array of two 4 byte ints

// 32-bit environments - all compilers
sizeof(int) :     4 -> Fact 1       -32 bit int -> 4 bytes 
sizeof(int*) :    4 -> Fact 2       -this and other pointers in a 32-bit system are 4-bytes long
sizeof(b) :      20 -> Case 1       -array of 5 32 bit ints -> 20 bytes
sizeof((int*)b) : 4 -> Case 2
sizeof(&b[0]) :   4 -> Case 3
sizeof(a) :       4- > Case 4
sizeof(a[0]) :    8 -> Case 5       -array of two 4 byte ints
sizeof(a[1]) :    8 -> Case 6       -array of two 4 byte ints

解释 - 所有结果都与以下模式一致:

  • int 的大小取决于编译器,可能仍然是这样,据我所知在所有测试环境和编译器中均为4字节(事实1)。
  • 所有指针的大小默认为环境,即64位或32位(事实2,案例2、3、4)。
  • 两个四字节整数数组的大小等于2*sizeof(int)(案例5, 6)。
  • a[0]可以重写为*a; a[1]也可以写成*(a + 1)。以下SO post详细阐述了它。

    希望这能对您的主题有所贡献。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接