22

在 C++ 中,数组不能简单地作为参数传递。这意味着如果我创建一个这样的函数:

void doSomething(char charArray[])
{
    // if I want the array size
    int size = sizeof(charArray);
    // NO GOOD, will always get 4 (as in 4 bytes in the pointer)
}

我无法知道数组有多大,因为我只有一个指向数组的指针。

在不更改方法签名的情况下,我有哪种方式来获取数组的大小并迭代它的数据?


编辑:只是关于解决方案的补充。具体来说,如果 char 数组是这样初始化的:

char charArray[] = "i am a string";

然后\0已经附加到数组的末尾。在这种情况下,答案(标记为已接受)开箱即用,可以这么说。

4

12 回答 12

45

使用模板。这在技术上不符合您的标准,因为它会更改签名,但不需要修改调用代码。

void doSomething(char charArray[], size_t size)
{
   // do stuff here
}

template<size_t N>
inline void doSomething(char (&charArray)[N])
{
    doSomething(charArray, N);
}

Microsoft 的Secure CRT 函数和 STLSoft 的array_proxy类模板使用了这种技术。

于 2009-01-14T21:47:31.193 回答
26

不改签名?附加一个哨兵元素。特别是对于 char 数组,它可能'\0'是用于标准 C 字符串的空终止符。

void doSomething(char charArray[])
{
    char* p = charArray;
    for (; *p != '\0'; ++p)
    {
         // if '\0' happens to be valid data for your app, 
         // then you can (maybe) use some other value as
         // sentinel
    }
    int arraySize = p - charArray;

    // now we know the array size, so we can do some thing
}

当然,那么您的数组本身不能包含哨兵元素作为内容。对于其他类型的(即非字符)数组,它可以是任何不是合法数据的值。如果不存在这样的值,则此方法不起作用。

此外,这需要调用方的合作。您确实必须确保调用者保留一个arraySize + 1元素数组,并始终设置哨兵元素。

但是,如果您真的无法更改签名,那么您的选择就相当有限。

于 2009-01-14T21:25:27.743 回答
6

在数组的第一个元素中传递长度实际上曾经是一种非常常见的解决方案。这种结构通常被称为BSTR(用于“BASIC 字符串”),尽管这也表示不同(但相似)的类型。

与公认解决方案相比的优势在于,对于大字符串,使用哨兵确定长度的速度很慢。缺点显然是这是一个相当低级的黑客攻击,既不尊重类型也不尊重结构。

在下面给出的形式中,它也仅适用于长度 <= 255 的字符串。但是,这可以通过将长度存储在一个以上字节中来轻松扩展。

void doSomething(char* charArray)
{
    // Cast unnecessary but I prefer explicit type conversions.
    std::size_t length = static_cast<std::size_t>(static_cast<unsigned char>(charArray[0]));
    // … do something.
}
于 2009-01-14T22:03:35.017 回答
6

通常,在使用 C 或低级 C++ 时,您可能会考虑重新训练您的大脑,不要考虑将数组参数写入函数,因为 C 编译器无论如何都会将它们视为指针。本质上,通过键入这些方括号,您是在自欺欺人地认为正在传递一个真正的数组,其中包含大小信息。实际上,在 C 中您只能传递指针。功能

void foo(char a[])
{
    // Do something...
}

是,从 C 编译器的角度来看,完全等同于:

void foo(char * a)
{
    // Do something
}

显然,nekkid char 指针不包含长度信息。

如果您被困在角落并且无法更改函数签名,请考虑使用上面建议的长度前缀。一个不可移植但兼容的技巧是在位于数组之前的size_t 字段中指定数组长度,如下所示:

void foo(char * a)
{
    int cplusplus_len = reinterpret_cast<std::size_t *>(a)[-1];
    int c_len = ((size_t *)a)[-1];
}

显然,您的调用者需要在将它们传递给 foo 之前以适当的方式创建数组。

不用说这是一个可怕的 hack,但这个技巧可以在紧要关头摆脱麻烦。

于 2009-01-14T22:44:41.577 回答
4

如果它是空终止的, strlen() 会起作用。

于 2009-01-14T21:26:30.987 回答
2

您无法charArray单独确定大小。该信息不会自动传递给函数。

当然,如果它是一个以 null 结尾的字符串,您可以使用strlen(),但您可能已经考虑过了!

考虑传递一个std::vector<char>& 参数,或者一对指针,或者一个指针加上一个大小参数。

于 2009-01-14T21:26:36.430 回答
2

这实际上是 C 而非 C++,在 C++ 中您可能更愿意使用 std::vector。然而,在 C 中没有办法知道数组的大小。如果数组是在当前范围内声明的,编译将允许您执行 sizeof,并且仅当它使用大小显式声明时(编辑:和“具有大小”,我的意思是它要么是用整数大小声明的)或在声明时初始化,而不是作为参数传递,感谢您的反对)。

C 中常见的解决方案是传递第二个参数来描述数组中元素的数量。

编辑:
对不起,错过了不想更改方法签名的部分。然后除了其他人描述的那样没有解决方案,如果数组中有一些不允许的数据,它可以用作终止符(C字符串中的0,-1也很常见,但这取决于你的实际数据类型,假设 char 数组是假设的)

于 2009-01-14T21:28:44.163 回答
1

为了让函数知道传递给它的数组中的项目数,您必须执行以下两项操作之一:

  1. 传入一个大小参数
  2. 以某种方式将大小信息放入数组中。

您可以通过以下几种方式执行后者:

  • 用 NULL 或其他一些不会出现在正常数据中的标记来终止它。
  • 如果数组包含数字,则将项目计数存储在第一个条目中
  • 如果数组包含指针,则存储指向最后一个条目的指针
于 2009-01-14T21:34:12.703 回答
0

尝试使用 strlen(charArray); 使用 cstring 头文件。这将产生包括空格在内的字符数,直到它到达结尾“。

于 2014-06-07T06:03:57.450 回答
-1

保证您在 32 位 PC 中收到 4,这是正确的答案。因为这里这里解释的原因。简短的回答是,您实际上是在测试指针而不是数组的大小,因为“数组被隐式转换或衰减为指针。唉,指针不存储数组的维度;它不甚至告诉你有问题的变量是一个数组。”

现在您正在使用 C++,boost::array是比原始数组更好的选择。因为它是一个对象,所以您现在不会丢失维度信息。

于 2009-03-18T08:52:22.907 回答
-2

我认为你可以这样做:

size_t size = sizeof(array)/sizeof(array[0]);

PS:我觉得这个题目的标题也不对。

于 2012-11-19T17:49:18.153 回答
-3

伙计,您可以有一个全局变量来存储数组的大小,该数组可以在整个程序中访问。至少您可以将数组的大小从 main() 函数传递给全局变量,并且您甚至不必更改方法签名,因为该大小将在全局范围内可用。

请看例子:

#include<...>
using namespace std;

int size; //global variable

//your code

void doSomething(char charArray[])
{
    //size available

}
于 2013-01-16T08:36:22.510 回答