Find End of Array Declared as Struct Type C++ - c++

I was recently learning to use struct datatype in c++. I know how the basics of struct datatype work and how to manipulate its variables. But I was wondering how would I determine the end of struct datatype array. For example consider the code below:
struct PersonDetails
{
string name, address;
int age, number;
}
Now in c++ program I create an array of struct type as follows:
PersonDetails Data[500];
Now consider that I have 30 records in data array and I have to display these records by looping through data array's index. So how would I determine that I have to loop through only first 30 indexes as the data is only stored in these indexes. As in char array we compare all indexes with '\0' to determine the end of array. Then what method will we use for Data[] array?
An edit that I have no idea about Vectors and the project i am working on requires me to use basics of c++(functions, control structures, loops, etc.).

It's not feasible.
For char[], back in times of C standardization, developers agreed to use \0 (integer value 0) as a special character marking end-of-string. Everything works as long as everyone is following this convention (i.e. both standard library functions and developers using those functions).
If you wanted to have such a convention for your type, you could just write down "Data object with both strings empty and both ints equal to 0 is array terminator", but you would have to follow this convention. You'd have to write functions that would stop processing array upon finding such an object. You'd have to make sure that in every array there is at least one such object.
Instead
You should use std::vector<Data> which can automatically accomodate for any number of Data objects and will now precisely how many of them are currently stored (using size() method)
or
use std::array<Data, 30>, which can store exactly 30 objects and you can assume all of them are valid objects.

IMHO the correct way to solve this is to not use a C-style array, but instead use a std::array or std::vector that knows it's .size().
Iterating a std::vector or std::array is trivial:
for (const auto& element : Data_array) {
// Do something with the array element
}
See also:
https://en.cppreference.com/w/cpp/container/array
https://en.cppreference.com/w/cpp/container/vector
https://en.cppreference.com/w/cpp/language/for
https://en.cppreference.com/w/cpp/language/range-for

The simplest solution is to just have a separate variable specifying how many array elements are filled in.
PersonDetails Data[500];
int numPersons = 0;
Data[0].name = ... ;
Data[0].address = ...;
Data[0].age = ...;
Data[0].number = ...;
numPersons = 1;
Data[1].name = ... ;
Data[1].address = ...;
Data[1].age = ...;
Data[1].number = ...;
numPersons = 2;
...
Then you use that variable when looping through the array.
for (int i = 0; i < numPersons; ++i)
{
// use Data[i] as needed...
}

I don't really agree using std::array makes any difference.
The problem you currently have doesn't occur in whether we have such an element in the container, but whether the element we are inspecting useful.
Consider the example you gave, for an array of chars, we simply check whether one of the elements is \0 to decide whether or not we should halt the iteration.
How does that work? The ramaining elements, of course, default initialized to be \0, they exist, but of no use.
Similarly, you can check, in this example, whether
name.empty()
Or, in order to avoid any possible exception, as mentioned in the comment section, do this:
add user-defined constructor to the class ( or struct, they are same actually.) which initialize age to -1 and then check if age == -1.
because it's impossible for a people not having any name, that means, you have not assign to any of the remaining elements. Thus, stop iteration.
As a supplement, using std::vector makes sense, but if that isn't a option for you for the time being, you don't need to consider it.

Related

How to create variable name with integer appended to the end?

I want to create a for loop that will fill a bunch of arrays with data in c++. Now to save space and in the future once more arrays are added which they will, I have the for loop. Each array for demonstration purposes is called Array# (# being a number) The point of the for loop would be to set a constant with maximum arrays, then cycle through each array filling by appending i to the end of the Array name.
For example in pseudo code:
for (i = 1; i < numberofarrays; i++)
{ fill (Array & i) with ("Array" & i & "-default.txt")}
It is impossible to generate Variable Names by any type of code.
(Meaning it is impossible to generate dynamic variable names on Runtime or on Compiletime)
The best solution possible would be a array of arrays:
int Arrays[][];
Calling Arrays[0] would give you the first array.
If you want to determine the number of arrays during Runtime you need to use pointers!
That would look like that:
(int[])* Arrays = new (int[])[numberofarrays];
Accessing the arrays in the array would work the same!
An alternative would be using the container vector from std.
The code would the look like this:
#include<vector>
// More includes
// Optional
using namespace std;
// Somewhere in your code
vector<vector<int>> Arrays;
You still would acces the elements by using your standard array method (Arrays[15][78] e.g.)
You don't really need the name. You can use an std::vector of arrays. This will not work out of the box, see Correct way to work with vector of arrays
Another approach would be to have an std::map of arrays. You could have the name as the key, if that is what you really want. You will still have to use the same workaround as before to have an array as a value. See Character Array as a value in C++ map for example.

Array as parameter, variable confusion

Ok, so I was getting some pretty off course answers so I thought I would edit this post and add the notes from the textbook for clarity:
Sometimes, the number of elements in the array might be less than the size of the array. For example, the number of elements in an array storing student data might increase or decrease as students drop or add courses. In such situations, we want to process only the components of the array that hold actual data. To write a function to process such arrays, in addition to declaring an array as a formal parameter, we declare another formal parameter specifying the number of elements in the array, as in the following function:
void initialize(int list[], int listSize)
{
int count;
for (count = 0; count < listSize; count++)
list[count] = 0;
}
The first parameter of the function initialize is an int array of any size. When the function initialize is called, the size of the actual array is passed as the second parameter of the function initialize.
Ok, now that I posted the entire example with textbook notes in it, my confusion is why they set the array to zero. The notes give me the impression that this function is allowing a user to use the array for any size that they wish because the size is set to zero which (I am guessing here) allows the user to pick any size array they want? and it will just reset every time back to zero so if you need more or less units for the next time, it will be default to zero so you can fill it again?
you said:
I know the function initialize is used to determine the value of the
array list by passing the value of the array to listsize
no. it's not true. this function is not to determine something but to INITIALIZE all the array (up to listsize index, btw: it might be dangerous since you can pass listsize greater than this list size in fact) with 0.
and
by passing the value of the array to listsize
no! listsize is here not the value of element, it is array size, look at "for" loop #Jason xD.
have you tried to call this function on some array with some listsize?
If your code was properly formatted, it might be more apparent that the statement list[count] = 0; gets executed each time through your for loop.
That is, it sets an element to zero each time through the loop. The result is that after the loop is complete, all elements in the array will be set to zero.
If you are talking about the "count = 0", then:
The more usual syntax is:
for(int blah = 0; blah < max; blah++)
But there's no reason why 'blah' has to be declared inside the for() statement itself:
int blah;
for(blah = 0; blah < max; blah++)
...is also acceptable.
Or:
int blah = 0;
for( ; blah < max; blah++)
Sometimes (but not in your example) it's desired to have 'blah' exist beyond the scope of the for() statement, so you can do something with it afterward:
int fileNum = 0;
for( ; fileNum < maxFiles && file_exists(fileNum); fileNum++)
{
//...do something...
}
int lastFileWas = fileNum; //Either it's 'maxFile', or the first file that didn't exist.
Another reason for putting variables outside of the for() statement, is when variables are really really large, and it would make the code easier to read if it's outside of the statement:
std::vector< std::pair<std::string, int> >::iterator myIterator = myVector.begin();
for( ; myIterator != myVector.end(); myIterator++)
This would be very messy if it was inside the for-statement itself (std::vector< std::pair >::iterator is a very long variable name to write out that would be messy to cram into a for() statement). (Though this is less a problem with C++11's 'auto' keyword).
If you are talking about the "list[count] = 0;", then:
With arrays, to assign a value, you "index into" the array using the square brackets (called the 'subscript operator'), and you can access individual variables (called 'elements') held in the memory of the array:
int myArray[10]; //10 integers in a block of memory.
myArray[0] = 5; //Accessing the first element in the block of memory.
myArray[3] = 17; //Accessing the fourth element in the block of memory.
In general:
Since you are using C++, you usually (90% of the time) would be better off using a std::vector. Arrays are rather odd, because they are very similar to pointers to blocks of memory, and can't be treated like regular variables. A std::vector, aside from many other benefits, wraps the array so you can treat it like a regular variable (because a vector is one).
Another quick way of accomplishing the same thing:
memset(list, 0, sizeof(int)*listSize);
This gets the whole block of memory allocated to list by calculating the size of the data type times the number of elements and setting it all to 0.
It is doing what it says, it is initializing. In this case the method is just setting every element of list to 0. It is probably more clear if you add braces:
for (count = 0; count < listSize; count++)
{
list[count] = 0;
}
So based on your updated post, the book's description is saying 1) You may not want to process the whole array because only a portion of it may have valid data 2) In order to write functions to process arrays that behave this way, functions that process these array's must not only take the array as a parameter but also the number of valid elements. 3) We are going to provide an example function initialize which follows the rules we just described BUT nothing in the text actually speaks to end result of initialize.
My above description as well as the other posts provide an accurate description of initialize.

How to pass a dynamic 2D array from C++ to C as void *

A function written in C e.g.
extern "C" void cityManipulator( void * data, int size);
takes an array similar to this,
Shanghai, China, 17
Delhi, India, 16
Cairo, Egypt, 7
It then capitalizes the city names and multiplies the population number by a one million.
The function works with the value that I pass not the copy.
The function requires to know the dimensions of the array: how many rows and columns and the size in bytes of each element ( i.e. each city name ) in a given column
I am not interested in how the C function works but I want to use it as is from C++.
The array has to be dynamic i.e. the columns and rows are not static.
How should my data structure that I need to pass to this function look like?
This is my attempt. Use a nested vector of boost::variant
typedef boost::variant<std::string, int> Var;
typedef std::vector<Var> OneRow;
std::vector<OneRow> theArray;
But I can't figure out how to pass theArray to cityManipulator( void *d ).
&theArray[0] does not work.
I wouldn't even attempt to pass an instance of a class to C. My approach would be:
export my data from my C++ classes into a memory suitable for the function to be called
Call the function
import the modified data back to my C++ classes.
Either that, or if the function is trivial, re-implement it in C++.
EDIT: Implementing variable number of elements in a structure in C.
If all you have is access to a single parameter, you cannot pas the number of elements in an array explcitly. In that case, there are two methods you can use.
The first method is flagging the last entry in some manner. The explicit way of doing this is to add a field isLastEntry in the structure, and set it to false in all entries, except the last one. The implicit way of doing it is to allocate a special element, and make it the last entry in the array. Zero terminated strings fall into this category. For complex structures, such as the one provided in npclaudiu's answer, you can add a final dummy field where all pointers are set to NULL as the terminator.
The alternative method is having a structure that contains the length as its first element, and the array as its last. Since C doesn't provide support for variable length arrays, you can use a trick such as the one asked in this question. Simply replace the BYTE type with your structure to handle complex types instead of strings.
You could also try this:
// C++
struct Row {
char* City;
char* Country;
int Population;
};
std::vector<Row> rows;
// Then call your C function like this:
cityManipulator(&rows[0]);

C++ Array of Objects

I have an array in a class that should hold some instances of other objects. The header file looks like this:
class Document {
private:
long arraysize;
long count;
Row* rows;
public:
Document();
~Document();
}
Then in the constructor I initialize the array like this:
this->rows = new Row[arraysize];
But for some reason this just sets rows to an instance of Row rather than an array of rows. How would I initialize an array of Row objects?
Both SharpTooth and Wok's answers are correct.
I would add that if you are already struggling at this level you may be better off using a std::vector instead of a built-in array in this case. The vector will handle growing and shrinking transparently.
This should work. One possible "error" would be an incorrect value for arraySize.
However you should better use a std::vector from the standard library for that purpose.
#include <vector>
class Document {
// ...
std::vector<Row> rows;
// ...
};
and in your constructor:
Document::Document() : rows(arraySize) { // ... }
or
Document::Document() { rows.assign(arraySize, Row()); }
If arraySize contains a reasonable value at that point you actually get an array. I guess you trust your debugger and the debugger only shows the 0th element (that's how debuggers treat pointers), so you think there's only one object behind that pointer.
For i in [0;arraysize[, *(this->rows+i) should be an instance of row.
What precisely makes you think that rows is only one element? Make certain that you arraysize isn't 1. If it is, you'll get an array of 1 element. Mind you, you must still call delete [] with an array of size 1.
Also, why is arraysize different than count? Using that terminology, you should be making an array of count elements and arraysize should be equal to sizeof(Row) * count.
Also, you specifically ask "How would I initialize an array of Row objects?". Do you mean allocate? If so, that's how you would do so. If you mean initialize, the default constructor of Row will be called on each element of the array when the array is allocated.

C++ How can I iterate till the end of a dynamic array?

suppose I declare a dynamic array like
int *dynArray = new int [1];
which is initialized with an unknown amount of int values at some point.
How would I iterate till the end of my array of unknown size?
Also, if it read a blank space would its corresponding position in the array end up junked?
Copying Input From users post below:
Thing is:
a) I'm not allowed to use STL (means: no )
b) I want to decompose a string into its characters and store them. So far I wanted to use a function like this:
string breakLine (string line){
int lineSize = line.size();
const char *aux;
aux=line.data();
int index=0;
while (index<=lineSize){
mySynonyms[index]=aux[index];
index++;
}
I thought that the array aux would end up junked if there was a large blank space between the two numbers to be stored (apparently not). And I was wondering if there was a way to iterate till an undefined end in this type of array. Thanks for you answers.
You don't: wrap the array into a structure that remembers its length: std::vector.
std::vector v(1);
std::for_each( v.begin(), v.end(), ... );
No portable way of doing this. Either pass the size together with the array, or, better, use a standard container such as std::vector
Short answer is that you can't. If you have a pointer to the first element of an array, you can't know what the size of the array is. Why do you want to use a array in the first place. You would be much better off using a std::vector if your array can change size dynamically, or a boost::Array if it will be a fixed size.
I don't understand your second question.
Your code needs to keep to track of the array, so the size would never be unknown. (Or you would have to use some library with code that does this.)
I don't understand the last part of your quesiton. Could you elaborate?
You explained in your post below that you want to look at the guts of a std::string.
If you are expecting your stirng to be like a c-string (aka doesn't contain NULLs), then use line.c_str() instead of line.data(). This will guarantee that aux points to a null terminates c-style string.
After that you can iterate until aux[index] == '\0';
Otherwise, you can use line.data() and string.length/size to get it's size like in your example.
However, "decomposing a string into its characters" is pretty pointless, a string is an array of characters. Just make of copy of the string and store that. You are allowed to do:
char ch = line[index];
Better yet, use iterators on the original string!
for(std::string::const_iterator it = line.begin(); it != line.end(); ++it) {
const char ch = *it;
// do whatever with ch
}
a) I'm not allowed to use STL (means:
no )
What?? Who's moronic idea was that?
std::vector isn't part of the "STL" (which is a copyrighted product of HP), but is (and has been for nearly a decade) part of the C++ Language Standard.
If you're not allowed to use the STL (for whatever reason), the first thing you want to do is actually to implement your own version of it – at least the parts you need, with the level of customizability you need. For example, it's probably overkill to make your own vector class parametrizable with a custom allocator. But nevertheless do implement your own lightweight vector. Everything else will result in a bad, hardly maintainable solution.
This smells like homework, and the teacher's objective is to give you a feeling of what it takes to implement dynamic arrays. So far you're getting an F.
You need to realize that when you allocate memory like this
int *dynArray = new int [1];
you allocate precisely one integer, not an indefinite number of integers to be expanded by some unidentified magic. Most importantly, you can only say
dynArray[0] = 78;
but you cannot say
dynArray[1] = 8973;
The element at index 1 does not exist, you're stepping into memory that was not reserved for you. This particular violation will result in a crash later on, when you deallocate the array, because the memory where you stored 8973 belongs to the heap management data structures, and you corrupted your heap.
As many other responders mention, you must know how many elements you have in the array at all times. So, you have to do something along the lines of
int arraySize = 1;
int *dynArray = new int [arraySize];
arraySize goes together with the array, and is best combined with dynArray in one C++ object.
Now, before you assign to dynarray[1], you have to re-allocate the array:
if (index > arraySize) {
int newSize = index+1;
int *newArray = new int[newSize]
// don't forget to copy the data from old array to new
memcpy(newarray dynArray, sizeof *newArray * arraySize);
arraySize = newSize;
dynArray = newArray;
}
// now you're ready!
dynArray[index] = value;
Now, if you want to make it a bit more efficient, you allocate more than you need, so you don't have to allocate each time you add an element. I'll leave this as an exercise to the reader.
And after doing all this, you get to submit your homework and you get to appreciate the humble std::vector that does all of this for you, plus a lot more.
Use a vector, which has a vector.size() function that returns an integer and a vector.end() function that returns an iterator.
You could create a simple Vector class that has only the methods you need. I actually had to recreate the Vector class for a class that I took this year, it's not very difficult.
If there's a value that cannot be valid, you can use that as a sentinel, and make sure all of your arrays are terminated with that. Of course, it's error-prone and will cause hard-to-find bugs when you happen to miss doing it once, but that's what we used to do while reading files in FORTRAN (back in the all-caps days, and before END= became standard).
Yes, I'm dating myself.