Passing 2-D array with dynamic size between functions in C/++ - c++

This is "popular" question so I already checked the similar threads but still didnt resolve my issue.
How can I declare 2-D array to hold "strings" - I need array of array of chars AFAIK - and use it as argument to 5 functions one after another where I can pass it by reference and update the content dynamically so following function can compare it. (I might get 10 "strings" or even empty array, so I want to do it correctly with dynamic array coz array content is different from system to system).
"string" => C style string aka array of chars. MAXLEN < 32;
C solution would be more disirable but if vectors can work, why not.

One possible solution in C is as follows:
char **p_strings = calloc(num_strings, sizeof(*p_strings));
for (i = 0; i < num_strings; i++)
{
// Allocate storage for the i-th string (always leave room for '\0')
p_strings[i] = calloc(len_string[i]+1, sizeof(*p_strings[i]));
}
...
// Call a function
my_function(p_strings, num_strings);
You will need to remember to free all this data when you're done with it.
If you need to alter the length of a string, or change the number of strings, you will have to do some fairly painful reallocation. So if you're working in C++, you should probably just be using a std::vector<std::string>.
std::vector<std::string> strings;
strings.push_back("Foo");
strings.push_back("Bar");
...
my_function(strings);
You can even get const pointers to C-style strings for each element, using c_str().

Assuming C++; for this I see no problem with using a vector to string (the string serves as the second dimension):
void foo(vector<string> v) {
cout << v[0]; // Assuming the elements exist!
}
int main(int argc, char *argv[])
{
vector<string> vString; // Make the vector
vString.push_back("something"); // Add a string
foo(vString); // Print out 'something'
}
In your edit you also described that the only thing that will change would be the actual string, so instead of push_backing your strings when they are needed, you can init the vector with the length:
vector<string> vString(10); // Assuming a size of 10
and then use them normally:
vString[4] = "something";
and (in response to the comment), to resize at runtime:
vString.resize(15); // Make it bigger, generates new blank strings

Related

Array of pointers to pointers: managing strings for different languages

I currently have a lot of character arrays that store simple character strings in English for display. I also have pointers to these character arrays.
char helloAr[20] = "Hello";
char timeAr[20] = "Time";
char dogAr[20] = "Dog";
char* helloPtr = helloAr;
char* timePtr = timeAr;
char* dogPtr = dogAr;
I am adding more character arrays in a different language, French to begin with.
char helloArFr[20] = "Bonjour";
char timeArFr[20] = "temps";
char dogArFr[20] = "chien";
If the user selects French I plan to change the address that all my pointers (currently pointing at the English character arrays) point to, so they now point to the French character arrays. I assume I can simply assign a new values to these pointers to do this,
helloPtr = helloArFr;
timePtr = timeArFr;
dogPtr = dogArFr;
however my actual code will have a lot of arrays so I wanted to use a loop to do this rather than lots of statements like the one above.
To do this I plan to create an array of character pointers to the addresses of my character arrays.
char* charArrAddresses [NUMBER_OF_TEXT_ARRAYS]=
{
&helloAr,
&timeAr,
&dogAr,
&helloArFr,
&timeArFr,
&dogArFr,
};
I also plan to store all the pointers in an array so that I can reference them by their location in the array in my single statement, but this is where I need some help as I am unsure on how to do this. Something like this maybe?
char ** langPtrs[NUMBER_OF_LANG_POINTERS]=
{
helloPtr,
timePtr,
dogPtr,
};
In the single statement inside the loop I will use the selected language and the number of char arrays to calculate the correct index for charArrAddresses, lets call this X for now. I will use the value of my loop (say i) to index my langPtrs array. So in my single want to say something like,
for(i = 0; i<NUMBER_OF_LANG_POINTERS; i++)
{
langPtrs[i]= charArrAddresses[X];
}
I am trying to assign the address of the character array stored in index X of charArrAddresses to the pointer in index i in the langPtrs array. I just need help on exactly how to declare the langPtrs array and on how to write my single statement above.
Rather than placing the strings for all languages in a single array, make a 2D array of strings and languages. If you don't plan on modifying any of these strings, you can make them string literals instead of arrays.
enum langs {
LANG_ENGLISH,
LANG_FRENCH,
NUMBER_OF_LANGS
};
enum strings{
STR_HELLO,
STR_TIME,
STR_DOG,
NUMBER_OF_STRINGS
};
const char *langPtrs[NUMBER_OF_LANGS][NUMBER_OF_STRINGS]=
{
{ "Hello", "Time", "Dog" }
{ "Bonjour", "temps", "chien" }
};
Since the values of the symbols in an enum start at 0 and increment by 1 for each subsequent symbol, you can use them an array indexes. The last member of each enum, which does not correspond to an actual element in the array, can be used as the size of the array.
So if you want to, for example, print the string for "time" in French, you would use:
printf("%s\n", langPtrs[LANG_FRENCH][STR_TIME]);
If you want to always print string from whatever the "current" language is, you can create a pointer to the subarray for the current language:
const char **currentLang = langPtrs[LANG_FRENCH];
Then you can use that:
printf("%s\n", currentLang[STR_TIME]);
EDIT:
If you want to keep helloPtr, timePtr, and dogPtr, and you want to set them based on the current language, you can put their addresses in another array:
const char **usedInClassesPtrs[NUMBER_OF_STRINGS] = {
&helloPtr,
&timePtr,
&dogPtr
}
Then loop through the array, dereferencing each element to give you the actual pointers you want to change, and assign them a string from the 2D language array based on the current language as follows:
for (i=0; i<NUMBER_OF_STRINGS; i++) {
*usedInClassesPtrs[i] = langPtrs[currentLang][i];
}
The #debush answer should work quite well.
From your example i assume that only 1 language is active at a time in your code. For that, i suggest to use a resource approach to manage your multi-language support. You can basically load the corresponding resource file of a language and store it in std::vector<std::string>, std::map<const enum/id/etc., std::string> or any similar data structure. This can be done either on the fly or at initialization stage depending on your need. In your code you save a pointer to this data structure std::vector<std::string>* ptr to refer to currently loaded language by invoking (*ptr)[index].

C++ copying char to a char array (Debug assertion failed) says string is not null terminated

Just trying to assign chars to the char array and it says string in not null terminated?
I want to be able to change the teams around in the array like a scoreboard.
#include <string.h>
#include <iostream>
int main(int argc, char* argv[])
{
char Team1[7] = "Grubs";
char Team2[7] = "Giants";
char Team3[7] = "Bulls";
char Team4[7] = "Snakes";
char Team5[7] = "Echos";
char TeamList[5][7];
strcpy_s(TeamList[0], Team1);
strcat_s(TeamList[1], Team2);
strcat_s(TeamList[2], Team3);
strcat_s(TeamList[3], Team4);
strcat_s(TeamList[4], Team5);
TeamList[5][7]= '\0';
system("pause");
return 0;
}
strcat() (which is a "less-safe" version of strcat_s()) requires both strings to be null-terminated. That's because strcat() appends its second parameter (source) where first parameter (dest) ends. It replaces null-terminator of dest with first character of source, appends rest of source and then
a null-character is included at the end of the new string formed by
the concatenation of both
I would simply change
strcpy_s(TeamList[0], Team1);
strcat_s(TeamList[1], Team2);
strcat_s(TeamList[2], Team3);
strcat_s(TeamList[3], Team4);
strcat_s(TeamList[4], Team5);
to
strcpy_s(TeamList[0], Team1);
strcpy_s(TeamList[1], Team2);
strcpy_s(TeamList[2], Team3);
strcpy_s(TeamList[3], Team4);
strcpy_s(TeamList[4], Team5);
strcpy_s() does not have any requirements regarding contents of destination - only its capacity matters.
If you want to stick with strcat_s(), do this:
char TeamList[5][7];
memset(TeamList, 0, sizeof(char) * 5 * 7);
Then, this line:
TeamList[5][7]= '\0';
is not required, It is incorrect anyway, because for N-element array valid indexes are [0; N-1].
EDIT
Since in your case swapping comes into play, I would suggest you totally different approach.
First of all:
#include <string>
Then, initialize teams this way:
std::string TeamList[] =
{
"Grubs",
"Giants",
"Bulls",
"Snakes",
"Echos"
};
Now, TeamList is an array containing 5 elements and each of these elements is an object of type std::string, containing name of a particular team.
Now, if you want to swap, let's say, teams 1 and 3:
std::swap(TeamList[1], TeamList[3]);
std::swap() is a standard C++ function extensively used in standard library implementation. It is overloaded for many standard types, including std::string. This solution has one, critical benefit: if string's content is held on the heap, swapping two strings is as simple as swapping pointers (and some length/capacity variables).
Oh, and one more thing: if you are not familiar with std::string and you would need to get pointer to a buffer containing string's data, you can do it this way:
const char* team_1_raw_name = TeamList[0].c_str();
See this page for more info about std::string
strcat requires that there already be a null-terminated string in the destination to concatenate the source string onto; you're calling it with uninitialised values in the destination.
It looks like you want strcpy in every case, not just the first.
Also, remove the bogus TeamList[5][7]= '\0';. Even if you fix it to write inside the array bounds, each string has already been terminated by strcpy so there's no need to try to do that yourself.
Then stop messing around with low-level arrays and pointers. std::vector<std::string> would be much friendlier.

Insert char * with new into a vector C++

I have code sample like below..
std::vector<char*> vNameList;//it will be defined globally..
int main(int argc, char* argv[])
{
CollectName();
for(int i = 0; i<(int)vNameList.size(); i++)
{
printf("\n %s" , vNameList[i]);//Here gabage values are getting printed on console
}
return 0;
}
void CollectName()
{
char *Name = new char[sizeof(NAME)+1];//NAME datatype is defined having size of 32 char
//processing for Name is performed , which includes assigning Name variable with value..
//now insert it into vector
vNameList.push_back(Name);
delete[] Name; //at this Name value inserted into vector become garbage
}
I believe if we initialize char * with new it must be deleted to avoid memory leaks. But this is leading to modifying values from a vector.
Please guide me so that I can correct my code which will give me correct values.
I have some restriction to use Char * only , so suggest way to achieve this using char*.
You can correct your code by deleting the pointers only after you're done using them (assuming you actually have code which uses the pointers. Your example code doesn't print anything at all, garbage or otherwise).
But it's usually better design to store character strings in a std::string and when you store std::strings in a std::vector, you no longer need to manage the memory manually.
It is because the name of array is treated as pointer in c++(or c, it is inherted).
Therefore when you do
vNameList.push_back(Name);
It inserts the char * into vector,i.e the pointer to the first char(ans hence the string) to the vector, but you delete the pointer afterwards , therefore you get junk values. However if you don't delete the pointer , it works just fine as the pointer still exists , but this way you will not free up memory. Therefore : DONOT USE THIS
here it is : LIVE EXAMPLE
To avoid this hassle : you should use
std::vector<std::string> vNameList;
instead.

Parsing a string to a pointer array of chars: char[0] contains the full string and [1] onward contains nothing

I'm trying to parse a simple string to an array of *char and for some reason when I use string.c_str() it puts the entire string into *char[0] and the rest of the array is left blank (I originally thought that chars could only hold one ASCII character but I guess that they act differently as pointers), could anyone have a scan through my function and tell me if there are any obvious mistakes?
static void SetGame()
{
// Variable Initiation
int myRandom = rand() % (numOfWords - 1);
lengthOfString = wordArray[myRandom].length();
// Reinitiate Pointer Arrays
stringArray = new string[lengthOfString];
isDiscoveredArray = new bool[lengthOfString];
// Parse string to the array of characters
*stringArray = wordArray[myRandom].c_str();
// Set each boolean array value to false
for (int i = 0; i < sizeof(isDiscoveredArray); i++)
{
isDiscoveredArray[i] = false;
}
}
Here are my decelerations of the pointers
// Global Variable and pointer Declerations
string *wordArray;
int numOfWords;
string *stringArray;
int lengthOfString;
bool *isDiscoveredArray;
Any ideas? Thanks.
You are mixing types here. First you build an array of strings and store it in a pointer, then you assign to the first element a const char* coming from c_str. The code you currently have would be if your were creating a string for every character in your selected word.
Make your "stringArray" a const char* to fit with the code you already have, but remove the memory allocation.
You've got an array of std::string and when you deference it (i.e. *stringArray) its the same as stringArray[0], so that is why it always going into the first element of your array.
Since you are setting your array have the same number of elements as the string your are copying has characters, you may just want to use a string rather than a string array to copy it into.
If it supposed to be char* (character array) then you will need to explicitly copy the source, which is the result of wordArray[myRandom].c_str(), into your character array rather than using simple assignment.

(C++) Very Basic Questions Regarding Syntax

C++ novice here. I have some basic questions. In int main( int argc, char *argv[] )
How is char *argv[] supposed to be read (or spoken out to humans)?
Is it possible to clear/erase specific content(s), character(s) in this case, of such array? If yes, how?
Can arrays be resized? If yes, how?
How can I copy the entire content of argv[] to a single std::string variable?
Are there other ways of determining the number of words / parameters in argv[] without argc? If yes, how? (*)
I'd appreciate explanations (not code) for numbers 2-5. I'll figure out the code myself (I learn faster this way).
Thanks in advance.
(*) I know that main(char *argv[]) is illegal. What I mean is whether there's at least a way that does not involve argcat all, like in the following expressions:
for( int i = 0; i < argc; ++i ) {
std::cout << argv[i] << std::endl;
}
and
int i = 0;
while( i < argc ) {
std::cout << argv[i] << std::endl;
++i;
}
Or
int i = 0;
do {
std::cout << argv[i] << std::endl;
++i; } while( i < argc );
It's an array of pointers to char.
Sort of - you can overwrite them.
Only by copying to a new array.
Write a loop and append each argv[i] to a C++ string.
Most implementations terminate the array with a NULL pointer. I can't remember if this is standard or not.
char **argv[]
Is wrong. It should be either char **argv or char *argv[], not a mixture of both. And then it becomes a pointer-to-pointer to characters, or rather a pointer to c-strings, i.e., an array of c-strings. :) cdecl.org is also quite helpful at thing like this.
Then, for the access, sure. Just, well, access it. :) argv[0] would be the first string, argv[3] would be the 4th string. But I totally wouldn't recommend replacing stuff in an array that isn't yours or that you know the internals of.
On array resize, since you're writing C++, use std::vector, which does all the complicated allocation stuff for you and is really safe. Generally, it depends on the array type. Dynamically allocated arrays (int* int_arr = new int[20]) can, static arrays (int int_arr[20]) can't.
To copy everything in argv into a single std::string, loop through the array and append every c-string to your std::string. I wouldn't recommend that though, rather have a std::vector<std::string>, i.e., an array of std::strings, each holding one of the arguments.
std::vector<std::string> args;
for(int i=0; i < argc; ++i){
args.push_back(argv[i]);
}
On your last point, since the standard demands argv to be terminated by a NULL pointer, it's quite easy.
int myargc = 0;
char** argv_copy = argv;
while(++argv_copy)
++myargc;
The while(++argv_copy) will first increment the pointer of the array, letting it point to the next element (e.g., after the first iteration it will point at c-string #2 (argv[1])). After that, if the pointer evaluates to false (if it is NULL), then the loop brakes and you have your myargc. :)
Several options: array of pointer to char OR array of C-string.
You can assign to particular characters to clear them, or you can shift the rest of the array forwards to "erase" characters/elements.
Normal C-style arrays cannot be resized. If you need a resizable array in C++ you should use std::vector.
You'll have to iterate over each of the items and append them to a string. This can be accomplished with C++ algorithms such as copy in conjunction with an ostream_iterator used on an ostringstream.
No. If there was such a way, there wouldn't be any need for argc. EDIT: Apparently for argv only the final element of the array is a null pointer.
1) It is supposed to be char **argv or char *argv[] which is a pointer to an array of characters more commonly known as an array of strings
2) CString is the std library to manipulate C strings (arrays of characters). You cannot resize an array without reallocating, but you can change the contents of elements by referencing it by index:
for(int i = 0; i < argc; ++i)
{
//set all the strings to have a null character in the
//first slot so all Cstring operations on this array,
//will consider it a null (empty) string
argv[i] = 0;
}
3) Technically no, however they can be deleted then reallocated:
int *array = new int[15]; //array of size 15
delete[] array;
array = new int[50]; //array of size 50
4) This is one way:
string *myString;
if(argc > 0)
{
myString = new string(argv[0]);
for(int i = 1; i < argc; ++i)
myString->append(argv[i]);
}
5) Yes, according to Cubbi:
POSIX specifies the final null pointer
for argv, see for example "The
application shall ensure that the last
member of this array is a null
pointer." at
pubs.opengroup.org/onlinepubs/9699919799/functions/exec.html
Which means you can do:
char *val = NULL;
int i = 0;
do
{
val = argv[i++]; //access argv[i], store it in val, then increment i
//do something with val
} while(val != NULL); //loop until end of argv array
It is spoken as "array of pointers to pointers to character" (note that this is not the signature of the main function, which is either int argc, char **argv or int argc, char *argv[] -- which is equivalent).
The argv array is modifiable (lack of const). It is illegal to write beyond the end of one of the strings though or extend the array; if you need to extend a string, create a copy of it and store a pointer in the array; if you need to extend the array, create a copy of the array.
They cannot be resized per se, but reinterpreted as a smaller array (which sort of explains the answer to the last question).
You will be losing information this way -- argv is an array of arrays, because the individual arguments have already been separated for you. You could create a list of strings using std::list<std::string> args(&argv[1], &argv[argc]);.
Not really. Most systems have argv NULL terminated, but that is not a guarantee.
char *argv[] can be read as: "an array of pointers to char"
char **argv can be read as: "a pointer to a pointer to char"
Yes, you may modify the argv array. For example, argv[0][0] = 'H' will modify the first character of the first parameter. If by "erase/clear" you mean remove a character from the array and everything automatically shift over: there is no automatic way to do that - you will need to copy all the characters one-by-one over to the left (including the NULL termination)
No, arrays cannot be resized. You will need to create a new one and copy the contents
How do you want to represent ALL the parameter strings as 1 std::string? It would make more sense to copy it to an array of std::strings
No, there is no special indication of the last entry in the array. you need to use argc
Array in C/C++ is not an object, but just a pointer to first element of array, so you cannot simply delete or insert values.
Answering your questions:
char *argv[] can be read as 'array of pointers to char'
It's possible, but involves direct manipulations with data in memory, such as copying and/or moving bytes around.
No. But you may allocate new array and copy necesary data
By manually copying each element into std::string object
No.
As a summary: C++ is much more low-level language that you think.