3

I am basically looking for some sort of "dynamic" way of passing the size/length of an array to a function.


I have tried:

void printArray(int arrayName[])
{
    for(int i = 0 ; i < sizeof(arrayName); ++i)
    {
        cout << arrayName[i] << ' ';
    }
}

But I realized it only considers its bytesize and not how many elements are on the array.


And also:

void printArray(int *arrayName)
{
    while (*arrayName)
    {
        cout << *arrayName << ' ';
        *arrayName++;    
    }
}

This has at least printed me everything but more than what I expected, so it doesn't actually work how I want it to. I reckon it is because I don't exactly tell it how big I need it to be so it plays it "safe" and throws me some big size and eventually starts printing me very odd integers after my last element in the array.


So I finally got this work around, yet I believe there is something better out there!:

void printArray(int *arrayName)
{
    while (*arrayName)
    {
        if (*arrayName == -858993460)
        {
            break;
        }
        cout << *arrayName << ' ';
        *arrayName++;
    }
    cout << '\n';
}

After running the program a few times I realized the value after the last element of the array that I have input is always: -858993460, so I made it break the while loop once this value is encountered.


include <iostream>
include <conio.h>

using namespace std;

    // functions prototypes
void printArray (int arrayName[], int lengthArray);

    // global variables

    //main
int main ()
{
    int firstArray[] = {5, 10, 15};
    int secondArray[] = {2, 4, 6, 8, 10};
    printArray (firstArray,3);
    printArray (secondArray,5);

    // end of program
    _getch();
    return 0;
}

    // functions definitions
void printArray(int arrayName[], int lengthArray) 
{
    for (int i=0; i<lengthArray; i++)
    {
        cout << arrayName[i] << " ";
    }
    cout << "\n";
}

Thank you very much.

Useless
  • 64,155
  • 6
  • 88
  • 132
Hugo Santos
  • 103
  • 2
  • 7
  • 5
    Your fundamental misunderstanding is that your function receives an array when, in reality, it receives a pointer. Arrays cannot be passed to functions (or returned from them). They degrade to pointers. – Ed S. May 19 '13 at 18:19
  • I guess I get it. So when I create an array this is siting somewhere in the memory. When I want to "use" this array to pass it to a function, I am actually passing to the function an address of where this array can be encountered in the memory, which by itself is far superior memory-wise than it would be passing an actual array? – Hugo Santos May 19 '13 at 19:06

8 Answers8

11

TL;DR answer: use std::vector.


But I realized it [sizeof()] only considers its bytesize and not how many elements are on the array.

That wouldn't be a problem in itself: you could still get the size of the array using sizeof(array) / sizeof(array[0]), but the problem is that when passed to a function, arrays decay into a pointer to their first element, so all you can get is sizeof(T *) (T being the type of an element in the array).

About *arrayName++:

This has at least printed me everything but more than what I expected

I don't even understand what inspired you to calculate the size of the array in this way. All that this code does is incrementing the first object in the array until it's zero.

After running the program a few times I realized the value after the last element of the array that I have input is always: -858993460

That's a terrible assumption and it also relies on undefined behavior. You can't really be sure what's in the memory after the first element of your array, you should not even be accessing it.


Basically, in C++, if you want to know the size of a raw array from within a function, then you have to keep track of it manually (e. g. adding an extra size_t size argument), because of the way arrays are passed to functions (remember, they "decay into" a pointer). If you want something more flexible, consider using std::vector<int> (or whatever type of objects you want to store) from the C++ standard library -- it has a size() method, which does exactly what you want.

  • `sizeof` is not a function. – rubenvb May 19 '13 at 18:20
  • 2
    @rubenvb When/where did I assert it was one? –  May 19 '13 at 18:20
  • Oh! Meaning that I would multiply the bytesize of an int by how many ints I have on the array? – Hugo Santos May 19 '13 at 18:21
  • @H2CO3 When you said `but the problem is that when passed to a function, arrays decay into a pointer to their first element, so all you can get is sizeof(T *)`, implying `T[]` decays when passed to the `sizeof` "function". – rubenvb May 19 '13 at 18:23
  • 2
    @rubenvb: I don't see that implication. I see an explicit statement that the array decays to a pointer *when passed to a function* as it is does in the example provided by the OP – Ed S. May 19 '13 at 18:23
  • 1
    @rubenvb he didn't. He implied that the array was passed as a pointer to the function, and _inside that function_ `sizeof` works on that pointer. –  May 19 '13 at 18:24
  • 1
    @HugoSantos Yes, but not even that (basically, `sizeof()` works correctly on array only before passed to the function. In the function, you don't have an array anymore). –  May 19 '13 at 18:30
4

1st try

When arrays are passed into functions they decay to pointers. Normally, using sizeof on an array would give you its size in bytes which you could then divide by the size in bytes of each element and get the number of elements. But now, since you have a pointer instead of an array, calling sizeof just gives you the size of the pointer (usually 4 or 8 bytes), not the array itself and that's why this fails.

2nd try

The while loop in this example assumes that your array ends with a zero and that's very bad (unless you really did use a zero as a terminator like null-terminated strings for example do). If your array doesn't end with a zero you might be accessing memory that isn't yours and therefore invoking undefined behavior. Another thing that could happen is that your array has a zero element in the middle which would then only print the first few elements.

3rd try

This special value you found lurking at the end of your array can change any time. This value just happened to be there at this point and it might be different another time so hardcoding it like this is very dangerous because again, you could end up accessing memory that isn't yours.

Your final code

This code is correct and passing the length of the array along with the array itself is something commonly done (especially in APIs written in C). This code shouldn't cause any problems as long as you don't pass a length that's actually bigger than the real length of the array and this can happen sometimes so it is also error prone.

Another solution

Another solution would be to use std::vector, a container which along with keeping track of its size, also allows you to add as many elements as you want, i.e. the size doesn't need to be known at runtime. So you could do something like this:

#include <iostream>
#include <vector>
#include <cstddef>

void print_vec(const std::vector<int>& v)
{
    std::size_t len = v.size();

    for (std::size_t i = 0; i < len; ++i)
    {
        std::cout << v[i] << std::endl;
    }
}

int main()
{
    std::vector<int> elements;

    elements.push_back(5);
    elements.push_back(4);
    elements.push_back(3);
    elements.push_back(2);
    elements.push_back(1);

    print_vec(elements);

    return 0;
}

Useful links worth checking out

Undefined behavior: Undefined, unspecified and implementation-defined behavior

Array decay: What is array decaying?

std::vector: http://en.cppreference.com/w/cpp/container/vector

Community
  • 1
  • 1
Tuntuni
  • 481
  • 1
  • 7
  • 19
  • Thanks a tons! I could understand your explanations of why I was doing it wrong. Then again, I barely have any knowledge of C++. – Hugo Santos May 19 '13 at 18:56
  • @HugoSantos I'm glad you understood what I wrote. I'll edit the question in a bit to provide some useful links. If you feel like this answer satisfies your needs, accept it as an answer because that's how it works on SO. :) – Tuntuni May 19 '13 at 18:57
  • This answer system is so unfair. Everyone has helped me to understand the issue I had yet I can only "vote" for one person? – Hugo Santos May 19 '13 at 19:31
  • You don't have to worry about fairness: just accept the answer you found most helpful. Visitors can up-vote (or down-vote) as well. – Useless May 19 '13 at 22:17
3

As all the other answers say, you should use std::vector or, as you already did, pass the number of elements of the array to the printing function.

Another way to do is is by putting a sentinel element (a value you are sure it won't be inside the array) at the end of the array. In the printing function you then cycle through the elements and when you find the sentinel you stop.

Luke Morgan
  • 1,900
  • 2
  • 14
  • 18
  • Having a sentinel element is the correct approach? Or should I focus on learning std::vector? – Hugo Santos May 19 '13 at 18:49
  • I think you should learn std::vector, but sometimes sentinel elements can be useful (for the linked lists, for example) – Luke Morgan May 19 '13 at 18:51
  • @HugoSantos There's no one "correct" approach, anymore than there is one "correct" tool for every job. Sometimes you need a hammer, sometimes you need a screwdriver. If you are programming in C++ using `std::vector` is likely a better approach and the one you should consider before anything else when dealing with dynamically sized arrays. – Nik Bougalis May 19 '13 at 18:52
3

A possible solution: you can use a template to deduce the array length:

template <typename T, int N>
int array_length(T (&array)[N]) {
    return N;
}

Note that you have to do this before the array decays to a pointer, but you can use the technique directly or in a wrapper.

For example, if you don't mind rolling your own array wrapper:

template <typename T>
struct array {
    T *a_;
    int n_;

    template <int N> array(T (&a)[N]) : a_(a), n_(N) {}
};

You can do this:

void printArray(array<int> a)
{
    for (int i = 0 ; i < a.n_; ++i)
        cout << a.a_[i] << ' ';
}

and call it like

int firstArray[] = {5, 10, 15};
int secondArray[] = {2, 4, 6, 8, 10};
printArray (firstArray);
printArray (secondArray);

The key is that the templated constructor isn't explicit so your array can be converted to an instance, capturing the size, before decaying to a pointer.

NB. The wrapper shown isn't suitable for owning dynamically-sized arrays, only for handling statically-sized arrays conveniently. It's also missing various operators and a default constructor, for brevity. In general, prefer std::vector or std::array instead for general use.


... OP's own attempts are completely addressed elsewhere ...

Useless
  • 64,155
  • 6
  • 88
  • 132
2

Using the -858993460 value is highly unreliable and, in fact, incorrect.

You can pass a length of array in two ways: pass an additional parameter (say size_t length) to your function, or put a special value to the end of array. The first way is preferred, but the second is used, for example, for passing strings by char*.

nullptr
  • 11,008
  • 1
  • 23
  • 18
  • Thanks! I reckon I will be using a second parameter. – Hugo Santos May 19 '13 at 18:34
  • @Hugo, just to supplement: the -858993460 value you see (hex 0xCCCCCCCC) is a almost certainly special value that the *debug* version of your compiler uses to fill uninitialized variables in an effort to help detect access to variables before they are assigned a value and or detect buffer overflows. – Nik Bougalis May 19 '13 at 18:55
0

In C/C++ it's not possible to know the size of an array at runtime. You might consider using an std::vector class if you need that, and it has other advantages as well.

Devolus
  • 21,661
  • 13
  • 66
  • 113
  • 2
    -1 This is completely incorrect. `sizeof(some_array) / sizeof(some_array[0])` gives the size of an *array*. The OP's problem is that he thinks he has an array when he actually has a pointer. – Ed S. May 19 '13 at 18:19
  • 1
    @EdS. Technically, that's a compile-time calculation, at least in C++. (In C, the calculation might happen at runtime, but that feature of C (VLA) is not part of C++.) – Dietrich Epp May 19 '13 at 18:21
  • @DietrichEpp: Yes, `sizeof` is a compile time calculation, and arrays cannot be sized at runtime. So I suppose I should have said *misleading* instead. The answer is not helpful though. The OP needs to understand when an array is an array and when it is a pointer in disguise (as in the example) – Ed S. May 19 '13 at 18:21
  • An array is always a pointer. Using brackets is just a syntatical convenience. – Devolus May 19 '13 at 18:25
  • 1
    @Devolus: No, no they are not. Really, an array is *never* a pointer. , but it can *degrade* to one in certain situations. They are completely different things. You should go study up a bit on the subject, this sort of comment only helps to further mislead beginners who think that pointers and arrays are the same thing. – Ed S. May 19 '13 at 18:25
  • Thank you for your answer, as I have said I am a total beginner I barely understand some things you are saying but I will definitely try to learn to use std::vector class then. – Hugo Santos May 19 '13 at 18:33
  • A pointer is then just addressing the function to the array without actually passing the array? Sorry for questioning so much. – Hugo Santos May 19 '13 at 19:00
  • Yes. That's why I said above that an array is always a pointer, which is not 100% correct as such. When you pass it around it usually is though. – Devolus May 19 '13 at 19:08
0

When you pass the length of the array to printArray, you can use sizeof(array) / sizeof(array[0]), which is to say the size in bytes of the whole array divided by the size in bytes of a single element gives you the size in elements of the array itself.

More to the point, in C++ you may find it to your advantage to learn about std::vector and std::array and prefer these over raw arrays—unless of course you’re doing a homework assignment that requires you to learn about raw arrays. The size() member function will give you the number of elements in a vector.

Community
  • 1
  • 1
Jon Purdy
  • 53,300
  • 8
  • 96
  • 166
  • Thank you, wondering though, when calculating: int arraySize = sizeof(arrayName) / sizeof(arrayName[0]); inside main() I get the correct size of elements in the array, if I do it within the function it tells me something different. – Hugo Santos May 19 '13 at 18:45
  • @HugoSantos: Yes, in the function you have a pointer, not an array. They will produce different `sizeof` values. Without going into too much detail, a pointer is typically 4 bytes on a 32-bit system and 8 bytes on a 64-bit system. You would be seeing the result of that value divided by `sizeof(int)`. – Jon Purdy May 19 '13 at 18:50
  • Right! Got it. Amazing, too bad I have no rep at all to be able to upvote you! Thanks. – Hugo Santos May 19 '13 at 18:56
0

In C/C++, native arrays degrade to pointers as soon as they are passed to functions. As such, the "length" parameter has to be passed as a parameter for the function.

C++ offers the std::vector collection class. Make sure when you pass it to a function, you pass it by reference or by pointer (to avoid making a copy of the array as it's passed).

#include <vector>
#include <string>

void printArray(std::vector<std::string> &arrayName)
{
    size_t length = arrayName.size();
    for(size_t i = 0 ; i < length; ++i)
    {
        cout << arrayName[i] << ' ';
    }
}

int main()
{
    std::vector<std::string> arrayOfNames;
    arrayOfNames.push_back(std::string("Stack"));
    arrayOfNames.push_back(std::string("Overflow"));
    printArray(arrayOfNames);

    ...
}
selbie
  • 100,020
  • 15
  • 103
  • 173