离开循环时矢量数据丢失

Vector Data Loss When Leaving A Loop

本文关键字:数据 循环 离开      更新时间:2023-10-16

我不认为自己是一个编程新手,但我一直在努力理解向量在六个月的大部分时间里。

我试过在"生产"代码中使用向量,但它似乎从来没有像我期望的那样起作用。这个例子是我遇到的许多问题之一,正如标题所述,Vectors似乎在离开while循环后神秘地丢失了数据。这可能会归结为我只是使用了错误的向量,但我想认为我的过程至少是合理的。正如您将注意到的,这是一个对象向量,这可能也是问题的关键所在。

在我所做的这个例子中,一个char指针内部有一个随机字符被添加到向量中,正好是10次。每次都会擦除char指针的内容,并添加新内容以确保数据的唯一性。在离开循环并启动另一个循环以打印vector的内容之后,刚刚添加到vector中的数据都不存在了。为什么?

此外,从我看到的例子中,向量的初始化是非标准的,但似乎是使向量工作而不崩溃的唯一方法。

为我糟糕的格式道歉,代码块中的样式不完全是我如何格式化代码(括号间距稍微搞砸了)提前感谢!

#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: %sn", 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: %sn", counter, place );
        avector.push_back( place );
        printf("Post-placement element %d: %sn", 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)

代码中的主要错误是您向vector添加了一个指针,但随后使存储在vector中的指针无效。

place = (char *)malloc( 10 * sizeof( char ) );
avector.push_back( place );
free( place );   // <<--- BUG; you destroyed what's pointed to by place

错误是placeplaceavector中添加的位置(例如,在avector[avector.size() - 1])都指向相同的内存位置,因此在给定的循环迭代中,free指向placefree指向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 ...

你的版本和我的版本之间的一些差异需要注意,包括错误修复和其他细节:

  1. 我创建,但不delete, vector输入在同一循环(这是修复);
  2. 你的代码很难遵循,使错误更难捕获/调试;
  3. 它使用newdelete,这是在c++中分配/释放内存的正确方式(malloc和朋友是C)
  4. 它使用<iostream>中的cout,这是在c++中打印输出的正确方式(printf用于C)
  5. 它使用正确的结构化代码,并且依赖于goto[1],这导致了面条式代码;
  6. 和我的代码更容易跟随/理解,使任何错误更容易找到/修复

如果你真的不想成为一个"编程新手",你需要在你的代码结构、简化逻辑和其他细节上下功夫。

还需要注意的是,你并没有真正的"objects"的vector;你有一个指向char s的指针向量。名为my_char_pointersvector是一个对象,但它的内容是基本类型。(是的,术语确实有区别。)

[1]为什么在Dijkstra的名字上使用goto语句?它们会给你自己和那些将来不可避免地需要阅读你代码的人带来很多麻烦。