Passing 2D arrays to function - c++

I am trying to make an ASCII world, however I am unable to pass 2D arrays between functions. It is a 20 x 20 array, and I want to place houses randomly across it. The array won't pass like I want it to, and my tutorial told me that global variables are evil, so a solution without those would be great.
using namespace std;
void place_house(const int width, const int height, string world[width][length])
{
int max_house = (width * height) / 10; //One tenth of the map is filled with houses
int xcoords = (0 + (rand() % 20));
int ycoords = (0 + (rand() % 20));
world[xcoords][ycoords] = "#";
}
int main(int argc, const char * argv[])
{
srand((unsigned)time(NULL));
const int width = 20;
const int height = 20;
string world[width][height];
string grass = ".";
string house = "#";
string mountain = "^";
string person = "Å";
string treasure = "$";
//Fill entire world with grass
for (int iii = 0; iii < 20; ++iii) {
for (int jjj = 0; jjj < 20; ++jjj) {
world[iii][jjj] = ".";
}
}
place_house(width, height, world);
for (int iii = 0; iii < 20; ++iii) {
for (int jjj = 0; jjj < 20; ++jjj) {
cout << world[iii][jjj] << " ";
}
cout << endl;
}
}

Try passing string ** instead of string[][]
So your function should be declared like this:
void place_house(const int width, const int height, string **world)
and then you access your array usual way.
Remember to handle bounds correctly (probably you want to pass them together with array).
edit:
This is how you might achieve what you need:
#include <string>
#include <iostream>
using namespace std;
void foo (string **bar)
{
cout << bar[0][0];
}
int main(void)
{
string **a = new string*[5];
for ( int i = 0 ; i < 5 ; i ++ )
a[i] = new string[5];
a[0][0] = "test";
foo(a);
for ( int i = 0 ; i < 5 ; i ++ )
delete [] a[i];
delete [] a;
return 0;
}
edit
Another way of achieving what you want achieve (that is passing static array to a function) is to pass it as one dimmensional array and then use C-like way of accessing it.
Example:
#include <string>
#include <iostream>
using namespace std;
void foo (string *bar)
{
for (int r = 0; r < 5; r++)
{
for (int c = 0; c < 5; c++)
{
cout << bar[ (r * 5) + c ] << " ";
}
cout << "\n";
}
}
int main(void)
{
string a[5][5];
a[1][1] = "test";
foo((string*)(a));
return 0;
}
This little example is nicely described here (see Duoas post).
So I hope this will describe different ways of doing similar thing. This, however, does look quite ugly and probably is not the best programming practice (I would do everything to avoid doing that this way, dynamic arrays are quite nice you just need to remember releasing them).

Since your array has compile-time known dimensions, you could use templates to detect it like this:
template <std::size_t W, std::size_t H>
void place_house(string (&world)[W][H])
{
int max_house = (W * H) / 10; //One tenth of the map is filled with houses
int xcoords = (0 + (rand() % 20));
int ycoords = (0 + (rand() % 20));
world[xcoords][ycoords] = "#";
}
// ...
place_house(world); // Just pass it
Note, that this trick will not works with dynamically allocated arrays. In that case, you should use something like std::vector.

You don't need to size the parameter in the declaration and can't because the [][] syntax requires compile time constants.
Replace with string world[][] and it should work.
If it doesn't then use string[]* world (an array of array of strings is actually an array of pointers to an array of string)
I hope this helps, my C++ is becoming increasingly rusty.

Related

Trying to reverse a C string

I cannot use any c functions except strlen(), and I also cannot use strings. I have been at this for an unfortunate amount of time. I keep getting weird characters as part of the output. i.e question marks and essentially weird alt codes is what it looks like.
#include <iostream>
#include <cstring>
using namespace std;
int lastIndexOf(const char*, char[]);
void reverse(char*);
int replace(char*, char, char);
int main() {
int i = 0, count = 0, counter = 0, SIZE = 100;
char charArray[SIZE];
cout << "Enter a string of no more than 50 characters: ";
cin.getline(charArray, SIZE);
reverse(charArray);
}
void reverse(char s[])
{
int n = 100;
for (int i = 0; i < n / 2; i++) {
swap(s[i], s[n - i - 1]);
cout << (s[i]);
}
}
I have tried several different things, swap function, using pointers to manually swap them with a temp variable. So I went to the internet to see what other people did, but to no avail. I am convinced there is a simple solution.
The function uses a magic number 100
int n = 100;
though in main there is a prompt to enter no more than 50 characters.
cout << "Enter a string of no more than 50 characters: ";
You need to calculate the length of the passed string by using the standard C function strlen.
The function can look the following way
char * reverse( char s[] )
{
for ( size_t i = 0, n = std::strlen( s ); i < n / 2; i++ )
{
std::swap( s[i], s[n-i-1] );
}
return s;
}
Pay attention to that variable length arrays is not a standard C++ feature.
You should write
const size_t SIZE = 100;
char charArray[SIZE];

Creating strings by splitting a char array

As part of my homework assignment, I have to split a char[] by its indices. So for example, the main function looks like:
int main()
{
char str[] = "A string to be split into given number of parts";
int split_size;
cout << "Enter the size of the part: ";
cin >> split_size;
int size = sizeof(str) / sizeof(str[0]);
SplitString(str, split_size, size);
int wait;
cin >> wait;
return 0;
}
Then using the function SplitString, the first x elements are printed, new line, then the next.
My first idea, was to use two for loops. One loops through the splits (i.e. if there are 4 splits, the range on this loop is 0 to 3), then the second loops through the split itself, iterating over the array elements.
My SplitString() function looks like this:
void SplitString(char str[], int split_size, int size) {
int parts = size / split_size;
for (int i = 0; i < parts; i++) {
for (int j = 0; j < split_size; j++) {
j = split_size * i;
cout << str[j];
}
cout << endl;
}
}
Is there an easier way to do this? I know in Python, you can use the arr[1:] to grab a range of elements from the array. Is there anything similar in C++? Is there some flaw in my logic? Is there something wrong with my code?
cout comes with a write function that takes a pointer and a size argument.
for (int i = 0; i < parts; i++) {
cout.write (str+i*split_size, split_size)
cout << endl;
}
Note that the code above does not check if the string is actually long enough. If the total size is not equal the split_size times a whole number, you will have to add an additional check.
Also, note that this:
int size = sizeof(str) / sizeof(str[0]);
can be written as:
int size = sizeof(str);
instead because the size of a char is always 1.
You can use std::string for this. Alternatively, if your compiler supports C++17, you can use std::string_view as the first argument of SplitString to avoid unnecessary copying.
#include <algorithm>
#include <iostream>
#include <string>
void SplitString(std::string s, std::size_t split_size)
{
while(!s.empty())
{
auto size = std::min(split_size, s.size());
std::cout << s.substr(0, size) << '\n';
s = s.substr(size, std::string::npos);
}
}
int main()
{
char str[] = "A string to be split into given number of parts";
int split_size = 5;
SplitString(str, split_size);
return 0;
}
Live example.

add element to array of struct in C++ encountered "error no match for ‘operator=’"

My code was
#include <iostream>
#include <string>
using namespace std;
struct Numbers{
int a;
int b;
int c;
};
struct NumbersArray{
int size;
Numbers *numbers;
};
int main(){
NumbersArray numArr;
numArr.size = 10;
numArr.numbers = new Numbers[10];
for(int i = 0; i < 10; i++){
Numbers *num = new Numbers;
num->a = i * 3 + 0;
num->b = i * 3 + 1;
num->c = i * 3 + 2;
numArr.numbers[i] = num;
}
}
The basic idea is I create a struct called Numbers which contains 3 numbers and put it in a wrapper struct called NumbersArray.
And when I compile it with g++, I got error message
testArrayStruct.cc: In function ‘int main()’:
testArrayStruct.cc:23:27: error: no match for ‘operator=’ (operand types are ‘Numbers’ and ‘Numbers*’)
numArr.numbers[i] = num;
^
testArrayStruct.cc:23:27: note: candidate is:
testArrayStruct.cc:4:8: note: Numbers& Numbers::operator=(const Numbers&)
struct Numbers{
^
testArrayStruct.cc:4:8: note: no known conversion for argument 1 from ‘Numbers*’ to ‘const Numbers&’
Just ignore memory management here.
I cant figure it how to add elements to the array.
You are practising on the wrong things, and you will just
pick up bad habits this way. C++ thrive on value semantics,
focus on that:
#include <iostream>
#include <string>
#include <vector>
using namespace std;
struct Numbers {
int a;
int b;
int c;
};
using NumbersArray = vector<Numbers>;
int main()
{
NumbersArray numArr;
numArr.resize(10);
for(int i = 0; i < 10; i++)
{
Numbers num;
num.a = i * 3 + 0;
num.b = i * 3 + 1;
num.c = i * 3 + 2;
numArr[i] = num;
}
}
It should be (ignoring your memory management as you asked):
numArr.numbers[i] = *num;
Instead of
numArr.numbers[i] = num;
since numArr.numbers[i] is of type Numbers while num is of type Numbers*.
EDIT:
The error actually tells you that there is no operator= to perform assignment of Numbers * to Numbers. So actually you could implement your own operator= which would perform such operation:
Numbers& operator=(Numbers& left, const Numbers * const right)
{
if (right != NULL) // or nullptr for C++11
{
left = *right;
}
return left;
}
With such implementation your original code would also compile.
One remark regarding the memory management. There is no real need to allocate new Numbers inside the for loop since you copy that content into the numArr.numbers anyway. So you could write it that way:
for(int i = 0; i < 10; i++){
numArr.numbers[i].a = i * 3 + 0;
numArr.numbers[i].b = i * 3 + 1;
numArr.numbers[i].c = i * 3 + 2;
}
how to add elements to the array
You can't.
When you said new Numbers[10] you created an array of ten default-initialized elements. An array can't resize itself after initialization.
It sounds like you should be using std::vector:
int main() {
std::vector<Numbers> numArr;
for(int i = 0; i < 10; i++) {
Numbers num;
num.a = i * 3 + 0;
num.b = i * 3 + 1;
num.c = i * 3 + 2;
numArr.push_back(num);
}
std::cout << "numArr contains " << numArr.size() << " elements." << std::endl;
}
Now you can add as many elements as you like and don't have to keep track of size etc. yourself. std::vector will resize itself when it needs to.
Try the following code. You are try to assign a pointer of an object to the object itself. The following one fixes it.
#include <iostream>
#include <string>
using namespace std;
struct Numbers{
int a;
int b;
int c;
};
struct NumbersArray{
int size;
Numbers **numbers;
};
int main(){
NumbersArray numArr;
numArr.size = 10;
numArr.numbers = new Numbers* [10];
for(int i = 0; i < 10; i++){
Numbers *num = new Numbers;
num->a = i * 3 + 0;
num->b = i * 3 + 1;
num->c = i * 3 + 2;
numArr.numbers[i] = num;
}
}
This is the important part of your error message:
error: no match for ‘operator=’ (operand types are ‘Numbers’ and ‘Numbers*’)
You are trying to assign a pointer to Numbers into a Numbers value. The rest of the error message is not helpful.
Assuming you don't want to change the NumbersArray class like all the other answers are suggesting, your inner loop should look like this:
for(int i = 0; i < 10; i++){
Numbers num;
num.a = i * 3 + 0;
num.b = i * 3 + 1;
num.c = i * 3 + 2;
numArr.numbers[i] = num;
}
Read about values and pointers in C++.

How to make a multidimensional array from a normal array in c++

If i had an array of lets say 15 elements is there anyway for me to make it into a 2d array having it 5x3?
Or if i had a string with 15 letters would it be possible to make it into a 2d array having it 5x3?
This is what i have(using variables but using 5 as a and 3 as b in console)
void finishMap(string map, int a, int b)
{
string finalMap[a][b];
for(int i = 0; b>i; i++)
{
for(int x = 0; a>x; x++)
{
finalMap[a][b] += {{map[x]}, {i}};
}
}
}
Also pretty new to c++ so if you see anything i shouldn't be please tell me :3
I'm using char arrays (c strings) in my answer because I think they are useful to illustrate how arrays work - and thre really isn't a point in using std::string in your case. std::string hides a lot of the underlying nuts and bolts so I would generally recommend to play around with C strings first to understand how std::string works. Also, check out this tutorial: http://www.cplusplus.com/doc/tutorial/arrays/
A 2-dimensional array has the same memory layout as a 1-d array. In terms of memory layout, char[3][5] is the same as char[3*5] is the same as char[15]. You can use a 1-d array as a 2-d array using char[column+row*width]. The only difference if you use subscripts is that the compiler remembers how many dimensions there are and will do the whole column+row*width calculation for you.
Take this example:
char temp[5] = "abcd"; //need 5 for string termination char `\0`
for(int i = 0; i < 4; ++i) {
std::cout << temp[i];
}
std::cout << "\n\n";
for(int i = 0; i < 2; ++i) {
for(int j = 0; j < 2; ++j) {
std::cout << temp[j+2*i];
}
std::cout << std::endl;
}
Will print:
abcd
ab
cd
You can always access an array in strides. Here's a possible example using templates to restride a 1D array as a 2D array:
template <typename T, unsigned int N1, unsigned int N2>
struct strider
{
using array_type = T[N1 * N2];
array_type & data_;
Strider(array_type & data) : data_(data) {}
T & operator()(std::size_t i1, std::size_t i2)
{
return data_[i1 * N2 + i2];
}
};
template <unsigned int N1, unsigned int N2, T>
strider<T, N1, N2> stride(T (&a)[N1, N2]) { return {a}; }
Usage:
int a[15] = {};
strider<int, 3, 5> s(a);
s(1, 2) = 20;
assert(a[7] == 20);
stride<5, 3>(a)(4, 2) = 10;
assert(a[14] == 10);
I've overloaded operator() for the strided access, since unlike operator[] it can have arbirary signatures.
With some more work you could make the rank of the strided view variadic.
Okey so i used something a bit different then what i mentioned. What i did was have the user enter 3 lines of 5 length letters, which i figured out how to add into the 2d array. If your having the same issue as me, heres my code:
int main()
{
string path;
int a, b;
cin >> a >> b;
string finalMap[a][b];
for(int i = 0; b>i; i++){
cin >> path;
for(int x = 0; a>x; x++){
finalMap[x][i] = (path[x]);
}
}
for(int x = 0; b>x; x++)
{
for(int y = 0; a>y; y++)
{
cout << finalMap[y][x];
}
cout << endl;
}
return 0;
}
Thanks for trying tho, really appreciate it ^.-
You can try to use reinterpret_cast. Complete example:
#include <iostream>
using namespace std;
typedef char mtrx[5][3];
int main(){
char data[15] = "Some String";
mtrx &m = *reinterpret_cast<mtrx*>(&data);
m[1][2] = '*';
cout << data << endl;
return 0;
}

How to implement infinite multidimensional array?

I want to use the code below and I want to use it for "unknown size of input". For example there is an array int cac[1000][1000]. I can use vector<vector<int> > array;, then how can i initialize it with -1 ? Any suggestions?
#include <sstream>
#include <iostream>
#include <vector>
#include <cstdlib>
#include <memory.h>
using namespace std;
int cac[1000][1000];
string res[1000][1000];
vector<string> words;
int M;
int go(int a, int b){
if(cac[a][b]>= 0) return cac[a][b];
if(a == b) return 0;
int csum = -1;
for(int i=a; i<b; ++i){
csum += words[i].size() + 1;
}
if(csum <= M || a == b-1){
string sep = "";
for(int i=a; i<b; ++i){
res[a][b].append(sep);
res[a][b].append(words[i]);
sep = " ";
}
return cac[a][b] = (M-csum)*(M-csum);
}
int ret = 1000000000;
int best_sp = -1;
for(int sp=a+1; sp<b; ++sp){
int cur = go(a, sp) + go(sp,b);
if(cur <= ret){
ret = cur;
best_sp = sp;
}
}
res[a][b] = res[a][best_sp] + "\n" + res[best_sp][b];
return cac[a][b] = ret;
}
int main(int argc, char ** argv){
memset(cac, -1, sizeof(cac));
M = atoi(argv[1]);
string word;
while(cin >> word) words.push_back(word);
go(0, words.size());
cout << res[0][words.size()] << endl;
}
What you can do is to use a associative array, where the key is a pair (rowPosition, ColumnPosition). When you want to set array[i][j] you just add or update the value assoArray[Pair(i,j)]. You can assume that any element which is not in the associative array has the initial value.
In general infinite multidimensional arrays are used for theoretical purpose.I hope i didn't misunderstood the question.
Using std::vector from the STL is much more straightforward than the following solution, which was pointed out in the comments for this post. I find that this site explains that topic effectively: http://www.learncpp.com/cpp-programming/16-2-stl-containers-overview/
An array of infinite size is not actually possible. However, you can achieve basically that effect using dynamic allocation. Here's some sample code:
int counter = 0;
int* myArray = new int[1000];
Fill the array with data, incrementing counter each time you add a value. When counter reaches 1000, do the following:
int* largerArray = new int[2000];
for( int i = 0; i < 1000; i++ )
{
largerArray[i] = myArray[i];
}
delete[] myArray;
myArray = largerArray;
With this method, you create the closest thing possible to an infinitely sized array, and I don't believe performance will be an issue with the copy piece