我是一名新手程序员,试图在夏季学期开始前在某些课程上抢先一步,并且在尝试用C ++创建Quick Union算法时遇到了这个问题。
我一直试图弄清楚为什么我的程序会创建两个相同的数组,尽管有两个独立的for循环用于创建两个不同的数组。每当我的程序运行完成并打印id []和sz []时,它总是输出1作为两个数组中每个索引的元素。
class quickUnionUF{
private:
int id[];
int sz[];
int root(int);
public:
quickUnionUF(int, int);
bool connected(int, int);
void unionPoint(int, int);
void print();
};
quickUnionUF::quickUnionUF(int n, int b){
id[n];
sz[b];
for(int i=0;i<n;i++){
id[i] = i;
}
for(int j=0;j<b;j++){
sz[j] = 1;
}
}
例如,如果我创建quickUnionUF(5,5);
id []现在应该包含元素:
0,1,2,3,4
并且sz []包含元素:
1,1,1,1,1
但是,程序使用元素
创建一个数组sz []和数组id []1,1,1,1,1
有关为何发生这种情况的任何想法?
答案 0 :(得分:4)
标准C ++没有无大小的数组成员。
在C ++中使用std::vector<int>
作为动态大小的数组。
#include <vector>
class quickUnionUF{
private:
std::vector<int> id;
std::vector<int> sz;
int root(int);
public:
quickUnionUF(int, int);
bool connected(int, int);
void unionPoint(int, int);
void print();
};
quickUnionUF::quickUnionUF(int n, int b)
: id(n)
, sz(b)
{
for(int i=0;i<n;i++){
id[i] = i;
}
for(int j=0;j<b;j++){
sz[j] = 1;
}
}
答案 1 :(得分:3)
您的代码暗示了两个非常重要的错误:
C ++不像Java那样工作。 int id[]
不对垃圾收集堆上任意大小的数组的引用。它是一个未定义大小的成员数组,用于实现动态数组(和类似功能)in C99。你不应该使用这种语法,除非你确切地知道你在做什么,因为它几乎可以保证是错误的。
id[n]
根本不分配数组。相反,它只是索引id
并丢弃结果。
首先,您的代码不应该编译,因为只有结构的最后一个成员可能是灵活的数组类型。实际上铿锵叫道:
main.cpp:53:9: error: field has incomplete type 'int []'
int id[];
MSVC嚎叫:
1>main.cpp(54): error C2229: class 'quickUnionUF' has an illegal zero-sized array
g ++只会警告(好吧,g ++在它接受的内容中很奇怪):
main.cpp:53:12: warning: ISO C++ forbids zero-size array ‘id’ [-Werror=pedantic]
int id[];
注意:即使允许灵活的数组成员,g ++ 在编译此时也是错误的。这在C996.7.2.1§16和C116.7.2.1§18中定义,两者都以(重点是我的)开头:
作为一种特殊情况,具有多个命名成员的结构的 last 元素可以 有一个不完整的数组类型;这被称为灵活的阵列成员。 [...]
好吧,假设您无论如何都要编译代码,它基本上意味着以下内容:
创建一个整数对齐的对象,但根本没有元素。看看以下测试程序:
quickUnionUF q;
::std::cout << sizeof(quickUnionUF) << "\n";
::std::cout << &q << "\n" << &q.id[0] << "\n" << &q.sz[0] << "\n";
根本设法编译它的唯一编译器(gcc 4.9.0)给出了以下结果:
0
0x7fff1bf6274c
0x7fff1bf6274c
0x7fff1bf6274c
所以,这是一个零字节对象(是的,这是非法的C ++,因为每个C ++对象的大小都是> 0),并且每个数组的第一个元素位于相同的位置(OUTSIDE YOUR OBJECT!)。请记住,您声明id
和sz
要零元素!
因此,您正在写相同的任意位置。您可以将此视为缓冲区溢出的极端情况:通过将5个整数写入零大小的缓冲区,您将从第一个零大小缓冲区溢出,再通过第二个零大小缓冲区溢出到完全不受您控制的内存中。
这也解释了你观察到的结果:第二个循环只是覆盖了第一个循环(而仍然通过破坏你的堆栈来完成它)。
只需使用vector
即可。您可以告诉它how big you want it,当您索引某个不属于您的位置时,您可以ask it to tell you。