How to make an array to store char arrays of variable size? - c++

I need an array to store char arrays of variable size. I could use vectors or anything else, but unfortunately this is for a MPI project and I am forced to use an array so I can send it using MPI::COMM_WORLD.Send(...) function.
My idea comes from this link.
This is a simplified example of the problem I have:
char* arrayStorage[3]; //I want to store 3 char arrays of variable size!
int index = 0;
char array_1[RANDOM_SIZE] = {.....};
char array_2[RANDOM_SIZE] = {.....};
char array_3[RANDOM_SIZE] = {.....};
arraySorage[index] = array_1;
index++;
arraySorage[index] = array_2;
index++;
arraySorage[index] = array_3;
index++;
I have also seen people talking about malloc and stuff like that, but I don't know much about pointers. I do malloc, I have to call free and I don't know where, so I am avoiding that for now.
This code obviously doesn't work, array_1, array_2, array_3 are all OK, but when I try to access them I get garbage. The problem seems to be inside the index variable. Maybe I shouldn't be doing index++, perhaps I should be doing index += RANDOM_SIZE, but that also fails.
How can I store variable size char arrays in an array?

Use malloc and free (or new and delete in C++). You can do it with vectors too - as vectors can be treated as arrays.
char *str = "hello world";
// need the +1 for null character
arraySorage[0] = (char *)malloc (strlen(str) + 1);
strcpy(arraySorage[0], str);
...
free(arraySorage[0]);
with new/delete
arraySorage[0] = new char[strlen(str)+1];
strcpy(arraySorage[0], str);
...
delete arraySorage[0];
Using vector and std::string is the correct C++ way, for lots of reasons, including not leaking memory and proper handling of exceptions.

Related

Do not understand about strcpy in C++

char* s1;
strcpy(s1,"smilehihi");
s1[6] = 'a';
When I compile, VS do not have any errors. But in the runtime, my code makes mistake. I think I do not really understand about strcpy
The main issue here is not the strcpy() function but the fact that you don't allocate any memory for the string itself.
If I were you, I would do something like
char* s1=(char*)malloc(SIZE); // the SIZE is the predefined maximum size of your string
strcpy(s1,"smilehihi");
s1[6] = 'a';
Edit:
Just as an advice, consider using stpncpy(). It helps to avoid buffer overflow, and, in your case, will help you avoid exceeding the maximum size of char*
char * stpncpy(char * dst, const char * src, size_t len);
The problem is that you have not allocated any space for what you wish to store in s1: "smilehihi". You declare s1 as a pointer variable, but it needs something to point at. You can allocate space by using the new operator.
char* s1 = new char[stringLength + 1]; //stringLength = length of string stored
// + 1 to hold null terminator character
strcpy(s1, "smilehihi");
s1[6] = 'a';
You have to declare #define _CRT_SECURE_NO_WARNINGS at the top of your main file to avoid an error during compilation due to strcpy() being deprecated.
You need to allocate variable first by malloc() or by using the keyword new .
Also deallocate the memory at the end
At first you should allocate char* s1.
char *s1 = new char[9]; // C++ version
or you can you use C version:
char *s1 = (char*)malloc(9);
Then you can use following code:
strcpy(s1, "smilehihi");
s1[6] = 'a';

char array with size got from user - cpp

I want to create an array of chars (char path[size] = "") while getting "size" from user.
When I try something like this:
int size = getSizeFromUser();
char path[size] = "";
I get a warning says "expression must have a constant value".
How can I do it right?
Thanks a lot!
How can I do it right?
Variable length arrays aren't standard c++. The correct way to do it is to use a std::vector or a std::string instead of a raw array:
int size = getSizeFromUser();
std::string path(size,'\0');
In order to create a table with the users size you have to allocate your memory on the heap
int size = 4;
char *path = new char[size];
after using your array you have to manually delete it from the heap
delete path;
If you declare char path[size]; then size has to be known at compile time. You read it at runtime so you need to use dynamic memory allocation, like char* path = new char[size]; and when you are finished call delete []path;.
If you want this to be a string with size visible characters, please consider that C-strings are null-terminated, meaning that you would have to reserve one extra char at the end of the array and set it to 0.
A better solution for a C++ program would probably be to use a std::string instead of a char*.
When creating an array, its size must be constant.
If you want a dynamically sized array, you will have to allocate memory for it and you'll also need to free it when you're done with it.
To simply all these its always nice to use std::string

Deleting a dynamic array after it has been returned from a function

I am new to C++ and paranoid of memory leaks. I'll strip my code down to just the important bits:
If I have a function like this:
char * myString = "Discombobulate";
char * ToUppercase()
{
int length = strlen(myString);
char * duplicateString = new char [length];
strcpy(duplicateString, myString);
//char arithmetic to turn every letter in duplicateString to uppercase
return duplicateString;
}
Obviously, I need to perform a delete[] to avoid memory leaks. Now what I wanted to know is if I can do the delete in main(), like so:
int main () {
char * result = Upper();
std::cout << result << std::endl;
delete[] result;
}
Will this work properly? Is there any catch to doing it like this?
Now what I wanted to know is if I can do the delete in main()
Yes, you can and you should.
BTW1: Think about using std::string, std::vector, smart pointers, to avoid such kind of manual memory management, since it's c++.
BTW2:
char * duplicateString = new char [length];
should be
char * duplicateString = new char [length + 1];
The last position will be used for the terminating null character '\0'.
Will this work properly?
Yes. As long as it is a valid pointer, you could delete it outside of the function that called new. Should you? Well...
Is there any catch to doing it like this?
Yes. It's bad practice. You're allocating resources in a function and expecting the caller to clean them up. It goes against RAII, as people in the comments have explained. Along with the advice to use std::string (do use it), you can use std::unique_ptr and friends instead of raw pointers.
YEs you can delete it the way you have... By the way, you can also assign memory for the pointer and pass that as a parameter to the function and delete it after it returns from the function.
char * duplicateString = new char [length + 1];
ToUppercase(char* duplicateString );
if( duplicateString ){ delete []duplicateString ; duplicateString = NULL;}

C++: How to use new to find store for function return value?

I'm reading the 3rd edition of The C++ Programming Language by Bjarne Stroustrup and attempting to complete all the exercises. I'm not sure how to approach exercise 13 from section 6.6, so I thought I'd turn to Stack Overflow for some insight. Here's the description of the problem:
Write a function cat() that takes two C-style string arguments and
returns a single string that is the concatenation of the arguments.
Use new to find store for the result.
Here's my code thus far, with question marks where I'm not sure what to do:
? cat(char first[], char second[])
{
char current = '';
int i = 0;
while (current != '\0')
{
current = first[i];
// somehow append current to whatever will eventually be returned
i++;
}
current = '';
i = 0;
while (current != '\0')
{
current = second[i];
// somehow append current to whatever will eventually be returned
i++;
}
return ?
}
int main(int argc, char* argv[])
{
char first[] = "Hello, ";
char second[] = "World!";
? = cat(first, second);
return 0;
}
And here are my questions:
How do I use new to find store? Am I expected to do something like std::string* result = new std::string; or should I be using new to create another C-style string somehow?
Related to the previous question, what should I return from cat()? I assume it will need to be a pointer if I must use new. But a pointer to what?
Although the problem doesn't mention using delete to free memory, I know I should because I will have used new to allocate. Should I just delete at the end of main, right before returning?
How do I use new to find store? Am I expected to do something like std::string* result = new std::string; or should I be using new to create another C-style string somehow?
The latter; the method takes C-style strings and nothing in the text suggests that it should return anything else. The prototype of the function should thus be char* cat(char const*, char const*). Of course this is not how you’d normally write functions; manual memory management is completely taboo in modern C++ because it’s so error-prone.
Although the problem doesn't mention using delete to free memory, I know I should because I will have used new to allocate. Should I just delete at the end of main, right before returning?
In this exercise, yes. In the real world, no: like I said above, this is completely taboo. In reality you would return a std::string and not allocate memory using new. If you find yourself manually allocating memory (and assuming it’s for good reason), you’d put that memory not in a raw pointer but a smart pointer – std::unique_ptr or std::shared_ptr.
In a "real" program, yes, you would use std::string. It sounds like this example wants you to use a C string instead.
So maybe something like this:
char * cat(char first[], char second[])
{
char *result = new char[strlen(first) + strlen(second) + 1];
...
Q: How do you "append"?
A: Just write everything in "first" to "result".
As soon as you're done, then continue by writing everything in "second" to result (starting where you left off). When you're done, make sure to append '\0' at the end.
You are supposed to return a C style string, so you can't use std::string (or at least, that's not "in the spirit of the question"). Yes, you should use new to make a C-style string.
You should return the C-style string you generated... So, the pointer to the first character of your newly created string.
Correct, you should delete the result at the end. I expect it may be ignored, as in this particular case, it probably doesn't matter that much - but for completeness/correctness, you should.
Here's some old code I dug up from a project of mine a while back:
char* mergeChar(char* text1, char* text2){
//Find the length of the first text
int alen = 0;
while(text1[alen] != '\0')
alen++;
//Find the length of the second text
int blen = 0;
while(text2[blen] != '\0')
blen++;
//Copy the first text
char* newchar = new char[alen + blen + 1];
for(int a = 0; a < alen; a++){
newchar[a] = text1[a];
}
//Copy the second text
for(int b = 0; b < blen; b++)
newchar[alen + b] = text2[b];
//Null terminate!
newchar[alen + blen] = '\0';
return newchar;
}
Generally, in a 'real' program, you'll be expected to use std::string, though. Make sure you delete[] newchar later!
What the exercise means is to use new in order to allocate memory. "Find store" is phrased weirdly, but in fact that's what it does. You tell it how much store you need, it finds an available block of memory that you can use, and returns its address.
It doesn't look like the exercise wants you to use std::string. It sounds like you need to return a char*. So the function prototype should be:
char* cat(const char first[], const char second[]);
Note the const specifier. It's important so that you'll be able to pass string literals as arguments.
So without giving the code out straight away, what you need to do is determine how big the resulting char* string should be, allocate the required amount using new, copy the two source strings into the newly allocated space, and return it.
Note that you normally don't do this kind of memory management manually in C++ (you use std::string instead), but it's still important to know about it, which is why the reason for this exercise.
It seems like you need to use new to allocate memory for a string, and then return the pointer. Therefore the return type of cat would be `char*.
You could do do something like this:
int n = 0;
int k = 0;
//also can use strlen
while( first[n] != '\0' )
n ++ ;
while( second[k] != '\0' )
k ++ ;
//now, the allocation
char* joint = new char[n+k+1]; //+1 for a '\0'
//and for example memcpy for joining
memcpy(joint, first, n );
memcpy(joint+n, second, k+1); //also copying the null
return joint;
It is telling you to do this the C way pretty much:
#include <cstring>
char *cat (const char *s1, const char *s2)
{
// Learn to explore your library a bit, and
// you'll see that there is no need for a loop
// to determine the lengths. Anything C string
// related is in <cstring>.
//
size_t len_s1 = std::strlen(s1);
size_t len_s2 = std::strlen(s2);
char *dst;
// You have the lengths.
// Now use `new` to allocate storage for dst.
/*
* There's a faster way to copy C strings
* than looping, especially when you
* know the lengths...
*
* Use a reference to determine what functions
* in <cstring> COPY values.
* Add code before the return statement to
* do this, and you will have your answer.
*
* Note: remember that C strings are zero
* terminated!
*/
return dst;
}
Don't forget to use the correct operator when you go to free the memory allocated. Otherwise you'll have a memory leak.
Happy coding! :-)

Dealing with char arrays in C++

I have this C-styled piece of initialization code:
const char * const vlc_args[] =
{
"-I", "dummy",
"--ignore-config",
"--extraintf=logger",
"--verbose=2"
"--plugin-path=/usr/lib/vlc"
};
//tricky calculation of the char space used
libvlc_new(sizeof(vlc_args)/sizeof(vlc_args[0]), vlc_args, &exc);
Since I need to make the --plugin-path parameter dynamic, I can't use a static array anymore. So I came up with a C++ alternative:
std::string pluginpath = "test";
libvlc_exception_t exc;
std::vector<std::string> args;
args.push_back("-I");
args.push_back("dummy");
args.push_back("--ignore-config");
args.push_back("--extraintf=logger");
args.push_back("--verbose=2");
args.push_back("--ipv4");
args.push_back("--plugin-path=" + pluginpath);
std::string combinedString;
for (size_t idx = 0; idx < args.size(); ++idx)
{
combinedString.append(args[idx]);
combinedString.resize(combinedString.size() + 1);
combinedString[combinedString.size() - 1] = 0;
}
combinedString.resize(combinedString.size() + 1);
combinedString[combinedString.size() - 1] = 0;
size_t size = combinedString.size();
const char * data = combinedString.c_str();
libvlc_new(size, &data, &exc); // => error occurs here (not at end of scope or anything)
But this results in a segmentation fault. So there must be an error in my code, which I can't seem to find.. Can anyone spot it?
Solved!
Thanks to Joseph Grahn and Jason Orendorff. My idea on the memory layout of the C-style array was wrong. I thought all data was organized as a big sequential block. In reality it's a list of pointers to the first character of each individual string.
This code works:
std::vector<const char*> charArgs;
for (size_t idx = 0; idx < args.size(); ++idx)
{
charArgs.push_back(&(args[idx][0]));
}
mVLCInstance = libvlc_new(charArgs.size(),
&charArgs[0],
&mVLCException);
I think Josef Grahn is right: the API wants an actual array of pointers.
If you don't need to add arguments programmatically, you can just go back to using an array:
std::string pluginpath = "test";
std::string pluginpath_arg = "--plugin-path=" + pluginpath;
const char *args[] = {
"-I", dummy, "--ignore-config", ..., pluginpath_arg.c_str()
};
libvlc_exception_t exc;
libvlc_new(sizeof(args) / sizeof(args[0]), args, &exc);
EDIT: There might also be a problem with using c_str() here. This is true if VLC keeps the pointer and uses it again later; I can't tell if that's the case from the docs.
You are appending all arguments into a single string, then you pass a pointer to the const char * string to libvlc_new as if it were an array of char *.
(I'm not sure this is the problem, but it seems a bit strange.)
Have you tried:
libvlc_new(size, data, &exc);
instead of
libvlc_new(size, &data, &exc);
It seems you use the null bytes to make the string act like an array of characters, but then you pass a pointer to the char* "array" instead of just the array.
The library call expects a pointer to an array of const char* (that is several pointers), but you pass it a single pointer. That more characters got appended to the end of that string doesn't matter.
To dynamically build an array of the required pointers you could use another vector:
// store c_str() pointers in vector
std::vector<const char*> combined;
for (size_t idx = 0; idx < args.size(); ++idx) {
combined.push_back(args[idx].c_str());
}
// pass pointer to begin of array (it is guaranteed that the contents of a vector
// are stored in a continuous memory area)
libvlc_new(combined.size(), &(combined[0]), &exc);
Jason Orendorff's remark is also valid here: This will not work if libvlc_new stores the passed pointer internally for later use.
Regarding the segmentation violation
No solution, as there will probably be more problems
You are sending only 1 string in. (not sure if it is allowed by libvlc_new) So the first parameter should be set to 1, ie size = 1. I believe this will solve the segmentation problem. But I doubt libvlc_new can be called with just one line of multiple parameters.
In the original code sizeof(vlc_args)/sizeof(vlc_args[0]) will have the number of parameters as entries in the vector. In your example equal 6.
Your code
size_t size = combinedString.size(); // a long string, size >> 1
const char * data = combinedString.c_str(); // data is a pointer to the string
libvlc_new(size, &data, &exc);
// size should be 1 because data is like an array with only one string.
// &data is the adress to the "array" so to speak. If size > 1 the function
// will try to read pass the only string available in this "array"
I think Jason Orendorff has a good solution to fix it all...
I had this same issue; I wanted to dynamicly generate the arguments, but in a safe c++ way.
The solution I hit on was to use the unique_ptr<[]> new to C++ 11. For example:
unique_ptr<char *[]> vlc_argv;
int vlc_argc;
...
auto vlc(libvlc_new(vlc_argc, vlc_argv.get()));
This gives me a nice RAII object to hold my arguments in that I can still pass into libvlc_new(). Since the arguments are raw pointers in argv, managed by the OS, we can just use those in our vlc_argv directly.
As a further example assume I process the first few args out of argv (somewhere in the "..." area above), and want to pass everything from next_arg on to libvlc_new():
vlc_argv = std::unique_ptr<char *[]>(new char *[2 + argc - next_arg]);
vlc_argv[0] = argv[0];
for (vlc_argc=1; vlc_argc <= argc - next_arg; vlc_argc++) {
vlc_argv[vlc_argc] = argv[next_arg + vlc_argc - 1];
}
vlc_argv[vlc_argc] = 0;
The problem is due to the use of c_str() and storing the pointer.
See stringstream, string, and char* conversion confusion
EDIT Forget what I said... it was late... see the comments :)