copy std::vector<unsigned char> address to unsigned char[] array [duplicate] - c++

I have an std::vector. I want to copy the contents of the vector into a char* buffer of a certain size.
Is there a safe way to do this?
Can I do this?
memcpy(buffer, _v.begin(), buffer_size);
or this?
std::copy(_v.begin(), _v.end(), buffer); // throws a warning (unsafe)
or this?
for (int i = 0; i < _v.size(); i++)
{
*buffer = _v[i];
buffer++;
}
Thanks..

std::copy(_v.begin(), _v.end(), buffer);
This is preferred way to do this in C++. It is safe to copy this way if buffer is large enough.

If you just need char*, then you can do this:
char *buffer=&v[0];//v is guaranteed to be a contiguous block of memory.
//use buffer
Note changing data pointed to by buffer changes the vector's content also!
Or if you need a copy, then allocate a memory of size equal to v.size() bytes, and use std::copy:
char *buffer = new char[v.size()];
std::copy(v.begin(), v.end(), buffer);
Dont forget to delete []buffer; after you're done, else you'll leak memory.
But then why would you invite such a problem which requires you to manage the memory yourself.. especially when you can do better, such as:
auto copy = v; // that's simpler way to make copies!!
// and then use copy as new buffer.
// no need to manually delete anything. :-)
Hope that helps.

The safest way to copy a vector<char> into a char * buffer is to copy it to another vector, and then use that vector's internal buffer:
std::vector<char> copy = _v;
char * buffer = &copy[0];
Of course, you can also access _vs buffer if you don't actually need to copy the data. Also, beware that the pointer will be invalidated if the vector is resized.
If you need to copy it into a particular buffer, then you'll need to know that the buffer is large enough before copying; there are no bounds checks on arrays. Once you've checked the size, your second method is best. (The first only works if vector::iterator is a pointer, which isn't guaranteed; although you could change the second argument to &_v[0] to make it work. The third does the same thing, but is more complicated, and probably should be fixed so it doesn't modify buffer).

Well, you want to assign to *buffer for case 3, but that should work. The first one almost certainly won't work.
EDIT: I stand corrected regarding #2.

static std::vector<unsigned char> read_binary_file (const std::string filename)
{
// binary mode is only for switching off newline translation
std::ifstream file(filename, std::ios::binary);
file.unsetf(std::ios::skipws);
std::streampos file_size;
file.seekg(0, std::ios::end);
file_size = file.tellg();
file.seekg(0, std::ios::beg);
std::vector<unsigned char> vec(file_size);
vec.insert(vec.begin(),
std::istream_iterator<unsigned char>(file),
std::istream_iterator<unsigned char>());
return (vec);
}
and then:
auto vec = read_binary_file(filename);
auto src = (char*) new char[vec.size()];
std::copy(vec.begin(), vec.end(), src);
but remember to delete []src later

Related

Is there a way to fill a vector using an array?

I'm trying to fill a vector with integers from an array I have but when I check the contents of the vector all the values are zero.
I'm using vector.push_back() to try and fill the vector so it will be in the same order as the array as I needed it ordered in a specific way.
unsigned char* buffer = new unsigned char[size];
std::vector<unsigned char> *data = new std::vector<unsigned char>(size);
fread(buffer, sizeof(unsigned char), size, f);
for(int transfer = 0; transfer < size; transfer += 1){
std::cout << buffer[transfer];
data->push_back(buffer[transfer]);
std::cout << int(data->at(transfer));
}
fclose(f);
When I print the output I can see that the values aren't zero when they're coming from the buffer array but they are when I read from the data vector. Here is some example output φ0$0.
std::vector has a constructor for this purpose:
std::vector<unsigned char> data(buffer, buffer + size);
newing a vector almost always should be avoided.
Live
The overload of the constructor of std::vector that you are using takes the number of elements to initialize the vector with and initializes these (to 0 in this case).
After the line
std::vector<unsigned char> *data = new std::vector<unsigned char>(size);
*data therefore contains already size elements set to zero.
Then with data->push_back(...) you are adding additional elements after these size elements. You are not overwriting the previous ones.
Either use
std::vector<unsigned char> *data = new std::vector<unsigned char>();
to default-initialize an empty vector, or use
(*data)[transfer] = ...
to set the elements at the given location.
Furthermore your program has undefined behavior if the fread reads less than size bytes into the array. You need to check the number of bytes read from its return value and you are not allowed to access any elements beyond that in data, because you never initialized it.
You can initialize it to zero with:
unsigned char* buffer = new unsigned char[size]{};
If you want to write C++, don't use C library functions like fread, use the facilities provided by <fstream>, i.e. std::ifstream and std::ofstream instead.
Similarly there is no need for dynamic memory allocation here. Declare variables with automatic storage:
unsigned char buffer[size]{};
std::vector<unsigned char> data(size);
and the rest of the syntax also simplifies:
data[transfer] = ...
Finally, as mentioned in the other answer, there is a constructor for std::vector that will perform the whole copy loop for you. Note however that my argument about undefined behavior still applies when using that.
Defining data as automatic array as in
unsigned char buffer[size]{};
works only if size is a compile-time constant. If it is not, then this part of my advice does not apply. However there is no real need to use arrays at all in any case. You can initialize a std::vector of proper size (compile-time constant or not) and provide that as buffer via its .data() method, which returns a pointer to the underlying (continuous) storage:
std::vector<unsigned char> buffer(size);
fread(buffer.data(), sizeof(unsigned char), buffer.size(), f);
You don't need a separate buffer or any dynamic allocation in your code. You can create the std::vector with the desired size and then read from the file directly into the vector. The std::vector::data member function returns a pointer to the vector's underlying array that you can pass to the fread() function
std::vector<unsigned char> vec(size);
fread(vec.data(), sizeof(unsigned char), size, f);
Ideally you'll also check the return value from fread() to know how many elements were read.

Safe use of a function that writes data after a pointer

I have a function foo(void* buffer, size_t len) that calculates a hash from the data at buffer (of size len) and appends it at the end of buffer.
Usually I have a vector that I would pass to foo(&myVec[0], myVec.size())
How would I safely use this function with a vector? Resize it before passing it?
void foo(void* buffer, size_t len)
{
if(buffer == NULL)
{
printf("Error\n");
return;
}
std::vector<unsigned char> hash(128);
gethash(buffer, len, &hash[0]);
unsigned char *data = ((unsigned char*) buffer) + len;
memcpy(data, &hash[0], hash.size());
}
Assuming the vector is a vector<char>, you could avoid all the messyness and do:
void foo(vector<char>& buffer)
{
std::vector<unsigned char> hash(128);
gethash(buffer.data(), buffer.size(), hash.data());
buffer.resize(buffer.size()+hash.size();
unsigned char *data = buffer.data() + buffer.size();
memcpy(data, hash.data(), hash.size());
}
This is still a bit "messy", but a lot less than the code you posted.
As suggested in the comments, something like:
buffer.insert(buffer.end(), hash.begin(), hash.end());
is probably better than the last three lines I wrote.
You can't do it!
If bufferpoints to memory with given len you can't reallocate more memory at exact this place.
2 ways to deal with it:
The buffer comes already with enough size for data and hash, but I would prefer a struct for this solution!
or
Allocate new memory, copy data and hash value to it and return the pointer the new data memory. But don't forget to free this memory later and also the input memory.
The second solution can be done with a vector
void foo( vector<char> &vec )
{
...
gethash(&vec[0], len, &hash[0]);
...
vec.resize(...); // reallocate and copy data if needed
memcpy // which I do not want to use with a vector :-)
}
A resize of a vector results in allocating new memory and copy data from old to new memory and free the old buffer allocated. It is possible that the vector holds (much) more memory as expected so that no reallocation must happen. But how it behaves must not be known until speed is a criteria. But you can also create a vector with a minimum of internal size so that you prevent automatic allocation and copy.
You can do this before calling foo:
int main() {
std::vector<int> buffer;
size_t sz = buffer.size();
size_t tsz = sizeof(decltype(buffer)::value_type);
buffer.resize(128 / tsz + buffer.size());
foo(buffer.data(), sz * tsz);
return 0;
}
You can do it like this:
void foo(vector<unsigned char>& data)
{
if(data.empty())
return;
vector<unsigned char> result(128);
gethash(&data[0], data.size(), &result[0]);
data.insert(data.end(), result.begin(), result.end());
}

Copy the contents of std::vector<char> into a char* buffer?

I have an std::vector. I want to copy the contents of the vector into a char* buffer of a certain size.
Is there a safe way to do this?
Can I do this?
memcpy(buffer, _v.begin(), buffer_size);
or this?
std::copy(_v.begin(), _v.end(), buffer); // throws a warning (unsafe)
or this?
for (int i = 0; i < _v.size(); i++)
{
*buffer = _v[i];
buffer++;
}
Thanks..
std::copy(_v.begin(), _v.end(), buffer);
This is preferred way to do this in C++. It is safe to copy this way if buffer is large enough.
If you just need char*, then you can do this:
char *buffer=&v[0];//v is guaranteed to be a contiguous block of memory.
//use buffer
Note changing data pointed to by buffer changes the vector's content also!
Or if you need a copy, then allocate a memory of size equal to v.size() bytes, and use std::copy:
char *buffer = new char[v.size()];
std::copy(v.begin(), v.end(), buffer);
Dont forget to delete []buffer; after you're done, else you'll leak memory.
But then why would you invite such a problem which requires you to manage the memory yourself.. especially when you can do better, such as:
auto copy = v; // that's simpler way to make copies!!
// and then use copy as new buffer.
// no need to manually delete anything. :-)
Hope that helps.
The safest way to copy a vector<char> into a char * buffer is to copy it to another vector, and then use that vector's internal buffer:
std::vector<char> copy = _v;
char * buffer = &copy[0];
Of course, you can also access _vs buffer if you don't actually need to copy the data. Also, beware that the pointer will be invalidated if the vector is resized.
If you need to copy it into a particular buffer, then you'll need to know that the buffer is large enough before copying; there are no bounds checks on arrays. Once you've checked the size, your second method is best. (The first only works if vector::iterator is a pointer, which isn't guaranteed; although you could change the second argument to &_v[0] to make it work. The third does the same thing, but is more complicated, and probably should be fixed so it doesn't modify buffer).
Well, you want to assign to *buffer for case 3, but that should work. The first one almost certainly won't work.
EDIT: I stand corrected regarding #2.
static std::vector<unsigned char> read_binary_file (const std::string filename)
{
// binary mode is only for switching off newline translation
std::ifstream file(filename, std::ios::binary);
file.unsetf(std::ios::skipws);
std::streampos file_size;
file.seekg(0, std::ios::end);
file_size = file.tellg();
file.seekg(0, std::ios::beg);
std::vector<unsigned char> vec(file_size);
vec.insert(vec.begin(),
std::istream_iterator<unsigned char>(file),
std::istream_iterator<unsigned char>());
return (vec);
}
and then:
auto vec = read_binary_file(filename);
auto src = (char*) new char[vec.size()];
std::copy(vec.begin(), vec.end(), src);
but remember to delete []src later

Reading raw byte array into std::string

I've been wondering about the following issue: assume I have a C style function that reads raw data into a buffer
int recv_n(int handle, void* buf, size_t len);
Can I read the data directly into an std:string or stringstream without allocating any temporal buffers? For example,
std::string s(100, '\0');
recv_n(handle, s.data(), 100);
I guess this solution has an undefined outcome, because, afaik, string::c_str and string::data might return a temporal location and not necessarily return the pointer to the real place in the memory, used by the object to store the data.
Any ideas?
Why not use a vector<char> instead of a string? That way you can do:
vector<char> v(100, '\0');
recv_n(handle, &v[0], 100);
This seems more idiomatic to me, especially since you aren't using it as a string (you say it's raw data).
Yes, after C++11.
But you cant use s.data() as it returns a char const*
Try:
std::string s(100, '\0');
recv_n(handle, &s[0], 100);
Depending on situation, I may have chosen a std::vector<char> especially for raw data (though it would all depend on usage of the data in your application).

Attaching char buffer to vector<char> in STL

What is the correct (and efficient) way of attaching the contents of C buffer (char *) to the end of std::vector<char>?
When you have a vector<char> available, you're probably best calling the vector<char>::insert method:
std::vector<char> vec;
const char* values="values";
const char* end = values + strlen( values );
vec.insert( vec.end(), values, end );
Delegating it to the vector is to be preferred to using a back_inserter because the vector can then decide upon its final size. The back_inserter will only push_back, possibly causing more reallocations.
I think the proper way would be to
vec.insert(vec.end(),buf,buf+length);
or
std::copy(buf,buf+length,std::back_inserter(vec));
Edit: I reordered two examples, so it's not that commenters are wrong, it's just me ;-)
I haven't compiled it, but it should be something like:
const char string1[] = "a string";
std::vector<char> vData;
vData.insert(vData.end(), string1, string1+strlen(string1));