这是一个仅支持非负整数的十进制大整数类 BigInt。本文用通俗语言解释每个构造函数与运算符的作用、何时使用,以及典型示例与注意事项。
快速开始
- 代码结构:
BigInt.h:类声明与字段BigInt.cpp:具体实现main.cpp:示例与测试Makefile:构建脚本
- 构建与运行:
make
./bigint_demo
运行后若显示 All tests passed.,说明功能正常。
字段与内部表示
bool isZero:是否表示数字 0(便于快速判断)。size_t len:当前数字的位数(例如 12345 的位数是 5)。size_t size:内部字符缓冲区的容量(为动态扩容服务)。char* s:存储每一位数字的数组,最高位在s[0],每个位置是字符'0'..'9'。
构造函数详解
-
BigInt(size_t value)- 作用:用普通无符号整数初始化一个大整数。
- 场景:数值在机器整型范围内(如 0、12345)时最简便;常用于循环累加、简单测试。
- 示例:
BigInt a(static_cast size_t (0)); BigInt b(12345); - 注意:如果写
BigInt(0)且同时存在BigInt(const char*)构造,某些编译器可能把 0 误解为空指针产生歧义;使用static_cast size_t (0)更稳妥。
-
BigInt(const char* str)- 作用:用十进制字符串创建大整数,支持任意多位,允许前导零。
- 场景:需要表示超过机器整型范围的超大数,或从文本/输入中读取数字。
- 示例:
BigInt c("00012345"); BigInt d("9999999999999999999999999999"); - 规范化:会自动去掉前导零,保证内部没有多余的零(
"000123"会变成"123")。 - 无效输入:如果包含非数字字符或是空串,按
处理以免程序崩溃。
-
BigInt(const BigInt& other)- 作用:拷贝已有的
BigInt,得到一个内容相同、独立存储的新对象。 - 场景:函数传参、返回值、需要保留快照等。
- 示例:
BigInt e(b); // e 与 b 数值相同,但互不影响
- 作用:拷贝已有的
赋值运算符
BigInt& operator=(const BigInt& other)- 作用:用另一个
BigInt覆盖当前对象的值(深拷贝)。 - 场景:复用已有变量存结果,或在计算链中更新同一对象。
- 示例:
BigInt x(1); BigInt y("999"); x = y; // x 变成 999 - 注意:支持自赋值(
x = x;),内部已做安全处理。
- 作用:用另一个
加法运算符
-
BigInt operator+(const BigInt& rhs) const- 作用:返回两数之和,不修改原对象(就像
a + b)。 - 场景:需要保持输入不变时,用该形式更清晰。
- 示例:
BigInt z = BigInt("999") + BigInt("1"); // z = 1000 - 实现要点:竖式加法,从最低位向最高位处理,正确处理进位。
- 作用:返回两数之和,不修改原对象(就像
-
BigInt& operator+=(const BigInt& rhs)- 作用:把
rhs加到当前对象上,并返回当前对象引用(就地更新)。 - 场景:累加场景(如循环求和)。
- 示例:
BigInt sum(0); for (size_t i = 0; i < 1000; ++i) sum += BigInt(i); - 等价:
a += b;基本等价于a = a + b;,但可能更易读。
- 作用:把
乘法运算符
-
BigInt operator*(const BigInt& rhs) const- 作用:返回乘积,不修改原对象。
- 场景:计算两大数的乘积。
- 示例:
BigInt p = BigInt("1234") * BigInt("5678"); // p = 7006652 - 实现要点:朴素竖式乘法,时间复杂度约为 O(n⋅m),其中 n、m 为位数。
- 特殊:只支持非负数,遇到 0 会直接返回 0。
-
BigInt& operator*=(const BigInt& rhs)- 作用:把当前对象乘以
rhs并更新自身。 - 场景:链式运算或迭代更新(如多次相乘)。
- 示例:
BigInt t(1); t *= BigInt("123"); // t = 123 - 等价:
a *= b;基本等价于a = a * b;。
- 作用:把当前对象乘以
比较运算符
-
bool operator==(const BigInt& rhs) const- 作用:判断两数是否完全相等(位数与每位都相同)。
- 示例:
BigInt("00123") == BigInt("123") // true,因为会规范化
-
bool operator!=(const BigInt& rhs) const- 作用:与
==相反。
- 作用:与
-
bool operator<(const BigInt& rhs) const- 作用:按数值大小比较(先比位数,再比每位字符)。
- 示例:
BigInt("99") < BigInt("100") // true
-
bool operator<=(const BigInt& rhs) const- 作用:小于或等于。
-
bool operator>(const BigInt& rhs) const- 作用:大于。
-
bool operator>=(const BigInt& rhs) const- 作用:大于或等于。
输出运算符
std::ostream& operator<<(std::ostream& os, const BigInt& x)- 作用:把
BigInt直接打印为十进制字符串。 - 场景:调试输出、记录日志、展示结果。
- 示例:
std::cout << BigInt("00100") << "\n"; // 打印 100
- 作用:把
示例
#include "BigInt.h"
#include
<iostream>
using namespace std;
int main() {
BigInt a(static_cast
<size_t>(0)); // 0
BigInt b(12345); // 12345
BigInt c("00012345"); // 12345(会去掉前导零)
BigInt s = b + c; // 24690
s += BigInt("10"); // 24700
BigInt p = BigInt("1234") * BigInt("5678"); // 7006652
cout << s << "\n";
cout << p << "\n";
}
设计理念与边界约束
- 只支持非负整数;不处理负号和小数。
- 输入有误(包含非数字或空串)时按 0 处理,避免异常。
- 规范化保证没有前导零,方便比较与输出一致。
- 算法简洁易懂,适合教学与中等规模数据;如需更高性能可考虑更高级算法(不在本项目范围内)。
测试与验证
- 运行示例:
make && ./bigint_demo
- 包含的测试覆盖:构造(含前导零与超大数)、比较、加法与
+=、乘法与*=、与 0/1 的乘加、以及大数相乘场景。全部通过将输出All tests passed.。
常见疑问(FAQ)
- 为什么要写
BigInt(static_cast size_t (0))?- 因为存在
BigInt(const char*)构造,BigInt(0)在个别编译环境中可能与空指针构造产生歧义。显式转换能避免这个问题。
- 因为存在
- 可以打印或读取负数吗?
- 当前版本不支持负数;若课程后续需要,可扩展存储符号位并调整算术与比较逻辑。
- 为什么最高位在
s[0]?- 为与课程字段要求和直观的十进制字符串表示保持一致,简化规范化与比较逻辑。
维护建议
- 新增操作(如减法、除法)时继续使用规范化策略,保持内部表示一致。
- 若切换存储结构(如低位在前或改用
std::vector char),请同步修改所有算术、比较与输出代码。
源码
本项目仅用于交流学习。请按课程与学校规定提交作业。
BigInt.h
#ifndef BIGINT_H
#define BIGINT_H
#include
<cstddef>
#include
<iostream>
using namespace std;
// BigInt: stores non-negative integers in base-10 as a char buffer of digits
// Fields required by assignment:
// - bool isZero
// - size_t len : number of digits currently used (without leading zeros)
// - size_t size : capacity of the buffer
// - char* s : dynamically allocated buffer of digits (most significant first)
class BigInt {
public:
// Constructors
BigInt(size_t value = 0); // BigInt(size_t)
BigInt(const char* str); // BigInt(const char*)
BigInt(const BigInt& other); // BigInt(const BigInt&)
// Destructor
~BigInt();
// Assignment
BigInt& operator=(const BigInt& other);
// Arithmetic
BigInt operator+(const BigInt& rhs) const;
BigInt& operator+=(const BigInt& rhs);
BigInt operator*(const BigInt& rhs) const;
BigInt& operator*=(const BigInt& rhs);
// Comparisons
bool operator==(const BigInt& rhs) const;
bool operator!=(const BigInt& rhs) const;
bool operator<(const BigInt& rhs) const;
bool operator<=(const BigInt& rhs) const;
bool operator>(const BigInt& rhs) const;
bool operator>=(const BigInt& rhs) const;
// Output
friend ostream& operator<<(ostream& os, const BigInt& x);
private:
bool isZero;
size_t len;
size_t size;
char* s; // digits stored most significant first, each '0'..'9'
// Helpers
void init_with_zero();
void ensure_capacity(size_t cap);
void normalize(); // remove leading zeros and set isZero appropriately
};
#endif // BIGINT_H
BigInt.cpp
#include "BigInt.h"
#include
<cstring>
#include
<algorithm>
using namespace std;
static bool is_valid_digits(const char* str) {
if (!str || *str == '\0') return false;
// allow leading zeros, but must be digits only
for (const char* p = str; *p; ++p) {
if (*p < '0' || *p > '9') return false;
}
return true;
}
void BigInt::init_with_zero() {
isZero = true;
len = 1;
size = 1;
s = new char[size];
s[0] = '0';
}
void BigInt::ensure_capacity(size_t cap) {
if (cap <= size) return;
size_t newCap = max(cap, size * 2);
char* ns = new char[newCap];
memcpy(ns, s, len);
delete[] s;
s = ns;
size = newCap;
}
void BigInt::normalize() {
// remove leading zeros from s (MSD at s[0])
size_t i = 0;
while (i < len && s[i] == '0') ++i;
if (i == len) {
// all zeros -> represent as single '0'
isZero = true;
len = 1;
s[0] = '0';
return;
}
if (i > 0) {
// shift left by i positions
size_t newLen = len - i;
for (size_t j = 0; j < newLen; ++j) s[j] = s[j + i];
len = newLen;
}
isZero = false;
}
BigInt::BigInt(size_t value) {
if (value == 0) {
init_with_zero();
return;
}
isZero = false;
// compute number of digits
size_t v = value;
size_t digits = 0;
while (v) { v /= 10; ++digits; }
len = digits;
size = max
<size_t>(digits, 1);
s = new char[size];
// fill from end
size_t idx = len;
v = value;
while (v) {
s[--idx] = char('0' + (v % 10));
v /= 10;
}
}
BigInt::BigInt(const char* str) {
if (!is_valid_digits(str)) {
// treat invalid input as zero
init_with_zero();
return;
}
len = strlen(str);
size = max
<size_t>(len, 1);
s = new char[size];
memcpy(s, str, len);
normalize();
}
BigInt::BigInt(const BigInt& other) : isZero(other.isZero), len(other.len), size(other.size) {
s = new char[size];
memcpy(s, other.s, len);
}
BigInt::~BigInt() {
delete[] s;
}
BigInt& BigInt::operator=(const BigInt& other) {
if (this == &other) return *this;
isZero = other.isZero;
len = other.len;
if (other.size > size) {
delete[] s;
size = other.size;
s = new char[size];
}
memcpy(s, other.s, len);
return *this;
}
BigInt BigInt::operator+(const BigInt& rhs) const {
if (isZero) return rhs;
if (rhs.isZero) return *this;
// add from LSD: since MSD at s[0], we iterate from back
size_t i = len;
size_t j = rhs.len;
size_t maxLen = max(len, rhs.len);
BigInt result(static_cast
<size_t>(0));
result.ensure_capacity(maxLen + 1);
result.len = maxLen + 1; // possible carry
result.isZero = false;
// temp fill zeros
for (size_t k = 0; k < result.len; ++k) result.s[k] = '0';
int carry = 0;
size_t ri = result.len;
while (i > 0 || j > 0 || carry) {
int a = (i > 0) ? (s[--i] - '0') : 0;
int b = (j > 0) ? (rhs.s[--j] - '0') : 0;
int sum = a + b + carry;
carry = sum / 10;
int digit = sum % 10;
result.s[--ri] = char('0' + digit);
}
// if any leading (unused) positions remain, keep '0' there
// normalize to remove leading zeros
result.normalize();
return result;
}
BigInt& BigInt::operator+=(const BigInt& rhs) {
*this = *this + rhs;
return *this;
}
BigInt BigInt::operator*(const BigInt& rhs) const {
if (isZero || rhs.isZero) {
return BigInt(static_cast
<size_t>(0));
}
// result length at most len + rhs.len
BigInt result(static_cast
<size_t>(0));
result.ensure_capacity(len + rhs.len);
result.len = len + rhs.len;
result.isZero = false;
for (size_t i = 0; i < result.len; ++i) result.s[i] = '0';
// multiply from LSD
// indices: our LSD at s[len-1], rhs LSD at rhs.s[rhs.len-1]
for (size_t i = 0; i < len; ++i) {
int carry = 0;
int a = s[len - 1 - i] - '0';
size_t pos = result.len - 1 - i; // starting position in result
for (size_t j = 0; j < rhs.len; ++j) {
int b = rhs.s[rhs.len - 1 - j] - '0';
int cur = (result.s[pos - j] - '0') + a * b + carry;
carry = cur / 10;
result.s[pos - j] = char('0' + (cur % 10));
}
// place remaining carry
size_t k = pos - rhs.len;
while (carry > 0) {
int cur = (result.s[k] - '0') + carry;
carry = cur / 10;
result.s[k] = char('0' + (cur % 10));
if (k == 0) break; else --k;
}
}
result.normalize();
return result;
}
BigInt& BigInt::operator*=(const BigInt& rhs) {
*this = *this * rhs;
return *this;
}
bool BigInt::operator==(const BigInt& rhs) const {
if (isZero && rhs.isZero) return true;
if (len != rhs.len) return false;
for (size_t i = 0; i < len; ++i) {
if (s[i] != rhs.s[i]) return false;
}
return true;
}
bool BigInt::operator!=(const BigInt& rhs) const { return !(*this == rhs); }
bool BigInt::operator<(const BigInt& rhs) const {
if (isZero && rhs.isZero) return false;
if (isZero && !rhs.isZero) return true;
if (!isZero && rhs.isZero) return false;
if (len != rhs.len) return len < rhs.len;
for (size_t i = 0; i < len; ++i) {
if (s[i] != rhs.s[i]) return s[i] < rhs.s[i];
}
return false; // equal
}
bool BigInt::operator<=(const BigInt& rhs) const { return (*this < rhs) || (*this == rhs); }
bool BigInt::operator>(const BigInt& rhs) const { return rhs < *this; }
bool BigInt::operator>=(const BigInt& rhs) const { return !(*this < rhs); }
ostream& operator<<(ostream& os, const BigInt& x) {
os.write(x.s, static_cast
<streamsize>(x.len));
return os;
}
main.cpp
#include "BigInt.h"
#include
<iostream>
#include
<vector>
// #include
<cassert>
using namespace std;
int main() {
cout << "1. Equality & Comparison" << endl << "----------" << endl;
// Constructors
BigInt a(static_cast
<size_t>(0));
BigInt b(12345);
BigInt c("00012345");
BigInt d("9999999999999999999999999999");
BigInt e(b); // copy
cout << "a=0" << "\n";
cout << "b=12345" << "\n";
cout << "c=00012345" << "\n";
cout << "d=9999999999999999999999999999" << "\n";
cout << "e=b" << "\n";
// Equality
cout << "a==0:" << (a == BigInt(static_cast<size_t>(0))) << endl;
cout << "b==c:" << (b == c) << endl;
cout << "b==e:" << (b == e) << endl;
cout << "b!=a:" << (b != a) << endl;
// Comparisons
cout << "a<b:" << (a < b) << endl;
cout << "b>=c:" << (b >= c) << endl;
cout << "d>b:" << (d > b) << endl;
cout << "b<c:" << (b < c) << endl;
cout << "b<=c:" << (b <= c) << endl;
// Addition
cout << "--------------------" << endl << "2. Addition" << endl << "----------" << endl;
BigInt x("999");
BigInt y("1");
BigInt z = x + y; // 1000
cout << "x=999,y=1"<<endl;
cout << "z=x+y=" << z << "\n";
BigInt aa("12345678901234567890");
BigInt bb("98765432109876543210");
cout << "aa=12345678901234567890,bb=98765432109876543210" << endl;
BigInt cc = aa + bb; // 111111111011111111100
cout << "aa+bb=" << cc << "\n";
// +=
BigInt m(static_cast
<size_t>(0));
cout << "m=0" << endl;
m += BigInt("5");
cout << "m+=5:" << m <<endl;
m += BigInt("95");
cout << "m+=95:" << m <<endl;
// Multiplication
cout << "--------------------" << endl << "3. Multiplication" << endl << "----------" << endl;
BigInt p1("1234");
BigInt p2("5678");
BigInt p = p1 * p2; // 7006652
cout << "p1=1234,p2=5678" <<endl;
cout << "p1*p2:" << p << "\n";
// *= with zero and one
BigInt t(static_cast
<size_t>(0));
t *= BigInt("98765");
cout << "t=0" << endl;
cout << "t*=98765:" << t << endl;
BigInt u("1");
u *= BigInt("12345678901234567890");
cout << "u=1" << endl;
cout << "u*=12345678901234567890:" << u <<endl;
cout << "--------------------" << endl << "All End.\n";
return 0;
}
Makefile
CXX := g++
CXXFLAGS := -std=c++17 -O2 -Wall -Wextra -pedantic
TARGET := bigint_demo
SRCS := BigInt.cpp main.cpp
OBJS := $(SRCS:.cpp=.o)
all: $(TARGET)
$(TARGET): $(OBJS)
$(CXX) $(CXXFLAGS) -o $@ $^
%.o: %.cpp BigInt.h
$(CXX) $(CXXFLAGS) -c $< -o $@
clean:
rm -f $(OBJS) $(TARGET)
.PHONY: all clean
程序下载
正文完


