概念 Flashcards

1
Q

Dangling pointer

A

常见于concurrent threads并发线程编程,容易引发race condition。
如果线程之间没有正确同步,它们可能会竞争访问共享数据。某个线程可能还没完成使用,另一个线程就释放了数据或改变了指针指向,导致悬空指针。

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
2
Q

编译过程

A
  1. 预处理(处理 #include#define,删除注释)
    2.Lexical Analysis(拆成token)
    3.Parsing(生成语法树(AST))
    4.Semantic Analysis(检查类型、作用域等)
    5.IR Generation(生成 CPU 无关的代码)
    6.优化(删除死代码,常量折叠,循环优化)
    7.目标代码生成(生成机器码)
    8.链接(连接库文件,合并多个目标文件)
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
3
Q

函数式编程怎么处理变量和值

A

不会修改变量,而是创建新值。
你可以创建一个新值,并使用相同的变量名,这样新值会遮蔽(shadow)旧值,但旧值仍然存在于历史状态中。

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
4
Q

Pure Functions以及高等函数是pure function的好处。

A

输出只依赖输入:

只使用输入参数计算结果,不依赖外部状态。
只要输入相同,输出永远相同。
没有副作用(No Side Effects):

不会修改外部变量
不会修改全局状态
不会进行 I/O 操作(打印、写文件、请求网络等)
不会修改参数(没有 mut 引用)

如果函数没有副作用,编译器就能自由优化它们,比如 Map-Fusion。但如果函数有副作用(比如 println!),编译器就不能优化,否则可能会改变程序的行为!

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
5
Q

映射(Mapping)

A

对集合中的每个元素应用一个函数

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
6
Q

immutable data意味着什么

A

函数式编程默认数据不可变(Immutable),意味着多个线程可以安全访问同一份数据,而不用担心修改冲突。

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
7
Q

high order functions的特性

A

天然支持并行
比如高阶函数map
map 对每个元素的计算是独立的,不同的元素可以分配到不同的 CPU 核心,从而自动并行。

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
8
Q

Rust 的所有权(ownership)、移动(move)、借用(borrow) 规则

为什么下面两个函数一个的r1可以,一个的s1不行。

fn main() {
let x = 5;
let r1 = &x; // ✅ 不可变引用
let r2 = &x; // ✅ 另一个不可变引用
println!(“{} {}”, r1, r2); // ✅ 允许多个不可变引用
}

fn main() {
let s1 = String::from(“Hello”);
let s2 = s1; // ❌ s1 失效,s2 获得所有权

println!("{}", s1); // ❌ 编译错误:s1 已移动 }
A

在 Rust 中,移动语义(Move Semantics)指的是:
-变量赋值或传递时,默认移动数据的所有权,而不是复制。
- 被移动的变量将失效,无法再使用,从而避免 二次释放(Double Free) 和 悬垂指针(Dangling Pointer)问题。
比如
fn main() {
let s1 = String::from(“Hello”);
let s2 = s1; // ❌ s1 失效,s2 获得所有权

println!("{}", s1); // ❌ 编译错误:s1 已移动 } String 由三部分组成: 指针(指向堆上数据) 长度 容量 这些数据存储在 栈 上,但 指针指向的内容存储在堆上。 当 let s2 = s1; 发生时,Rust 只复制了栈上的指针、长度、容量,而没有克隆堆上的数据。 这意味着 s1 和 s2 指向同一块内存,如果 s1 仍然有效,Rust 可能会导致 双重释放(double free)错误,所以 s1 直接失效。

对于
x = 5 是 i32 类型,它是一个存储在栈上的数据类型。
所有存储在栈上的基本类型(Copy trait)都默认使用“复制”而不是“移动”。
let r1 = &x; let r2 = &x; 只是创建了不可变引用(borrow),并没有移动 x 的所有权,所以 x 仍然有效。

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
9
Q

用shallow copy解释下会发生什么?
let s1 = String::from(“Hello”);
let s2 = s1; // 如果是浅拷贝

rust会进行浅拷贝吗

A

在 Rust 中,浅拷贝(Shallow Copy) 指的是复制数据结构的指针、长度等元数据,但不复制实际存储的数据。然而,Rust 默认不会进行浅拷贝,而是 所有权移动(Move),防止悬垂指针和重复释放内存的问题。

为什么 Rust 采用 Move,而不是 Copy?
Rust 的 String 存储在堆(Heap)上,如果 Rust 默认采用浅拷贝(Shallow Copy)**,那么:

  • s1 和 s2 指向同一块堆内存。
  • 当 s1超出作用域,Rust 会释放这块内存。
  • 但s2仍然在使用它,导致悬垂指针(Dangling Pointer)或 二次释放(Double Free)。

为了解决这个问题,Rust 默认移动(Move),让 s1 失效,确保内存安全。

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
10
Q

声明更小的数据类型会节省空间吗

A

不一定,因为变量通常会按照内存对齐(alignment)的方式存储,比如4字节或8字节,因此声明更小的数据类型未必能节省空间

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
11
Q

static scoping

int x = 10; // 全局变量

void func() {
printf(“%d”, x); // 这里的 x 一定是全局的 10
}

int main() {
int x = 5; // 局部变量
func();
}
是static scoping的话,此时打印出来的是什么?

是lexical scoping的话,此时打印出来的是什么?

如果func有参数会是什么结果?

A

静态作用域(Static Scoping)
只显示全局变量和函数块开始时声明的变量。
不允许在嵌套作用域中声明新的变量(例如 x)。
作用域严格,只能使用全局变量和函数参数。
过去某些语言(如早期的 Fortran、C90 之前的 C 语言)使用静态作用域。
变量作用域在编译时确定,不会随运行时的调用顺序变化。

是10。
还是10。func() 内部没有定义 x,所以它会向外层作用域查找。
func()是在全局作用域下定义的,因此它会找到全局变量x = 10。
main() 里的 x = 5 不会影响 func(),因为 func()不会查找调用它的地方的变量(不像动态作用域)

在 Lexical Scoping(词法作用域) 规则下,变量的作用域在 编译时 就已经确定,遵循“内层作用域会屏蔽外层作用域”的原则。

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
12
Q

dynamic scoping

A

变量的查找顺序依赖于运行时的调用栈,而不是词法结构。
从最近的调用环境开始查找变量,而不是按照代码的嵌套结构。

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
13
Q

deep binding和shallowing binding?

python用的deep binding还是shallow binding。

A

在dynamic scoping动态作用域中,深绑定 是另一种实现方式,变量的绑定与定义时的调用环境关联,而不是调用时的环境。
即变量沿用定义时的值

shallowing binding是对子程序 S 选择的引用环境是在使用引用时确定的。绑定的环境是在调用该过程时确定的。

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
14
Q

def A(I,P):
def B():
print(I)
# body of A()
if I > 1:
P()
else:
A(2,B)
def C():
pass
A(1,C)
写出该函数的调用流程。

A
  1. A(1, C)运行
    I = 1
    P = C() 空函数
    因为i < 1,所以递归调用A(2, B),并把B传给P。
  2. A(2, B)运行
    I=2
    P=B(),这个B()是从A(1, C)里定义的
    因为i>1,执行P(),即3. 执行B()
    因为B在A[1, C]的作用域中定义,所以B记住的是I=1

回顾:
浅绑定(Shallow Binding):
当调用子程序(如 B())时,它会使用当前作用域的变量(即调用时的环境)。
绑定是在调用时确定的。
深绑定(Deep Binding):
当创建子程序(如 B())时,它会绑定定义时的作用域(即创建时的环境)。
绑定是在函数被作为参数传递时确定的。

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
15
Q

变量是动态意味着什么?举个例子

A

A variable type is dynamic. It can store numbers, strings, or Boolean at any time. 变量的类型是动态的,它可以在任何时候存储数字、字符串或布尔值。
Example:
val = 5
val = “Hello”

How well did you know this?
1
Not at all
2
3
4
5
Perfectly