C++静态数组与指针:为何这段代码能获取长度?另一段却失效?
关于数组长度计算的两个问题解答
一、为什么*(&A+1)-A能得到数组长度5?
咱们把这段代码的每个操作拆解开,就能明白背后的逻辑:
- 首先,
A是固定大小的数组,实际类型是double[5](因为初始化了5个double元素)。 &A取的是整个数组的地址,它的类型是double(*)[5]——简单说就是“指向包含5个double的数组的指针”。- 执行
&A + 1时,C++的指针算术会根据指针类型计算偏移量:这里指针指向的是5元素的double数组,所以加1会直接跳过整个数组的大小(比如5*8=40字节,假设double占8字节),最终指向数组A末尾之后的第一个内存位置。 *(&A+1)对这个指针解引用,得到的是double[5]类型的数组,但在表达式中数组会自动转换成指向其第一个元素的指针(double*类型),这个指针正好指向A最后一个元素的下一个位置(可以理解为A[5]的位置,C++允许指针指向这里,即使该位置不属于数组)。- 而
A本身在表达式里也会转换成指向A[0]的double*指针。 - 最后,两个同类型指针相减,结果是它们之间相差的元素个数(不是字节数)。
*(&A+1)指向数组末尾后,A指向起始位置,相减自然得到5,也就是数组的元素个数。
至于&A和&A[0]输出结果一致,是因为它们指向的是同一个内存地址(数组的起始位置),但类型完全不同:&A是double(*)[5],&A[0]是double*,只是打印地址值时显示的是同一个数值而已。
二、为什么void test(double B[])里的*(&B+1)...无法运行?
这里的核心坑点是:函数参数中的数组类型会被自动转换成指针类型。你写的void test(double B[])其实和void test(double* B)完全等价——B根本不是数组,而是一个普通的指针变量。
拆解问题原因:
B的类型是double*,不是数组类型。&B取的是这个指针变量本身的地址,类型是double**(指向double指针的指针)。&B + 1会偏移一个double*的大小(比如64位系统是8字节),指向的是B这个变量在栈上后面的内存位置,和传入的原数组没有任何关系。*(&B+1)是一个double*,但它指向的是无效内存区域,用它和B相减得到的结果完全没有意义,甚至会触发未定义行为(程序崩溃、输出垃圾值都有可能)。
修复方法
要在函数里正确获取数组长度,有几种常用方案:
方案1:手动传递长度参数
这是最直接的方式,调用函数时把数组长度一起传进去:
#include <iostream> using namespace std; void test(double B[], int len) { cout << "The array has " << len << " elements." << endl; } int main() { double A[] = {2.4, 1.2, 4.6, 3.04, 5.7}; int len = sizeof(A)/sizeof(A[0]); // 先计算长度 test(A, len); return 0; }
方案2:用模板推导数组大小
利用C++模板自动推导固定大小数组的类型,直接获取长度:
#include <iostream> using namespace std; template <size_t N> void test(double (&B)[N]) { // N就是数组的元素个数 cout << "The array has " << N << " elements." << endl; } int main() { double A[] = {2.4, 1.2, 4.6, 3.04, 5.7}; test(A); // 模板会自动推导N=5 return 0; }
方案3:改用标准库容器
如果可以的话,直接用std::vector代替原生数组,它自带size()方法获取元素个数,更安全方便:
#include <iostream> #include <vector> using namespace std; void test(const vector<double>& B) { cout << "The vector has " << B.size() << " elements." << endl; } int main() { vector<double> A = {2.4, 1.2, 4.6, 3.04, 5.7}; test(A); return 0; }
内容的提问来源于stack exchange,提问作者user3112666
相关产品推荐
相关产品推荐

