Passing arrays to functions - c++

I am currently in an introductory c++ class and am working assignment that sorts data. We recently covered structs and I decided to use structs to approach the problem rather than create 3 arrays to hold the information from our data file.
The trouble i'm having is when i'm trying to pass my struct to my function.
Here is my error:
analyze_data.cpp: In function ‘int main()’:
analyze_data.cpp:76: error: conversion from ‘weather*’ to non-scalar type ‘weather’ requested
analyze_data.cpp: In function ‘int find_pos_of_smallest(weather, int, int)’:
analyze_data.cpp:110: error: no match for ‘operator[]’ in ‘data[pos]’
analyze_data.cpp:110: error: no match for ‘operator[]’ in ‘data[pos_of_smallest]’
I don't understand the error from line 76. I have done some research about passing structs to functions, and what i found was adding the "&" in the type declarations. However, i have no idea what it does or why i would need to do that as we haven't covered it in class. I also did try it, but i just got a different set of errors. So i figured i'd not post those and just start from what i know.
Here is my code
/*
Program name: Analyze data
Program discription: This program will read a data file named data.txt.
This data file is expected to be formated in a specific way and contain
specific weather information. The program will analyize this data and
return max, min temperatures, 'perfect days', how many cold fronts per
year, 10 coldest and hottest days in a year and finally find the 5
median days of the year.
Date: 10/1/2012
*/
#include <iostream>
#include <fstream>
using namespace std;
/*
* Declare struct's here
*
*/
struct weather
{
string date;
int high;
int low;
};
/*
* Forward declaration of a function. This declares the function,
* but does not define it. (Notice that there is no code, just
* a function header with a semicolon after it.
*
*/
int find_pos_of_smallest (weather data, int start_pos, int end_pos);
/* Our main function.
*
* Parameters:
* none
*
* Return value:
* 0 if we complete successfully, 1 if there was an error.
*/
int main()
{
//read the data file
ifstream weather_data("data.txt");
//declare array size, and then create array using struct
int days = 365;
weather data[days];
//store the data.txt in the array and then close the file
for (int i=0; i<days; i++)
{
weather_data >> data[i].date;
weather_data >> data[i].high;
weather_data >> data[i].low;
}
weather_data.close();
ofstream data_results ("results.txt");
// create the first 3 lines of the output reults in the following formated
data_results << "Assignment #5\n"
<< "CS 1410/2000\n"
<< "Jonathan Larsen\n";
/*
for(int i=0; i<3; i++)
{
cout<<data[i].date << " "<<data[i].high << " " << data[i].low<<endl;
}
cout<<endl;
*/
cout<<find_pos_of_smallest(data, 0, days)<<endl;
return 0; //no error so return a zero
}//end of program
/**** FUNCTIONS ****/
/* Write down exactly what the function will do (a postcondition).
* Write down what is required to use the function (any preconditions).
* Write down any other behavior or comments that will help a programmer.
*
* Parameters: (list parameters by type and name, and explain them)
* int example -- an example parameter
*
* Returns:
* double -- an example return value
*/
/* Returns the position of the smallest value found in the specified
* subarray. (Only the elements in the subarray
* between start_pos and end_pos inclusive are checked.)
*
* Parameters:
* d - a data array
* start_pos - the first position to check
* end_pos - the last position to check
*/
int find_pos_of_smallest (weather data, int start_pos, int end_pos)
{
int pos_of_smallest = start_pos;
for (int pos = start_pos+1; pos <= end_pos; pos++)
if (data[pos].low < data[pos_of_smallest].low)
pos_of_smallest = pos;
return pos_of_smallest;
}

You are passing an array to your function. Your function needs to either take type weather[] or weather*. For instance:
int find_pos_of_smallest (weather* data, int start_pos, int end_pos)
You should note that even if you were not passing an array but a regular struct, you should still pass by reference. Either pass a pointer (weather*) and use the dereference memeber operator (->) or pass by reference (weather&) and use the normal member operator(.). This is because passing by value (no */&) causes the struct to be copied into a new value on the stack which can take a considerable amount of time for large values (for instance if the string gets large).

You need to pass your array as a pointer:
int find_pos_of_smallest (weather *data, int start_pos, int end_pos)
Declaring the parameter as weather data means just a single instance of the struct. Making it a pointer means you can pass an array (the pointer will get the address of the first element in the array, and when you index it with data[pos] you will get the relevant element in that array).

Whenever you declare an array of something, like your data array, writing the array's name by itself (in this case, data) in subsequent code is, confusingly enough, actually equivalent to writing
&(data[0])
which means "the address of the first element of the data array". This is how arrays are usually "passed" into functions in C and C++ -- by passing the address of their first element. It's done this way because the usual way of passing other types of arguments -- namely, copying them -- would be very wasteful in the case of large arrays. It also has the side-effect of allowing any changes that the function makes to the array to be visible to the calling code (sometimes this is needed, sometimes not).
How do you work with that address inside the function? You use a pointer-to-weather:
int find_pos_of_smallest(weather* data, int start_pos, int end_pos)
A pointer is a variable that contains the address of some other thing. You can access the pointed-to thing using the * indirection operator, the -> operator (if the pointed-to thing is a struct or class), and, conveniently, the array subscripting operator []: e.g. inside find_pos_of_smallest(),
data[5]
refers to the sixth element in an array of weather elements starting at the address stored inside the pointer data.

Related

Passing a pointer to an int array to a member function, error: invalid types 'int[int]' for array subscript

Ok, I'm fairly new to programming, and c++ so please take it easy on me. I am trying to write a program that takes in the dimensions of a metal plate for a 2-D finite element method analysis (thickness neglected). So, I created a class for my part (the plate), the elements for the mesh, and the nodes for the elements. The mesh will consist of square elements and will be applied over the front face of the plate. Right now, I'm working on getting the mesh sorted out before I move on to the element and node classes.
I'm using (or wanting to use) dynamic allocation to create a 2-D array (my mesh) containing the elements of the mesh. I'm trying to write a function, "meshingPart", to create the 2-D array with the number of rows being the height of the plate, and the columns being the length of the plate.
When I run the program, I get these errors and I'm not sure how to fix them:
In member function 'void PartClass::meshingPart(int&, int, int)':
error: invalid types 'int[int]' for array subscript
At global scope:
error: expected constructor, destructor, or type conversion before '(' token
Also, when I use my printPart() function, will it print the pointer's address, or the values of the array? I'm not completely sure about this, I'm also new to pointers.
Any help would be much appreciated! Thanks in advance.
class PartClass
{
private:
const int HEIGHT; // mm
const int LENGTH; // mm
const int WIDTH; // mm
const int SEED; // mm
const int MESHROW;
const int MESHCOL;
int *partMesh; // Mesh array - an int pointer
// Creates the mesh for the part by generating elements to fill the width and length
// of the part. The elements are stored in a 2-D array.
void meshingPart(const int &partMesh, int inRow, int inCol);
public:
// Constructs a part with the given parameters, seeds the part for the mesh,
// then creates the mesh by generating square elements with length = height = SEED.
PartClass(int inHeight, int inLength, int inWidth, int inSeed);
void printPart()
{
cout << "Part mesh:" << *partMesh << endl;
}
};
class ElementClass
{
private:
int elemID;
static int numElems;
// Shape functions:
int N1;
int N2;
int N3;
int N4;
public:
// Default constructor
ElementClass()
{
elemID = numElems;
numElems++;
};
};
PartClass :: PartClass(inHeight, inLength, inWidth, inSeed)
{
HEIGHT = inHeight;
LENGTH = inLength;
WIDTH = inWidth;
SEED = inSeed;
MESHROW = HEIGHT/SEED;
MESHCOL = LENGTH/SEED;
// Dynamically declares an array, gets memory, assigns address to partMesh.
partMesh = new int[MESHROW][MESHCOL];
meshingPart(&partMesh, MESHROW, MESHCOL);
}
void PartClass :: meshingPart(int &partMesh, int inRow, int inCol)
{
for( int i; i < inRow; i++)
{
for( int j; j < inCol; j++)
{
partMesh[i][j] = ElementClass();
}
}
}
There are multiple problems with the shown code, not a single problem. All of the problems must be fixed in order to resolve all compilation errors.
void PartClass :: meshingPart(int &partMesh, int inRow, int inCol)
The first parameter to this class method is declared as a reference to a single, lonely int. It is not an array, hence the code in this class method that treats it as an array will make your C++ compiler very, very sad.
int *partMesh; //
partMesh = new int[MESHROW][MESHCOL];
partMesh is declared as a pointer to an int. The new expression produces, effectively, a pointer to an array of MESHCOL ints. In C++ you cannot convert a pointer to an array into a different kind of a pointer.
Furthermore, nothing shown here requires the use of new in the first place. partMesh could simply be a std::vector<vector<int>>, and the new replaced by a strategic resize(). As an extra bonus your C++ program will automatically delete all this memory when it is no longer needed, not leak it, and also implement correct copy/move semantics where needed!
meshingPart(&partMesh, MESHROW, MESHCOL);
As we've concluded, the first parameter to the function is a reference to an array. Passing to it an address of a pointer to int will also not work.
Furthermore, since partMesh is a member of the same class, having one function in the class pass, in some form, a member of the same class to another class method accomplishes absolutely useful, whatsoever. Since it's a member of the same class it doesn't need passing, the class method can access it directly. This is what, after all, classes are all about.
In conclusion:
There are several problems regarding the C++ type system that are causing these compilation errors.
It is not necessary to even use new here, to initialize the pointer, and either its type needs to be adjusted to reflect that it's a pointer to a 2D array, or the new statement itself needs to be adjusted to allocate a one-dimensional array, since that's the only thing C++ allows you to convert to a plain pointer. And even that is overnegineered, since a std::vector will take care of all these pesky details by itself.
It is not necessary to even pass the member of the same class to the same class's method, as a parameter, just have the class method access it directly.
It's apparent that the likely process that produced the shown code was to write it in its entirety, and only try to compile after the whole thing was written. An avalanche of compilation errors is almost guaranteed any time this approach is used. It is far more productive to write a large program by writing only a few lines at a time, testing them, make sure they work correctly, and only then write a few more. This way, the number of errors that need to be fixed will remain quite small, and manageable.

C - passing an array by reference - only first element set

I have a function which should modify an array (of floats) in the original parent function. I am using the following code:
void sortFunction(Word**words, int wordCount){ //to sure if two * are correct (pointer to an array..?)
int i = 0;
for(i=0;i<wordCount-1;i++){
Word first = *words[i]; //values fine
Word second = *words[i+1]; //weird values, causes segfault
if(first.data[0] > second.data[0]){
//do stuff
}
}
}
int main(int argc, char ** argv){
Word* words = NULL;
int wordsCount = ...
//filling the array in a loop and using realloc for memory allocation
//Here, the array is filled correctly (verified)
sortFunction(&words, wordsCount);
}
Where Word is a typedef struct and Word.data is the (dynamic) float array. When checking in the parent function, the array is allocated and the values set correctly.
I have tried with about 10 elements in the array, but always only the first ([0]) element is fine in the sortFunction(), second and all others are messed up. I also have an int propery in the struct, and when I try to print it for the second element, I get something over 1 billion.
I assume I am not passing the array correctly - I use the following code (just a sample) to pass regular variables, so I tried to modify it for an array, but apparently, not correctly. What is the right way to do this for an array?
void foo(int*var){
*var=8;
}
int main(){
int var = 5;
changeVar(&var);
}
Thanks in advance for any tips!
Postfix [] has higher precedence than unary *, so *words[i] is parsed as *(words[i]), which isn't what you want.
You need to dereference the words pointer before applying the subscript, so you need to explicitly group the * operator with words using parentheses:
Word first = (*words)[i];
Word second = (*words)[i + 1];
First, you do not need to pass **, just one is enough, because you will be passing the address of your array anyway:
void sortFunction(Word* words, int wordCount)
and call it as:
sortFunction(words, wordsCount);
Second, the Undefined behavior originates in the following statement:
Word first = *words[i]; Word second = *words[i+1];
It should have been (*words)[i] but still, you are copying structs, so your dynamic data array is not copied correctly. avoid this useless copy, and use this instead, AFTER changing the protoype of sortFunction:
Word* first = &words[i];
Word* second = &words[i+1];
if(first->data[0] > second->data[0])
p.s: This does not guarantee that the rest of your code is correct, just comments of the parts you showed of the code.

Array and sizeof troubles compiling errors C++

I'm writing an array-based code that is throwing me some confusing errors after failing to compile. I have searched the internet for sample code that I understand more or less but it is helpful for me to identify errors in my own code / thought process.
The task at hand is to create a function that accepts an array of an unknown amount of integers and sums the even numbers in the array. I am told the last entry of the array is -1, but I don't think this information is useful.
Here is my code:
#include <iostream>
using namespace std;
int sumEven(int myArray[])
{
int len = (sizeof(myArray) / sizeof(myArray[0]));
int i = 0;
int count = 0;
while (i < len)
{
if (myArray[i] % 2 == 0)
{
count = count + myArray[i];
}
i++;
}
return count;
}
I attempt to define len as the number of array elements. I think this didn't work since one error refers to this line:
prog.cpp:13:18: error: sizeof on array function parameter will return size of 'int *' instead of 'int []' [-Werror,-Wsizeof-array-argument]
int len = (sizeof(myArray) / sizeof(myArray[0]));
^
prog.cpp:11:17: note: declared here
int sumEven(int myArray[])
^
1 error generated.
I have experience with Matlab, Mathematica, and Python, and so my C++ formatting may be strange. Thanks for taking a look.
The problem is that when passed as arguments to a function, arrays decays to pointers to the first element, so the function
int sumEven(int myArray[]) { ... }
is actually equal to
int sumEven(int *myArray) { ... }
And taking the size of a pointer returns the size of the pointer and not what it points to.
If you need to know the size in the function, you should pass the number of elements as an argument:
int sumEven(int *myArray, size_t len) { ... }
Arrays decay to pointers when you pass them to a function. That means that the information regarding the size of the array is lost.
This means that (sizeof(myArray) / sizeof(myArray[0])) will not do what you want in this context, because here myArray is a pointer.
The canonical solution is to add another parameter representing the array size, and use that instead. This is the approach used in C.
In C++, however, you should probably be using std:: vector, unless you have a specific reason to stick with arrays, which are notoriously error-prone.

Returning two dimensional array of strings

string** flowFile() {
string line;
string word[8];
int i=0;
static string flow[23][2];
ifstream myfile ("test.txt");
if (myfile.is_open())
{
while ( getline (myfile,line) )
{
strSplit(line,word);
flow[i][0]=word[1];
flow[i++][1]=word[2];
}
myfile.close();
}
else cout << "Unable to open file";
return flow;
}
int main()
{
string **fl=flowFile();
}
I'm getting this error:
error: cannot convert ‘std::string (*)[2] {aka std::basic_string<char> (*)[2]}’
to ‘std::string** {aka std::basic_string<char>**}’
in return
What is wrong with my code?
string flow[23][2] and string ** are two different incompatible types. One cannot convert to another implicitly. Thats all. The solution is to make them compatible, by making the later string [23][2], return reference and accept reference, but that would still be a bad solution, because you're still working with raw arrays.
A good solution is to use std::vector and std::string. Maybe, you need std::pair also, or std::array.
Here is one possible solution:
#include <vector>
#include <array>
#include <string>
//C++11 style typedef
using flow_data_t = std::vector<std::array<std::string,2>>;
//reimplementation of your function
flow_data_t flowFile()
{
std::string line;
std::string word[8];
int i=0;
flow_data_t flow;
std::ifstream myfile ("test.txt");
if ( !myfile )
cout << "Unable to open file";
while ( std::getline (myfile,line) )
{
strSplit(line,word);
flow.push_back({word[0], word[1]});
}
return flow;
}
int main()
{
flow_data_t data=flowFile();
for(auto const & row : data)
for(auto const & col : row)
//work!
}
Hope that helps.
You cannot return array from a function even though you can return a pointer and let your array decay to a pointer: Array Decay
However 2D array can decay to neither T* nor T** because of the memory layout of the array is different from "2D pointer array" (it is actually more like flattened), and you cannot return array from function. However in C++ you can return array reference Full Code:
//This does not work
//typedef string * string2d[2];
//typedef string *(&string2d)[2];
typedef string (&string2d)[23][2];
string2d flowFile() {
static string flow[23][2];
return flow;
}
Array reference would even preserve the information of how big each row and columns are and no array decaying happen.
Of course, a more suggested "C++ way" to do this is using std::vector (as always).
In C++, arrays have type std::vector. You should use these, not low-level builtin arrays declared with [].
In C++, string [23] is sometimes interchangeable with string*, but string[23][2] is never interchangeable with string**. That's one reason you should not use builtin arrays.
In C++, you cannot return a local builtin array variable. It will compile but then your program will probably crash. This is another reason you should not use builtin arrays. (Returning a static array should be OK though).
There are many more reasons.
There is nothing wrong with returning a pointer to a static variable. It's just that the return type must be declared properly. It kind of makes sense if you try to reproduce what the declarations mean, and what the compiler accordingly tries to do. Consider the declaration static string flow[23][2];. It declares 23 rows of strings, each with 2 elements. It helps if you look at it as a one-dimensional array. It just so happens that the array elements are arrays, but that's not so important right now (but we'll come back to it). From this perspective the array has just 23 elements, and each element has the size of 2 strings. Like with all arrays, the elements (here: arrys of 2 strings) are simply lined up in memory.
Like any array, flow will in most contexts decay to a pointer to its first element. Incrementing that pointer will point to the next element, i.e the second row. Numerically the compiler must add 2*sizeof(string) to the address of flow in order to compute the address of flow's next element, which would be flow[1]. (It comes directly behind flow[0]. No magic here.)
Now if you declare string **flowpp, flowpp is a pointer already, no need to decay. If we think it is pointing to the first element in an array, what type would the elements have? Sure enough: plain pointers. Incrementing flowpp would let it point to the next element. My pointers are 4 bytes large, so that numerically adding just 4 to flowpp would be enough to access flowpp's next element. Compared to what needs to be added to flow (remember, 2*sizeof(string)), that's completely different. The compiler computes the offsets of elements depending of what the pointers point to! Which is very different in the two cases.
So what can your function return? What does flow decay to when you return it? It decays to a pointer to its first element. The elements are arrays of two strings. It must be string xxx[2], with xxx being a pointer: hence string (*p)[2]. If the pointer is actually returned by a function, we have a function call instead of plain p, so it's (*f())[2].
Here is a complete example:
#include<iostream>
using namespace std;
const int numFlowElems = 3, numArrElems = 2;
/** #return a pointer to the first element of a static array
of string[numArrElems]s.
*/
string (*flowFile())[numArrElems]
{ // init so that we see something below.
static string flow[numFlowElems][numArrElems]
= {{"1","2"},
{"3","4"},
{"5","6"}
};
// your function code ...
return flow;
}
int main()
{
// array decays to ptr, like usual. Ptr elems are string[numArrElems].
// ptrToArr is a pointer to arrays of two strings.
string (*ptrToArr)[numArrElems] = flowFile();
for( int flowInd= 0; flowInd<numFlowElems; ++flowInd )
{
for(int strInd = 0; strInd<numArrElems; ++strInd)
{
cout << ptrToArr[flowInd][strInd] << ' ';
}
cout << endl;
}
return 0;
}
How do you parse string (*flowFile())[numArrElems]? I needed two attempts to get the declaration right, if that's any consolation. The key is that in C and C++ (not in C#, mind you!) a declaration has the shape of an expression.
You can do it from the inside to the outside: flowFile() is a function. The result is dereferenced because the function call has higher precedence than the star: *flowFile() is the dereferenced result. Apparently that result is an array of size numArrElems, with elements which are strings.
You can do it outside in: The result of (*flowFile())[numArrElems] is declared as a string. (*flowFile()) is an array of strings with numArrElems elements. Apparently flowFile() must be dereferenced to obtain that array so that flowfile is a function which returns a pointer to an array of numArrElems strings. That's true! It returns the first element of flow, which is exactly an array of strings.
Vectors of vectors might indeed be easier; if you want to retain the semantics you should pass references, as others mentioned: After all, all functions in your original program will operate on the same static array. If you pass vectors by value that will not be the case any longer. But then, that may actually be beneficial.

error: invalid types 'double*[double]' for array subscript

I'm trying to create a programme which will place all of the real numbers in an array - with the ordering of the numbers taking place in a separate function - into descending order, and print them out.
The following is the programme as I have it so far, but there are 2 issues with it, according to the compiler:
(i) On line 22 ("return N[t];"), I get "error: invalid types 'double*[double]' for array subscript".
(ii) On line 28 ("cout << sort_array(Q[100]) << " " "), I get "error: cannot convert 'double' to 'double*' for argument '1' to 'double* sort_array(double*)'".
I'm not quite understanding why these two errors are coming up, but I would love some help in resolving them.
#include <iostream>
#include <cstdlib>
using namespace std;
double *sort_array (double *N) {
double t;
int size=100, a, b;
for (t=0; t<size; t++)
*N = rand()%250;
for (a=1; a<size; a++) {
for (b=size-1; b>=a; b--) {
if (N[b-1] < N[b]) {
t = N[b-1];
N[b-1] = N[b];
N[b] = t;
}
}
}
return N[t];
}
int main()
{
double Q[100];
cout << sort_array(Q[100]) << " ";
cout << endl;
return 0;
}
The problem is the sort_array(Q[100]) statement. This is telling the compiler to call sort_array with the 101st double in the Q array (which is actually out of bounds). You really wanted to just pass in Q instead of Q[100].
However, note that passing C-style arrays as double* for example loses the length information and is not canonical C++. Instead you can use vector to carry the array and size with it.
EDIT: And since you're modifying the data in-place there is no need for your function to return anything at all. Change it to void and just skip the return at the end. You'll then have to iterate over the vector/array to print out each of the elements. cout doesn't provide builtin capability to print aggregates.
Finally a book from The Definitive C++ Book Guide and List might help get you up to speed on C++ concepts.
The first error is because N[t] is a double (it means "The t'th element of N"), but your function returns a double*. Your function doesn't look like it should return anything, actually. It sorts the data pointed to by N, so there's no need to return anything. You should probably switch to a void return value.
The second error is because Q[100] is a double (it means "the 101th element of Q, which is an error anyway since the last element of Q is Q[99], as array indexes in C++ begin at 0, not 1), but your function expects a double. I assume that what you actually mean to do is:
sort_array(Q)
to pass the pointer to the first element directly.
Remember that when passing arrays around, you only need to pass the address of the arrays's first element. In this case, Q, which is equivalent to &Q[0] but is easier to write.