我可以在不必硬编码的情况下用C++确定数组的大小/长度吗

Can I determine the size/length of an array in C++ without having to hardcode it?

本文关键字:数组 C++ 不必 编码 情况下 我可以      更新时间:2023-10-16

我基本上是在寻找某种将数组的大小/长度传递给函数的"动态"方式。


我试过:

void printArray(int arrayName[])
{
    for(int i = 0 ; i < sizeof(arrayName); ++i)
    {
        cout << arrayName[i] << ' ';
    }
}

但我意识到它只考虑字节大小,而不考虑数组中有多少元素。


还有:

void printArray(int *arrayName)
{
    while (*arrayName)
    {
        cout << *arrayName << ' ';
        *arrayName++;    
    }
}

这至少打印了我所有的东西,但比我预期的要多,所以它实际上并没有按照我想要的方式工作。我想这是因为我没有确切地告诉它我需要它有多大,所以它"安全"地向我抛出一些大的大小,并最终在数组中的最后一个元素之后开始打印非常奇怪的整数。


所以我终于完成了这项工作,但我相信还有更好的东西!:

void printArray(int *arrayName)
{
    while (*arrayName)
    {
        if (*arrayName == -858993460)
        {
            break;
        }
        cout << *arrayName << ' ';
        *arrayName++;
    }
    cout << 'n';
}

在运行了几次程序后,我意识到我输入的数组的最后一个元素之后的值总是:-858993460,所以一旦遇到这个值,我就让它中断while循环。


include <iostream>
include <conio.h>
using namespace std;
    // functions prototypes
void printArray (int arrayName[], int lengthArray);
    // global variables
    //main
int main ()
{
    int firstArray[] = {5, 10, 15};
    int secondArray[] = {2, 4, 6, 8, 10};
    printArray (firstArray,3);
    printArray (secondArray,5);
    // end of program
    _getch();
    return 0;
}
    // functions definitions
void printArray(int arrayName[], int lengthArray) 
{
    for (int i=0; i<lengthArray; i++)
    {
        cout << arrayName[i] << " ";
    }
    cout << "n";
}

非常感谢。

TL;DR答案:使用std::vector


但我意识到它[sizeof()]只考虑它的字节大小,而不考虑数组上有多少元素。

这本身并不是问题:您仍然可以使用sizeof(array) / sizeof(array[0])来获取数组的大小,但问题是,当传递给函数时,数组会衰减为指向其第一个元素的指针,因此您只能获得sizeof(T *)(t是数组中元素的类型(。

关于*arrayName++:

这至少打印了我所有的东西,但超出了我的预期

我甚至不明白是什么启发你用这种方式计算数组的大小。这段代码所做的只是递增数组中的第一个对象,直到它为零。

在运行程序几次后,我意识到我输入的数组的最后一个元素之后的值总是:-858993460

这是一个可怕的假设,它也依赖于未定义的行为。你不能真正确定数组的第一个元素之后的内存中有什么,你甚至不应该访问它


基本上,在C++中,如果你想知道函数中原始数组的大小,那么你必须手动跟踪它(例如,添加一个额外的size_t size参数(,因为数组传递给函数的方式(记住,它们"衰减"为指针(。如果你想要更灵活的东西,可以考虑使用C++标准库中的std::vector<int>(或者你想存储的任何类型的对象(——它有一个size()方法,它可以完全满足你的需求。

第一次尝试

当数组被传递到函数中时,它们会衰减为指针。通常,在数组上使用sizeof会给出其字节大小,然后除以每个元素的字节大小,得到元素的数量。但现在,由于您有一个指针而不是数组,因此调用sizeof只会给出指针的大小(通常为4或8字节(,而不是数组本身,这就是失败的原因。

第2次尝试

本例中的while循环假设您的数组以零结尾,这非常糟糕(除非您真的像以null结尾的字符串那样使用零作为终止符(。如果您的数组没有以零结尾,那么您可能正在访问非您的内存,从而调用未定义的行为。另一件可能发生的事情是,数组中间有一个零元素,然后只打印前几个元素。

第3次尝试

您在数组末尾发现的这个特殊值可以随时更改。这个值恰好在此时存在,下次可能会有所不同,所以像这样硬编码是非常危险的,因为你最终可能会访问不属于你的内存。

您的最终代码

这段代码是正确的,通常会将数组的长度与数组本身一起传递(尤其是在用C编写的API中(。只要您没有传递实际大于数组实际长度的长度,此代码就不会引起任何问题,而且这种情况有时会发生,因此也很容易出错。

另一种解决方案

另一种解决方案是使用std::vector,这是一个容器,它在跟踪其大小的同时,还允许您添加任意多的元素,即在运行时不需要知道大小。所以你可以这样做:

#include <iostream>
#include <vector>
#include <cstddef>
void print_vec(const std::vector<int>& v)
{
    std::size_t len = v.size();
    for (std::size_t i = 0; i < len; ++i)
    {
        std::cout << v[i] << std::endl;
    }
}
int main()
{
    std::vector<int> elements;
    elements.push_back(5);
    elements.push_back(4);
    elements.push_back(3);
    elements.push_back(2);
    elements.push_back(1);
    print_vec(elements);
    return 0;
}

值得一看的有用链接

未定义的行为:未定义、未指定和实现定义的行为

数组衰减:什么是数组衰减?

std::矢量:http://en.cppreference.com/w/cpp/container/vector

正如所有其他答案所说,您应该使用std::vector,或者像您已经做过的那样,将数组的元素数传递给打印函数。

另一种方法是在数组的末尾放置一个sentinel元素(您确信它不会在数组中的值(。在打印功能中,您可以循环浏览元素,当您找到哨兵时,您可以停止。

一个可能的解决方案:您可以使用模板来推导数组长度:

template <typename T, int N>
int array_length(T (&array)[N]) {
    return N;
}

请注意,在数组衰减为指针之前,您必须执行操作,但您可以直接或在包装器中使用该技术。

例如,如果您不介意滚动自己的数组包装器:

template <typename T>
struct array {
    T *a_;
    int n_;
    template <int N> array(T (&a)[N]) : a_(a), n_(N) {}
};

你可以这样做:

void printArray(array<int> a)
{
    for (int i = 0 ; i < a.n_; ++i)
        cout << a.a_[i] << ' ';
}

并称之为

int firstArray[] = {5, 10, 15};
int secondArray[] = {2, 4, 6, 8, 10};
printArray (firstArray);
printArray (secondArray);

关键是模板化构造函数不是explicit,因此您的数组可以转换为实例,在衰减为指针之前捕获大小

注:。所示的包装器不适合拥有动态大小的数组,只适合方便地处理静态大小的数组。为了简洁起见,它还缺少各种运算符和一个默认构造函数。通常,对于一般用途,优选std::vectorstd::array


。。。OP自己的尝试在其他地方得到了完全解决

使用-858993460值是非常不可靠的,事实上也是不正确的。

可以通过两种方式传递数组的长度:向函数传递一个额外的参数(比如size_t length(,或者在数组末尾放置一个特殊值。第一种方式是首选的,但第二种方式用于例如通过char*传递字符串。

在C/C++中,不可能在运行时知道数组的大小。如果需要的话,可以考虑使用std::vector类,它还有其他优点。

当您将数组的长度传递给printArray时,您可以使用sizeof(array) / sizeof(array[0]),也就是说,整个数组的字节大小除以单个元素的字节大小,即可获得数组本身的元素大小。

更重要的是,在C++中,你可能会发现学习std::vectorstd::array对你有利,并且更喜欢它们而不是原始数组——当然,除非你正在做一项要求你学习原始数组的家庭作业。size()成员函数将为您提供向量中元素的数量。

在C/C++中,本机数组一传递给函数就降级为指针。因此,必须将"length"参数作为函数的参数传递。

C++提供了std::vector集合类。确保在将其传递给函数时,通过引用或指针传递(以避免在传递时复制数组(。

#include <vector>
#include <string>
void printArray(std::vector<std::string> &arrayName)
{
    size_t length = arrayName.size();
    for(size_t i = 0 ; i < length; ++i)
    {
        cout << arrayName[i] << ' ';
    }
}
int main()
{
    std::vector<std::string> arrayOfNames;
    arrayOfNames.push_back(std::string("Stack"));
    arrayOfNames.push_back(std::string("Overflow"));
    printArray(arrayOfNames);
    ...
}