high performance 'proper' c++ alternative to variable length array - c++

I am writing a function which requires an array to be created at runtime. The array will be of small size so I am not worried about unsafe code, however, I want to write 'proper' code. As such I am considering three alternatives:
char array[len];
char array = new char(len);
std::vector array(len);
Using Compiler Explorer to compare them with -O3.
The results were as such:
12 instructions, 0 calls to new
21 instructions, 1 call to new
118 instructions, 2+ calls to new
Am I missing an optimisation for std::vector<> or is the 'proper' c++ way slower or have I entirely missed a way of coding this?
edit: I forgot to delete the heap-allocated array
Test code:
code 1:
#include <string.h>
void populate_array(char* arr);
int compute_result(char* arr);
int str_to_arr(const char* str)
{
auto len = strlen(str);
char array[len];
populate_array(array);
return compute_result(array);
}
code 2:
#include <string.h>
void populate_array(char* arr);
int compute_result(char* arr);
int str_to_arr(const char* str)
{
auto len = strlen(str);
char* array = new char[len];
populate_array(array);
auto result = compute_result(array);
delete[] array;
return result;
}
code 3:
#include <string.h>
#include <vector>
void populate_array(std::vector<char> arr);
int compute_result(std::vector<char> arr);
int str_to_arr(const char* str)
{
auto len = strlen(str);
std::vector<char> array(len);
populate_array(array);
return compute_result(array);
}

There are a few issues in the code, that may be leading you astray in the comparison.
new char(len) allocates a single char, initialized with the value len. You'd be after new char[len] to allocate len chars. There should be a matching delete [], too.
The std::vector<char> object is passed to populate_array by value, making a copy (and consequently not actually populating the array you want), and similarly for compute_result. These copies will engender new allocations. Passing by reference would be appropriate here.
Without using a custom allocator, std::vector will value-initialize all its elements. Effectively, it means that every element in this vector is set to zero. This is not performed by new char[len].
VLAs are not part of C++, but may be provided as an extension. While in this instance, for small len, the compiler has the option of allocating the space for the array on the stack, they are probably best avoided because of their non-standard nature; even in C, they are not required to be supported.

Related

How to initialize a constant array in heap memory using C++?

In C++, we can allocate heap memory for a dynamic array, but how can we initialize it if it's a read only array? Here is an example:
const char* str = new char[3];
After operating this statement, the system seems to initialize variable str with garbage value implicitly which means i cannot change its value since it has a constant qualifier. So how can i creat a constant string in heap memory and intialize it explicitly?
If i want to creat a object in heap memory, i need a pointer to the object. But if it's constant, i cannot even change it with the pointer after its creation in heap memory. So it became a vicious circle for me.
You can start with char * modify the array, then convert it to const char *:
char *str = new char[3];
// str[i] = ...
const char *cstr = str;
But unless you're trying to practice dynamic memory management, none of this should be necessary. Just use std::string or std::vector<char>.
Your operator new call doesn't allocate const memory. It gets converted to const when you assign it to the variable. The solution is to make a temporary variable that's not const, write the data to it and then finally convert it to a const pointer:
#include <cstring>
#include <memory>
std::unique_ptr<const char[]> PutBytesOntoHeap(const char* data, size_t size)
{
std::unique_ptr<char[]> result(new char[size]);
memcpy(result.get(), data, size);
return result;
}
In current c++ avoid calling new/delete explicitly, only use it internally in datastructures (and even then std::make_unique is prefered). So use std::vector (or alternatively std::string/std::string_view)
#273K Also note most C++ books (teachers, online material) are out of date.
#include <vector>
int main()
{
std::vector<char> str{ 'a', 'b', 'c' }; // this will do the memory allocation for you
// for local use (when a legacy api needs a pointer, otherwise don't use)
const char* ptr = str.data();
return 0;
// std::vector goes out of scope
// will free the allocated memory (so you can't forget to call delete[])
}
if it's constant, i cannot even change it with the pointer after its creation in heap memory
In
const char* str = new char[3];
you actually create a non-const char array and assign it to a const char*. You could just assign it to a char* instead, make the changes you want and then return a const char*. Example:
auto str = []() -> const char* {
char* rv = new char[3];
rv[0] = '1';
rv[1] = '2';
rv[2] = '\0';
return rv;
}();
how can i creat a constant string in heap memory and intialize it explicitly?
You use new const char[] with an initializer:
auto str = new const char[3]{'1', '2', '\0'};
A helper function could look like this:
#include <cstddef>
#include <iostream>
#include <utility>
template <std::size_t N>
auto make_const_cstring(const char (&s)[N]) {
return [&]<std::size_t... Is>(std::index_sequence<Is...>) {
return new const char[N]{s[Is]...};
}(std::make_index_sequence<N>());
}
int main() {
auto str = make_const_cstring("Hello world");
std::cout << str << '\n';
delete[] str;
}
If you want to use an array after the function that created it returns, allocate that array in the heap, not in the run-time stack. Expression new T[size] allocates a new array with size variables in it, each of type T. Remember that an array is treated just like a pointer to the first thing in the array. So expression new int[25] has type int*. Statement
int* A = new int[25];
allocates a new array of 25 ints and stores a pointer to the first one into variable A.
The size can be given by any expression that yields an integer. For example, if you already have an integer variable called n that currently holds 50, then
double* B = new double[n];
allocates an array of 50 doubles.

Why compiler doesn't pass size of array char *arr[] in parameters?

Why compiler doesn't pass size of array char *arr[] in parameters? I wanted to get get size of array passed by parameter but I guess it doesn't work because even char *a[] is char ** my question is why is it and can I make it work?
#include <stdio.h>
#include <stddef.h>
#include <stdio.h>
template<class T, size_t len>
constexpr size_t lengthof(T(&)[len])
{
return len;
}
void printarr(const char *a[]);
int main()
{
const char *a[] = { "aba", "bd", "cd" };
printarr(a);
}
void printarr(const char *a[])
{
for(size_t i = 0, c = lengthof(a); i < c; i++) {
printf("str = %s\n", a[i]);
}
}
You can make it work by using the same trick you used in your lengthof function template.
template<size_t len>
void printarr(const char* (&a)[len])
{
for(size_t i = 0, c = lengthof(a); i < c; i++) {
printf("str = %s\n", a[i]);
}
}
That has been a feature of C since the beginning and carried through into C++.
In the case of string arrays, the solution has been to add a trailing null character to mark the end.
The was probably done for the sake of efficiency when C started on ancient PDP computers.
Use strlen; or better yet std::string or std::vector.
Because this is required by the C++ standard, which inherited this behavior from C (and since C++ wants to stay compatible with C).
As function parameters, arrays are decayed into pointers.
You really want to use something like std::vector<std::string> instead. Learn more about standard C++ STL containers.
C++ is a language what provides low level access to system resources.
Processor is not working with objects, but with memory addresses.
If you want predefined structures, use Java, C#, python, or c++ libs like stl.
From the point of view of the language, your array doesn't have a length; the compiler cannot read your mind and decide where the array begins and where it ends, and it cannot waste memory and processing to perform range checking.
The closest thing to an array length is the number of consecutive memory spaces that have been allocated by a single new[] invocation, but such information is only authoritative for the purpose of allocating and deallocating memory: the "array" you want to process in your function might be only a portion of a single allocation (for example, you might allocate memory to load a whole text file and process each line separately).

How to initialize an array whose size is initially unknown?

Say I have this:
int x;
int x = (State Determined By Program);
const char * pArray[(const int)x]; // ??
How would I initialize pArray before using it?
Because the initial size of the Array is determined by user input
Thanks!
Size of dynamically created array on the stack must be known at compile time.
You can either use new:
const char* pArray = new char[x];
...
delete[] pArray;
or better to use std::vector instead (no need to do memory management manually):
vector<char> pArray;
...
pArray.resize(x);
You cannot initialize an array at compile-time if you are determining the size at run-time.
But depending on what you are trying to do, a non-const pointer to const data may provide you with what you're going for.
const char * pArray = new const char[determine_size()];
A more complete example:
int determine_size()
{
return 5;
}
const char * const allocate_a( int size )
{
char * data = new char[size];
for( int i=0; i<size; ++i )
data[i] = 'a';
return data;
}
int main()
{
const char * const pArray = allocate_a(determine_size());
//const char * const pArray = new char[determine_size()];
pArray[0] = 'b'; // compile error: read-only variable is not assignable
pArray = 0 ; // compile error: read-only variable is not assignable
delete[] pArray;
return 0;
}
I do agree with others that a std::vector is probably more what you're looking for. If you want it to behave more like your const array, you can assign it to a const reference.
#include <vector>
int main()
{
std::vector<char> data;
data.resize(5);
const std::vector<char> & pArray = data;
pArray[0] = 'b'; // compile error: read-only variable is not assignable
}
The example you provided attempts to build the array on the stack.
const char pArray[x];
However, you cannot dynamically create objects on the stack. These types of items must be known at compile time. If this is a variable based on user input then you must create the array in heap memory with the new keyword.
const char* pArray = new char[x];
However, not all items need to be created on the heap. Heap allocation is normally a lot slower then stack allocation. If you want to keep your array on the stack you could always use block based initialization.
#define MAX_ITEMS 100
const char pArray[MAX_ITEMS]
It should be noted that the second option is wasteful. Because you can not dynamically resize this array you must allocate a large enough chunk to hold the maximum number of items your program could create.
Finally, you can always use data structures provide by C++. std::vector is such a class. It provides you a good level of abstraction and item are stored in contingent memory like an array. As noted by one of the other answers you should use the resize option once you know the final size of your vector.
std::vector<char> pArray;
pArray.resize(X);
The reason for this is every time you add an element to a vector, if it no longer has enough room to grow, it has to relocate all items so they can exist next to one another. Using the resize method helps prevent vector from having to grow as you add items.

get length of buffer using lower-level method on stack and heap

Like following code:
#include <iostream>
#include <string>
#define BUF_LEN_HEAP 32
#define BUF_LEN_STACK 64
int getBufLen(const char *buf)
{
//...
}
void foo(const char *buf)
{
int len = getBufLen(buf);
//memcpy(new_buf, buf, len);
//...
}
int main()
{
char buf_stack[BUF_LEN_STACK];
char *buf_heap = new char[BUF_LEN_HEAP];
std::string str("abcdef");
foo(buf_stack);
foo(buf_heap);
foo(str.c_str());
delete [] buf_heap;
return 0;
}
If get length of buffer very difficult pass argument to the functions.
Is there lower-level method to get length of buffer whatever the buffer allocated on stack or heap?
No, there is no way to get the size of an array outside of the method that declared it, and only if the array was declared with a static length (known at compile time).
The usual way to handle this is to pass both a pointer and a size to any function writing to the buffer, just as memcpy does.
With c++ there's always the better option to use a std::vector instead of a plain array. The std::vector carries all needed information within one object that's easy to pass around.

Resizing a C++ std::vector<char> without initializing data [duplicate]

This question already has answers here:
Using vector<char> as a buffer without initializing it on resize()
(6 answers)
Closed 6 years ago.
With vectors, one can assume that elements are stored contiguously in memory, allowing the range [&vec[0], &vec[vec.capacity()) to be used as a normal array. E.g.,
vector<char> buf;
buf.reserve(N);
int M = read(fd, &buf[0], N);
But now the vector doesn't know that it contains M bytes of data, added externally by read(). I know that vector::resize() sets the size, but it also clears the data, so it can't be used to update the size after the read() call.
Is there a trivial way to read data directly into vectors and update the size after? Yes, I know of the obvious workarounds like using a small array as a temporary read buffer, and using vector::insert() to append that to the end of the vector:
char tmp[N];
int M = read(fd, tmp, N);
buf.insert(buf.end(), tmp, tmp + M)
This works (and it's what I'm doing today), but it just bothers me that there is an extra copy operation there that would not be required if I could put the data directly into the vector.
So, is there a simple way to modify the vector size when data has been added externally?
vector<char> buf;
buf.reserve(N);
int M = read(fd, &buf[0], N);
This code fragment invokes undefined behavior. You can't write beyond than size() elements, even if you have reserved the space.
The correct code is like:
vector<char> buf;
buf.resize(N);
int M = read(fd, &buf[0], N);
buf.resize(M);
PS. Your statement "With vectors, one can assume that elements are stored contiguously in memory, allowing the range [&vec[0], &vec[vec.capacity()) to be used as a normal array" isn't true. The allowable range is [&vec[0], &vec[vec.size()).
It looks like you can do what you want in C++11 (though I haven't tried this myself). You'll have to define a custom allocator for the vector, then use emplace_back().
First, define
struct do_not_initialize_tag {};
Then define your allocator with this member function:
class my_allocator {
void construct(char* c, do_not_initialize_tag) const {
// do nothing
}
// details omitted
// ...
}
Now you can add elements to your array without initializing them:
std::vector<char, my_allocator> buf;
buf.reserve(N);
for (int i = 0; i != N; ++i)
buf.emplace_back(do_not_initialize_tag());
int M = read(fd, buf.data(), N);
buf.resize(M);
The efficiency of this depends on the compiler's optimizer. For instance, the loop may increment the size member variable N times.
Another, newer, question, a duplicate of this one, has an answer, which looks like exactly what is asked here. Here's its copy (of v3) for quick reference:
It is a known issue that initialization can not be turned off even
explicitly for std::vector.
People normally implement their own pod_vector<> that does not do
any initialization of the elements.
Another way is to create a type which is layout-compatible with char,
whose constructor does nothing:
struct NoInitChar
{
char value;
NoInitChar() {
// do nothing
static_assert(sizeof *this == sizeof value, "invalid size");
static_assert(__alignof *this == __alignof value, "invalid alignment");
}
};
int main() {
std::vector<NoInitChar> v;
v.resize(10); // calls NoInitChar() which does not initialize
// Look ma, no reinterpret_cast<>!
char* beg = &v.front().value;
char* end = beg + v.size();
}
Writing into and after the size()th element is an undefined behavior.
Next example copies whole file into a vector in a c++ way (no need to know the file's size and no need to preallocate the memory in the vector):
#include <algorithm>
#include <fstream>
#include <iterator>
#include <vector>
int main()
{
typedef std::istream_iterator<char> istream_iterator;
std::ifstream file("example.txt");
std::vector<char> input;
file >> std::noskipws;
std::copy( istream_iterator(file),
istream_iterator(),
std::back_inserter(input));
}
Your program fragment has entered the realm of undefined behavior.
when buf.empty() is true, buf[0] has undefined behavior, and therefore &buf[0] is also undefined.
This fragment probably does what you want.
vector<char> buf;
buf.resize(N); // preallocate space
int M = read(fd, &buf[0], N);
buf.resize(M); // disallow access to the remainder