Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

How to initialize std::vector<unsigned char> from "C string literal" or from std::string?

2,152 views
Skip to first unread message

Petr Prikryl

unread,
Aug 26, 2003, 5:28:17 AM8/26/03
to
Hi,

I need to use "std::vector<unsigned char> buf" as a buffer
that can be passed to some C functions as "f(&buf[0], buf.size())".

When building test, I tried to initialize that buffer manually
from a string literal or from std::string. I did finally use something
like this with strings:

typedef std::vector<unsigned char> vec;

string penguin_zero_penguin("penguin\0penguin");
CPPUNIT_ASSERT(penguin_zero_penguin.length() == 15);

vec vpenguin_zero_penguin(penguin_zero_penguin.length());
strncpy(reinterpret_cast<char *>(&vpenguin_zero_penguin[0]),
penguin_zero_penguin.c_str(), penguin_zero_penguin.length());

or similarly with literals:

vec vpenguin_zero_penguin(15);
strncpy(reinterpret_cast<char *>(&vpenguin_zero_penguin[0]),
"penguin\0penguin", 15);

I am not very good in standard C++ library. I believe that there must be
some
nicer way to do the same with comparable performance.

Thanks for your help and experience,
Petr
--
Petr Prikryl (prikrylp at skil dot cz)


Craig Powers

unread,
Aug 26, 2003, 10:06:49 AM8/26/03
to
Petr Prikryl wrote:
>
> I need to use "std::vector<unsigned char> buf" as a buffer
> that can be passed to some C functions as "f(&buf[0], buf.size())".

You're supposed to be able to do it using iterators. Are you using VC6
or VC7(.1)? If the former, you'll likely run into trouble with missing
member templates. You may be able to edit the <vector> header to
restore them. It's been my experience that there are no problems
restoring the template member functions, but the template constructors
don't work.

Then you can do something like,
buf.insert(penguin_zero_penguin.begin(),
penguin_zero_penguin.end());

Alternatively, there's std::copy:
buf.reserve(penguin_zero_penguin.length());
std::copy(penguin_zero_penguin.begin(), penguin_zero_penguin.end(),
std::back_inserter(buf));

As long as you reserve first, so that you only allocate once, this
method should do well.

> When building test, I tried to initialize that buffer manually
> from a string literal or from std::string. I did finally use something
> like this with strings:
>
> typedef std::vector<unsigned char> vec;
>
> string penguin_zero_penguin("penguin\0penguin");
> CPPUNIT_ASSERT(penguin_zero_penguin.length() == 15);
>
> vec vpenguin_zero_penguin(penguin_zero_penguin.length());
> strncpy(reinterpret_cast<char *>(&vpenguin_zero_penguin[0]),
> penguin_zero_penguin.c_str(), penguin_zero_penguin.length());

Then again, I don't like the unsigned char vs. char thing here, but
otherwise this isn't so bad.

--
Craig Powers
MVP - Visual C++

Igor Tandetnik

unread,
Aug 26, 2003, 4:20:04 PM8/26/03
to
"Petr Prikryl" <prikrylp at skil dot cz> wrote in message
news:uHZslR7a...@tk2msftngp13.phx.gbl...

> typedef std::vector<unsigned char> vec;
>
> string penguin_zero_penguin("penguin\0penguin");

This won't work. You are using std::string constructor that takes
NULL-terminated C string. It will stop at the first '\0'.

> CPPUNIT_ASSERT(penguin_zero_penguin.length() == 15);

This should assert, penguin_zero_penguin.length() should be 7.

> vec vpenguin_zero_penguin(penguin_zero_penguin.length());
> strncpy(reinterpret_cast<char *>(&vpenguin_zero_penguin[0]),
> penguin_zero_penguin.c_str(),
penguin_zero_penguin.length());

strncpy copies up to the first NULL character or to the provided length,
whichever comes first. Thus it too will stop after first "penguin".

> or similarly with literals:
>
> vec vpenguin_zero_penguin(15);
> strncpy(reinterpret_cast<char *>(&vpenguin_zero_penguin[0]),
> "penguin\0penguin", 15);

Same problem with strncpy.

This should work:

char penguin[] = "penguin\0penguin";
const int len = sizeof(penguin) - 1; // sizeof counts terminating NULL.

vec vpenguin(len);
std::copy((unsigned char*)penguin, (unsigned char*)(penguin + len),
vpenguin.begin());
// or
memcpy(&vpenguin[0], penguin, len);

--
With best wishes,
Igor Tandetnik

"For every complex problem, there is a solution that is simple, neat,
and wrong." H.L. Mencken


Ron Natalie

unread,
Aug 26, 2003, 5:14:44 PM8/26/03
to

"Igor Tandetnik" <itand...@mvps.org> wrote in message news:ept4n%23AbDH...@TK2MSFTNGP12.phx.gbl...

> char penguin[] = "penguin\0penguin";
> const int len = sizeof(penguin) - 1; // sizeof counts terminating NULL.
>

By the way if you wan tto init the string:
string penguin_zero_penguin(penguin, len);


Petr Prikryl

unread,
Aug 27, 2003, 2:12:22 AM8/27/03
to
"Igor Tandetnik" wrote...

> "Petr Prikryl" <prikrylp at skil dot cz> wrote in message
> > [...] string penguin_zero_penguin("penguin\0penguin");
>
> This won't work. [... the] constructor [...] stop at the first '\0'.

Yes. I did not pay attention here. I was composing the content
in several steps in the original code like (pseudo):

string penguin("penguin");
string zero(1, '\0');
string penguin_zero_penguin(penguin + zero + penguin);

> > vec vpenguin_zero_penguin(penguin_zero_penguin.length());
> > strncpy(reinterpret_cast<char *>(&vpenguin_zero_penguin[0]),
> > penguin_zero_penguin.c_str(),
> > penguin_zero_penguin.length());
>
> strncpy copies up to the first NULL character or to the provided length,
> whichever comes first. Thus it too will stop after first "penguin".

Oh yes. Thanks many times. It would be a bug in my source. (Being spoiled
by std::string, I did forgot the details of the hard-core C functions ;)

> [...] This should work:


> char penguin[] = "penguin\0penguin";
> const int len = sizeof(penguin) - 1; // sizeof counts terminating NULL.
> vec vpenguin(len);
> std::copy((unsigned char*)penguin, (unsigned char*)(penguin + len),
> vpenguin.begin());
> // or
> memcpy(&vpenguin[0], penguin, len);

Thanks for that code. I have finally also found the std::copy() algorithm.
But I had some problem in cases when I did not know exactly the size
of the output vector. Because of that I did use:

vec v;
string s;
[... some code here ...]
s = ...filled somehow...
v.reserve(my_size); // knowing that the block will be big and aproximately
of that size.
copy(s.begin(), s.end(), back_inserter(v));

The key difference here was to use the back_inserter() to adjust the size
according
to the copied content.

Question: Is the space reserved for a vector ever shortened? I mean, when
I do v.clear() or whatever, can the v.capacity() be lowered during the
lifetime
of the vector? Does reserve() with smaller argument free the "not needed"
space?

Thanks for your valuable comments,

Petr Prikryl

unread,
Aug 27, 2003, 2:24:25 AM8/27/03
to
"Ron Natalie" wrote:

> "Igor Tandetnik" wrote:
> > char penguin[] = "penguin\0penguin";
> > const int len = sizeof(penguin) - 1; // sizeof counts terminating NULL.
>
> By the way if you wan tto init the string:
> string penguin_zero_penguin(penguin, len);

Thanks. I did not use that version of the constructor (with non-default
len).
So, the possibility to use it did not came to my mind until reading your
post
and checking the documentation.

Petr


Petr Prikryl

unread,
Aug 27, 2003, 2:33:35 AM8/27/03
to
Thanks, Craig,

"Craig Powers" wrote:
> Petr Prikryl wrote:
> > I need to use "std::vector<unsigned char> buf" as a buffer
> > that can be passed to some C functions as "f(&buf[0], buf.size())".
>

> You're supposed to be able to do it using iterators. [...]


> Then you can do something like,
> buf.insert(penguin_zero_penguin.begin(),
> penguin_zero_penguin.end());

Thanks, the insert() method is new (for me) alternative to std::copy()
with back_inserter() that I finally used and that you also mentioned in your
post.

> > vec vpenguin_zero_penguin(penguin_zero_penguin.length());
> > strncpy(reinterpret_cast<char *>(&vpenguin_zero_penguin[0]),
> > penguin_zero_penguin.c_str(),
penguin_zero_penguin.length());
>
> Then again, I don't like the unsigned char vs. char thing here, but
> otherwise this isn't so bad.

Caught you! ;-) As Igor Tandetnik noticed, the strncpy stops at the first
zero character.

The reason for using vector<unsigned char > is that I am calling zlib
functions that expect unsigned char buffer[n]. This way I did avoid
the reinterpret_cast<> many times later.

Thanks again,
Petr


Igor Tandetnik

unread,
Aug 27, 2003, 10:01:19 AM8/27/03
to
"Petr Prikryl" <prikrylp at skil dot cz> wrote in message
news:%237bawIG...@TK2MSFTNGP10.phx.gbl...

> Question: Is the space reserved for a vector ever shortened?

Never. But you can use the following "shrink-to-fit" idiom:

vec myVec; // overallocated
vec(myVec).swap(myVec);

This creates a temporary vector, copies the contents of the original one
into the temp, then swaps the contents of the two, so the temp holds
overallocated space and the original vector holds a freshly allocated
space. Finally, temporary vector is destroyed and frees overallocated
memory.

This relies on the fact that most STL implementations allocate just
enough memory to hold existing elements in this situation, or maybe
slighly larger.

0 new messages