2

I'm performing a binary search in an array, looking for a specific value inside of it. When I wrote the code the first time, my for loop for sorting the array in ascending order always added a 0 right in the middle of it so that I could not search for the last element of the array, since the middle part got now replaced with 0 and I don't know why, then I rewrote the program in the exact same way and suddenly it worked.

I noticed in the new rewritten program that when I write a for loop for iterating through the array and printing out its contents before the for loop for sorting the array that it adds a 0 in the middle again, if I delete that for loop everything works fine. I don't understand why that is, could somebody explain that to me please?

#include <iostream>

using namespace std;

int main()
{
    int Arr[] = {1,-1,2,-2, 3,-3,4,-4,5,-5,6,-6,7,-7};
    int Temp, Size, Low = 0, High, Mid, Key, Found = 0;
    Size = (sizeof(Arr) / sizeof(Arr[0]));
    High = Size - 1;

    cout<<"Enter value of key you want to testsearch for:\n";
    cin>>Key;
/*
    for (int i = 0; i < Size; i++)     //if I don't comment out this loop the 0 will get added in
    {                                  //the middle of the array again and I don't know why
        cout<<Arr[i]<<" ";
    }
*/
    for (int Rep = 1; Rep <= Size-1; Rep++)
    {

        for (int i = 0, Temp = 0; i < Size; i++)
        {
            if (Arr[i] > Arr[i+1])
            {
                Temp = Arr[i];
                Arr[i] = Arr[i+1];
                Arr[i+1] = Temp;
            }
        }
    }

    for (int i = 0; i < Size; i++)
    {
        cout<<Arr[i]<<" ";
    }

    for (int i = 0; i < Size; i++)
    {
        Mid = (Low+High)/2;
        if (Arr[Mid] == Key)
        {
            Found = 1;
            break;
        }
        else if (Arr[Mid] < Key)
        {
            Low = Mid+1;
        }
        else if (Arr[Mid] > Key)
        {
            High = Mid-1;
        }
    }

    if (Found)
    {
        cout<<"\nGiven key value "<<Key<<" was found.";
    }
    else
    {
        cout<<"\nGiven key value "<<Key<<" was not found.";
    }

    return 0;
}

user438383
  • 5,716
  • 8
  • 28
  • 43
NoName
  • 35
  • 5
  • 2
    A good rule of thumb: backups. If it compiles with no errors, make a copy. If the program runs with no issues, make a copy. Invest in a Software Change Management tool (ex. Git). – Thomas Matthews Sep 10 '21 at 16:34
  • 1
    For me, even the version with the first loop commented out still [exhibits](https://godbolt.org/z/e8zdhh56b) the spurious 0. If changes to seemingly irrelevant parts of the code start changing other things, it's likely that your actual search is invoking undefined behavior. Have you stepped through with a debugger and made sure your values were what you expected at each step? – Nathan Pierson Sep 10 '21 at 16:36
  • 1
    The inner loop of your bubble sort accesses Arr[Size] (i = Size-1 and you access Arr[i+1]), which is undefined behavior. – Botje Sep 10 '21 at 16:38
  • 1
    One spot where you get undefined behavior is in your initial sorting. One possible value for `i` is `Size - 1`, at which point `Arr[i + 1]` is out of bounds. – Nathan Pierson Sep 10 '21 at 16:38
  • 2
    @463035818_is_not_a_number `High` is initialized, right after `Size`, long before `Mid` is initialized. In general this code could benefit from keeping the scopes of variables more tightly constrained and initializing them closer to where they're actually used. `Temp` doesn't need to be a variable in `main` at all, and the version that shadows it in your bubble sort could have even narrower scope. – Nathan Pierson Sep 10 '21 at 16:40
  • 1
    *"when I write a for loop for iterating through the array and printing out its contents before the for loop for sorting the array that it adds a 0 in the middle again"* can be written more abstractly as *"when I write* [code that only reads data] *before the* [code that changes data] *that* [the behavior changes]", which is often a symptom of [undefined behavior](https://stackoverflow.com/questions/2397984/undefined-unspecified-and-implementation-defined-behavior). – JaMiT Sep 10 '21 at 16:44
  • Thanks alot to everyone for the fast replies! Reading through these comments and answers I can see that I never even thought about the issue of [i+1] being out of bounds and that that could possibly trigger undefined behaviour. The course I'm going through didn't cover using the debugger tool aso yet, I just tried to figure out what my program was doing by adding "cout" statements everywhere, which didn't rly work out aswell for me. I used the sort() function another program, but I didn't know if I was allowed to do so so I wrote my own loop, sadly it didn't work out apparently. Thanks again! – NoName Sep 10 '21 at 16:49

2 Answers2

4

This for loop

    for (int i = 0, Temp = 0; i < Size; i++)
    {
        if (Arr[i] > Arr[i+1])
        {
            Temp = Arr[i];
            Arr[i] = Arr[i+1];
            Arr[i+1] = Temp;
        }
    }

invokes undefined behavior because there is an attempt to dereference memory outside the array when i is equal to Size - 1 in this if statement

        if (Arr[i] > Arr[i+1])
        {
            Temp = Arr[i];
            Arr[i] = Arr[i+1];
            Arr[i+1] = Temp;
        }

in the expression Arr[i+1].

You could rewrite the loop the following way

    for (int i = 1; i < Size; i++)
    {
        if (Arr[i] < Arr[i-1])
        {
            int Temp = Arr[i];
            Arr[i] = Arr[i-11];
            Arr[i-1] = Temp;
        }
    }

The same problem can occur in this loop

for (int i = 0; i < Size; i++)
{
    Mid = (Low+High)/2;
    if (Arr[Mid] == Key)
    {
        Found = 1;
        break;
    }
    else if (Arr[Mid] < Key)
    {
        Low = Mid+1;
    }
    else if (Arr[Mid] > Key)
    {
        High = Mid-1;
    }
}

because the number of the loop iterations can be greater than required for the binary search method. As a result again Mid can have an invalid value as an index in the array.

Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335
  • Thank you alot for this! It worked perfectly, no matter how much other stuff I added the 0 didn't get added to the array again, I will keep in mind that you should never try to operate outside of the bounds of an array or it will be really hard to figure out what's going on in the program, you guys are amazing for answering so fast and being so helpful! I just saw your comment about the other loop too, I will try to rewrite that aswell later on, but now I know what to look out for when trying to program stuff like this :) – NoName Sep 10 '21 at 16:58
0

Don’t write using namespace std;.

You can, however, in a CPP file (not H file) or inside a function put individual using std::string; etc. (See SF.7.)


int Temp, Size, Low = 0, High, Mid, Key, Found = 0;
Don't declare variables before they are ready to be initialized.
Don't gang together multiple variable declarations in one statement.

You don't actually need Temp (see later), and Found should be bool.


Temp = Arr[i];
Arr[i] = Arr[i+1];
Arr[i+1] = Temp;

Learn that there exists std::swap. In general, read through the <algorithms> to be familiar with what's available.


Size = (sizeof(Arr) / sizeof(Arr[0]));
Don't do that!
This is a C idiom that is fragile as it's very easy to accidentally use a value that decays to a pointer instead of getting the size of the array. There are direct ways to get this size in C++, but you don't need it, because of the next point.


Instead of using subscripts, use iterators.
You can use the non-member functions begin and end with primitive arrays.

using std::begin;
using std::end;

auto low= begin(Arr);
auto high= end(Arr);

note that by convention (that is, everything in the standard library, the end is one past the end, not pointing at the last element.

In real life, you will call sort to sort the array, and then either upper_bound or lower_bound to do the binary search. But you are learning how the algorithm works, so you are implementing it yourself from scratch. You can, however, compare your result against the library function to test the results!

while (low < high) {
   const auto dist = high-low;
   const auto mid = low+(dist/2);
   if (*mid == target)  return mid;
   if (*mid < target)  high=mid-1;
   else low=mid+1;
}

A fully generic algorithm will be more careful and only use operations on the iterators that are universal, so it will work for anything not just primitive arrays. But it's starting to do things in the way of the standard library and following common conventions.


Postscript on array size

It's rare that you would need the obtain the size of a primitive array just on its own in the middle of other code. As I showed, you normally use begin and end as iterators, and don't care about the corresponding index values. Not all kinds of collections even have indexes like that!

It can be naturally picked up when passing a whole array using templates. For example,

template <typename T, size_t N>
void do_something (T (&arr)[N]) { 

inside this function, N will have the array size.

There are standard functions to get the size though. Most directly, and specific to primitive arrays, is extent_v, so you could write:
size_t Size = std::extent_v<typeof(Arr)>;
which is awkward because you have a variable name (Arr) not a type name. But never fear, there is a more general function, the non-member size, that works for anything including arrays:

size_t Size = std::size(Arr);

that works OK because you know that Arr is in fact a primitive array. But it's not really kosher; you should write code to be general and generic, which, even if you're not writing a template, will greatly help maintenance. Changing the type of something is a common thing to do when editing the program to make improvements and fixes, and it's great when that "just works" and doesn't require other changes to be made to match!

The "std two-step" idiomatic usage

using std::size;
size_t Size = std::size(Arr);

The C++20 update

But the issues requiring the "std two-step" are now incorporated into the standard library, so on a newer compiler you can instead use:

size_t Size = std::ranges::size(Arr);

and it always works, for primitive arrays, collections defined in std, and other collection classes as well.

JDługosz
  • 5,592
  • 3
  • 24
  • 45
  • Thank you alot for your answer! I really appreciate you commenting on my bad way of writing code, I think the course I'm attending should have at least given a warning to look out for the things you've mentioned, I am really new to the whole thing and I don't want to develop any bad habits that will lead to me having a hard time later on. I will try to rewrite my code in a better way, I've been doing this specific challenge for some hours now, trying to figure out how everything works and trying to implement better coding practices, thank you again! – NoName Sep 10 '21 at 18:15
  • 1
    I tried solving it with just the knowledge I've acquired so far without googling, I didn't yet get introduced to most stuff yet but your points are really important so I think they should've said something right at the beginning about those practices, I didn't know about the sizeof(array) idiom aswell, I just knew that the function exists and when I printed out sizeof(array) it showed 56, probably because 1 integer takes up 4 bytes, so I figured it would be good to divide it by the value of one integer to get the actual size, but if that is a bad practice I have to use your suggested methods. – NoName Sep 10 '21 at 18:20
  • I agree, it sounds like they tosses you into the deep end without any preparation. We expect that from people teaching themselves informally by just trying it, but not from a formal course! I'd _love_ to see the teacher's code!! What kind of class is it? I'm wondering if it's a _Data Structures and Algorithms_ course, or a C++ language class, or what. – JDługosz Sep 10 '21 at 18:26
  • (continues) when I learned this, in what I still think was the best formal class I ever had concerning computer programming, I was a Jr in high school taking the first A.P. Computer Science class the year it was introduced (1982-83 school year). The first semester was teaching the Pascal language. _Then_ the second semester taught data structures and algorithms, and we had enough language skills to implement them. – JDługosz Sep 10 '21 at 18:31
  • @NoName I updated my answer with more on array sizes. – JDługosz Sep 10 '21 at 18:49
  • Oh it is actually not an in person course, it's a course called "Mastering C++ Programming- From Zero to Hero" from Udemy, so far I've had quite alot of fun with it and the teacher specifically said that all videos should be watched in full length in the right order, because they all build on top of each other. I did that so far and I'll continue doing so, but from now on I will also look up other stuff on coding practices aso aswell, because it was never once mentioned for example that it is a bad practice using "using namespace std;" globally. – NoName Sep 10 '21 at 18:58
  • (continues) This seems minor for now, but I don't want to get used bad habits and I really appreciate your in-depth answer, thanks alot again! – NoName Sep 10 '21 at 18:58
  • 1
    BTW, "a lot" is two words. – JDługosz Sep 10 '21 at 18:59
  • Oops, I never knew that this was a common misspelling, I always assumed you could write it in both ways for some reason although it makes sense :D I'm sorry, I'm not a native english speaker but I will keep that in mind the next time I'm using it, so thank you a lot again ! :D Have a wonderful weekend :) – NoName Sep 10 '21 at 19:11
  • It looks like you get to "STL" eventually, many chapters later. I suggest you continue to post your (working) assignment results on the [Code Review](https://codereview.stackexchange.com/) stack, to get feedback and un-learn any bad habits you pick up from that presenter. Just skimming the preview of the STL overview, I notice he had `using namespace std;` in his file; he doesn't use `const` or `auto`; he wrote `it++` instead of `++it`; and has `iter` declared at the top for future need (the hard way) rather than where needed and using `auto`; (etc.) – JDługosz Sep 10 '21 at 19:14
  • I should probably use a spell checker I guess, I don't even have it turned on at the moment ^^ I will definitely use the code review section for future programming challenges and will keep your suggestions in mind, I'm right at the beginning now and it all seems a bit overwhelming but I'm sure I will improve if I just keep on practicing and applying suggestions from experienced programmers like you. It's actually a lot of fun trying to figure out howto solve a problem by trying out different ways until you eventually get it to work, which is far from being the right way as I've learned today. – NoName Sep 10 '21 at 19:39