如何在 C 中获取数组参数的实际大小?

How to get the real size of an array parameter in C?

编辑: 虽然有人认为这个问题与他们发布 link 的问题相同。这是一个非常不同的问题。

考虑以下因素:

#define HUNDRED 100
typedef WCHAR BUFFER_SPACE[HUNDRED]; 

类型BUFFER_SPACE是一个大小为200字节的数组(假设UTF16)

在大多数情况下,声明一个变量如:

BUFFER_SPACE abuffer         = { 0 };

wprintf(L"sizeof abuffer        : %zd\n", sizeof(abuffer));

将导致 wprintf 输出 200(如预期的那样。)

现在,考虑以下简单的示例程序:

#include "stdafx.h"
#include <Windows.h>
#include <WinNt.h>

#define HUNDRED 100
typedef WCHAR BUFFER_SPACE[HUNDRED]; 

void Function(BUFFER_SPACE abuffer)
{
  BUFFER_SPACE anotherbuffer = { 0 };

  wprintf(L"sizeof  abuffer      : %zd\n", sizeof(abuffer));         // 8
  wprintf(L"sizeof *abuffer      : %zd\n", sizeof(*abuffer));        // 2
  wprintf(L"sizeof anotherbuffer : %zd\n", sizeof(anotherbuffer));   // 200

  // a highly undesirable solution is to apply sizeof to the type but, if the
  // type of the parameter changes, the programmer has to be aware that the
  // original/old type is being used in sizeof() someplace else in the
  // function/program

  wprintf(L"sizeof BUFFER_SPACE  : %zd\n", sizeof(BUFFER_SPACE));  // 200

  getchar();
}

int main()
{
  BUFFER_SPACE abuffer         = { 0 };

  WCHAR anotherbuffer[HUNDRED] = { 0 };

  wprintf(L"sizeof abuffer        : %zd\n", sizeof(abuffer));
  wprintf(L"sizeof anotherbuffer  : %zd\n", sizeof(anotherbuffer));
  wprintf(L"\n");

  Function(abuffer);

  return 0;
}

我想获取函数中缓冲区参数的整个大小。使用 VC++ 并针对 64 位进行编译,

sizeof(abuffer) 是 8,这是有道理的,因为它是指向数组的指针

sizeof(*abuffer) 是 2,我认为这很值得怀疑,但我不想讨论它。

sizeof(BUFFER_SPACE) 应该是 200。

我的问题是:有没有办法从参数(在本例中为 abuffer)获取 200 的值,或者使用类型是获取它的唯一方法?

让我们先看看数组是如何传递给函数的 -

引用 C11,章节 §6.7.6.3p7

A declaration of a parameter as "array of type" shall be adjusted to "qualified pointer to type", where the type qualifiers (if any) are those specified within the [ and ] of the array type derivation. ...

这意味着您的函数原型实际上变成了 -

void Function(WCHAR *abuffer);

在这个函数体中,编译器不再有类型信息。它可能是 WCHAR[100]WCHAR[200] 甚至 WCHAR[]

我想您已经明白为什么 sizeof(abuffer)8 -- 因为它是一个指针。

现在 sizeof(*abuffer)abuffer 的类型是 WCHAR*,因此 *abuffer 的类型将是 WCHAR,因此 sizeof(*abuffer) == sizeof(WCHAR) == 2.

为了回答你的主要问题,有没有办法让函数知道缓冲区的大小,有两种方法 -

  1. 您将信息作为第二个参数传递。但这不是必需的,因为大小将始终为 sizeof(BUFFER_SPACE)
  2. 您将指针传递给 BUFFER_SPACE 而不是 BUFFER_SPACE。您可以将原型更改为 -

    void Function(BUFFER_SPACE *abuffer);
    

    并在所有地方使用 *abuffer 而不是 abuffersizeof(*abuffer) 现在也会 return 200,因为正确地保留了类型信息。

要完成 :在您的特定情况下,您知道数组的大小(实际上是维度),并且您可以使用预处理器常量 HUNDRED.

但通常(尤其是指向堆分配数组的指针)没有自动获取它们大小的方法,你应该采用一些约定关于数组大小。一个非常常见的方法是将数组的维度作为另一个参数传递给函数。一个典型的例子就是fwrite standard function, or the qsort one, or the snprintf

您也可以考虑使用具有 flexible array members 的结构,约定一些固定字段给出该灵活数组成员的大小。

然后你可以考虑一些abstract data type. I gave a simple Matrix example 。但是你仍然需要约定。

请注意,C 是一种低级语言,coding conventions 非常重要(特别是当您处理指向堆分配的指针时 -malloc-ed 或 calloc- ed- 数据:你应该定义和记录一些约定,解释这些指针应该是谁以及如何free-d,以及指向数据的大小是多少)。

请记住,在 C 数组中,数组会衰减为指针(例如,当您将它们作为参数传递给您的 Function 时),并且 sizeof 某些指针始终是固定的(在我的 x86-64 上Linux 系统它总是 8) 并且不依赖于指向的内存区域(以及它分配的大小,如果有的话)。

is there a way to get the value of 200 from the parameter

没有,一般不会。你需要约定来处理这个问题。您经常将维度作为一些附加参数传递(例如,即使您的 main 也应该同时具有 argcargv)。有时它以不同的方式为人所知(通过某些结构中的某些字段、某些全局变量等...)。