You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C++函数输出异常值:子类数组传入父类指针后的奇怪输出

为什么a[i].a会访问到B的b成员?

这是典型的指针类型不匹配导致的内存访问错位问题,本质是你把B类型的数组传递给了期望A类型指针的函数,从而触发了未定义行为(UB),我们一步步拆解来理解:

1. 类的内存布局

先看你的类定义:

class A { public: int a; public: A():a(1){}; }; 
class B:public A { public: int b; public: B():b(2){}; }; 

假设你的系统中int占4字节:

  • A类只有一个int成员,所以sizeof(A) = 4,每个A对象在内存中就是一个4字节的整数。
  • B类是A的公有派生类,继承了A的a,再加上自己的b,所以sizeof(B) = 8。每个B对象的内存布局是:先放A的a(前4字节),再放自己的b(后4字节),也就是每个B对象看起来像[a=1][b=2]。

2. 数组传递与指针步长的问题

当你在main中调用print(b)时,数组b会退化为指针,但这里有个关键:b是B类型的数组,退化后的指针是B*,但print函数接收的是A*类型的参数,C++允许这种隐式转换(因为B是A的派生类)。

问题出在print函数的循环里:

for(int i=0; i<10; ++i) { cout<<a[i].a<<" "; }

这里的a[i]等价于*(a + i),而指针加法的步长是由指针的类型决定的:a是A*类型,所以每次i增加1,指针会跳过sizeof(A)(4字节)。

但实际数组里的元素是B类型,每个元素占8字节。所以:

  • 当i=0:a+0指向第一个B对象的a,输出1,没问题。
  • 当i=1:a+1跳过4字节,指向的是第一个B对象的b成员的地址!此时代码把这个4字节的内存当成A对象的a成员来读取,而b的值是2,所以输出2。
  • 当i=2:a+2跳过8字节,刚好指向第二个B对象的a,输出1。
  • 以此类推,就出现了你看到的1 2 1 2 ...的循环输出。

3. 这是未定义行为!

虽然你看到了一个看似有规律的输出,但这种操作属于C++中的未定义行为——标准没有规定这种情况下程序的输出,不同编译器、不同平台可能会有不同的结果,甚至崩溃。

如何修复?

如果你想正确遍历B数组,应该让print函数接收B*类型的参数:

void print(B* b) { 
    for(int i=0; i<10; ++i) { 
        cout<<b[i].a<<" "; 
    } 
}

或者用模板来支持任意类型的数组,更灵活:

template<typename T>
void print(T* arr, int size) { 
    for(int i=0; i<size; ++i) { 
        cout<<arr[i].a<<" "; 
    } 
}
// 调用时:print(b, 10);

内容的提问来源于stack exchange,提问作者Eduard Rostomyan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.25 06:57:47