Returning Array in C++ returns unaccessable elements - c++

I am working on a project where I parse a string in to an array and then return it back to the main function. It parses fine but when I return it to the main function I can't get access to the array elements.
//This is from the Main function. It calls commaSeparatedToArray which returns the array.
for (int i = 0; i < numberOfStudents; i++) {
string * parsedToArray = mainRoster->commaSeparatedToArray(studentData[i]);
Degree degreeType = SOFTWARE;
for (int i = 0; i < 3; i++) {
if (degreeTypeStrings[i] == parsedToArray[8])
degreeType = static_cast<Degree>(i);
}
mainRoster->add(parsedToArray[0], parsedToArray[1], parsedToArray[2], parsedToArray[3], stoi(parsedToArray[4]), stoi(parsedToArray[5]), stoi(parsedToArray[6]), stoi(parsedToArray[7]), degreeType);
}
//Here is the commaSeparatedToArray function
string * roster::commaSeparatedToArray(string rowToParse) {
int currentArraySize = 0;
const int expectedArraySize = 9;
string valueArray[expectedArraySize];
int commaIndex = 0;
string remainingString = rowToParse;
while (remainingString.find(",") != string::npos) {
currentArraySize++;
if (currentArraySize <= expectedArraySize) {
commaIndex = static_cast<int>(remainingString.find(","));
valueArray[currentArraySize - 1] = remainingString.substr(0, commaIndex);
remainingString = remainingString.substr(commaIndex + 1, remainingString.length());
}
else {
cerr << "INVALID RECORD. Record has more values then is allowed.\n";
exit(-1);
}
}
if (currentArraySize <= expectedArraySize) {
currentArraySize++;
commaIndex = static_cast<int>(remainingString.find(","));
valueArray[currentArraySize - 1] = remainingString.substr(0, commaIndex);
remainingString = remainingString.substr(commaIndex + 1, remainingString.length());
}
if (currentArraySize < valueArray->size()) {
cerr << "INVALID RECORD. Record has fewer values then is allowed.\n";
exit(-1);
}
return valueArray;
}

1) You can't return arrays in C++. Your code (as I'm sure you know) returns a pointer to an array. That's an important difference.
2) The array is declared locally in the function and therefore no longer exists after the function has exitted.
3) Therefore once you have returned from the function you have a pointer to something which no longer exists. Bad news.
4) You must always consider the lifetime of objects when you program C++. One solution to this problem is to dynamically allocate the array (using new[]). This means that the array will still exist when you exit the function. But it has the signifcant disavantage that you must remember to delete[] the array at a suitable later time.
5) The best solution (in general) is to use a std::vector. Unlike an array a std::vector can be returned from a function. So this option leads to the simplest, most natural code.
vector<string> roster::commaSeparatedToArray(string rowToParse) {
...
vector<string> valueArray(expectedArraySize);
...
return valueArray;
}
Since your array/vector is constant size, you could also use a std::array
array<string, expectedArraySize> valueArray;

To complete the answer that John has already given, I made some example code to show you, how such function could look like.
Parsing, or tokenizing can be easily done with the std::sregex_token_iterator. That is one of the purposes for this iterator. You can see the simplicity of the usage below.
In the function we define a vector af string and use its range constructor to do the whole tokenizing.
Then we make a sanity check and return the data.
Please see:
#include <string>
#include <regex>
#include <iterator>
#include <vector>
#include <algorithm>
#include <iostream>
const std::regex separator(",");
constexpr size_t ExpectedColumnSize = 9;
std::vector<std::string> commaSeparatedToArray(std::string rowToParse)
{
// Parse row into substrings
std::vector<std::string> columns{
std::sregex_token_iterator(rowToParse.begin(),rowToParse.end(),separator ,-1),
std::sregex_token_iterator() };
// Check number of columns
if (columns.size() != ExpectedColumnSize) {
std::cerr << "Error. Unexpected number of columns in record\n";
}
return columns;
}
// test code
int main()
{
// Define test data
std::string testInputData{ "1,2,3,4,5,6,7,8,9" };
// Get the result from the parser
std::vector<std::string> parsedElements{ commaSeparatedToArray(testInputData) };
// show the result on the console
std::copy(parsedElements.begin(), parsedElements.end(), std::ostream_iterator<std::string>(std::cout, "\n"));
return 0;
}

Related

My code is right but not accepted by Leetcode platoform. (ZigZag Conversion)

It is a leet code problem under the subcategory of string, medium problem.
Query: My program is returning right result for all the test cases at the run time and but when I submit, same test cases are not passing.
I also made a video, click here to watch.
My Code is:
string convert(string s, int numRows) {
int loc_rows = numRows-2;
int i=0;
int a=0,b=0;
int arr[1000][1000];
while(i<s.length())
{
if(a<numRows)
{
arr[a][b] = s[i];
a++;
i++;
}
else if(a>=numRows)
{
if(loc_rows>=1)
{
b++;
arr[loc_rows][b]=s[i];
i++;
loc_rows--;
}
else{
loc_rows=numRows-2;
b++;
a=0;
}
}
}
string result="";
for(int d=0;d<numRows;d++)
{
for(int y=0;y<b+1;y++)
{
char temp = (char)arr[d][y];
if((temp>='a' and temp<='z') or (temp>='A' and temp<='Z') )
result+=temp;
}
}
return result;
}
I believe the issue might be your un-initialised arrays / variables.
Try setting initialising your array: int arr[1000][1000] = {0};
live example failing: https://godbolt.org/z/dxf13P
live example passing: https://godbolt.org/z/8vYEv6
You can't rely on the data that is in these arrays so initialising the values is quite important.
Note: this is because you rely on the empty values in the array to be not a letter ([a-zA-Z]). So that you can re-construct your output with your final loop which attempts to print the characters only. This works the first time around because luckily arr contains 0's in the gaps between your values (or at least not letters). The second time around it contains some junk from the first time around (really - you don't know what this is going to be, but in practise it is probably just the values you left in there from last time). So even though you put in the correct values into arr each time - your final loop finds some of the old non-alpha values in the array - hence your program is incorrect...
Alternatively, we could also use unsigned int to make it just a bit more efficient:
// The following block might slightly improve the execution time;
// Can be removed;
static const auto __optimize__ = []() {
std::ios::sync_with_stdio(false);
std::cin.tie(NULL);
std::cout.tie(NULL);
return 0;
}();
// Most of headers are already included;
// Can be removed;
#include <cstdint>
#include <vector>
#include <string>
static const struct Solution {
using ValueType = std::uint_fast16_t;
static const std::string convert(
const std::string s,
const int num_rows
) {
if (num_rows == 1) {
return s;
}
std::vector<std::string> res(num_rows);
ValueType row = 0;
ValueType direction = -1;
for (ValueType index = 0; index < std::size(s); ++index) {
if (!(index % (num_rows - 1))) {
direction *= -1;
}
res[row].push_back(s[index]);
row += direction;
}
std::string converted;
for (const auto& str : res) {
converted += str;
}
return converted;
}
};

Getting "vector subscript out of range" error

I'm currently trying to get "variadic functions" down and just trying to load 4 names in a string vector and then print them out. When I do this with 'int' type and use numbers, it works fine, but when I use a string vector I get the error.
#include "stdafx.h"
#include<cstdio>
#include<cstdarg>
#include<string>
#include<vector>
#include<iostream>
using namespace std;
int count;
vector<string> namesVector;
void names(int count, ...)
{
va_list namesList;
int i; // for loop
va_start(namesList, count);
for (i = 0; i < count; i++)
{
string currentElement;
currentElement = va_arg(namesList, string);
namesVector[i] = currentElement;
}
va_end(namesList);
}
int main()
{
int nameCount = 4;
names(nameCount,"jon", "maggie", "joan", "alfred");
for (int i = 0; i < nameCount; i++)
{
cout << "Name at element " << i << " is: " << namesVector[i] << endl;
}
}
C++ only allows to use trivially-copyable types as variadic arguments. As std::string is constructible from a char* pointer which points to a null-terminated buffer, you may use char* instead of std::string type. Just replace
currentElement = va_arg(namesList, string);
with
currentElement = va_arg(namesList, char*);
in your code. To get rid of this limitation, consider variadic templates, which generate code in compile-time for any type you use.
Your code also contains a run-time error. This:
namesVector[i] = currentElement;
is very likely to crash your program as you didn't allocate any memory in the vector. Vectors are actually dynamic arrays, so you should either pass a size argument to appropriate constructor, or call resize on the vector. In you case you may do neither of it, but just use push_back method:
namesVector.push_back(currentElement);

Returning a string * type array from a function back into the main

I'm new to C++ and I am working on a function to shuffle strings
It takes an array of strings, shuffles them, and returns them back to the main.
I am returning a pointer to an array of strings called shuffled. The problem I have is that when I try to save that new pointer to the array to another pointer in the main, I start getting weird values that either reference to a file location in my computer or a bunch of numbers.
I'll post the entire code here but really what you want to look at is the return types, how I return it and how I save it in main. Please tell me why my pointer is not referencing the working array that is created in the function. Here's the code:
#include <cstdio>
#include <string>
#include <ctime>
#include <new>
#include <cstdlib>
using namespace std;
const char * getString(const char * theStrings[], unsigned int stringNum)
{
return theStrings[stringNum];
}
string * shuffleStrings(string theStrings[])
{
int sz = 0;
while(!theStrings[sz].empty())
{
sz++;
}
sz--;
int randList[sz];
for(int p = 0; p < sz; p++)
{
randList[p] = sz;
}
srand(time(0));//seed randomizer to current time in seconds
bool ordered = true;
while(ordered)
{
int countNumberInRandList = 0;//avoid having a sz-1 member list length (weird error I was getting)
for(int i = 0; i < sz; i++)
{
int count = 0;
int randNum = rand()%(sz+1);//get random mod-based on size
for(int u = 0; u < sz; u++)
{
if(randList[u] != randNum)
{
count++;
}
}
if(count == sz)
{
randList[i] = randNum;
countNumberInRandList++;
}
else
i--;
}
//check to see if order is same
int count2 = 0;
for(int p = 0; p < sz; p++)
{
if(randList[p] == p)
{
count2++;
}
}
if(count2 < sz-(sz/2) && countNumberInRandList == sz)
{
ordered = false;
}
}
string * shuffled[sz];
for(int r = 0; r < sz; r++) //getting random num, and str list pointer from passed in stringlist and setting that value at shuffled [ random ].
{
int randVal = randList[r];
string * strListPointer = &theStrings[r];
shuffled[randVal] = strListPointer;
}
for(int i = 0; i < sz; i++)
{
printf("element %d is %s\n", i, shuffled[i]->c_str());//correct values in a random order.
}
return *shuffled;
}
int main()
{
string theSt[] = {"a", "b", "pocahontas","cashee","rawr", "okc", "mexican", "alfredo"};
string * shuff = shuffleStrings(theSt);//if looped, you will get wrong values
return 0;
}
Strings allocate their own memory, no need to give them the "length" like you would have to do for char arrays. There are several issues with your code - without going into the details, here are a few working/non-working examples that will hopefully help you:
using std::string;
// Returns a string by value
string s1() {
return "hello"; // This implicitly creates a std::string
}
// Also returns a string by value
string s2() {
string s = "how are you";
return s;
}
// Returns a pointer to a string - the caller is responsible for deleting
string* s3() {
string* s = new string;
*s = "this is a string";
return s;
}
// Does not work - do not use!
string* this_does_not_work() {
string s = "i am another string";
// Here we are returning a pointer to a locally allocated string.
// The string will be destroyed when this function returns, and the
// pointer will point at some random memory, not a string!
// Do not do this!
return &s;
}
int main() {
string v1 = s1();
// ...do things with v1...
string v2 = s2();
// ...do things with v2...
string* v3 = s3();
// ...do things with v3...
// We now own v3 and have to deallocate it!
delete v3;
}
There are a bunch of things wrong here -- don't panic, this is what happens to most people when they are first wrapping their brains around pointers and arrays in C and C++. But it means it's hard to put a finger on a single error and say "this is it". So I'll point out a few things.
(But advance warning: You ask about the pointer being returned to main, your code does indeed do something wrong with that, and I am about to say a bunch of things about what's wrong and how to do better. But that is not actually responsible for the errors you're seeing.)
So, in shuffleStrings you're making an array of pointers-to-string (string * shuffled[]). You're asking shuffleStrings to return a single pointer-to-string (string *). Can you see that these don't match?
In C and C++, you can't actually pass arrays around and return them from functions. The behaviour you get when you try tends to be confusing to newcomers. You'll need to understand it at some point, but for now I'll just say: you shouldn't actually be making shuffleStrings try to return an array.
There are two better approaches. The first is to use not an array but a vector, a container type that exists in C++ but not in C. You can pass arrays around by value, and they will get copied as required. If you made shuffleStrings return a vector<string*> (and made the other necessary changes in shuffleStrings and main to use vectors instead of arrays), that could work.
vector<string *> shuffleStrings(...) {
// ... (set things up) ...
vector<string *> shuffled(sz);
// ... (fill shuffled appropriately) ...
return shuffled;
}
But that is liable to be inefficient, because your program is then having to copy a load of stuff around. (It mightn't be so bad in this case, because a smallish array of pointers isn't very large and because C++ compilers are sometimes able to figure out what you're doing in cases like this and avoid the copying; the details aren't important right now.)
The other approach is to make the array not in shuffleStrings but in main; to pass a pointer to that array (or to its first element, which turns out to be kinda equivalent) into shuffleStrings; and to make shuffleStrings then modify the contents of the array.
void shuffleStrings(string * shuffled[], ...) {
// ... (set things up) ...
// ... (fill shuffled appropriately) ...
}
int main(...) {
// ...
string * shuffled[sz];
shuffleStrings(shuffled, theSt);
// output strings (main is probably a neater place for this
// than shuffleStrings)
}
Having said all this, the problems that are causing your symptoms lie elsewhere, inside shuffleStrings -- after all, main in your code never actually uses the pointer it gets back from shuffleStrings.
So what's actually wrong? I haven't figured out exactly what your shuffling code is trying to do, but that is where I bet the problem lies. You are making this array of pointers-to-string, and then you are filling in some of its elements -- the ones corresponding to numbers in randList. But if the numbers in randList don't cover the full range of valid indices in shuffled, you will leave some of those pointers uninitialized, and they might point absolutely anywhere, and then asking for their c_strs could give you all kinds of nonsense. I expect that's where the problem lies.
Your problem has nothing to do with any of the stuff you are saying. As you are a beginner I would suggest not presuming that your code is correct. Instead I would suggest removing parts that are not believed to be problematic until you have nothing left but the problem.
If you do this, you should quickly discover that you are writing to invalid memory.
part two : you can't seem to decide on the type of what you are returning. Are you building a pointer to an array to return or are you returning an array of pointers.... you seem to switch between these intermittently.
part three : read #Gareth's answer, he explains about passing parameters around nicely for your instance.

Dynamically Set Array Size C++

I don't know any C++ at all but I am trying to make a very small update to a C++ library that my application is using. Before I start hacking away at this, I am hoping someone can tell me the proper syntax for the following:
I have the following lines of code:
#define A_NUMBER 100
#define ANOTHER_NUMBER 150
enum {
type1,
type2,
};
static int someMethod(int type)
{
char command[A_NUMBER];
//...more code
}
What I need to be able to do is based on the type argument (type1 or type2) I need to be able to set the size of the array to be either A_NUMBER or ANOTHER_NUMBER.
In pseudo code it would be something like:
if (type == type1) {
char command [A_NUMBER]
}
else if (type == type2) {
char command [ANOTHER_NUMBER]
}
Is there a way to dynamically define the size?
Yes, you can use an std::vector<char>:
if (type == type1) {
std::vector<char> x(A_NUMBER);
} else if (type == type2) {
std::vector<char> x(ANOTHER_NUMBER);
}
Remember to include the header with:
#include <vector>
While your example code matches the "pseudo code" in the question, I think part of the question is how to decide the size via type and then use the resulting storage unconditionally, i.e. outside the conditional blocks.
Then it gets as simple as:
std::vector<char> x;
if (type == type1) {
x.resize(A_NUMBER);
} else if (type == type2) {
x.resize(ANOTHER_NUMBER);
}
I believe this is what you want
std::vector<char> x; // x is empty, with size 0
if (type == type1) {
x.resize(A_NUMBER); // change size to A_NUMBER
} else if (type == type2) {
x.resize(ANOTHER_NUMBER); // change size to ANOTHER_NUMBER
}
Yes and no. In standard C++, you cannot keep the array on the stack and have its size determined in runtime.
However, you can turn the array into a dynamically-allocated one (i.e. on the heap). In C++, the standard way to do this is to use std::vector:
std::vector<char> command(A_NUMBER); // or ANOTHER_NUMBER
Indexing will work just as before: command[5]
However, if you need to pass the array to something which expects a C-style array (i.e. a char *), you'll have to use one of these:
command.data(); // if your compiler supports C++11
&command[0]; // if it does not
And of course, to use std::vector, you'll have to #include <vector>.
Here's an example that works in C and C++:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int
main(int argc, char *argv[])
{
char *opt;
if(argc == 2) {
opt = *++argv;
}
else {
printf("Usage: %s [small|large]\n", *argv);
return EXIT_FAILURE;
}
int *arr;
int arrlen = 0;
if (strcmp(opt, "small") == 0) {
arrlen = 3;
arr = (int *) malloc(arrlen*sizeof(int));
int i;
for(i = 0; i < arrlen; i++)
arr[i] = i+1;
}
else if (strcmp(opt, "large") == 0) {
arrlen = 5;
arr = (int *) malloc(arrlen*sizeof(int));
int i;
for(i = 0; i < arrlen; i++)
arr[i] = i+1;
}
if (arrlen > 0) {
int i;
for(i = 0; arr[i]; i++)
printf("%i, ", arr[i]);
printf("\n");
free(arr);
}
return EXIT_SUCCESS;
}
Example:
[gyeh#gyeh stackoverflow]$ ./dynarr
Usage: ./dynarr [small|large]
[gyeh#gyeh stackoverflow]$ ./dynarr small
1, 2, 3,
[gyeh#gyeh stackoverflow]$ ./dynarr large
1, 2, 3, 4, 5,
The raw C++ way is new and delete
char * command = new char[A_NUMBER];
// and later delete it like this
delete[] command;
Of course you'll have to manage the memory, and it is not recommended to use this approach because of many reasons you should be able to find online. So in conclusion... don't use this method if vector is an option
If using a big array the best way would be to use C++ vector, you could even consider other data structures like list based on your needs (for example a lot of insert, deletions operations on your array).

cannot convert from 'std::string' to 'char'

Changed completely due to suggestions from other member. Most problems solved, still having problems. Now won't output any names from the array in main. Not sure if I'm passing them back correctly from function.
#include <iostream>
#include <fstream>
#include <string>
using namespace std;
void bubblesort(string[], const int);
int sub = 0;
int main()
{
const int maxsize = 100;
string friendArray[maxsize];
ifstream friends;
friends.open("myFriends.dat");
while (sub < maxsize)
{
getline(friends, friendArray[sub]);
sub++;
}
bubblesort(friendArray, maxsize);
cout<<friendArray[0]<<" "<<friendArray[1]<<" "<<friendArray[2];
system("pause");
return 0;
}
void bubblesort(string *array, const int size)
{
bool swap;
string temp;
do
{
swap = false;
for (int count = 1; count < (size - 1); count++)
{
if(array[count-1] >array[count])
{
temp = array[count-1];
array[count-1] = array[count];
array[count] = temp;
swap = true;
}
}
}
while(swap);
}
Your problem isn't necessarily that temp inside bubblesort is not a char, the problem is that array is declared as a string and not a string[].
The reason you're getting the error is because array[count+1] is of type char, and temp is of type string. std::swap expects two elements of the same type.
However, that may be the least of your problems, your code doesn't compile for quite a few reasons. Not just that but you're passing in maxsize to bubblesort at each iteration. There's a flaw in both your logic and your syntax.
EDIT: Since you're still having trouble getting the sorting to work, here's a working modification of your code:
#include <iostream>
void bubblesort(std::string array[], size_t size)
{
bool bSwapped;
std::string temp;
do
{
bSwapped = false;
for (size_t count = 1; count < size; count++)
{
if(array[count-1] > array[count])
{
std::swap(array[count-1], array[count]);
bSwapped = true;
}
}
}
while(bSwapped);
}
int main(void)
{
std::string array[] = { "def", "ghk", "abc", "world", "hello" };
bubblesort(array, sizeof(array)/sizeof(*array));
for (size_t i = 0; i < sizeof(array)/sizeof(*array); ++i)
std::cout << array[i] + " ";
std::cout << std::endl;
return 0;
}
bubblesort could also be written as: void bubblesort(std::string *array, size_t size). There's no difference in this case since, when passed to a function, arrays decay into pointers.
Since arrays are passed by reference, a pointer to the first element, any modifications made to array inside of bubblesort will actually be modifying your array in main. So that's how arrays are "returned".
std::vector is a good alternative to the standard array, since it automatically resizes and obviously contains the length of the internal array so that you don't have to pass the size everywhere you pass an std::vector. You can also use it the same way as a regular array.
temp is a string, array[count] is a char (since an std::string is a vector of char elements.) I'm not sure what you're trying to do here, but the compiler is correct - you can't assign a char to a string.
You could change temp to be a char, since all you do with it is assign a char to it, and then assign it back to an element of array, which is also a char.
You need to declare temp as char. You can use std::swap to avoid such mistakes in the future:
std::swap(array[count], array[count+1]);
This would make your code compile, but it would not do what you're trying to do (bubblesort). The problem is that you are passing a single string (which is also an "array" of characters) instead of an array of strings, which is, in a very lose sense, "an array of arrays of characters". Your bubblesort needs to accept string *array as its first parameter.