HOWTO · C++
在 C++ 中比较数组
本文介绍了如何在 C++ 中比较数组。
本页内容
根据输入和所需结果选择比较方法。对于长度相同的定长数组,可以使用循环或 std::equal 返回布尔值。对于字节序列,std::memcmp 比较对象表示。对于向量,std::vector 的 operator== 会比较大小和值。如果需要找到第一个不同的位置,请先检查范围长度,再使用 std::mismatch。
以下示例都是完整的 C++ 程序,应分别编译。提供的运行结果显示,六个示例均成功执行。原始数组参数会退化为指针:接收此类参数的函数无法得知数组长度。请显式传入长度,并确保每个指针指向的范围至少有这么长。
使用循环比较定长数组
循环能明确展示比较规则:逐一检查对应元素,一旦发现一对元素不同,就立即返回 false。如果所有对应元素都相同,则返回 true。调用者会提供两个长度,因此应在按下标访问前先检查长度;此示例遇到长度不同的情况时会直接处理,不会读取任一范围。
本节中的代码以及显示的标签或输出仍为英文。
#include <cstddef>
#include <iostream>
bool equalArrays(const int* left, std::size_t leftSize,
const int* right, std::size_t rightSize) {
if (leftSize != rightSize) return false;
for (std::size_t i = 0; i < leftSize; ++i) {
if (left[i] != right[i]) return false;
}
return true;
}
int main() {
const int first[] = {2, 4, 6, 8};
const int same[] = {2, 4, 6, 8};
const int different[] = {2, 4, 6, 9};
constexpr std::size_t count = sizeof(first) / sizeof(first[0]);
std::cout << std::boolalpha
<< equalArrays(first, count, same, count) << '\n'
<< equalArrays(first, count, different, count) << '\n';
}
true
false
第一次调用会比较所有对应的值并返回 true;第二次调用会在最后一对元素处发现差异并返回 false。只要元素类型的 != 运算符合你的比较需求,就可以采用这种逐元素比较方式。它不会自动比较数组大小,因此应传入实际长度,不要假定两个指针指向的数组长度相同。
编写可复用的比较函数
对于支持不等比较的类型,可以用模板复用循环逻辑。和整数版本一样,函数会显式接收长度,并在读取元素前检查长度。此示例是独立的,可以单独编译。
本节中的代码以及显示的标签或输出仍为英文。
#include <cstddef>
#include <iostream>
#include <string>
template <typename T>
bool equalRanges(const T* left, std::size_t leftSize,
const T* right, std::size_t rightSize) {
if (leftSize != rightSize) return false;
for (std::size_t i = 0; i < leftSize; ++i) {
if (left[i] != right[i]) return false;
}
return true;
}
int main() {
const std::string namesA[] = {"Ada", "Lin"};
const std::string namesB[] = {"Ada", "Lin"};
std::cout << std::boolalpha
<< equalRanges(namesA, 2, namesB, 2) << '\n';
}
true
模板参数 T 会根据元素类型推导。函数要求 T 支持 !=,但不要求使用特定容器。请让指针与对应长度保持匹配,并确保所指范围仍然有效且长度足够。对于标准容器,如果使用迭代器或容器自身的操作能更清楚地表达边界,就优先采用这些方式。
使用 std::equal 比较范围
std::equal 定义于 <algorithm>,会比较对应元素并返回布尔值。三迭代器重载接收第一个范围的起始迭代器和结束迭代器,以及第二个范围的起始迭代器。调用前应先比较两个范围的长度:第二个范围至少要和第一个范围一样长。若要判断整个范围是否相等,应检查长度相同,这样也能避免读取越过第二个范围。
本节中的代码以及显示的标签或输出仍为英文。
#include <algorithm>
#include <iostream>
#include <vector>
int main() {
const std::vector<int> left{3, 1, 4};
const std::vector<int> same{3, 1, 4};
const std::vector<int> longer{3, 1, 4, 1};
const bool firstEqual = left.size() == same.size() &&
std::equal(left.begin(), left.end(), same.begin());
const bool secondEqual = left.size() == longer.size() &&
std::equal(left.begin(), left.end(), longer.begin());
std::cout << std::boolalpha << firstEqual << '\n' << secondEqual << '\n';
}
true
false
长度检查是比较过程的一部分,不是可有可无的优化。std::equal 按顺序比较元素;相同位置的元素必须相等。请确保迭代器范围在使用期间有效,且边界正确。在支持四迭代器重载的语言版本中,可以直接指定两个范围的起止位置;不过,上面的长度检查方式无需依赖该重载。
使用 std::memcmp 比较字节
std::memcmp 定义于 <cstring>,用于比较指定数量的字节。若这些字节相同,它会返回零;若不同,则返回非零值。返回值的符号可以表示按字节比较的顺序,但不要依赖某个确切的非零值。请确保指针有效且指向的范围至少包含指定数量的字节,并以字节而非元素为单位计算长度。
本节中的代码以及显示的标签或输出仍为英文。
#include <cstring>
#include <iostream>
int main() {
const unsigned char left[] = {0x10, 0x20, 0x30};
const unsigned char same[] = {0x10, 0x20, 0x30};
const unsigned char different[] = {0x10, 0x20, 0x31};
constexpr std::size_t byteCount = sizeof(left);
std::cout << (std::memcmp(left, same, byteCount) == 0) << '\n'
<< (std::memcmp(left, different, byteCount) == 0) << '\n';
}
1
0
此示例比较字节数组;对于这类数据,逐字节比较可以直接判断列出的字节是否相同。对于一般对象,值相等并不一定意味着对象表示完全相同:填充字节或同一值的多种表示形式,都可能导致 std::memcmp 不适用。普通值应优先使用元素相等比较;只有当数据格式将表示形式是否相同作为比较规则时,才使用字节比较。字节数为零时不会比较任何字节;调用方仍须按照程序需要妥善处理范围有效性。
使用 std::vector::operator== 比较向量
如果需要判断整个向量的值是否相等,可以使用 ==。它会检查大小是否相同,并比较对应元素,因此不必另行检查大小。元素顺序也很重要:即使两个向量包含相同的值,只要顺序不同,它们就不相等。
本节中的代码以及显示的标签或输出仍为英文。
#include <iostream>
#include <vector>
int main() {
const std::vector<int> first{5, 8, 13};
const std::vector<int> same{5, 8, 13};
const std::vector<int> reordered{8, 5, 13};
const std::vector<int> shorter{5, 8};
std::cout << std::boolalpha
<< (first == same) << '\n'
<< (first == reordered) << '\n'
<< (first == shorter) << '\n';
}
true
false
false
当两个输入都是 std::vector,且只需要布尔结果时,这是最直接的选择。元素类型必须支持相等比较。如果你的任务不考虑顺序,应明确规定这一点,并采用适合该规则的方法;普通的向量相等比较会按位置比较元素。
使用 std::mismatch 查找第一个差异
如果你需要找到第一对不同的元素,而不只是得到 true 或 false,可以使用 std::mismatch。它的三迭代器重载会同时遍历两个范围,因此应先比较长度,确保两个范围会同时结束。返回的第一个迭代器指向第一个范围中不同的元素;如果它等于第一个范围的结束迭代器,则所有已比较的元素对都相同。
本节中的代码以及显示的标签或输出仍为英文。
#include <algorithm>
#include <iostream>
#include <iterator>
#include <vector>
int main() {
const std::vector<int> left{7, 8, 9, 10};
const std::vector<int> right{7, 8, 0, 10};
if (left.size() != right.size()) {
std::cout << "Different lengths\n";
return 0;
}
const auto result = std::mismatch(left.begin(), left.end(), right.begin());
if (result.first == left.end()) {
std::cout << "Equal\n";
} else {
const auto index = std::distance(left.begin(), result.first);
std::cout << "First mismatch at zero-based index " << index << '\n';
}
}
First mismatch at zero-based index 2
这里的 std::distance 返回从零开始的偏移量。如果要展示面向读者、从一开始计数的位置,应明确标注为一基位置,并在偏移量上加一。先比较长度还能让结束条件准确表示整个范围是否相等,并确保第二个迭代器不会越过其范围。
选择比较方法
如果你需要明确控制逐元素检查过程,或需要自定义逻辑,可以使用循环。对于迭代器范围的布尔比较,可以使用 std::equal;使用它的三迭代器重载前,应确认范围长度兼容。对于 std::vector,operator== 是简洁的整体容器比较方式。如果需要知道第一个差异的位置,则使用 std::mismatch。
std::memcmp 适用于数据本身就是按字节表示的序列;它不能普遍替代值相等比较。对于所有基于指针的方法,都要确保指针与正确的长度相匹配。条件允许时,优先使用容器大小和迭代器范围;在使用会推进第二个范围迭代器的算法前,确保第二个范围足够长。