Return string array in C++ function - c++

I am new to C++.
For a school project I need to make a function which will be able to return a string array.
Currently I have this in my header:
Config.h
string[] getVehicles(void);
Config.cpp
string[] Config::getVehicles(){
string test[5];
test[0] = "test0";
test[1] = "test1";
test[2] = "test2";
test[3] = "test3";
test[4] = "test4";
return test;}
Obviously this does not work but that's the idea of what I am trying to do.
In Java this would be the way to do it. I've tried googling my problem but I didn't come across any answers that were clear to be honest.

Maybe it is better to use a vector in this case, but this is not a correct answer for the question. The reason why it doesn't work is that the variable test just exists in the scope of your function.
So you have to manage the memory on your own. Here is an example:
string* getNames() {
string* names = new string[3];
names[0] = "Simon";
names[1] = "Peter";
names[2] = "Dave";
return names;
}
In this case you return a pointer of the position in the heap. All the memory in the heap has to free manually. So it is now your work to delete the memory, if you don't need it anymore:
delete[] names;

In C++ you don't use an array, but a std::vector instance. Arrays in C++ must have a compile-time fixed length while std::vector instances can change their length at runtime.
std::vector<std::string> Config::getVehicles()
{
std::vector<std::string> test(5);
test[0] = "test0";
test[1] = "test1";
test[2] = "test2";
test[3] = "test3";
test[4] = "test4";
return test;
}
std::vector can also grow dynamically, so in a C++ program you will find more often something like
std::vector<std::string> Config::getVehicles()
{
std::vector<std::string> test; // Empty on creation
test.push_back("test0"); // Adds an element
test.push_back("test1");
test.push_back("test2");
test.push_back("test3");
test.push_back("test4");
return test;
}
Allocating dynamically an array of std::string is technically possible but a terrible idea in C++ (for example C++ doesn't provide the garbage collector that Java has).
If you want to program in C++ then grab a good C++ book and read it cover to cover first... writing Java code in C++ is a recipe for a disaster because the languages, despite the superficial braces similarity, are very very different in many fundamental ways.

Try this
#include <iostream>
#include <string>
using namespace std;
string * essai()
{
string * test = new string[6];
test[0] = "test0";
test[1] = "test1";
test[2] = "test2";
test[3] = "test3";
test[4] = "test4";
cout<<"test et *test\t"<<&test<<' '<<*(&test)<<'\n';
return test;
}
main()
{
string * toto;
cout<<"toto et *toto\t"<<&toto<<' '<<*(&toto)<<'\n';
toto = essai();
cout<<"**toto\t"<<*(*(&toto))<<'\n';
cout<<"toto\t"<<&toto<<' '<<*(&toto)<<'\n';
for(int i=0; i<6 ; i++)
{
cout<<toto[i]<<' '<<&toto[i]<<'\n';
}
}
For example, in my computer, the result is
toto et *toto 0x7ffe3a3a31b0 0x55dec837ae20
test et *test 0x7ffe3a3a3178 0x55dec9ddd038
**toto test0
toto 0x7ffe3a3a31b0 0x55dec9ddd038
test0 0x55dec9ddd038
test1 0x55dec9ddd058
test2 0x55dec9ddd078
test3 0x55dec9ddd098
test4 0x55dec9ddd0b8
0x55dec9ddd0d8
Getting addresses and contents of addresses could help you to understand that an array in c++ is really rudimentary : it offers no methods and you could access an index without allocating memory (the value 6 in the loop).
Your first example show a direct allocation of a local array (test), so you can't return it (the local array dies), in this example, the local variable dies also but there is always a variable that access at this part of allocated memory, the function, and then the variable that receive the result of the function, so the variable test is dead after the calling of the function but the memory is still allocated. Regards.

Related

Updated: Memory Location being outputted instead of desired string

->Please see my edited question below the horizontal divider:
I was trying to return a string array from a function to another function. The code compiled successfully; however, it failed upon execution. I have included my code below:
string Occupant::LoadDataFunction()
{
string array[5] = {"hello", "you", "are", "a", "human"};
return array[5];
}
void Occupant::LoadData()
{
string loc_array[5];
loc_array[5] = Occupant::LoadDataFunction();
string park_array[5];
park_array[5] = Occupant::LoadDataFunction();
string lease_array[5];
lease_array[5] = Occupant::LoadDataFunction();
}
When I debugged the code, I found out that the problem was in the return statement of the function:
return array[5]
The debugger had the following output:
Signal = SIGSEGV (Segmentation Fault)
this = { Occupant * const | 0x61ff2f} 0x61ff2f
array = {std::_cxx11::basic_string< char, std::char_traits, std::allocator> [5]}
Can someone tell me what is wrong with my return statement, why it is causing a segmentation error? Thanks
** I know there are many other similar questions on the web, but none of them involved a segmentation fault in the return statement Therefore, I would appreciate it if this question is not marked as a duplicate. If you need any more information, just inform me in the comments box. Thanks for your help!
EDIT Thanks everyone, I didn't notice that glitch I just fixed it. I actually wanted to return the entire array, I did it with pointers this time. Here's my code:
string* Occupant::LoadDataFunction()
{
string* array = new string[5];
array[0] = "hello";
array[1] = "hello";
array[2] = "hello";
array[3] = "hello";
array[4] = "hello";
return array;
}
void Occupant::LoadData()
{
string **loc_array = new string*[5];
loc_array[5] = Occupant::LoadDataFunction();
string **park_array = new string*[5];
park_array[5] = Occupant::LoadDataFunction();
string **lease_array = new string*[5];
lease_array[5] = Occupant::LoadDataFunction();
for (int i = 0; i < 5; i++)
{
cout << &loc_array[i] << " : location" << endl;
cout << &park_array[i] << " : parking" << endl;
cout << &lease_array[i] << " : leased" << endl;
}
}
The problem now is that when I run the code, rather than printing hello fifteen times in total, it prints the memory address. Here's what I got:
0xf56d50 : location
0xf56df8 : parking
0xf56ea0 : leased
0xf56d54 : location
0xf56dfc : parking
0xf56ea4 : leased
0xf56d58 : location
0xf56e00 : parking
0xf56ea8 : leased
0xf56d5c : location
0xf56e04 : parking
0xf56eac : leased
0xf56d60 : location
0xf56e08 : parking
0xf56eb0 : leased
I expected a "Hello" word outputted wherever a memory address is outputted. Can anyone explain this now? Thanks for all your answers!
To return an array, use std::array, as a std::array is copyable and assignable, unlike a vanilla array.
#include <array>
#include <algorithm>
typedef std::array<std::string, 5> Array5Strings;
Array5Strings LoadDataFunction()
{
Array5Strings ret;
std::fill(ret.begin(), ret.end(), "hello");
return ret;
}
Also, I used std::fill to quickly set the items to "hello".
Live Example
If for some weird reason you can't use std::array, then the other alternative is to create a struct that contains an array of 5 strings, and return the struct. A struct is copyable, thus can be returned directly from a function.
#include <algorithm>
#include <string>
#include <algorithm>
#include <iostream>
struct Array5Strings
{
std::string sArray[5];
};
Array5Strings LoadDataFunction()
{
Array5Strings ret;
std::fill(std::begin(ret.sArray), std::end(ret.sArray), "hello");
return ret;
}
int main()
{
Array5Strings val = LoadDataFunction();
std::cout << val.sArray[0]; // prints the first value
}
Live Example
Regardless of which you choose, note that there are no pointers involved.
You are accessing the arrays using out of bounds index.
When you have an array declared as:
string loc_array[5];
the valid indices are 0 - 4.
Use of
loc_array[5] = Occupant::LoadDataFunction();
is cause for undefined behavior. I suspect you meant to use:
loc_array[4] = Occupant::LoadDataFunction();
Similarly, you need to use:
park_array[4] = Occupant::LoadDataFunction();
and
lease_array[4] = Occupant::LoadDataFunction();
You also need to change Occupant::LoadDataFunction.
string Occupant::LoadDataFunction()
{
string array[5] = {"hello", "you", "are", "a", "human"};
return array[4];
}
Update
The updated implementation of of Occupant::LoadDataFunction is better but there are still problems.
Each time the function gets called, you are allocating an array of strings. It's not clear whether that's the intention. If that's the intention, then the calling code has to take responsibility for deallocating that memory.
The calling code still suffers from out our bounds memory access. The line
string **loc_array = new string*[5];
allocates memory for 5 string*. The valid indices for loc_array is still 0 - 4. Hence,
loc_array[5] = Occupant::LoadDataFunction();
suffers from out of bounds memory access problem. It's not clear how you wish to use the return value of Occupant::LoadDataFunction. Hence, I am not able to suggest a way to solve the problem.
The lines
park_array[5] = Occupant::LoadDataFunction();
lease_array[5] = Occupant::LoadDataFunction();
suffer from the same problem.
Try returning array[4]. There is no array[5], since arrays start at index 0.
The range of an array of size N is from 0 to N-1. So the 5 is out of the range. And you may want to return an array as a result, but the array is constructed in the function LoadData, after the function is executed, the array is invalid. You may use dynamic allocation for your purpose or use a global variable.
When you use the dynamic allocation array, you can just use string* instead of string**, and you should release the allocated memory.

C++ error on delete[] after iterating through pointer

Environment: Windows 7 pro x64, Microsoft Visual Studio 2015 Enterprise, Version 14.0.25424.00 Update 3
int testFunction()
{
std::string _orig = "[188 80% (1/2)O:152]";
std::string _orig2 = "[999 99% (1/1)O:999]";
char *orig = NULL;
char *orig2 = NULL;
orig = new char[_orig.length() + 1];
strcpy(orig, _orig.c_str());
orig2 = new char[_orig2.length() + 1];
strcpy(orig2, _orig2.c_str());
*orig++;
*orig2++;
int a = atoi(orig);
int b = atoi(orig2);
delete[] orig;
delete[] orig2;
return 0;
}
Running the above code crashes with the "_CrtIsValidHeapPointer(block)" error.
If I don't iterate (*orig++ and *orig2++), then no issues.
So my question is, how can I iterate through the pointers and then when I'm done doing what I need to do with them, delete[] them correctly?
You did not delete the pointers you allocated!
delete must be called on the original memory address returned by new. Since you did orig++, you cant delete the address being pointed at!
Iterating can be done with an index, and using array subscription to dereference:
orig[i] = 'a';
Which is the same as doing this:
*(orig+i) = 'a';
Or you can get another pointer onto the same data, and modify this one.
char* pOrig = orig;
++pOrig;
Why did you write
*orig++; // why dereferencing?
Just ++ by itself would do the iteration.
Avoid to use raw pointers. Your code can be simpler:
std::string orig = "[188 80% (1/2)O:152]";
std::string orig2 = "[999 99% (1/1)O:999]";
int a = atoi(orig.c_str() + 1);
int b = atoi(orig2.c_str() + 1);
Your mistake is that you try to delete the shifted pointers instead of the original pointers. As the result heap manager gets wrong allocated block information usually put before the allocated pointer and you got heap corruption.
how can I iterate through the pointers and then when I'm done doing what I need to do with them, delete[] them correctly?
Create a copy of the pointer:
char* orig = new char[size];
char* i = orig;
*i++ = 'a';
delete orig;
A perhaps more common idiom is to dereference a temporary:
for(int i = 0; i < size - 1; i++)
orig[i] = 'a';
I would love to [use std::string], but I need to use atoi(), which won't work on std::string
You are mistaken. atoi works with std::string just fine. Simply use std::string::c_str() just like you did with strcpy. There is absolutely no reason to allocate a block of memory with new.
int testFunction()
{
std::string _orig = "[188 80% (1/2)O:152]";
int a = 0;
for (std::string::iterator it = _orig.begin(); it != _orig.end(); ++it)
{
if (isdigit((char)*it))
a = (atoi(it._Ptr));
}
return 0;
}
I got it. Thanks for everyone who helped me come to this conclusion. Staying with std::string was in fact the best approach.

Array as out parameter in c++

I created a function that returns an error code (ErrCode enum) and pass two output parameters. But when I print the result of the function, I don't get the correct values in the array.
// .. some codes here ..
ErrCode err;
short lstCnt;
short lstArr[] = {};
err = getTrimmedList(lstArr, &lstCnt);
// list returned array (for comparison)
for (int i=0; i<lstCnt; ++i)
printf("lstArr[%3d] = %d", i, lstArr[i]);
// .. some codes here ..
The getTrimmedList function is like this:
ErrCode getTrimmedList(short* vList, short* vCnt)
{
short cnt;
ErrCode err = foo.getListCount(FOO_TYPE_1, &cnt);
if (NoError!=err) return err;
short* list = new short [cnt];
short total = 0;
for (short i=0; i<cnt; ++i)
{
FooBar bar = foo.getEntryByIndex(FOO_TYPE_1, i);
if (bar.isDeleted) continue;
list[total] = i;
++total;
}
*vCnt = total;
//vList = (short*)realloc(index, sizeof(short)*total);
vList = (short*)malloc(sizeof(short)*total);
memcpy(vList, list, sizeof(short)*total)
// list returned array (for comparison)
for (int i=0; i<lstCnt; ++i)
printf("lstArr[%3d] = %d", i, lstArr[i]);
return NoError;
}
where:
foo is an object that holds arrays of FooBar objects
foo.getListCount() returns the number of objects with type FOO_TYPE_1
FOO_TYPE_1 is the type of object we want to take/list
foo.getEntryByIndex() returns the ith FooBar object with type FOO_TYPE_1
bar.isDeleted is a flag that tells if bar is considered as 'deleted' or not
What's my error?
Edit:
Sorry, I copied a wrong line. I commented it above and put the correct line.
Edit 2
I don't have control over the returns of foo and bar. All their function returns are ErrCode and the outputs are passed through parameter.
Couple of questions before I can answer your post...
Where is "index" defined in:
vList = (short*)realloc(index, sizeof(short)*total);
Are you leaking the memory associated with:
short* list = new short [cnt];
Is it possible you have accidentally confused your pointers in memory allocation? In any case, here is an example to go from. You have a whole host of problems, but you should be able to use this as a guide to answer this question as it was originally asked.
WORKING EXAMPLE:
#include "stdio.h"
#include "stdlib.h"
#include "string.h"
int getTrimmedList(short** vList, short* vCnt);
int main ()
{
// .. some codes here ..
int err;
short lstCnt;
short *lstArr = NULL;
err = getTrimmedList(&lstArr, &lstCnt);
// list returned array (for comparison)
for (int i=0; i<lstCnt; ++i)
printf("lstArr[%3d] = %d\n", i, lstArr[i]);
// .. some codes here ..
return 0;
}
int getTrimmedList(short** vList, short* vCnt)
{
short cnt = 5;
short* list = new short [cnt];
short* newList = NULL;
short total = 0;
list[0] = 0;
list[1] = 3;
list[2] = 4;
list[3] = 6;
total = 4;
*vCnt = total;
newList = (short*)realloc(*vList, sizeof(short)*total);
if ( newList ) {
memcpy(newList, list, sizeof(short)*total);
*vList = newList;
} else {
memcpy(*vList, list, sizeof(short)*total);
}
delete list;
return 0;
}
You have serious problems.
For starters, your function has only one output param as you use it: vCnt.
vList you use as just a local variable.
realloc is called with some index that we kow nothing about, not likely good. It must be something got from malloc() or realloc().
The allocated memory in vList is leaked as soon as you exit getTrimmedList.
Where you call the function you pass the local lstArr array as first argument that is not used for anything. Then print the original, unchanged array, to bounds in cnt, while it has 0 size still -- behavior is undefined.
Even if you managed to pass that array by ref, you could not reassign it to a different value -- C-style arrays can't do that.
You better use std::vector that you can actually pass by reference and fill in the called function. eliminating the redundant size and importantly the mess with memory handling.
You should use std::vector instead of raw c-style arrays, and pass-by-reference using "&" instead of "*" here. Right now, you are not properly setting your out parameter (a pointer to an array would look like "short **arr_ptr" not "short *arr_ptr", if you want to be return a new array to your caller -- this API is highly error-prone, however, as you're finding out.)
Your getTrimmedList function, therefore, should have this signature:
ErrCode getTrimmedList(std::vector<short> &lst);
Now you no longer require your "count" parameters, as well -- C++'s standard containers all have ways of querying the size of their contents.
C++11 also lets you be more specific about space requirements for ints, so if you're looking for a 16-bit "short", you probably want int16_t.
ErrCode getTrimmedList(std::vector<int16_t> &lst);
It may also be reasonable to avoid requiring your caller to create the "out" array, since we're using smarter containers here:
std::vector<int16_t> getTrimmedList(); // not a reference in the return here
In this style, we would likely manage errors using exceptions rather than return-codes, however, so other things about your interface would evolve, as well, most likely.

How to allocate two-pointer to Struct?

I have this code:
struct human
{
string name;
string adress;
string com_name;
string com_adress;
};
human **arr_human;
I need to allocate array of pointers.
I try it this:
arr_human = new human * [ 1000 ];
but I can't use this struct( example:
arr_human[0]->name = oName;
arr_human[0]->adress = oAddr;
arr_human[0]->com_name = cName;
arr_human[0]->com_adress = cAddr;
Why?
Like jrok said, you allocated the pointers but not the objects. You probably want to do something like this:
for (int i = 0; i < 1000; i++)
arr_human[i] = new human[numObjectsYouWant];
Since you're using C++, you might as well use vectors though. Does away with many of the problems you might encounter with using raw pointers.
vector<human> arr_humans;

Is it possible to create an array on the heap at run-time, and then allocate more space whenever needed?

Lets say I do
char *a;
a = new char[4];
Now, is it possible to extend the size of the array further?
Say I want to keep any values I put inside a[0] to a[3] but now I want more space for a[4] and a[5] etc. Is that possible in C++?
At first I felt like maybe I could just make:
char* a[10];
a[0] = new char[size];
Whenever more space is needed I can go to a[1] and allocate more space. This is the only alternative I can think of at the moment.
Unfortunately it's not possible in C++. You have to allocate a new area and copy the old into the new.
However, C++ have other facilities, like std::vector, that alleviates the need to manually handle these things.
Instead of char* a , you can use char** a;
i.e.
char** a = new char*[4];
for(int i=0; i<4; i++)
(*a)[i] = new char(i); /* feed values to array */
/* now you need a[4]? no problem */
char tmp = (*a)[3];
a[3] = new char[2];
a[3][0] = tmp;
a[3][1] = xyz; /* this is you a[4] */
Though this is a work around, I would suggest to used vector.