如何在 std::vector 中使用空指针

发布于 2024-12-06 10:16:51 字数 1405 浏览 1 评论 0 原文

假设我有一个空终止字符串向量,其中一些可能是空指针。我不知道这是否合法。这是一个学习练习。
示例代码

std::vector<char*> c_strings1;
char* p1 = "Stack Over Flow";
c_strings1.push_back(p1);
p1 = NULL; // I am puzzled you can do this and what exactly is stored at this memory location
c_strings1.push_back(p1);
p1 = "Answer";
c_strings1.push_back(p1);
for(std::vector<char*>::size_type i = 0; i < c_strings1.size(); ++i)
{
  if( c_strings1[i] != 0 )
  {
    cout << c_strings1[i] << endl;
  }
}

请注意,即使我在位置 c_strings1[1]
处有 NULL,向量的大小仍为 3 问题。如何使用 std::vector 重写此代码 当您推送空值时,向量中到底存储了什么?

编辑
我的问题的第一部分已经得到彻底回答,但第二部分还没有得到彻底回答。至少我不满意。我确实想看看vector的用法;;不是一些嵌套变体或 std::vector 这些是熟悉的。所以这是我尝试过的(提示:它不起作用)

std::vector<char> c_strings2;
string s = "Stack Over Flow";
c_strings2.insert(c_strings2.end(), s.begin(), s.end() );
//  char* p = NULL; 
s = ""; // this is not really NULL, But would want a NULL here
c_strings2.insert(c_strings2.end(), s.begin(), s.end() );
s = "Answer";
c_strings2.insert(c_strings2.end(), s.begin(), s.end() );

const char *cs = &c_strings2[0];
while (cs <= &c_strings2[2]) 
{
  std::cout << cs << "\n";
  cs += std::strlen(cs) + 1;
}

Say I have a vector of null terminates strings some of which may be null pointers. I don't know even if this is legal. It is a learning exercise.
Example code

std::vector<char*> c_strings1;
char* p1 = "Stack Over Flow";
c_strings1.push_back(p1);
p1 = NULL; // I am puzzled you can do this and what exactly is stored at this memory location
c_strings1.push_back(p1);
p1 = "Answer";
c_strings1.push_back(p1);
for(std::vector<char*>::size_type i = 0; i < c_strings1.size(); ++i)
{
  if( c_strings1[i] != 0 )
  {
    cout << c_strings1[i] << endl;
  }
}

Note that the size of vector is 3 even though I have a NULL at location c_strings1[1]
Question. How can you re-write this code using std::vector<char>
What exactly is stored in the vector when you push a null value?

EDIT
The first part of my question has been thoroughly answered but not the second. Not to my statisfaction at least. I do want to see usage of vector<char>; not some nested variant or std::vector<std::string> Those are familiar. So here is what I tried ( hint: it does not work)

std::vector<char> c_strings2;
string s = "Stack Over Flow";
c_strings2.insert(c_strings2.end(), s.begin(), s.end() );
//  char* p = NULL; 
s = ""; // this is not really NULL, But would want a NULL here
c_strings2.insert(c_strings2.end(), s.begin(), s.end() );
s = "Answer";
c_strings2.insert(c_strings2.end(), s.begin(), s.end() );

const char *cs = &c_strings2[0];
while (cs <= &c_strings2[2]) 
{
  std::cout << cs << "\n";
  cs += std::strlen(cs) + 1;
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

习惯成性 2024-12-13 10:16:51

您没有字符串的向量 - 您有一个指向字符的指针的向量。 NULL 是一个完全有效的字符指针,它恰好不指向任何东西,因此它存储在向量中。

请注意,您实际存储的指针是指向 char 文字的指针。字符串不会被复制。

将 C++ 风格的向量与 C 风格的 char 指针混合起来没有多大意义。这样做并不违法,但是像这样混合范例通常会导致混乱和混乱。破解代码。

为什么不使用 vector 而不是使用 vectorvector 呢?

编辑

根据您的编辑,您似乎想要做的是将多个字符串展平为单个 vector,并在每个展平字符串之间使用 NULL 终止符。

这是实现此目的的简单方法:

#include <algorithm>
#include <vector>
#include <string>
#include <iterator>
using namespace std;

int main()
{
    // create a vector of strings...
    typedef vector<string> Strings;
    Strings c_strings;

    c_strings.push_back("Stack Over Flow");
    c_strings.push_back("");
    c_strings.push_back("Answer");

    /* Flatten the strings in to a vector of char, with 
        a NULL terminator between each string

        So the vector will end up looking like this:

        S t a c k _ O v e r _ F l o w \0 \0 A n s w e r \0

    ***********************************************************/

    vector<char> chars;
    for( Strings::const_iterator s = c_strings.begin(); s != c_strings.end(); ++s )
    {
        // append this string to the vector<char>
        copy( s->begin(), s->end(), back_inserter(chars) );
        // append a null-terminator
        chars.push_back('\0');
    }
}

You don't have a vector of strings -- you have a vector of pointer-to-char. NULL is a perfectly valid pointer-to-char which happens to not point to anything, so it is stored in the vector.

Note that the pointers you are actually storing are pointers to char literals. The strings are not copied.

It doesn't make a lot of sense to mix the C++ style vector with the C-style char pointers. Its not illegal to do so, but mixing paradigms like this often results in confused & busted code.

Instead of using a vector<char*> or a vector<char>, why not use a vector<string> ?

EDIT

Based on your edit, it seems like what your'e trying to do is flatten several strings in to a single vector<char>, with a NULL-terminator between each of the flattened strings.

Here's a simple way to accomplish this:

#include <algorithm>
#include <vector>
#include <string>
#include <iterator>
using namespace std;

int main()
{
    // create a vector of strings...
    typedef vector<string> Strings;
    Strings c_strings;

    c_strings.push_back("Stack Over Flow");
    c_strings.push_back("");
    c_strings.push_back("Answer");

    /* Flatten the strings in to a vector of char, with 
        a NULL terminator between each string

        So the vector will end up looking like this:

        S t a c k _ O v e r _ F l o w \0 \0 A n s w e r \0

    ***********************************************************/

    vector<char> chars;
    for( Strings::const_iterator s = c_strings.begin(); s != c_strings.end(); ++s )
    {
        // append this string to the vector<char>
        copy( s->begin(), s->end(), back_inserter(chars) );
        // append a null-terminator
        chars.push_back('\0');
    }
}
遗失的美好 2024-12-13 10:16:51

所以,

char *p1 = "Stack Over Flow";
char *p2 = NULL;
char *p3 = "Answer";

如果你注意到的话,这三个的类型是完全相同的。它们都是char *。因此,我们希望它们在内存中也具有相同的大小。

您可能认为它们在内存中具有相同的大小没有意义,因为 p3 比 p1 短。实际发生的情况是,编译器在编译时会找到程序中的所有字符串。在本例中,它将找到“Stack Over Flow”和“Answer”。它将把它们扔到内存中它知道的某个固定位置。然后,当您尝试说出 p3 = "Answer" 时,编译器实际上会将其转换为 p3 = 0x123456A0 之类的内容。

因此,无论使用哪种版本的 Push_back 调用,您都只是将指针推入向量,而不是实际的字符串本身。

向量本身不知道也不关心 NULL char * 是空字符串。因此,在计数时,它发现您已将三个指针推入其中,因此它报告的大小为 3。

So,

char *p1 = "Stack Over Flow";
char *p2 = NULL;
char *p3 = "Answer";

If you notice, the type of all three of those is exactly the same. They are all char *. Because of this, we would expect them all to have the same size in memory as well.

You may think that it doesn't make sense for them to have the same size in memory, because p3 is shorter than p1. What actually happens, is that the compiler, at compile-time, will find all of the strings in the program. In this case, it would find "Stack Over Flow" and "Answer". It will throw those to some constant place in memory, that it knows about. Then, when you attempt to say that p3 = "Answer", the compiler actually transforms that to something like p3 = 0x123456A0.

Therefore, with either version of the push_back call, you are only pushing into the vector a pointer, not the actual string itself.

The vector itself, doesn't know, or care that a NULL char * is an empty string. So in it's counting, it sees that you have pushed three pointers into it, so it reports a size of 3.

橘虞初梦 2024-12-13 10:16:51

我有一种有趣的感觉,你真正想要的是让向量包含类似“Stack Over Flow Answer”的内容(可能在“Answer”之前没有空格)。

在这种情况下,您可以使用 std::vector,您只需推送整个数组,而不仅仅是指向它们的指针。

这无法通过 push_back 来完成,但是 vector 有一个接受范围的 insert 方法。

/// Maintain the invariant that the vector shall be null terminated
/// p shall be either null or point to a null terminated string
void push_back(std::vector<char>& v, char const* p) {
  if (p) {
    v.insert(v.end(), p, p + strlen(p));
  }

  v.push_back('\0');
} // push_back

int main() {
  std::vector<char> v;

  push_back(v, "Stack Over Flow");
  push_back(v, 0);
  push_back(v, "Answer");

  for (size_t i = 0, max = v.size(); i < max; i += strlen(&v[i]) + 1) {
    std::cout << &v[i] << "\n";
  }
}

这使用单个连续缓冲区来存储多个以 null 结尾的字符串。将空字符串传递给 push_back 会导致显示空字符串。

I have a funny feeling that what you would really want is to have the vector contain something like "Stack Over Flow Answer" (possibly without space before "Answer").

In this case, you can use a std::vector<char>, you just have to push the whole arrays, not just pointers to them.

This cannot be accomplished with push_back, however vector have an insert method that accept ranges.

/// Maintain the invariant that the vector shall be null terminated
/// p shall be either null or point to a null terminated string
void push_back(std::vector<char>& v, char const* p) {
  if (p) {
    v.insert(v.end(), p, p + strlen(p));
  }

  v.push_back('\0');
} // push_back

int main() {
  std::vector<char> v;

  push_back(v, "Stack Over Flow");
  push_back(v, 0);
  push_back(v, "Answer");

  for (size_t i = 0, max = v.size(); i < max; i += strlen(&v[i]) + 1) {
    std::cout << &v[i] << "\n";
  }
}

This uses a single contiguous buffer to store multiple null-terminated strings. Passing a null string to push_back results in an empty string being displayed.

各空 2024-12-13 10:16:51

当您推送空值时,向量中到底存储了什么?

一个NULL。您正在存储指针,并且 NULL 是指针的可能值。为什么这在某种程度上是出乎意料的?

另外,使用 std::string 作为值类型(即 std::vector),char* 应该'除非 C 互操作需要,否则不要使用。要使用 std::vector 复制代码,您需要 std::vector>

What exactly is stored in the vector when you push a null value?

A NULL. You're storing pointers, and NULL is a possible value for a pointer. Why is this unexpected in any way?

Also, use std::string as the value type (i.e. std::vector<std::string>), char* shouldn't be used unless it's needed for C interop. To replicate your code using std::vector<char>, you'd need std::vector<std::vector<char>>.

草莓酥 2024-12-13 10:16:51

在 STL 容器中存储指针时必须小心 - 复制容器会导致浅复制和类似的情况。

关于您的具体问题,向量将存储 char* 类型的指针,无论该指针是否指向某个东西。由于某种原因,您完全有可能希望在该向量中存储 char* 类型的空指针 - 例如,如果您决定稍后从向量中删除该字符串怎么办?向量仅支持 push_back 和 pop_back 的摊销常量时间,因此,如果您要删除该向量内的字符串(但不是在末尾),您很可能希望快速将其设置为空并节省一些时间。

继续 - 我建议制作一个 std::vector >如果您想要一个看起来像您想要的动态字符串数组。

您提到的 std::vector 与您的原始代码相比是无用的,因为您的原始代码存储动态字符串数组,而 std::vector 仅保存一个动态可更改的字符串(因为字符串本质上是一个字符数组) 。

You have to be careful when storing pointers in STL containers - copying the containers results in shallow copy and things like that.

With regard to your specific question, the vector will store a pointer of type char* regardless of whether or not that pointer points to something. It's entirely possible you would want to store a null-pointer of type char* within that vector for some reason - for example, what if you decide to delete that character string at a later point from the vector? Vectors only support amortized constant time for push_back and pop_back, so there's a good chance if you were deleting a string inside that vector (but not at the end) that you would prefer to just set it null quickly and save some time.

Moving on - I would suggest making a std::vector > if you want a dynamic array of strings which looks like what you're going for.

A std::vector as you mentioned would be useless compared to your original code because your original code stores a dynamic array of strings and a std::vector would only hold one dynamically changable string (as a string is an array of characters essentially).

时间你老了 2024-12-13 10:16:51

NULL就是0。值为0的指针是有意义的。但是值为 0 的 char 具有不同的含义。它用作分隔符来显示字符串的结尾。因此,如果您使用 std::vectorpush_back 0,向量将包含值为 0 的字符。vector > 是一个字符向量,而 std::vector 是一个 C 风格字符串向量——这是非常不同的东西。

更新。正如OP想要的那样,我给出了如何存储(在向量中)以null结尾的字符串(其中一些是空值)的想法。

选项 1:假设我们有 vector c_strings;。然后,我们定义一个函数来存储字符串 pi。由于我们需要区分空字符串和空 char*,因此引入了很多复杂性。我们选择一个在我们的使用中不会出现的分隔字符。假设这是“~”字符。

char delimiter = '~';
// push each character in pi into c_strings
void push_into_vec(vector<char>& c_strings, char* pi) {
  if(pi != 0) {
    for(char* p=pi; *p!='\0'; p++) 
      c_strings.push_back(*p);
    // also add a NUL character to denote end-of-string
    c_strings.push_back('\0');
  }
  c_strings.push_back(deimiter);
  // Note that a NULL pointer would be stored as a single '~' character
  // while an empty string would be stored as '\0~'.
}
 
// now a method to retrieve each of the stored strings. 
vector<char*> get_stored_strings(const vector<char>& c_strings) {
  vector<char*> r;
  char* end = &c_strings[0] + c_strings.size();
  char* current = 0;
  bool nullstring = true; 
  for(char* c = current = &c_strings[0]; c != end+1; c++) {
    if(*c == '\0') {
      int size = c - current - 1;
      char* nc = new char[size+1];
      strncpy(nc, current, size);  
      r.push_back(nc);     
      nullstring = false; 
    }
    if(*c == delimiter) {
      if(nullstring) r.push_back(0);     
      nullstring = true; // reset nullstring for the next string
      current = c+1; // set the next string
    }
  }
  return r;
}

您仍然需要对上面 new[] 分配的内存调用 delete[] 。所有这些复杂性都可以通过使用 string 类来解决。我很少在 C++ 中使用 char*

选项 2:您可以使用 vector > 。然后'~'可以替换为空的boost::Optional,其他部分与选项1相同。但是这种情况下的内存占用会更高。

NULL is just 0. A pointer with value 0 has a meaning. But a char with value 0 has a different meaning. It is used as a delimiter to show the end of a string. Therefore, if you use std::vector<char> and push_back 0, the vector will contain a character with value 0. vector<char> is a vector of characters, while std::vector<char*> is a vector of C-style strings -- very different things.

Update. As the OP wants, I am giving an idea of how to store (in a vector) null terminated strings some of which are nulls.

Option 1: Suppose we have vector<char> c_strings;. Then, we define a function to store a string pi. A lot of complexity is introduced since we need to distinguish between an empty string and a null char*. We select a delimiting character that does not occur in our usage. Suppose this is the '~' character.

char delimiter = '~';
// push each character in pi into c_strings
void push_into_vec(vector<char>& c_strings, char* pi) {
  if(pi != 0) {
    for(char* p=pi; *p!='\0'; p++) 
      c_strings.push_back(*p);
    // also add a NUL character to denote end-of-string
    c_strings.push_back('\0');
  }
  c_strings.push_back(deimiter);
  // Note that a NULL pointer would be stored as a single '~' character
  // while an empty string would be stored as '\0~'.
}
 
// now a method to retrieve each of the stored strings. 
vector<char*> get_stored_strings(const vector<char>& c_strings) {
  vector<char*> r;
  char* end = &c_strings[0] + c_strings.size();
  char* current = 0;
  bool nullstring = true; 
  for(char* c = current = &c_strings[0]; c != end+1; c++) {
    if(*c == '\0') {
      int size = c - current - 1;
      char* nc = new char[size+1];
      strncpy(nc, current, size);  
      r.push_back(nc);     
      nullstring = false; 
    }
    if(*c == delimiter) {
      if(nullstring) r.push_back(0);     
      nullstring = true; // reset nullstring for the next string
      current = c+1; // set the next string
    }
  }
  return r;
}

You still need to call delete[] on the memory allocated by new[] above. All this complexity is taken care of by using the string class. I very rarely use char* in C++.

Option 2: You could use vector<boost::optional<char> > . Then the '~' can be replaced by an empty boost::optional, but other other parts are the same as option 1. But the memory usage in this case would be higher.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文