0

When testing an array like this:

float test [3] = {1, 2, 3};

cout << "Test size: " << sizeof(test) << endl;

The size shown by the print statement is 12. Similarly, when I made the size 12, the number of elements in the array was 47. Isn't this the correct way to specify the size of an array in C++?

Abraham
  • 8,525
  • 5
  • 47
  • 53
Coder567
  • 51
  • 1
  • 5

6 Answers6

9

sizeof doesn't return the number of elements in the array, it returns the memory size in bytes. Since your array contains three regular integers, and integers are 4 bytes in size, that's 3 * 4 == 12 bytes.

To get the length of the array, you have a few options:


Option 1

int size = sizeof(test) / sizeof(test[0]);

What that does is get the size of the array (which is 12 bytes) then divides that by the size of a single element (which is 4 bytes). Obviously 12 / 4 == 3.


Option 2

As pointed out by @PaulMcKenzie, if you're able to use C++17 you can use std::size. That makes it very easy because you can just do this:

int size = std::size(test);

Option 3 (Not recommended)

int size = *(&test + 1) - test;

Which is a clever hack using pointers, explained here. Will result in undefined behaviour and may break, depending on the compiler and its optimisations.

Community
  • 1
  • 1
Clonkex
  • 3,373
  • 7
  • 38
  • 55
  • I think dereferencing *(&test + 1)* invokes Undefined Behavior, per C standard (there is no float array in that address, after all). Agressive optimizations by compiler might break code like that, even if common sense says it should be fine. Avoid. – hyde Sep 04 '18 at 03:43
  • @hyde this is a C++ question - in C++ it is unclear whether the code is UB or not – M.M Sep 04 '18 at 03:48
  • @M.M Oh right. But is this really different in C++? No standard handy, but I'd imgine rules about dereferencing pointers are about the same. – hyde Sep 04 '18 at 03:50
  • They aren't the same – M.M Sep 04 '18 at 03:51
  • 3
    Evaluating `&test + 1` is valid in C++, but undefined in C. Dereferencing it, as in `*(&test + 1)`, gives undefined behaviour in both languages. – Peter Sep 04 '18 at 04:34
  • Using `int` can overflow because the expressions return `std::size_t` which can be larger. Using `auto size = ...` is probably safer. – Galik Sep 04 '18 at 05:30
  • @Peter Updated my answer to reflect that. – Clonkex Sep 04 '18 at 05:30
  • @Galik I would say it's fair to assume the code writer would know what the expected size of the array is. – Clonkex Sep 04 '18 at 05:31
  • @Clonkex - there is no "could" on undefined behaviour. The behaviour is either undefined (the standard places no requirements or constraints on what happens) or it is not. While code with undefined behaviour might "break" depending on the compiler and its optimisations, that is a *consequence* of the behaviour being undefined, not the cause or definition of undefined behaviour. Even if the code doesn't "break", the behaviour is still undefined. – Peter Sep 04 '18 at 10:11
  • @Peter Not necessarily. For one compiler it might be well defined (as in, specifically documented behaviour), even if it's not defined in the standard. But I get your point so I'll update the post :) – Clonkex Sep 04 '18 at 22:48
  • @Clonkex - There has never been a compiler with documented behaviour of allowing safe dereference of a pointer that points past the end of an object or array. There are plenty of cases of compilers doing that by accident, but not consistently or reliably (e.g. the code can work with one version of a particular compiler, then fail when that compiler is updated - or the reverse). – Peter Sep 05 '18 at 08:43
  • @Peter My point was, I'm not a C++ expert, let alone a C++ compiler expert. I have absolutely no idea if that's a normal thing to expect from a compiler so I can't possibly know if it _maybe_ results in undefined behaviour or _definitely will_. I can't tell if you're just letting me know (friendly) or if you're calling me a noob for not knowing that (jerk) so I apologise if my response is a little blunt. – Clonkex Sep 06 '18 at 00:25
  • @Clonkex - I was explaining that your description demonstrates a complete misunderstanding of the meaning of undefined behaviour, and your responses have continued to demonstrate the same misunderstanding. I made no comment about you as an individual. I'll leave it there. – Peter Sep 06 '18 at 10:25
  • @Peter There's have no misunderstanding. Undefined behaviour stops being undefined if it's defined. I don't know much about compilers so I wasn't sure if there might be a compiler that documented safe dereference of a pointer that points past the end of an array. With that assumption, it's only _possible_ that the behaviour is undefined, not guaranteed, depending which compiler you use. However you stated factually that no compiler documents such behaviour, so if your statement is true (which I assume it is) then it _is_ guaranteed to have undefined behaviour. – Clonkex Sep 06 '18 at 22:26
  • @Clondex. You continue to demonstrate your misunderstanding. Read up on how the C++ standard specifically *defines* the term "undefined". The C++ standard is the authoritative yardstick on what is undefined, or not, in the context of C++ - because that is a *fundamental purpose* of a standard. Compiler documentation can only describe what a particular compiler does, but it *cannot* overrule the standard. Because the C++ standard is the yardstick against which *all* compilers (at least those that claim to comply with it) is assessed. – Peter Sep 07 '18 at 10:36
7

If you want the more efficient way of calculating the number of elements in an array, you could use templates.

template<typename T, std::size_t Size>
std::size_t GetArrLength(T(&)[Size]) { return Size; }

If we call it as such:

float test[3] = {1,2,3};
GetArrLength(test);

The compiler will try to deduce the template parameters. For the parameter type to match what you're passing, T must be float and size must be 3 (making the parameter a reference to an array of 3 floats).

sonulohani
  • 1,225
  • 9
  • 8
  • 1
    More efficient than what, and in what way? It'd be good if you could elaborate. Also it might be worth explaining in a more ELI5 fashion given the OP is probably new to C++ and templates are confusing to newbies (especially for clever uses like this). – Clonkex Sep 04 '18 at 05:34
  • You are basically reimplementing `std::size()`. – Jesper Juhl Sep 04 '18 at 06:18
  • @JesperJuhl What if you dont have C++ 17 supported compiler. BTW i didn't know that this is what std::size is. – sonulohani Sep 04 '18 at 11:39
3

You must use following way to get the actual size of array you requires.

 int size = sizeof(test)/sizeof(test[0]);

sizeof() actually returns no. of bytes. So, you get the no. of bytes in your array object. To get actually the no. of elements, we need to divide it by the size of an element there. For example, here sizeof(float)

Gimhani
  • 1,318
  • 13
  • 23
0

You need to divide the single element size to get the array size.

int sz = sizeof(test) / sizeof(test[0]);
cout << "test size: " << sz << endl;
delta
  • 3,778
  • 15
  • 22
0

This will work :

size_t n = sizeof(test)/sizeof(test[0]);

size_t is a type guaranteed to hold any array index, so use size_t.

Tas
  • 7,023
  • 3
  • 36
  • 51
Maifee Ul Asad
  • 3,992
  • 6
  • 38
  • 86
  • 1
    You should probably read [this](https://stackoverflow.com/editing-help) so you know how the formatting works :P – Clonkex Sep 04 '18 at 03:40
0

The standard library has a type trait for this purpose.

#include <type_traits>
#include <iostream>

int main()
{
    float test [3] = {1, 2, 3};

    std::cout 
        << std::extent<decltype(test)>::value 
        << '\n';
}
Richard Hodges
  • 68,278
  • 7
  • 90
  • 142