C++中向接收指针的函数传数组或数组地址为何效果一致?
两种数组传参表现一致的核心原因
你遇到的现象来自C++继承自C的数组隐式转换规则,不存在编译器异常,你对数组和指针的关联规则确实存在一个认知盲区。
你的成员函数定义如下:
unsigned int someclass::foo(const void* data)
const void*类型可以接收任意对象指针类型的隐式转换,只要传入的是对象的内存地址,都可以合法传参。
两种传参的底层逻辑
- 第一种调用写法
foo(someData):
当数组名单独出现在表达式中,且不是sizeof、取地址&、类型对齐运算符的操作数时,会隐式退化为指向数组首元素的指针。你的someData原本类型是float[2](长度为2的float数组),退化后得到float*类型的指针,值就是数组第一个元素的内存首地址,可以正常隐式转换为const void*,调用合法。 - 第二种调用写法
foo(&someData):
当数组名作为取地址&运算符的操作数时,不会发生退化,此时拿到的是指向整个数组的指针,类型为float (*)[2](指向“包含2个float元素的数组”的指针)。这里的关键是:整个数组的内存起始地址,和数组第一个元素的内存起始地址是完全相同的,所以这个指针的数值和第一种写法得到的首元素指针数值完全一致,同样可以隐式转换为const void*,传入函数的地址值没有任何区别,运行表现自然完全一致。
两者的核心差异(为什么平时不建议混用)
两种写法传进去的地址值相同,但指针类型完全不同,只是void*会丢失所有类型信息,才让你觉得两者没有区别:
你可以做个简单测试:如果把
foo的参数类型改成float*,foo(&someData)会直接编译报错,因为float(*)[2]无法隐式转换为float*;如果把参数类型改成float (*)[2],foo(someData)同样会编译报错。只有参数是无类型指针void*时,两者才会因为地址值一致表现相同。
两种指针的步长差异非常明显:
- 对首元素指针
float* p = someData执行p+1,指针会向后偏移1个float的长度(通常为4字节),指向数组的第二个元素 - 对全数组指针
float (*p)[2] = &someData执行p+1,指针会向后偏移整个float[2]数组的长度(通常为8字节),直接跳过当前整个数组
常见认知误区
- 数组本身不是指针:数组是连续存储的同类型元素集合,只有在特定表达式语境下才会退化为首元素指针,不存在“数组名就是常量指针”的绝对规则
- 地址值相同不代表指针类型相同:指针的类型决定了解引用、偏移运算的行为,不能只通过地址值判断两个指针是否等价
内容的提问来源于stack exchange,提问作者MattK
相关产品推荐
相关产品推荐

