离开循环时向量数据丢失
Vector Data Loss When Leaving A Loop
我不认为自己是编程新手,但在六个月的大部分时间里我一直在努力理解向量。
我曾尝试在 "production" 代码中使用向量,但它似乎从来没有像我期望的那样运行。这个例子是我遇到的许多问题之一,正如标题中所述,Vectors 似乎在离开 while 循环后神秘地丢失了数据。这可能归结为我完全错误地使用了向量,但我想我的过程至少在某种程度上是合理的。您会注意到,这是一个 objects 的向量,这可能也是问题的关键。
在我做的这个例子中,一个带有随机字符的字符指针被添加到向量中,恰好十次。每次都擦除字符指针的内容并添加新内容以确保数据唯一。离开循环并开始另一个打印出向量的内容后,刚刚添加到向量的数据的 none 已经存在了。为什么?
此外,向量的初始化是 non-standard 我见过的例子,但似乎是让向量在没有崩溃的情况下工作的唯一方法。
为我糟糕的格式道歉,code-block 中的样式与我格式化代码的方式不完全相同(括号间距稍微搞砸了)提前致谢!
#include <stdio.h>
#include <vector>
#include <stdlib.h>
int main( int argc, char * argv[ ] ) {
std::vector<char *> avector( 0 );
avector.reserve( 400 );
int counter = 0;
char * place;
bool ok;
Back:
if( counter == 10 ) {
counter = 0;
while( counter != 10 ){
printf( "Element %d: %s\n", counter, avector.at( counter ) );
++counter
}
ok = false;
}
while( counter != 10 ) {
ok = true;
place = (char *)malloc( 10 * sizeof( char ) );
*place = (char)( ( rand( ) % 26 ) + 65 );
printf("Pre-placement, element %d: %s\n", counter, place );
avector.push_back( place );
printf("Post-placement element %d: %s\n", counter, avector.at( counter ) );
free( place );
++counter;
}
if( ok == true ) {
goto Back;
}
exit( 0 );
}
place = (char *)malloc( 10 * sizeof( char ) );
您正在使用 malloc
分配数据
avector.push_back( place );
你把它推到矢量
free( place );
然后,你删除它。因此,指针仍在向量中,但内存地址不再属于您。
此外,您使用的是 C++,而不是 C,所以不要使用 printf、goto、malloc 和 free。
并尽量不使用 new 和 delete,而是使用智能指针 (C++11)
代码中的主要错误是您添加了一个指向向量的指针,但随后使存储在其中的指针无效。
place = (char *)malloc( 10 * sizeof( char ) );
avector.push_back( place );
free( place ); // <<--- BUG; you destroyed what's pointed to by place
错误是 place
和在 avector
中添加 place
的位置(比如,在 avector[avector.size() - 1]
)都指向同一个内存位置,因此 free
ing place
与 free
ing avector[avector.size() - 1]
在循环的给定迭代中相同。
你基本上是自食其果。
我想一个更简单的例子可以帮助你看得更清楚。这是代码的重构版本,使用适当的 C++ 而不是 C++ 和 C 的混合:
#include <iostream>
#include <vector>
using namespace std;
int main( int argc, char * argv[ ] ) {
vector<char *> my_char_pointers;
cout << "Filling up vector ..." << endl;
for(int i = 0; i < 5; ++i) {
cout << "Adding address of " << char(65 + i) << " to vector ..." << endl;
my_char_pointers.push_back(new char(65 + i));
}
cout << "Printing vector ..." << endl;
for(int i = 0; i < my_char_pointers.size(); ++i)
cout << my_char_pointers.at(i) << endl;
cout << "Deleting char pointer vector elements ..." << endl;
for(int i = 0; i < my_char_pointers.size(); ++i)
delete my_char_pointers[i];
cout << "Printing vector of now-INVALID pointers ..." << endl;
for(int i = 0; i < my_char_pointers.size(); ++i)
cout << my_char_pointers.at(i) << endl;
cout << "Removing the now invalid pointers from vector ..." << endl;
my_char_pointers.clear();
return 0;
}
运行 它在我的 Linux PC 上,你得到以下输出:
$ g++ -o test test.cpp
$ ./test
Filling up vector ...
Adding address of A to vector ...
Adding address of B to vector ...
Adding address of C to vector ...
Adding address of D to vector ...
Adding address of E to vector ...
Printing vector ...
A
B
C
D
E
Deleting char pointer vector elements ...
Printing vector of now-INVALID pointers ...
��
`��
@��
���
Removing the now invalid pointers from vector ...
你的版本和我的版本之间需要注意的一些差异,包括错误修复和其他细节:
- 我创建,但不
delete
,vector
输入在同一个循环中(这是修复);
- 您的代码很难理解,因此更难发现错误 catch/debug;
- 它使用
new
和 delete
,这是在 C++ 中 allocate/deallocate 内存的正确方法(malloc
和朋友用于 C)
- 它使用
<iostream>
中的 cout
,这是在 C++ 中打印输出的正确方法(printf
适用于 C)
- 它使用结构正确的代码并且不依赖
goto
[1],这导致了意大利面条代码;
- 我的代码更容易follow/understand,让任何错误更容易find/fix
如果您真的不想成为 "programming newbie"。
,则需要改进代码结构、简化逻辑和其他细节。
另请注意,您实际上并没有 "objects" 的 vector
;你有一个 指向 char
s 的向量。名为my_char_pointers
的vector
是一个对象,但它的内容是原始类型。 (是的,术语确实有所不同。)
[1] 为什么要在 Dijkstra 的名字上使用 goto
语句?它们会给您自己和那些将来不可避免地需要阅读您的代码的人带来很多麻烦。
我不认为自己是编程新手,但在六个月的大部分时间里我一直在努力理解向量。
我曾尝试在 "production" 代码中使用向量,但它似乎从来没有像我期望的那样运行。这个例子是我遇到的许多问题之一,正如标题中所述,Vectors 似乎在离开 while 循环后神秘地丢失了数据。这可能归结为我完全错误地使用了向量,但我想我的过程至少在某种程度上是合理的。您会注意到,这是一个 objects 的向量,这可能也是问题的关键。
在我做的这个例子中,一个带有随机字符的字符指针被添加到向量中,恰好十次。每次都擦除字符指针的内容并添加新内容以确保数据唯一。离开循环并开始另一个打印出向量的内容后,刚刚添加到向量的数据的 none 已经存在了。为什么?
此外,向量的初始化是 non-standard 我见过的例子,但似乎是让向量在没有崩溃的情况下工作的唯一方法。
为我糟糕的格式道歉,code-block 中的样式与我格式化代码的方式不完全相同(括号间距稍微搞砸了)提前致谢!
#include <stdio.h>
#include <vector>
#include <stdlib.h>
int main( int argc, char * argv[ ] ) {
std::vector<char *> avector( 0 );
avector.reserve( 400 );
int counter = 0;
char * place;
bool ok;
Back:
if( counter == 10 ) {
counter = 0;
while( counter != 10 ){
printf( "Element %d: %s\n", counter, avector.at( counter ) );
++counter
}
ok = false;
}
while( counter != 10 ) {
ok = true;
place = (char *)malloc( 10 * sizeof( char ) );
*place = (char)( ( rand( ) % 26 ) + 65 );
printf("Pre-placement, element %d: %s\n", counter, place );
avector.push_back( place );
printf("Post-placement element %d: %s\n", counter, avector.at( counter ) );
free( place );
++counter;
}
if( ok == true ) {
goto Back;
}
exit( 0 );
}
place = (char *)malloc( 10 * sizeof( char ) );
您正在使用 malloc
avector.push_back( place );
你把它推到矢量
free( place );
然后,你删除它。因此,指针仍在向量中,但内存地址不再属于您。
此外,您使用的是 C++,而不是 C,所以不要使用 printf、goto、malloc 和 free。
并尽量不使用 new 和 delete,而是使用智能指针 (C++11)
代码中的主要错误是您添加了一个指向向量的指针,但随后使存储在其中的指针无效。
place = (char *)malloc( 10 * sizeof( char ) );
avector.push_back( place );
free( place ); // <<--- BUG; you destroyed what's pointed to by place
错误是 place
和在 avector
中添加 place
的位置(比如,在 avector[avector.size() - 1]
)都指向同一个内存位置,因此 free
ing place
与 free
ing avector[avector.size() - 1]
在循环的给定迭代中相同。
你基本上是自食其果。
我想一个更简单的例子可以帮助你看得更清楚。这是代码的重构版本,使用适当的 C++ 而不是 C++ 和 C 的混合:
#include <iostream>
#include <vector>
using namespace std;
int main( int argc, char * argv[ ] ) {
vector<char *> my_char_pointers;
cout << "Filling up vector ..." << endl;
for(int i = 0; i < 5; ++i) {
cout << "Adding address of " << char(65 + i) << " to vector ..." << endl;
my_char_pointers.push_back(new char(65 + i));
}
cout << "Printing vector ..." << endl;
for(int i = 0; i < my_char_pointers.size(); ++i)
cout << my_char_pointers.at(i) << endl;
cout << "Deleting char pointer vector elements ..." << endl;
for(int i = 0; i < my_char_pointers.size(); ++i)
delete my_char_pointers[i];
cout << "Printing vector of now-INVALID pointers ..." << endl;
for(int i = 0; i < my_char_pointers.size(); ++i)
cout << my_char_pointers.at(i) << endl;
cout << "Removing the now invalid pointers from vector ..." << endl;
my_char_pointers.clear();
return 0;
}
运行 它在我的 Linux PC 上,你得到以下输出:
$ g++ -o test test.cpp
$ ./test
Filling up vector ...
Adding address of A to vector ...
Adding address of B to vector ...
Adding address of C to vector ...
Adding address of D to vector ...
Adding address of E to vector ...
Printing vector ...
A
B
C
D
E
Deleting char pointer vector elements ...
Printing vector of now-INVALID pointers ...
��
`��
@��
���
Removing the now invalid pointers from vector ...
你的版本和我的版本之间需要注意的一些差异,包括错误修复和其他细节:
- 我创建,但不
delete
,vector
输入在同一个循环中(这是修复); - 您的代码很难理解,因此更难发现错误 catch/debug;
- 它使用
new
和delete
,这是在 C++ 中 allocate/deallocate 内存的正确方法(malloc
和朋友用于 C) - 它使用
<iostream>
中的cout
,这是在 C++ 中打印输出的正确方法(printf
适用于 C) - 它使用结构正确的代码并且不依赖
goto
[1],这导致了意大利面条代码; - 我的代码更容易follow/understand,让任何错误更容易find/fix
如果您真的不想成为 "programming newbie"。
,则需要改进代码结构、简化逻辑和其他细节。另请注意,您实际上并没有 "objects" 的 vector
;你有一个 指向 char
s 的向量。名为my_char_pointers
的vector
是一个对象,但它的内容是原始类型。 (是的,术语确实有所不同。)
[1] 为什么要在 Dijkstra 的名字上使用 goto
语句?它们会给您自己和那些将来不可避免地需要阅读您的代码的人带来很多麻烦。