运算符
& 和 *
- 星号在类型后:用于声明指针
类型名* 指针名;
int x = 10;
int* p = &x; // 定义一个指向`int`类型的指针`p`,并将它初始化为`x`变量的地址,&x 表示获取变量 x 的内存地址
*p = 20; // `x`仍然等于`10`所在的地址,但是地址中的实际值`10`现在被改为了`20`
含义
- 定义一个指针,指向所指定类型的内存地址。
- 指针存储的是某个变量的 内存地址,而不是变量本身的值。
数据类型& 引用名 = 变量名;
方法参数中的 * 和 &
指针传递:*
表示传递指针,允许函数操作操作原始变量指向内存中实际存储的值:
void modify(int *p) { *p = 20; // 修改指针指向的值(修改这块内存实际存的值) } int x = 10; modify(&x); // 传递 x 的地址
引用传递:&
表示传引用,允许函数直接操作原始变量:
void modify(int &ref) { ref = 20; // 修改原始变量(相当于 x = 20, 而不是把20 = 10) } int x = 10; modify(x); // 直接传递变量
在 C++ 中,& 和 * 运算符有多种用途,主要体现在 引用、指针、以及 取地址和解引用 的操作上。下面将详细解释它们在 C++ 中的不同用途。
1. & 运算符的用途
(1) 取地址运算符
用法:
&在变量前表示 取地址,即获取该变量的内存地址。例子:
int x = 10; int* ptr = &x; // 取 x 的地址,并将其赋给指针 ptr- 在这个例子中,
&x返回x变量的内存地址,然后赋给指针ptr。
- 在这个例子中,
(2) 引用声明运算符
用法:
&在类型后面表示 引用。引用是一个变量的别名,它不存储数据本身,而是存储另一个变量的内存地址。例子:
int x = 10; int& ref = x; // ref 是 x 的引用,表示 ref 和 x 是同一个变量 ref = 20; // 修改 ref 的值,x 也变成了 20- 在这里,
ref是x的引用,修改ref会直接影响x。
- 在这里,
(3) 在函数参数中使用引用(传引用)
用法:在函数参数中使用
&来传递变量的引用。这样,函数会直接修改传入的参数,而不需要创建该参数的副本。例子
:
void modify(int& x) { x = 20; // 直接修改传入的 x } int main() { int a = 10; modify(a); // 传递 a 的引用 cout << a; // 输出 20,因为 a 被修改了 return 0; }
2. * 运算符的用途
(1) 指针声明
用法:
*在变量声明时表示 指针,即该变量存储的是另一个变量的内存地址。例子:
int x = 10; int* ptr = &x; // ptr 是指向 int 类型的指针,存储 x 的地址- 在这里,
*表示声明ptr为指向int类型的指针。
- 在这里,
(2) 解引用运算符
用法:
*在指针变量前面表示 解引用,即访问指针所指向的内存地址的值。例子:
int x = 10; int* ptr = &x; // ptr 存储 x 的地址 cout << *ptr; // 输出 10,解引用 ptr,访问指针指向的值*ptr通过解引用操作访问ptr指向的内存中的值,也就是x的值。
(3) 指针的赋值(指针指向)
用法:
*用于通过指针修改其指向的内容。例子:
int x = 10; int* ptr = &x; // ptr 指向 x *ptr = 20; // 通过解引用 ptr 修改 x 的值 cout << x; // 输出 20*ptr = 20;通过指针ptr修改了x的值。
(4) 返回指针
用法:函数可以返回指针,通常在返回函数中分配的内存地址时使用
*。例子:
int* getPointer() { int x = 10; return &x; // 返回指向局部变量 x 的指针(不推荐这样做,局部变量会被销毁) }
3. & 和 * 在函数中的结合使用
(1) 传递指针的引用
用法:可以使用
*和&结合在一起,传递指向指针的引用,这样可以修改传入的指针(即指针本身的地址)。例子:
void modifyPointer(int*& ptr) { int x = 20; ptr = &x; // 修改传入指针的值,使其指向 x } int main() { int y = 10; int* ptr = &y; modifyPointer(ptr); // 修改指针 ptr,使其指向新变量 x cout << *ptr; // 输出 20,因为 ptr 现在指向 x return 0; }
(2) 传递引用的指针
用法:传递一个指向引用的指针(指针指向一个引用)。
例子:
void modify(int* &ptr) { int x = 30; ptr = &x; // 修改指针 ptr,使其指向 x } int main() { int y = 10; int* ptr = &y; modify(ptr); // 修改指针,使其指向新的变量 x cout << *ptr; // 输出 30,因为 ptr 现在指向 x return 0; }
总结:& 和 * 的主要用途
| 运算符 | 用途 | 示例 |
|---|---|---|
& | 取地址运算符:获取变量的内存地址。 | int* ptr = &x; 获取 x 的地址并赋给 ptr。 |
& | 引用声明运算符:声明引用类型变量。 | int& ref = x; ref 是 x 的引用,ref 和 x 是同一个变量。 |
* | 指针声明:声明一个指针。 | int* ptr; 声明一个指向 int 的指针。 |
* | 解引用运算符:访问指针指向的值。 | int x = 10; int* ptr = &x; cout << *ptr; 输出 x 的值。 |
* | 修改指针指向的值:通过指针修改值。 | *ptr = 20; 修改指针所指向变量的值。 |
通过 & 和 *,C++ 提供了强大的指针和引用机制,允许直接操作内存和对象,提供了高效的内存管理能力。
模运算 %
在不同语言中分别对应 取余 | 取模 运算:
- 取余(JavaScript):
a % b = a - b * Math.trunc(a / b)Math.trunc()表示向0取整(即去掉小数部分)
- 取模(Python):
a % b = a - b * math.floor(a / b)math.floor()表示向下取取整(负数可以理解为向负无穷取整)
重要
这两种方式结果的 核心差异 就在于 被除数 和 除数 是否 异号 。
如果为异号,则计算结果就不同:
- 向0取整 的商会比 向下取整 的商大
1。 - 导致最终结果相差一个
|b|。
| 数学概念 | 取余 | 取模 |
|---|---|---|
| 核心思想 | 求除法运算后的 余数 | 求除法运算后的 模 |
| 商的取整方式 | 向0取整 | 向下取整(向负无穷) |
| 结果的符号 | 与 被除数 的符号相同 | 与 除数 的符号相同 |
示例 10 % 3 | 1 | 1 |
示例 -10 % 3 | -1 | 2 |
示例 10 % -3 | 1 | -2 |
示例 -10 % -3 | -1 | -1 |
| 常见语言 | C, C++, Java, JavaScript, Go | Python, Ruby, Haskell,Lua |
Python 内置取模函数(向下取整)
# quotient=-4, remainder=2(这是取模)
quotient, remainder = divmod(-10, 3)
如何模拟对方行为?
重要
由上方表格 四个示例 结合 核心差异 可以得出等式(仅在异号情况下):
所以模拟对方行为的关键代码就有了两个实现方式:
- 按照数学定义
- 根据公式实现
JavaScript模拟Python行为手动处理为总是返回非负结果(推荐)
// 利用双重取模技巧 function floor_remainder(a, b) { if (b === 0) return Number.NaN // 第二次“取模”是在避免处理同号问题时,余数未落在正确区间的问题 return ((a % b) + b) % b }重要
把内部的
(a % b)看做是x,(x + b) % b不会改变 模b 下的等价类。提示
那什么是"模 b 下的等价类"?
想象把所有整数按照"除以 b 的余数"来分组:
以 b = 3 为例:
- 余数为 0 的数:..., -6, -3, 0, 3, 6, 9, ...(这些数在 模3下 是"等价的")
- 余数为 1 的数:..., -5, -2, 1, 4, 7, 10, ...
- 余数为 2 的数:..., -4, -1, 2, 5, 8, 11, ...
每个这样的分组就是一个等价类。
对于任意
x = a % b:第一步:
x + b- 这保证了结果是正数吗?不一定!
- 但保证了我们还在同一个等价类中
第二步:
(x + b) % b- 这确保最终结果在标准范围内
[0, |b|)
- 这确保最终结果在标准范围内
控制结果符号与 除数 相同
function floor_remainder(a, b) { // 先得到 JS 原生的余数 let result = a % b // 如果结果的符号与除数 `b` 的符号不同 if (result !== 0 && (b < 0 ? result > 0 : result < 0)) { // 加上除数来调整符号(利用结果相差一个 `|b|` 特性) result += b } return result; }
Python模拟JavaScript行为使用
math.fmod()(推荐)import math def trunc_remainder(a, b): if b == 0: return float('nan') return math.fmod(a, b)使用
math.trunc去掉小数(向0取整)import math def trunc_remainder(a, b): if b == 0: return float('nan') # 或使用整除运算a - b * (a // b) return a - b * math.trunc(a / b)控制结果符号与 被除数 相同
def trunc_remainder(a, b): if b == 0: return float('nan') result = a % b # Python 的取模 # 调整符号:如果结果不为零且符号与 JS 期望的不同 if result != 0 and ((a < 0) != (result < 0)): # 结果符号应该与 a 相同,但现在是 b 的符号 if b > 0: result -= b else: result += b return result
