-2

I am trying to get the size of an array, using that for the expression in my for loop, and getting a random sum when I compile.

#include <iostream>

int main()

{
    int prime[5];

    prime[0] = 2;
    prime[1] = 3;
    prime[2] = 5;
    prime[3] = 7;
    prime[4] = 11;

    int holder = 0;

    for (int i = 0; i < sizeof(prime); i++)
    {
        holder += prime[i];
    }

    std::cout << "The sum of the 5 prime numbers in the array is " << holder << std::endl; 
}

The sum I get is 1947761361. Why is this? Shouldn't using the sizeof() function work here?

Shah Jacob
  • 137
  • 1
  • 9
  • 11
    Do `std::cout << sizeof(prime);` and make sure your assumption is correct. It sounds like you may need to learn how to use a debugger to step through your code. With a good debugger, you can execute your program line by line and see where it is deviating from what you expect. This is an essential tool if you are going to do any programming. Further reading: [How to debug small programs](http://ericlippert.com/2014/03/05/how-to-debug-small-programs/) and [Debugging Guide](http://idownvotedbecau.se/nodebugging/) – NathanOliver Sep 09 '21 at 15:21
  • 4
    `i < sizeof(prime)` -> `i < sizeof(prime)/sizeof(*prime)`. Or use `std::size` if your compiler supports it – UnholySheep Sep 09 '21 at 15:22
  • 4
    Or since the array is in the same scope as the loop, a range-based for will work fine. – sweenish Sep 09 '21 at 15:22
  • 2
    `sizeof(prime)` gives size in bytes, use `std::size(prime)` to get size in number of items or better use range-for loop. – Marek R Sep 09 '21 at 15:24
  • @NathanOliver i don't understand, why would i randomly output the size of the array? i want to output the sum of the elements in the array, not the size of the array. can you explain? i don't get it. – Shah Jacob Sep 09 '21 at 15:24
  • @UnholySheep why does dividing by sizeof(*prime) work, can you explain the logic. and second, what does *prime mean? i think thats the pointer symbol, why is it necessary? though are my two. questions. – Shah Jacob Sep 09 '21 at 15:25
  • 1
    @ShahJacob Please take the time to read the entire comment. The key part is **and make sure your assumption is correct**. HINT: it's not. – sweenish Sep 09 '21 at 15:25
  • @ShahJacob If you do `std::cout << sizeof prime << '\n';` you'll find that it's probably printing `20`, not `5` that you expect. – Ted Lyngmo Sep 09 '21 at 15:26
  • 1
    @ShahJacob It's a debugging techinique. Printing the value of key values to the screen gives you a look into how the programming is running, and lets you make sure that what you expect is what you are actually getting. If you had used `std::cout << sizeof(prime);`, then you would have seen `20` printed and then you'd know exactly where to start looking into why the size of the array is "wrong". – NathanOliver Sep 09 '21 at 15:26
  • https://godbolt.org/z/rK7jP59E7 – Marek R Sep 09 '21 at 15:27
  • Printing a variable is often a good debugging technique if you don't have access to a debugger where you can step through the code line by line and see that it was executing the loop way too many times. – drescherjm Sep 09 '21 at 15:27
  • @MarekR what do you mean sizeofprime gives size in bytes? since it's elements and ints are four bytes does that the size is equal to the integer? also, why does std::size(prime) work? last question, the user above you said to divide by sizeof(*prime), is dividing here necessary? – Shah Jacob Sep 09 '21 at 15:27
  • @ShahJacob read this literally - it just returns size of array in bytes. Since size of `int` is not equal to `1` (usually it is `4`) outcome is bigger then you are expecting. – Marek R Sep 09 '21 at 15:29
  • 1
    `for (int p: prime)` would have also simplified this, while avoiding the mistake here. – Drew Dormann Sep 09 '21 at 15:29
  • @MarekR .......i can't understand the link you just described. i just see a bunch of extremely confusing hexadecimal values and a bunch of 0s and works like stack and buffer and frames. i don't understand what any of it means and it's overwhelming. could you please just explain why you std::size(prime) would work? also, are you saying if i replace my code with std::size(prime) alone that'll make the whole thing work? – Shah Jacob Sep 09 '21 at 15:31
  • 2
    @ShahJacob in this link your program is run, but extra compiler flag was added. This flags adds to execution code extra checks to find memory issues. In your case tool found buffer overflow error and gives full technical description of the problem. – Marek R Sep 09 '21 at 15:34

3 Answers3

5

The sizeof operator returns the size in memory of its operand - in the case of arrays this is thus the number of element multiplied by the size in memory of each one - NOT the number of elements in the array. For that you want sizeof(array)/sizeof(prime[0])

#include <iostream>

int main()

{
    int prime[5];

    prime[0] = 2;
    prime[1] = 3;
    prime[2] = 5;
    prime[3] = 7;
    prime[4] = 11;

    int holder = 0;

    int arraySize = sizeof(prime)/sizeof(prime[0]);
    for (int i = 0; i < arraySize; i++)
    {
        holder += prime[i];
    }

    std::cout << "The sum of the 5 prime numbers in the array is " << holder << std::endl; 
}
Smeeheey
  • 9,906
  • 23
  • 39
  • 2
    `sizeof(prime)/sizeof(int)` -> `sizeof(prime)/sizeof(prime[0])` makes the code more future proof. – NathanOliver Sep 09 '21 at 15:28
  • 2
    Or `sizeof prime / sizeof *prime` – Ted Lyngmo Sep 09 '21 at 15:28
  • @smeeheey so you're saying the sizeof(prime) is retning 5 * 4 = 20 bytes right? okay great, why does taking size of the array and dividing it by specifically the size of the first element of the array work? – Shah Jacob Sep 09 '21 at 15:33
  • 1
    @ShahJacob Because `20 / 4 = 5` – Ted Lyngmo Sep 09 '21 at 15:34
  • @ShahJacob the first element is chosen arbitrarily, because with arrays all elements have the same size – Smeeheey Sep 09 '21 at 15:35
  • 1
    ... and all arrays have at least _one_ element so picking the first is the safe way of doing it – Ted Lyngmo Sep 09 '21 at 15:37
  • 1
    There's no need for this division complexity. Just use `std::size(prime)` to keep it readable. – eerorika Sep 09 '21 at 16:16
  • @eerorika agreed. In fact there's a bunch of things you could change about this code. However I want to keep my answer in the spirit of explaining why the original code didn't work, and I feel using `sizeof(prime)/sizeof(prime[0])` rather than `std::size(prime)` achieves this goal better – Smeeheey Sep 09 '21 at 16:19
  • @smeeheey could you tell me all the things you would change about my code and write it as a separate snippet? – Shah Jacob Sep 09 '21 at 21:32
1

The error is in your use of sizeof(). It returns the total size of what is passed in. You passed in an array of 5 integers. An int is typically 4 bytes, so your sizeof() should return 20.

The bare minimum fix is to change your for loop Boolean Expression:

i < sizeof(prime) becomes i < sizeof(prime) / sizeof(*prime)

It takes the total size of your array (20) and divides it by the size of the first element (*prime) to give you the number of elements in your array.

To explain a bit more about *prime, you need to understand that C-arrays decay to pointers to the first element if you look at them funny. The syntax here de-references the pointer and gives us the actual first element, an int. And so we get the size of an int.


All the stuff below is tangential to your actual question, but I like to put it out there.

Here's your code, squashing your array initialization and using a range-based for loop.

#include <iostream>

int main()
{
  int prime[]{2, 3, 5, 7, 11};  // CHANGED: Declare and initialize
  int holder = 0;

  // CHANGED: Range-based for loop
  for (auto i : prime) {
    holder += i;  // CHANGED: in a range-based for loop, i is the value of each
                  // element
  }

  std::cout << "The sum of the 5 prime numbers in the array is " << holder
            << std::endl;
}

The range-based for loop works here because the array is in the same scope as the array. If you were passing the C-array to a function, it wouldn't work.

Here's your code using a Standard Library function:

#include <iostream>
#include <iterator>  // std::begin and std::end because C-array
#include <numeric>   // std::reduce OR std::accumulate

int main() {
  int prime[]{2, 3, 5, 7, 11};

  std::cout << "The sum of the 5 prime numbers in the array is "
            << std::reduce(std::begin(prime), std::end(prime)) << std::endl;
}

The need for <iterator> is due to the fact that you are using a C-array. If we instead use a std::array or [better yet] std::vector, we can lose that requirement.

#include <iostream>
#include <numeric>  // std::reduce
#include <vector>

int main() {
  std::vector<int> prime{2, 3, 5, 7, 11};

  std::cout << "The sum of the 5 prime numbers in the array is "
            << std::reduce(prime.begin(), prime.end()) << std::endl;
}

We got rid of the #include <iterator> requirement because std::arrays and std::vectors come with their own iterators. I also got rid of the holder variable completely, as there was no demonstrated need to actually store the value; so we print it directly.

NOTES: std::reduce() requires C++17, which any fairly recent compiler should provide. You could also use std::accumulate() if you wish.

You can specify that you're compiling C++17 code by passing -std=c++17 to the compiler. It's always a good idea to specify what C++ standard you expect your code to run against. And while we're talking about compiler flags, it's in your best interest to enable warnings with -Wall -Wextra at a minimum.

sweenish
  • 4,793
  • 3
  • 12
  • 23
  • this makes sense to me. an int is 4 bytes, so 4 * 5 = 20 bytes. so really i'm dividing by 20 bytes right? okay, next question is, 1) why does using the sizeof function AGAIN work, and why is the pointer thing for *prime needed? why can't i just do size(prime)? – Shah Jacob Sep 09 '21 at 15:37
  • i used sizeof(prime) / sizeof(prime[0] and it worked out and printed 28 to the console. why is *prime necessary? – Shah Jacob Sep 09 '21 at 15:38
  • @ShahJacob Using `*prime` or `prime[0]` does the same thing. See [here](https://godbolt.org/z/8z5x1sxzx) for 3 ways to get the same result. – Ted Lyngmo Sep 09 '21 at 15:43
  • @ShahJacob An answer to that hinges a lot on how much you know about what `*prime` does in this context. `prime` is an array. Arrays can "decay" to a pointer. the `*` will "dereference" the pointer and provide you the value pointed at by the pointer. `sizeof` gets the size of the type of the value. Result: `*prime` is the same as `prime [0]`. – user4581301 Sep 09 '21 at 15:43
  • I've added an edit that attempts to explain `*prime` to my answer and a further simplified version of the program. – sweenish Sep 09 '21 at 15:47
  • @user4581301 that was a lot of information. okay first, you said prime is an array, got that for sure. 1) what does decay to a pointer mean? can you explain?i know pointers and arrays are somehow related but not sure how 2) why would * dereference the pointer (dont you need to use * and & together to dereference? 3) this is MAIN confusion aside from 1) - if you dereferenes and get the value pointed at by a pointer, wouldn't that mean it would divide by whatever is in the element at the time? like if i put 456 in prime[0] it would divide 20 / 456 no?? – Shah Jacob Sep 09 '21 at 16:06
  • If you haven't learned about pointers yet, I'd recommend leaving the topic alone until your curriculum reaches it. `*prime` can be replaced with `prime[0]` and you get the same result. – sweenish Sep 09 '21 at 16:07
  • 2
    @ShahJacob respectfully, you've asked 23 new questions in the comments here. [A good book](https://stackoverflow.com/questions/388242) might be the next appropriate step to learning these concepts. – Drew Dormann Sep 09 '21 at 16:09
  • @DrewDormann those are all intermediate or advanced books, which book out of there would be most helpful for me right now? – Shah Jacob Sep 09 '21 at 16:11
  • 1
    @ShahJacob I would recommend one from the [**Beginner**](https://stackoverflow.com/questions/388242/the-definitive-c-book-guide-and-list#answer-388282) header. Those are not intermediate or advanced. – Drew Dormann Sep 09 '21 at 16:12
  • [What is array to pointer decay?](https://stackoverflow.com/questions/1461432/what-is-array-to-pointer-decay) – user4581301 Sep 09 '21 at 17:12
  • *like if i put 456 in prime[0] it would divide 20 / 456* a reasonable bit of confusion, but remember that the formula is `sizeof(prime) / sizeof (*prime)`. `sizeof` works on the type of the value, `int` in the case of `*prime` case. And yeah it is a lot of information. That's the version I could fit in a comment. C++ takes a lot of explanation sometimes to get all the details right. Having a few good books is a must if you want to learn C++ in a reasonable amount of time. – user4581301 Sep 09 '21 at 17:18
0

You expect sizeof(prime) == 5, which will not be.

And int prime[5]; will always should be known size, so

to avoid problems use int const sizeOfArray = 5; which then you can use for the loop.

And for better understanding of what is happening

monitor holder inside the loop with cout/cerr.

stepger
  • 21
  • 5
  • Now there's a `5` in two places. If you define a constant, use it when defining the array too. `int prime[sizeOfArray];` – Ted Lyngmo Sep 09 '21 at 15:31
  • It is implied - sizeOfArray should be used for the arrays and for the loop, And for this example no dynamic array size will be used, so no need to invent bugs in the code. – stepger Sep 09 '21 at 15:34
  • @TedLyngmo why is it bad if there's a 5 in two places? – Shah Jacob Sep 09 '21 at 15:35
  • @ShahJacob Maintenance. You change one, but forget to change the other. Think what would happen if you have 1000 arrays that should all have the same size. – Ted Lyngmo Sep 09 '21 at 15:35
  • @TedLyngmo are you saying that the code becomes less reusable because you have dangling 5s everywhere? – Shah Jacob Sep 09 '21 at 15:42
  • @ShahJacob Yes and having the variable helps in loops etc. After a year of coding on other projects, `5` will not say much. A properly named variable will however tell you a lot. – Ted Lyngmo Sep 09 '21 at 15:44