Heidel
Heidel

Reputation: 3254

How to get size c++ dynamic array

I'm studying C++ and I need to create structure Airplane and work with it.

My structure Airplane.h

#include "stdafx.h"
using namespace std;

struct Airplane {
    string destination;
    int number;
    string type;
};

and it's my code

#include "stdafx.h"
#include "Airplane.h"

string SetDestination(int n);
string SetType(int n);
void PrintAirplaneList(Airplane * &airplaneList, int n, string title);
void SortByDestination (Airplane *&airplaneList, int n);
void FindAirplanesAndPrint(Airplane *&airplaneList, int n, string type);

int _tmain(int argc, _TCHAR* argv[])
{
using namespace std;

srand((unsigned)time(NULL));

int n;
cout << "Input n = ";
cin >> n;

Airplane * airplaneList = new Airplane[n];

for (int i = 0; i < n; ++i)
{
    airplaneList[i].destination = SetDestination(rand()%5);
    airplaneList[i].number = rand()%9001 + 1000;
    airplaneList[i].type = SetType(rand()%3);
}

PrintAirplaneList(airplaneList, n, "List:");
SortByDestination (airplaneList, n);
PrintAirplaneList(airplaneList, n, "Sorted list (by destination):");

string type;
cout << "Input type: ";
getline(cin, type);
FindAirplanesAndPrint(airplaneList, n, type);

delete [] airplaneList;

system("PAUSE");
return 0;
}

string SetDestination (int n)
{
    string destination;
    switch(n){
    case 0: destination = "Tokio"; break;
    case 1: destination = "Amsterdam"; break;
    case 2: destination = "Moscow"; break;
    case 3: destination = "Philadelphia"; break;
    case 4: destination = "San Diego"; break;
    default: destination = "Unknown city"; break;
    }
    return destination;
}

string SetType (int n)
{
    string type;
    switch(n){
    case 0: type = "passenger"; break;
    case 1: type = "cargo"; break;
    case 2: type = "post"; break;
    default: type = "unknown type"; break;
    }
    return type;
}

void PrintAirplaneList(Airplane *&airplaneList, int n, string title)
{
    cout << "\n";
    cout << title << "\n\n";
    for (int i = 0; i < n; ++i)
    {
        cout << "Destination: " << airplaneList[i].destination << "\n";
        cout << "Number: " << airplaneList[i].number << "\n";
        cout << "Type: " << airplaneList[i].type << "\n\n";
    }
}

void SortByDestination (Airplane *&airplaneList, int n)
{
    for (int i = 0; i < n - 1; ++i)
    {
        for (int j = 0; j < n -1; ++j)
        {
            if(airplaneList[j + 1].destination > airplaneList[j].destination) continue;
            Airplane tempAirplane = airplaneList[j];
            airplaneList[j] = airplaneList[j + 1];
            airplaneList[j + 1] = tempAirplane;
        }
    }
}

void FindAirplanesAndPrint(Airplane *&airplaneList, int n, string type) {
    cout << "Type - " << type << "\n";
    int count = 0;
    for (int i = 0; i < n; ++i)
    {
        if (airplaneList[i].type == type)
        {
            cout << "Destination: " << airplaneList[i].destination << "\n";
            cout << "Number: " << airplaneList[i].number << "\n";
            ++count;
        }
    }
    if (count == 0)
    {
        cout << "Not found\n";
    }
}

I have two questions.
1. I can't input type in

string type;
cout << "Input type: ";
getline(cin, type);
FindAirplanesAndPrint(airplaneList, n, type);

and my function FindAirplanesAndPrint starts to work without any value for type. How to make my programm to get value?
2. How to get size of dynamic array in functions? Because it seems the passing size of array n in every function is the wrong way.

Upvotes: 3

Views: 14200

Answers (6)

archo
archo

Reputation: 167

You cannot portalby find out the size of a dynamically allocated array in C++.

This is actually not entirely correct. There is widespread support for a particular kind of size retrieval - the size of an array of objects with destructors.

To anyone considering using this however - I have to warn you that to my knowledge, this is not in any way guaranteed by any standard or compiler vendor. It may change at any point in time and should not be relied upon for anything other than hobby projects.

The size of the array of destructible objects is stored right before the objects themselves, and can be accessed with a basic pointer cast: ((size_t*)mem)[-1].

When you think about it - when you call delete [], you are not passing the size of the array, so C++ must store the exact size somehow to know how many objects to call the destructor on, in a way that can be trivially and efficiently accessed from the pointer itself. However - there is no guarantee that it has to be the number of elements - it can also be the number of bytes or the end pointer, possibly with some flag bits mixed in. That said, once they've decided on a convention, it would likely break some kind of backwards compatibility to change it later.

For those interested to test this, here's the code: https://ideone.com/Z0Sta1

#include <stdio.h>

struct bytes10
{
    ~bytes10() { printf("dtor %p", this); }
    char _[10]; // to test whether the size or the count is returned
};

int main()
{
    size_t size1 = ((size_t*)new int[10])[-1]; // doesn't work (pointer on some platforms, allocation size-based number on others)
    printf("%zu (0x%zx)\n", size1, size1);
    printf("%zu\n", ((size_t*)new bytes10[5])[-1]);
    printf("%zu\n", ((size_t*)new bytes10[6])[-1]);
    printf("%zu\n", ((size_t*)new bytes10[7])[-1]);
    printf("%zu\n", ((size_t*)new bytes10[65536])[-1]);
    return 0;
}

Possible output (the first value may differ):

49 (0x31)
5
6
7
65536

P.S. In my view, the C++ committee should consider either standardizing access to the array size for all new[] allocations, or providing a new type of new-like operator that is capable of guaranteeing a size prefix. This has the power to allow certain uses of new[] for lower level code (e.g. much easier single-pointer immutable strings).

Upvotes: 0

Manuel Reyes
Manuel Reyes

Reputation: 323

One approach that i use when i work with dynamic arrays, is to pass the array as a reference to the function. This will help you to keep the size information of the array.

For Example:

string list1[3] = {
    "item1",
    "item2",
    "item3"
};

string list2[2] = {
    "item1",
    "item2"
};

template<typename T, size_t N>
void PrintItems(T(&items)[N]) {
    for (int i = 0; i < N; i++) {
        cout << items[i] << endl;
    }
}

int main() {
    PrintItems(list1);
    PrintItems(list2);
}

In the previous example, N stores the correct size information for the array. More information here

Upvotes: 0

platinoob_
platinoob_

Reputation: 161

you could probably make a dynamic array with 0 items, make an int counter, make a while loop with getline as a statement, while (getline(cin, string_var) != SomeText) /* SomeText = some kind of text so the user show you there are not going to be any more inputs*/, what you were going to do it in the for do it in the while and at the end of the while increase the counter by one, i++. And about the access to the array, if your dynamic array has 0 items, then SomeDynamicArray[1].something = SomeValue will just add a second item to the array and the "something" of that item will be equal to SomeValue.

type *ArrayPointer = new type[0];
string StringVar;
int i = 0;
while (getline(cin, StringVar) != "Text that show there are not going to be any more inputs") {
     /*code*/
     i++;
} 

idk if it will work on your case, but try it if you want. Also, about everyone saying about vectors, as I know at least, vectors are slower and spend more memory, bcs they double there size instead of just increasing it every time needed. I hope I will be some help.

Upvotes: 0

Vlad from Moscow
Vlad from Moscow

Reputation: 310990

The problem with entering type is that the input buffer contains the new line character after entering n. You should use member function ignore to clear the buffer before using function getline.

As for your second question then in general you should track the size of a dynamically allocated array yourself. Or you can set the last element of the array as NULL and use it as a sentinel.

Upvotes: 1

jrok
jrok

Reputation: 55395

1) Ommiting the irrelevant, this is basically what you got:

cin >> n;
getline(cin, type);

operator>> leaves a new-line character in the input buffer and that's the first character that getline sees. Since '\n' is the default line delimiter, you get an empty line. To fix it call cin.ignore() before you call getline to discard the '\n'.

2) If you wish to stick with raw pointers, passing the size as a parameter is your only choice. Switch to std::vector and you get size() method that you can query at any time.

Upvotes: 3

LihO
LihO

Reputation: 42083

"How to get size of dynamic array in functions? Because it seems the passing size of array n in every function is the wrong way."

Yet it is the only way when you use dynamically allocated C-style array.

If you want to avoid sending the size explicitly then pass some object that wraps this raw memory buffer and provides other means of retrieving the size. The most reasonable solution here would be using std::vector<Airplane>.

Upvotes: 4

Related Questions