如何可靠地获取 C 样式数组的大小?

Posted

技术标签:

【中文标题】如何可靠地获取 C 样式数组的大小?【英文标题】:How to reliably get size of C-style array? 【发布时间】:2010-03-08 20:40:58 【问题描述】:

经常推荐的方法好像是使用sizeof,但是在foo函数里面不行,这里传入的是x

#include <iostream>

void foo(int x[]) 
  std::cerr << (sizeof(x) / sizeof(int)); // 2  


int main()
    int x[] = 1,2,3,4,5;
    std::cerr << (sizeof(x) / sizeof(int)); // 5                              
    foo(x);
    return 0;

this question 的答案推荐sizeof,但他们并没有说如果您传递数组,它(显然?)不起作用。那么,我是否必须改用哨兵? (我不认为我的foo 函数的用户总是可以信任在最后放置一个哨兵。当然,我可以使用std::vector,但是我没有得到很好的速记语法1,2,3,4,5 .)

【问题讨论】:

***.com/questions/1810083/… 如果你的函数的用户没有按照文档告诉他们的去做,那是他们的问题,而不是你的问题。无法防止无能的人滥用 API。 x in foo 是一个指针,而不是一个数组。请参阅***.com/questions/232691/…、***.com/questions/1975128/…。另外,重复:***.com/questions/37538/… @Neil Butterworth:这就是 private 修饰符的作用。 【参考方案1】:

在 C 中,C 中的数组参数实际上只是指针,因此 sizeof() 将不起作用。您要么需要将尺寸作为另一个参数传递,要么使用哨兵 - 以最适合您的设计为准。

其他一些选项:

其他一些信息:

对于 C++,您可能希望参数使用将数组包装在类模板中的东西,而不是传递原始数组指针,该类模板跟踪数组大小并提供将数据复制到数组中的方法以安全的方式。 STLSoft's array_proxy template 或 Boost's boost::array 之类的东西可能会有所帮助。我之前使用过array_proxy 模板效果很好。在使用参数的函数内部,您会得到类似std::vector 的操作,但函数的调用者可以使用简单的 C 数组。没有数组的复制——array_proxy 模板几乎自动地打包了数组指针和数组的大小。

在 C 中用于确定数组中元素数量的宏(当 sizeof() 可能有所帮助时 - 即,您不是在处理简单的指针):Is there a standard function in C that would return the length of an array?

【讨论】:

【参考方案2】:

GNU Libstdc++ 文档中提到的common idiom 是lengthof 函数:

template<typename T, unsigned int sz>
inline unsigned int lengthof(T (&)[sz])  return sz; 

你可以把它当作

int x[] = 1,2,3,4,5;
std::cerr << lengthof(x) << std::endl;

警告:这仅在数组没有decayed into a pointer 时有效。

【讨论】:

【参考方案3】:

这个怎么样?..

template <int N>
void foo(int (&x)[N]) 
    std::cerr << N;

【讨论】:

@Brian:这可能是一个坏主意,也可能不是一个坏主意,具体取决于上下文。为什么你觉得它很可怕?这是一个好主意,因为如果您传入一个指针,它将不会编译。如果函数很大并且使用许多不同的数组大小调用,那就不好了 - 它会生成一堆函数。另一种方法可能是让这个 foo 然后转身并调用私有/内部 foo_internal(int *x, size_t length);这取决于使用情况。我发现这种技术对字符串函数很有用,如果使用不当,这些函数往往会溢出。没有解释的评论是一个可怕的想法。【参考方案4】:

您可以传递大小,使用哨兵,甚至更好地使用 std::vector。即使 std::vector 缺少初始化列表,用一组元素构造一个向量仍然很容易(虽然不是很好)

static const int arr[] = 1,2,3,4,5;
vector<int> vec (arr, arr + sizeof(arr) / sizeof(arr[0]) );

std::vector 类也使犯错变得更加困难,这值得它的黄金重量。另一个好处是所有 C++ 都应该熟悉它,并且大多数 C++ 应用程序应该使用 std::vector 而不是原始 C 数组。

作为一个简短的说明,C++0x 添加了Initializer lists

std::vector<int> v = 1, 2, 3, 4;

您也可以使用Boost.Assign 来做同样的事情,尽管语法有点复杂。

std::vector<int> v = boost::assign::list_of(1)(2)(3)(4);

std::vector<int> v;
v += 1, 2, 3, 4;

【讨论】:

【参考方案5】:

c 对此不提供本机支持。一旦数组超出其声明的范围,它的大小就会丢失。

可以通过数组传递大小。如果您始终保持大小,您甚至可以将它们捆绑到一个结构中,尽管这样做会产生一些记账开销。

【讨论】:

【参考方案6】:

我也同意上面Corwin的方法很好。

template <int N>
void foo(int (&x)[N]) 

    std::cerr << N;

我认为没有人给出一个很好的理由说明这不是一个好主意。 例如,在 java 中,我们可以这样写:

int numbers [] = 1, 2, 3, 4;
for(int i = 0; i < numbers.length(); i++)

   System.out.println(numbers[i]+"\n");

在 C++ 中,它会更好,而不是说

int numbers [] = 1, 2, 3, 4;
int size = sizeof(numbers)/sizeof(int);
for(int i = 0; i < size; i++)

    cout << numbers[i] << endl;

我们可以更进一步,继续前进

template <int N>
int size(int (&X)[N])

   return N;

或者如果这会导致问题,我想你可以明确地写:

template < int N >
int size(int (&X)[N])

   int value = (sizeof(X)/sizeof(X[0]));
   return value;

然后我们只需要进入 main:

int numbers [] = 1, 2, 3, 4;
for(int i = 0; i < size(numbers); i++)

   cout << numbers[i] << endl;

对我来说很有意义:-)

【讨论】:

【参考方案7】:

数组表达式的类型将从“T 的 N 元素数组”隐式转换为“指向 T 的指针”,其值将是数组中第一个元素的地址,除非数组表达式是sizeof 或地址 (&amp;) 运算符,或者如果数组表达式是用于在声明中初始化另一个数组的字符串文字。简而言之,你不能将数组传递给函数作为数组;函数接收的是指针值,而不是数组值。

您必须将数组大小作为单独的参数传递。

由于您使用的是 C++,因此请使用向量(或其他一些合适的 STL 容器)而不是 C 样式的数组。是的,你失去了方便的速记语法,但权衡是值得的。严重地。

【讨论】:

【参考方案8】:

现在,您可以使用 C++11 的 extent 和 rank。

举例:

#include <iostream>
#include <type_traits>

int main()

  int a[][3] = 1, 2, 3, 4, 5, 6;

  std::cout << "\nRank:           : " << std::rank<decltype(a)>::value;
  std::cout << "\nSize: [_here_][]: " << std::extent<decltype(a), 0>::value;
  std::cout << "\nSize: [][_here_]: " << std::extent<decltype(a), 1>::value;
  std::cout << "\nSize: [][]_here_: " << std::extent<decltype(a), 2>::value;

打印:

Rank:           : 2
Size: [_here_][]: 2
Size: [][_here_]: 3
Size: [][]_here_: 0

【讨论】:

【参考方案9】:

您需要将大小与数组一起传递,就像在许多库函数中所做的那样,例如strncpy()strncmp() 等。抱歉,这只是它在 C 中的工作方式:-)。

或者,您可以推出自己的结构,例如:

struct array 
    int* data;
    int size;
;

并将其传递给您的代码。

当然,如果你想要更多 C++ 风格,你仍然可以使用 std::liststd::vector

【讨论】:

【参考方案10】:

从c++11开始,有一个很方便的方法:

static const int array[] =  1, 2, 3, 6 ;
int size = (int)std::distance(std::begin(array), std::end(array))+1;

【讨论】:

不需要“+1”。

以上是关于如何可靠地获取 C 样式数组的大小?的主要内容,如果未能解决你的问题,请参考以下文章

如何使用滚动条可靠地获取屏幕宽度

如何在 iPhone 上可靠地获取位置

如何在javascript中可靠地获取没有后缀的文件名?

如果应用程序在 GC 中,如何可靠地获取 JVM 核心转储?

Git 'pre-receive' 钩子和 'git-clang-format' 脚本可靠地拒绝违反代码样式约定的推送

如何在 C 预处理器中可靠地检测 Mac OS X、iOS、Linux、Windows? [复制]