为什么数组之间的"=="相等比较不起作用?

70

请问为什么下面代码的输出结果显示数组不相等

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.";

    return 0;   
}

38
使用std::arraystd::vector。C数组没有任何优势,只会带来痛苦和悲伤。没有任何借口。 - daknøk
6
与C语言的互操作性如何? - weberc2
4
@weberc2 .data(). 没有任何借口。 - emlai
8
不,它并不是这样,但它可以用于C语言互操作。而且,将C数组转换为std::array只为了进行相等性检查是不可接受的,因为这会涉及到复制。当标准库中引入std::array_view后,我们就有了一种合理的方法来比较由C语言互操作生成的原始数组。 - emlai
1
多维 C 数组具有保证缓存局部性,而 std::array 和 std::vector 则不一定如此,因此如果数组足够大,则可以在执行速度方面获得巨大的改进。 - Mitch
显示剩余4条评论
12个回答

110
if (iar1 == iar2)

这里iar1iar2正在向各自数组的第一个元素的指针进行衰减。由于它们是两个不同的数组,指针值显然是不同的,因此您的比较测试结果为不相等。

要进行逐个元素的比较,您必须编写循环或使用std::array

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

}

3
给那个唯一使用了“decay”这个词或其他明确表达的答案加上一个赞。 - chris
7
更好的做法是使用 std::equal,但你也可以编写一个循环。不过需要注意的是,使用原始数组才是主要的问题所在。 - Ben
对于像我这样的人,在尝试使用std::array时遇到类似于“隐式实例化未定义的模板…”的问题,请尝试使用#import <array>或#include <array>。 - 拇指 muzhi.com
2
"decay" 的好解释:https://dev59.com/m3M_5IYBdhLWcg3wPAnU#1461449 - schoetbi

90
既然还没有人提到,你可以使用std::equal算法来比较数组:
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.";

在C++20中,你可以使用std::ranges::equal来简化这个过程。
if (std::ranges::equal(iar1, iar2))    

你需要包含<algorithm><iterator>。如果你还没有使用C++11,你可以这样写:
if (std::equal(iar1, iar1 + sizeof iar1 / sizeof *iar1, iar2))

5
有趣的事实是,即使 iar2 是 {1, 2, 3, 4, 5, 6},上述的条件语句仍然成立。 - 拇指 muzhi.com
2
这个美妙之处在于,如果有一天你恢复理智并想要将代码转换为使用std::vectorstd::array,那么完全相同的语法也适用于它们,而不是C数组。 - Ciro Santilli OurBigBook.com
4
如果iar1iar2大,这个程序是如何避免越过iar2的末尾的?难道你不应该在那里也传递std:end(iar2)吗? - David Given
2
是的,你应该使用4个参数的重载!std::equal(std::begin(iar1),std:end(iar1),std:begin(iar2),std:end(iar2); - Robin Davies

16

你并没有比较数组的内容,而是比较数组的地址。由于它们是两个独立的数组,所以它们具有不同的地址。

通过使用更高级别的容器(例如std::vectorstd::dequestd::array),可以避免此问题。


5
值得注意的是,这些容器已经实现了自己的==运算符来进行此检查。 - tadman

5
没人提及memcmp吗?这也是一个不错的选择。
/* 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;
}

参考:http://www.cplusplus.com/reference/cstring/memcmp/

该函数用于比较两个内存块的前n个字节。如果它们相等,则返回0,否则返回它们之间的差值。这个函数通常被用来比较数据结构,例如在排序算法中。


这段代码逻辑的问题在于 memcpy 函数中的 sizeof(buffer1) - luca
不是真的。memcmp比较的是内存是否相等,而不是数组。数组中的值可以填充并用随机数据填充间隙,在这种情况下,即使数组相等,memcmp也会报告内存不同。以下是一个示例,对我来说打印“Memory not equal, Arrays equal”:https://godbolt.org/z/fedW7veYn - Elviss Strazdins
@ElvissStrazdins 谢谢你的代码... 顺便问一下,我认为这是由于结构的对齐引起的,对吗?一旦我通过 attribute ((packed)) 禁用了对齐,内存和数组都相等了。 - Xiangyi Meng

4
如果您不想改变现有的代码来使用std :: array,那么可以使用一些方法,它们需要非类型模板参数
//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;
}

这里是演示。请注意,在调用时,我们只需要传递数组变量,例如equal(iar1, iar2),在你的情况下,不需要传递数组的大小。

4

数组不是原始类型,而且在C++内存中属于不同地址


"原始类型"在C++中并没有明确定义,这并不能真正解释为什么OP的代码无法工作。std::array也不是一个"原始"类型,然而你可以使用==进行比较。 - Jan Schultke

2
正如其他答案所提到的:
if (iar1 == iar2)
这是指针之间的比较,因为==会强制进行数组到指针的转换。C++的人们经常称之为衰变。 只有当iar1iar2实际上是同一个数组时,条件才为true
解决方案A - 使用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在一般情况下避免了许多C风格数组的问题,因此您可能希望将所有使用int[]的地方替换为它。

解决方案B - 使用std::span(C++20)
if (std::span{iar1} == std::span{iar2})

std::span是一个轻量级的非拥有视图,用于查看我们的数组。 将范围包装在std::span中,并使用==运算符应该和使用标准库算法一样廉价。

解决方案C - 使用std::equal(C++98)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 很可能会移除数组比较

关于语言发展的一点说明,C++23 已经弃用了数组比较,并且根据 P2865 - Remove Deprecated Array Comparisons from C++26 中的共识,它很可能会从语言中移除。

将来,你的代码可能会直接报错,这个陷阱将不再存在。


@WeijunZhou 好主意,我已经把它加到答案里了。 - Jan Schultke

1

没错。在大多数,如果不是所有的C实现中,数组标识符可以被隐式转换为指向第一个元素(即第一个元素的地址)的指针。你在这里做的是比较那些地址,显然是错误的。

相反,你需要遍历两个数组,将每个元素相互比较。如果在两个数组结尾都没有失败,它们就是相等的。


2
none中,数组标识符实际上并不是第一个元素的地址。数组标识符实际上就是数组本身。在int arr[6]中,arr指向类型为int[6]的值。该值可以隐式转换为int*类型,并具有值&arr[0](通常称为衰减)。但是,数组并不是“实际上”指针。 - GManNickG

1
如果您愿意使用std::array而不是内置数组,则可以使用以下代码:
std::array<int, 5> iar1 = {1,2,3,4,5};
std::array<int, 5> iar2 = {1,2,3,4,5};

if (iar1 == iar2)

-1

两个不同数组的第一个元素的内存地址被存储起来。因为这些地址不能相等,所以输出结果也不同。


几乎正确@Vachaspati - 它们可能相等(例如,在命令iar1 = iar2;之后),但在这种情况下,在初始化后不会相等。 - alle_meije

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接