C++字符串和指针操作

C++ String and Pointers Manipulation

本文关键字:操作 指针 字符串 C++      更新时间:2023-10-16

假设我在C++中有这样的数据结构:

struct Stash {
  int size;      // Size of each space
  int quantity;  // Number of storage spaces
  int next;      // Next empty space
   // Dynamically allocated array of bytes:
  unsigned char* storage;
  // Functions!
  void initialize(int size);
  void cleanup();
  int add(const void* element);
  void* fetch(int index);
  int count();
  void inflate(int increase);
};///:~
void Stash::initialize(int sz) {
  size = sz;
  quantity = 0;
  storage = 0;
  next = 0;
}
int Stash::add(const void* element) {
  if(next >= quantity) // Enough space left?
    inflate(increment);
  // Copy element into storage,
  // starting at next empty space:
  int startBytes = next * size;
  unsigned char* e = (unsigned char*)element;
  for(int i = 0; i < size; i++){
     storage[(startBytes + i)] = e[i];
  }
  next++;
  return(next - 1); // Index number
}
void* Stash::fetch(int index) {
  // Check index boundaries:
  assert(0 <= index);
  if(index >= next)
    return 0; // To indicate the end
  // Produce pointer to desired element:
  int value = (index*size);
  return &(storage[value]);
}
int Stash::count() {
  return next; // Number of elements in CStash
}
void Stash::inflate(int increase) {
  assert(increase > 0);
  int newQuantity = quantity + increase;
  int newBytes = newQuantity * size;
  int oldBytes = quantity * size;
  unsigned char* b = new unsigned char[newBytes];
  for(int i = 0; i < oldBytes; i++)
    b[i] = storage[i]; // Copy old to new
  delete []storage; // Old storage
  storage = b; // Point to new memory
  quantity = newQuantity;
}
void Stash::cleanup() {
  if(storage != 0) {
    cout << "freeing storage" << endl;
    delete []storage;
  }
}  ///:~

假设现在我使用数据结构以这种方式记忆字符串:

int main(){

    Stash* st1 = new Stash;
    st1->initialize(sizeof(string));
    string s1 = "This is a GOOD morning";
    st1->add(&s1);

    string s2 = "This is a BAD morning";
    st1->add(&s2);

    string* s3;
    s3 = static_cast<string*> (st1->fetch(0));
    cout << *s3 << endl;
    string* s3;
    s3 = static_cast<string*> (st1->fetch(1));
    cout << *s3 << endl;
    st1->cleanup();
    delete st1;
    return 0;
}

它有效!!!这是输出:

This is a GOOD morning
This is a BAD morning

但在另一方面:

int main(){

    Stash* st1 = new Stash;
    st1->initialize(sizeof(string));
    string s1 = "This is a GOOD morning";
    st1->add(&s1);
    s1 = "This is a BAD morning";
    st1->add(&s1);
    string* s3;
    s3 = static_cast<string*> (st1->fetch(0));
    cout << *s3 << endl;
    string* s4;
    s4 = static_cast<string*> (st1->fetch(1));
    cout << *s4 << endl;
    st1->cleanup();
    delete st1;
    return 0;
}

它不起作用。这是输出:

This is a BAD morning
This is a BAD morning

那么,当我尝试使用相同的引用时,机器中发生了什么?我尝试过其他数据类型,效果很好。

s1的第一次使用调用构造函数:

string s1 = "This is a GOOD morning";

然后将s1的地址添加到Stash中。接下来,您为s1分配一个新值:

s1 = "This is a BAD morning";

这不会创建新的字符串,而是调用赋值运算符,该运算符用新值替换相同的string对象。然后保存s1地址的另一个副本:

st1->add(&s1);

如果您查看st1中的数据,您将看到同一指针的两个副本,都指向s1。这是意料之中的事。在第一种情况下,您将存储指向两个不同对象的指针,这两个对象包含不同的值。

您的代码复制组成std::string容器的字节(而不是字符串数据中的字符)。这可能包括指向字符串数据的指针,以及大小和容量。

当您写入s1 = "stuff"时,std::string会在内部分配新内存,因此它以前的内部指针现在无效。

然后从数据结构中检索以前的内部指针并尝试使用它,从而导致未定义的行为。

如果要保存字符串中的字符,则需要添加s1.c_str()而不是&s1

如果您的意图是存储任何对象的副本,那么您需要调用副本构造函数来创建副本;不要像现在这样按位复制。如果您的目的是存储对象而不留下原始对象,您还可以调用移动构造函数移赋值运算符

在第二种方法中,您使用了s1的地址来存储在堆栈中。并且您没有复制堆栈中的内容,所以当您将s1更改为具有不同的内容时,早期的内容也会更改,因为您实际上是推送了指针,而不是内容的副本。例如,如果您执行以下操作(将内容复制到一个新的字符串中用于推送),这将起作用:

string s1 = "This is a GOOD morning";
st1->add(new string(s1));
s1 = "This is a BAD morning";
st1->add(new string(s1));