69

Can someone please explain to me why the output from the following code is saying that the arrays are not equal?

int main()
{    
    int iar1[] = {1, 2, 3, 4, 5};
    int iar2[] = {1, 2, 3, 4, 5};

    if (iar1 == iar2)
        cout << "Arrays are equal.";
    else
        cout << "Arrays are not equal.";
}
Jan Schultke
  • 17,446
  • 6
  • 47
  • 96
vladinkoc
  • 919
  • 1
  • 8
  • 13
  • 38
    Use `std::array` or `std::vector`. C arrays have no single advantage and they only bring pain and sorrow. No excuses. – daknøk Oct 12 '12 at 20:14
  • 6
    @daknøk What about interop with C? – weberc2 Mar 02 '15 at 01:12
  • 4
    @weberc2 `.data()`. No excuses. – Emil Laine Apr 19 '16 at 23:13
  • 8
    No it doesn't but it's there for C interop. And no, converting C arrays to std::arrays, just to check for equality, is completely unacceptable. It involves copying. When `std::array_view` is in the standard we'll have a sensible way of comparing raw arrays resulting from C interop. – Emil Laine Apr 20 '16 at 00:47
  • 1
    Your declared variables are pointers to the first int elements in two seperate arrays in two distinct locations in memory. They are not equal (the addresses are different) and so the conditional test evaluates to false. – Reno Feb 20 '20 at 10:11
  • 1
    multidimensional C array have guaranteed cache locality, not so std::array nor std::vector, so if large enough can have huge improvements in execution speed. – Mitch Sep 25 '21 at 08:30
  • @EmilLaine: They clearly meant *replacing* C arrays with `std::array`. Not *converting*. So there's no copy needed, and therefore no excuses. – Mooing Duck Sep 01 '23 at 20:50
  • @Reno: The variables are arrays, not pointers. However, since arrays lack `==`, the expression causes them to both decay to pointers. – Mooing Duck Sep 01 '23 at 20:50
  • @Mitch `std::array` has the exact same guaranteed cache locality as a C array, so there's no execution speed improvements. – Mooing Duck Sep 01 '23 at 20:51

12 Answers12

109
if (iar1 == iar2)

Here iar1 and iar2 are decaying to pointers to the first elements of the respective arrays. Since they are two distinct arrays, the pointer values are, of course, different and your comparison tests not equal.

To do an element-wise comparison, you must either write a loop; or use std::array instead

std::array<int, 5> iar1 {1,2,3,4,5};
std::array<int, 5> iar2 {1,2,3,4,5};

if( iar1 == iar2 ) {
  // arrays contents are the same

} else {
  // not the same

}
Praetorian
  • 106,671
  • 19
  • 240
  • 328
  • 3
    +1 for the only answer with the word "decay" (or anything else explicitly saying so). – chris Oct 12 '12 at 20:19
  • 7
    "... you must either write a loop...." Better would be to use std::equal. Not to detract from your point that using raw arrays is the main problem. – Ben Oct 19 '12 at 17:40
  • For people like me who got something like "implicit instantiation of undefined template ..." when trying to use std::array, try #import or #include . – Golden Thumb Mar 01 '18 at 03:36
  • 2
    Good explanation of "decay": https://stackoverflow.com/a/1461449/108238 – schoetbi Sep 30 '19 at 05:51
89

Since nobody mentioned it yet, you can compare arrays with the std::equal algorithm:

int iar1[] = {1,2,3,4,5};
int iar2[] = {1,2,3,4,5};

if (std::equal(std::begin(iar1), std::end(iar1), std::begin(iar2)))
    cout << "Arrays are equal.";
else
    cout << "Arrays are not equal.";

In C++20, you can simplify this with std::ranges::equal:

if (std::ranges::equal(iar1, iar2))    

You need to include <algorithm> and <iterator>. If you don't use C++11 yet, you can write:

if (std::equal(iar1, iar1 + sizeof iar1 / sizeof *iar1, iar2))
Jan Schultke
  • 17,446
  • 6
  • 47
  • 96
  • 5
    An interesting fact is that the above conditionals still hold true even if iar2 is {1,2,3,4,5,6}. – Golden Thumb Mar 01 '18 at 03:57
  • 2
    The beauty of this is that the exact same syntax also works for `std::vector` and `std::array` if one day you regain your sanity and want to convert the code to use them instead of C arrays. – Ciro Santilli OurBigBook.com Oct 26 '18 at 10:36
  • 4
    How does this manage to avoid overrunning the end of `iar2` if `iar1` is bigger than `iar2`? Shouldn't you be passing `std:end(iar2)` in there too? – David Given Mar 04 '19 at 19:31
  • 2
    Yes, you should be using the 4-argument overload! std::equal(std::begin(iar1),std:end(iar1),std:begin(iar2),std:end(iar2); – Robin Davies Jun 18 '21 at 16:54
16

You're not comparing the contents of the arrays, you're comparing the addresses of the arrays. Since they're two separate arrays, they have different addresses.

Avoid this problem by using higher-level containers, such as std::vector, std::deque, or std::array.

Fred Larson
  • 60,987
  • 18
  • 112
  • 174
  • 5
    It's important to note that these containers have implemented their own `==` operator that performs this check. – tadman Oct 12 '12 at 20:23
5

Nobody mentions memcmp? This is also a good choice.

/* memcmp example */
#include <stdio.h>
#include <string.h>

int main ()
{
  char buffer1[] = "DWgaOtP12df0";
  char buffer2[] = "DWGAOTP12DF0";

  int n;

  n=memcmp ( buffer1, buffer2, sizeof(buffer1) );

  if (n>0) printf ("'%s' is greater than '%s'.\n",buffer1,buffer2);
  else if (n<0) printf ("'%s' is less than '%s'.\n",buffer1,buffer2);
  else printf ("'%s' is the same as '%s'.\n",buffer1,buffer2);

  return 0;
}

Ref: http://www.cplusplus.com/reference/cstring/memcmp/

Xiangyi Meng
  • 103
  • 2
  • 7
  • The problem with the logic of this code is the `sizeof(buffer1) ` in memcpm – luca Mar 21 '21 at 10:45
  • Not true. `memcmp` compares if memory is equal not the arrays. The values in the array can be padded and the gaps filled with random data in which case memcmp will report that the memory is different despite the arrays being equal. Here is an example which for me prints "Memory not equal, Arrays equal": https://godbolt.org/z/fedW7veYn – Elviss Strazdins Jun 06 '21 at 02:26
  • @ElvissStrazdins Thanks for your code... BTW, I thought this is caused by the alignment of the struct, right? Once I disable the alignment by __attribute__ ((packed)), both memory and arrays are all equal. – Xiangyi Meng Jun 22 '21 at 02:58
4

Array is not a primitive type, and the arrays belong to different addresses in the C++ memory.

Paul S.
  • 4,362
  • 10
  • 35
  • 52
  • "Primitive type" is not a defined term in C++, and this doesn't really explain why OP's code doesn't work. `std::array` is also not a "primitive" type, and yet you can compare it with `==`. – Jan Schultke Sep 01 '23 at 21:01
4

If you are reluctant to change your existing code to std::array, then use a couple of methods instead which takes non-type template arguments :

//Passed arrays store different data types
template <typename T, typename U, int size1, int size2>
bool equal(T (&arr1)[size1], U (&arr2)[size2] ){
    return false;
}

//Passed arrays store SAME data types
template <typename T, int size1, int size2>
bool equal(T (&arr1)[size1], T (&arr2)[size2] ){
    if(size1 == size2) {
        for(int i = 0 ; i < size1; ++i){
            if(arr1[i] != arr2[i]) return false;
        }
        return true;
    }
    return false;
}

Here is the demo. Note that, while calling, we just need to pass the array variables e.g. equal(iar1, iar2) in your case, no need to pass the size of arrays.

Saurav Sahu
  • 13,038
  • 6
  • 64
  • 79
2

As mentioned by other answers:

if (iar1 == iar2)

This performs comparison between pointers, because == forces array-to-pointer conversion. C++ folks often refer to this as decay. The condition is true only if iar1 and iar2 are literally the same array.

Solution A - Use std::array(C++11)
std::array<int> iar1 = {1, 2, 3, 4, 5};
std::array<int> iar2 = {1, 2, 3, 4, 5};
if (iar1 == iar2) // true

std::array avoids a lot of C-style array problems in general, so you might want to replace all your uses of int[] with it.

Solution B - Use std::spans(C++20)
if (std::span{iar1} == std::span{iar2})

std::span is a lightweight non-owning view into our array. Wrapping a range in a std::span and using the == operator should be just as cheap as using standard library algorithms.

Solution C - Use std::equal(C++98) or std::ranges::equal(C++20)
if (std::ranges::equal(iar1, iar2))

// the last argument is optional, but may be helpful for optimizations
if (std::equal(std::begin(iar1), std::end(iar1), std::begin(iar2), std::end(iar2))

C++26 will likely remove array comparison

On a language evolution note, C++23 has deprecated array comparisons, and based on consensus in P2865 - Remove Deprecated Array Comparisons from C++26, it will likely be removed from the language.

In the future, your code could simply produce an error, and this pitfall will no longer exist.

Jan Schultke
  • 17,446
  • 6
  • 47
  • 96
1

Right. In most, if not all implementations of C, the array identifier can be implicitly casted to a pointer to the first element (i.e. the first element's address). What you're doing here is comparing those addresses, which is obviously wrong.

Instead, you need to iterate over both arrays, checking each element against each other. If you get to the end of both without a failure, they're equal.

slugonamission
  • 9,562
  • 1
  • 34
  • 41
  • 2
    In *none* is the array identifier actually the address of the first element. The array identifier is actually the array. In `int arr[6]`, `arr` refers to a value of type `int[6]`. That value is implicitly convertible to `int*`, with the value `&arr[0]` (often called decaying). But an array is not "actually" a pointer. – GManNickG Oct 12 '12 at 20:25
1

If you are willing to use std::array instead of built-in arrays, you may use:

std::array<int, 5> iar1 = {1,2,3,4,5};
std::array<int, 5> iar2 = {1,2,3,4,5};

if (iar1 == iar2)
R Sahu
  • 204,454
  • 14
  • 159
  • 270
0

You are comparing the addresses instead of the values.

Rahul Tripathi
  • 168,305
  • 31
  • 280
  • 331
0

Both store memory addresses to the first elements of two different arrays. These addresses can't be equal hence the output.

  • almost correct @Vachaspati -- they could be equal (for example, after the command `iar1 = iar2;`) but in this case, straight after initialisation, they won't be. – alle_meije May 02 '20 at 05:27
-1

When we use an array, we are really using a pointer to the first element in the array. Hence, this condition if( iar1 == iar2 ) actually compares two addresses. Those pointers do not address the same object.

vladinkoc
  • 919
  • 1
  • 8
  • 13