Swift 5.1 温故而知新笔记系列之第一天

1.编译流程

在这里插入图片描述

在这里插入图片描述

操作

swiftc -dump-ast main.swift // 生成语法树
swiftc -emit-sil main.swift  // 生成最简洁的SIL代码
swiftc -emit-ir main.swift -o main.ll  // 生成LLVM IR代码
swiftc -emit-assembly main.swift -o main.s  // 生成汇编代码
Contents/Developer/Toolchains/XcodeDefailt.xctoolchain/usr/bin

2.基础语法

2.1 基本运算

简单略

2.2 流程控制

简单略

2.3 函数

1、基本函数

/// 求和 【概述】
///
/// 求和 【详述】
///
/// - Parameters:
///   - v1: 参数1
///   - v2: 参数2
///
/// - Note: 传入两个Int类型的参数相加
///
func sum(v1: Int, v2: Int) -> Int {
    return v1 + v2
}

var result = sum(v1: 10, v2: 20)
print(result)

在这里插入图片描述

2、参数标签

  • 修改参数标签
func goToSleep(at time: String){
    print("It's time to Sleep -> \(time)")
}

goToSleep(at: "23:00")
  • 可以使用_省略参数标签
func sub(_ v1: Int, _ v2: Int) -> Int {
    return v1 - v2
}

var res = sub(10, 20)
print(res)

3、可变参数

func sum(_ numbers: Int...) -> Int {
    var res = 0
    for number in numbers {
        res += number
    }
    return res
}

print(sum(10,20,30,40,50))

// A parameter following a variadic parameter requires a label
func testFunc(_ numbers: Int..., name1:String, name2:String) {}

testFunc(10,20,30,41,name1: "MKJ", name2: "HY")

一个函数只能最多有一个可变参数
紧跟在可变参数后面的参数不能省略标签,报错 A parameter following a variadic parameter requires a label

4、输入输出参数
函数参数默认是let,默认是常量

var number = 10
func test(_ num: inout Int){
    num += 1
}

test(&number)
print(number)

可变参数不能标记为inout
inout参数不能有默认值
inout参数只能传入可以被多次赋值的 var
inout参数的本质是地址传递(引用传递)

5、函数重载 (Function Overload)

  • 函数名相同
  • 参数个数不同 或 参数类型不同 或 参数标签不同
  • 返回值的类型与函数重载无关
  • 默认参数和函数重载一起使用产生二义性时,编译器并不会报错(C++ 就会报错)
func sum(v1: Int, v2: Int) -> Int {
    v1 + v2
}

func sum(v1: Int, v2: Int, v3: Int = 10) -> Int {
    v1 + v2 + v3
}
// 有歧义,会调用第一个,但是不会报错
print(sum(v1: 10, v2: 20))

6、内联函数(函数调用直接展开成函数体)
如果开启了编译器优化,编译器会将某些函数变成内联函数(Release模式下默认开启了优化)
在这里插入图片描述
在这里插入图片描述
虽然断点了,但是控制台会直接打印输出,函数体直接内联在了main函数里面

除非加了@inline关键字

@inline(never) func test() {
    print("Mikejing")
}
test()

以上代码就永远不会被内联,即使开了编译优化,@inline(__always)该标识符就代表即使代码很长,也会被内联(动态派发或者递归调用除非),这里的动态派发可以理解为子类父类的多态

哪些不会被自动内联
1.函数体比较长
2.包含递归
3.包含动态派发

2.4 枚举

变量的取值就几种,就应该考虑用枚举

  • 原始值 不占用枚举变量的内存
enum Season : Int {
    case spring = 1, summber, autumn, winter
}
// s是枚举类型,不是Int 类型, 原始值,一一对应,存储关联
var s = Season.spring
s = .autumn
print(s)
print(s.rawValue)
print(Season.winter.rawValue)
  • 关联值 和枚举存储在一起,占用内存
enum Date {
    case digit(year: Int, month: Int, day: Int)
    case string(String)
}

var date = Date.digit(year: 2021, month: 11, day: 2)

date = .string("Ten Grade-12-IEG")

print(date)

switch date{
case let .digit(year, month, day):
    print("\(year)-\(month)-\(day)")
case let .string(name):
    print("\(name)")
}
  • MemoryLayout 测试枚举内存
enum Password {
    case number(Int, Int, Int, Int)
    case other1
    case other2
}

var password = Password.number(1, 2, 3, 4)
print(MemoryLayout.size(ofValue: password)) // 实际用到的大小 33
print(MemoryLayout.stride(ofValue: password)) // 分配占用的空间大小 40
print(MemoryLayout.alignment(ofValue: password)) //  内存对齐 8

password = .other1

print(MemoryLayout.size(ofValue: password)) // 33
print(MemoryLayout.stride(ofValue: password)) // 40
print(MemoryLayout.alignment(ofValue: password)) // 8

由于关联值是和枚举存储在一起的,那么.number有四个Int,占用了32个字节,其他枚举都可以用一个字节来区分,因此只要33个字节,由于内存对齐是8个字节,因此,最终分配的内存是40个字节。

  • 特殊案例测试
enum Grade: String {
    case perfect = "A"
    case great = "B"
    case good = "C"
    case bad = "D"
}

// print(MemoryLayout<Grade>.size)  // 1
// print(MemoryLayout<Grade>.stride) // 1
// print(MemoryLayout<Grade>.alignment) // 1


enum Direction {
    case north, south, east, west
}

//print(MemoryLayout<Direction>.size)  // 1
//print(MemoryLayout<Direction>.stride) // 1
//print(MemoryLayout<Direction>.alignment) // 1



enum Score {
    case points(Int)
    case grade(Character)
    case other
}

// print(MemoryLayout<Score>.size)  // 17
// print(MemoryLayout<Score>.stride) // 24
// print(MemoryLayout<Score>.alignment) // 8




enum Date {
    case digit(year: Int, month: Int, day: Int)
    case string(String)
}

//print(MemoryLayout<Date>.size)  // 25
//print(MemoryLayout<Date>.stride) // 32
//print(MemoryLayout<Date>.alignment) // 8

enum TestEnum {
    case test0
    case test1
    case test2
    case test3(Int)
    case test4(Int, Int)
    case test5(Int, Int, Int, Bool)
}
//print(MemoryLayout<TestEnum>.size)  // 25
//print(MemoryLayout<TestEnum>.stride) // 32
//print(MemoryLayout<TestEnum>.alignment) // 8

根据上面的例子,其中原始值类型,占用都是一个字节。枚举Score类型,Charater占用了16个字节,再多一个字节,就能区分其他三个类型,因此只要17个字节,Date类型也一样。再来看最后一种类型TestEnum,个人理解是,当.test5类型下,最少需要25个字节,那么最后一个字节,就可以用来区分作为用其他枚举类型的字节位,如果用来表示.test5,只要有特定值即可,然后其他枚举有对应的特定值即可。

上述的内存分布可以往后查看

2.5 可选类型(optional)

在这里插入图片描述

  • 可选项是对其他类型的一层包装,可以将它理解为一个盒子
  • 如果为nil,那么它是个空盒子
  • 如果不为nil,那么盒子里装的是:被包装类型的数据
  • 如果要从可选项中取出被包装的数据(将盒子里装的东西取出来),需要使用感叹号! 进行强制解包
  • 如果对值为nil的可选项(空盒子)进行强制解包,将会产生运行时错误
    Fatal error: Unexpectedly found nil while unwrapping an Optional value
可选项绑定

可以使用可选项绑定来判断可选项是否包含值
如果包含就自动解包,把值赋给一个临时的常量(let)或者变量(var),并返回true,否则返回false

示例一:

if let number = Int("123") { 
	print("字符串转换整数成功:\(number)") // number是强制解包之后的Int值
	// number作用域仅限于这个大括号
} else { 
	print("字符串转换整数失败")
}
// 字符串转换整数成功:123

示例二:条件语句中用到可选绑定,就需要,隔开,不能用&隔开

// 遍历数组,将遇到的正数都加起来,如果遇到负数或者非数字,停止遍历 
var strs = ["10", "20", "abc", "-20", "30"]
var index = 0
var sum = 0
while let num = Int(strs[index]), num > 0 {
	sum += num
	index += 1 
}
print(sum)
空合并运算符
public func ?? <T>(optional: T?, defaultValue: @autoclosure () throws -> T?) rethrows -> T?
public func ?? <T>(optional: T?, defaultValue: @autoclosure () throws -> T) rethrows -> T
  • a ?? b
  • a 必须是可选项
  • b 是可选项 或者 不是可选项
  • b 跟 a 的存储类型必须相同
  • 如果 a 不为nil,就返回 a
  • 如果 a 为nil,就返回 b
  • 如果 b 不是可选项,返回 a 时会自动解包

?? 返回的类型取决于最右边的类型

let a: Int? = 1
let b: Int = 2
let c = a ?? b // c Int, 1


let a: Int? = nil
let b: Int = 2
let c = a ?? b // c Int, 2

??if let配合使用

let a:Int? = nil
let b: Int? = 2
if let c = a ?? b{
	print(c)
}
// 等价于 if a != nil || b != nil

字符串插值消除警告

var name: String? = "Mk"
print("\(name)")
// String interpolation produces a debug description for an optional value; did you mean to make this explicit?

print("\(name!)")

print("\(String(describing: name))")

print("\(name ?? "")")

多重可选项
var num1: Int? = nil
var num2: Int?? = num1
var num3: Int?? = nil

(num2 ?? 1) ?? 2 // 2
(num3 ?? 1) ?? 2 // 1

在这里插入图片描述

也可以用lldb指令 frame variable -R 或者 fr v -R 查看区别

(lldb) fr v -R num1
(Swift.Optional<Swift.Int>) num1 = none {
  some = {
    _value = 0
  }
}

(lldb) fr v -R num2
(Swift.Optional<Swift.Optional<Swift.Int>>) num2 = some {
  some = none {
    some = {
      _value = 0
    }
  }
}

(lldb) fr v -R num3
(Swift.Optional<Swift.Optional<Swift.Int>>) num3 = none {
  some = some {
    some = {
      _value = 0
    }
  }
}

因此,上面的(num2 ?? 1) ?? 2第一个空合运算符,代表num2是有值的,只是一个有值的空盒子包装,第二个运算符就是nil,取2即可。

2.6 枚举内存布局

示例一:
可以看到该枚举实际占用了25个字节,内存对齐8个字节,因此实际分配了32个字节
可以看到前 24个字节用来存储,第25个字节用来区分具体哪个case,可以看出,这里其实有四种case,分别对应第25个字节的 01,02,03,00,如果是test0,test1,test2这三个case,都是03类型,然后通过第一个字节进行值区分

enum TestEnum {
    case test0
    case test1
    case test2
    case test3(Int)
    case test4(Int, Int)
    case test5(Int, Int, Int)
}

print(MemoryLayout<TestEnum>.size)  // 25
print(MemoryLayout<TestEnum>.stride) // 32
print(MemoryLayout<TestEnum>.alignment) // 8


/*
01 00 00 00 00 00 00 00
02 00 00 00 00 00 00 00
03 00 00 00 00 00 00 00
02
00 00 00 00 00 00 00
*/
var test1 = TestEnum.test5(1, 2, 3)
withUnsafePointer(to: &test1) { ptr in
    print("address-\(ptr)")
}
print("开始")


/*
04 00 00 00 00 00 00 00
05 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
01
00 00 00 00 00 00 00
*/
test1 = .test4(4, 5)
/*
 06 00 00 00 00 00 00 00
 00 00 00 00 00 00 00 00
 00 00 00 00 00 00 00 00
 00
 00 00 00 00 00 00 00
 */
test1 = .test3(6)
/*
 02 00 00 00 00 00 00 00
 00 00 00 00 00 00 00 00
 00 00 00 00 00 00 00 00
 03
 00 00 00 00 00 00 00
 */
test1 = .test2;
/*
 01 00 00 00 00 00 00 00
 00 00 00 00 00 00 00 00
 00 00 00 00 00 00 00 00
 03
 00 00 00 00 00 00 00
 */
test1 = .test1;
/*
 00 00 00 00 00 00 00 00
 00 00 00 00 00 00 00 00
 00 00 00 00 00 00 00 00
 03
 00 00 00 00 00 00 00
 */
test1 = .test0;

示例二:
该示例如果按上面的套路,应该是24 + 1 + 1 = 26个字节,但是实际只需要25个字节,可以看到这边的Bool类型这个字节可以用来做区分不同case,因此只需要25个字节即可,编译器不至于那么傻

enum TestEnum {
    case test0
    case test1
    case test2
    case test3(Int)
    case test4(Int, Int)
    case test5(Int, Int, Int,Bool)
}

print(MemoryLayout<TestEnum>.size)  // 25
print(MemoryLayout<TestEnum>.stride) // 32
print(MemoryLayout<TestEnum>.alignment) // 8


/*
 01 00 00 00 00 00 00 00
 02 00 00 00 00 00 00 00
 03 00 00 00 00 00 00 00
 81
 00 00 00 00 00 00 00
*/
var test1 = TestEnum.test5(1, 2, 3, true)
withUnsafePointer(to: &test1) { ptr in
    print("address-\(ptr)")
}
print("开始")


/*
 04 00 00 00 00 00 00 00
 05 00 00 00 00 00 00 00
 00 00 00 00 00 00 00 00
 40
 00 00 00 00 00 00 00
*/
test1 = .test4(4, 5)
/*
 06 00 00 00 00 00 00 00
 00 00 00 00 00 00 00 00
 00 00 00 00 00 00 00 00
 00
 00 00 00 00 00 00 00
 */
test1 = .test3(6)
/*
 02 00 00 00 00 00 00 00
 00 00 00 00 00 00 00 00
 00 00 00 00 00 00 00 00
 C0
 00 00 00 00 00 00 00
 */
test1 = .test2;
/*
 01 00 00 00 00 00 00 00
 00 00 00 00 00 00 00 00
 00 00 00 00 00 00 00 00
 C0
 00 00 00 00 00 00 00
 */
test1 = .test1;
/*
 00 00 00 00 00 00 00 00
 00 00 00 00 00 00 00 00
 00 00 00 00 00 00 00 00
 C0
 00 00 00 00 00 00 00
 */
test1 = .test0;

示例三:
该示例Bool类型不是最后一位,因此内存对齐的原因,实际都需要32位来支撑,但是不需要额外的一个字节来区分不同case,Bool类型占用的那8个字节,低地址最后一个就可以用来区分不同case,高地址01就可以用来表示Bool值了

enum TestEnum {
    case test0
    case test1
    case test2
    case test3(Int)
    case test4(Int, Int)
    case test5(Int, Int, Bool, Int)
}

print(MemoryLayout<TestEnum>.size)  // 32
print(MemoryLayout<TestEnum>.stride) // 32
print(MemoryLayout<TestEnum>.alignment) // 8


/*
 01 00 00 00 00 00 00 00
 02 00 00 00 00 00 00 00
 01 00 00 00 00 00 00 80
 03 00 00 00 00 00 00 00
*/
var test1 = TestEnum.test5(1, 2, true, 3)
withUnsafePointer(to: &test1) { ptr in
    print("address-\(ptr)")
}
print("开始")


/*
 04 00 00 00 00 00 00 00
 05 00 00 00 00 00 00 00
 00 00 00 00 00 00 00 40
 00 00 00 00 00 00 00 00
*/
test1 = .test4(4, 5)
/*
 06 00 00 00 00 00 00 00
 00 00 00 00 00 00 00 00
 00 00 00 00 00 00 00 00
 00 00 00 00 00 00 00 00
 */
test1 = .test3(6)
/*
 02 00 00 00 00 00 00 00
 00 00 00 00 00 00 00 00
 00 00 00 00 00 00 00 C0
 00 00 00 00 00 00 00 00
 */
test1 = .test2;
/*
 01 00 00 00 00 00 00 00
 00 00 00 00 00 00 00 00
 00 00 00 00 00 00 00 C0
 00 00 00 00 00 00 00 00
 */
test1 = .test1;
/*
 00 00 00 00 00 00 00 00
 00 00 00 00 00 00 00 00
 00 00 00 00 00 00 00 C0
 00 00 00 00 00 00 00 00
 */
test1 = .test0;

总结:
1.一个字节存储成员值(多个case的情况,如果有Bool的情况下,可以共用这8个字节或者这1个字节)
2.N个字节存储关联值(N取占用内存最大的关联值),任何一个case的关联值/原始值都共用这N个字节

3.AT&T汇编和lldb指令

3.1寄存器和内存

通常,CPU会先将内存中的数据存储到寄存器中,然后再对寄存器中的数据进行运算
假设内存中有块红色内存空间的值是3,现在想把它的值加1,并将结果存储到蓝色内存空间

  • 1.CPU首先会将红色内存空间的值放到rax寄存器中: movq 红色内存空间, %rax
  • 2.然后让rax寄存器与1相加:addq $0x1, %rax
  • 3.最后/z将值赋值给内存空间:movq %rax, 蓝色内存空间
    在这里插入图片描述
    x86和x64汇编根据编译器的不同,有两种书写格式
  • Intel:Window派系
  • AT&T:Unix派系
    咱们iOS开发,最主要的汇编语言是:
  • AT&T汇编 — iOS模拟器
  • ARM汇编 — iOS真机

以下是常见汇编指令

项目AT&TInter说明
寄存器命名%raxrax
操作数顺序movq %rax, %rdxmov rdx, rax将rax的值赋值给rdx
常数、立即数movq $3, %rax
movq $0x10 %rax
mov rax, 3
mov rax, 0x10
将3赋值给rax,将0x10赋值给rax
内存赋值movq $0xa, 0x1ff7(%rip)mov qword ptr [rip+0x1ff7], 0xa将0xa赋值给地址为rip+0x1ff7的内存空间
取内存地址leaq -0x18(%rbp), %raxlea rax, [rbp-0x18]将rbp-0x18这个地址赋值给rax
jmp指令jmp *%rdx
jmp 0x4001002
jmp *(%rax)
jmp rdx
jmp 0x4001002
jmp [rax]
call 和 jmp写法类似
操作数长度movl %eax, %edx
movb $0x10 %al
leaw 0x10(%dx), %ax
mov edx eax
mov al 0x10
lea ax, [dx + 0x10]
b=byte (8-bit)
s = short(16-bit integer or 32-bit floating point)
w = word (16-bit)
l = long (32-bit integer or 64-bit floating point)
q = quad (64 bit)
t = ten bytes (80 -bit floating point)

16个常用寄存器

  • rax,rbx,rcx,rdx,rsi,rdi,rbp,rsp
  • r8,r9,r10,r11,r12,r13,r14,r15

寄存器具体用途

  • rax、rdx常作为函数返回值使用
  • rdi、rsi、rdx、rcx、r8、r9等常用于存放函数参数
  • rsp和rbp用于栈操作
  • rip作为指令指针(存储着CPU下一条要执行的指令的地址,一旦CPU读取一个指令,rip会自动指向下一个指令)

3.2 lldb常用指令

  • 读取寄存器的值
    register read/格式 、 register read/x

  • 修改寄存器的值
    register write 寄存器名称 数值 、 register write rax 0

  • 读取内存中的值
    x/数量-格式-字节大小 内存地址
    x/3xw 0x00000100

  • 格式
    x是16进制 f是浮点 d是十进制

  • 字节大小
    b - byte 1字节
    h -half word 2字节
    w - word 4字节
    g - giant word 8字节

  • 修改内存中的值
    memory write 内存地址 数值
    memory write 0x000001010

  • thread step-over、next、n
    单步运⾏行行,把子函数当做整体⼀一步执⾏行行(源码级别)

  • thread step-in、step、s
    单步运⾏行行,遇到子函数会进⼊入子函数(源码级别)

  • thread step-inst-over、nexti、ni
    单步运⾏行行,把子函数当做整体⼀一步执⾏行行(汇编级别)

  • thread step-inst、stepi、si
    单步运⾏行行,遇到子函数会进⼊入子函数(汇编级别)

  • thread step-out、finish
    直接执⾏行行完当前函数的所有代码,返回到上一个函数(遇到断点会卡住)

看完上述基本的汇编指令和lldb指令,我们从汇编的角度看下枚举的汇编代码

enum TestEnum {
    case test0
    case test1
    case test2
    case test3(Int)
    case test4(Int, Int)
    case test5(Int, Int, Int)
}

print(MemoryLayout<TestEnum>.size)  // 25
print(MemoryLayout<TestEnum>.stride) // 32
print(MemoryLayout<TestEnum>.alignment) // 8

var test1 = TestEnum.test5(10, 20, 30) // 断点到此处,开启汇编Debug模式

可以看到如下汇编

    0x100001b5d <+2845>: movq   $0xa, 0x3b48(%rip)        ; Swift01.date : Swift01.Date + 28
    0x100001b68 <+2856>: leaq   0x3b41(%rip), %rax        ; Swift01.test1 : Swift01.TestEnum
    0x100001b6f <+2863>: movq   $0x14, 0x3b3e(%rip)       ; Swift01.test1 : Swift01.TestEnum + 4
    0x100001b7a <+2874>: movq   $0x1e, 0x3b3b(%rip)       ; Swift01.test1 : Swift01.TestEnum + 12
    0x100001b85 <+2885>: movb   $0x2, 0x3b3c(%rip)        ; Swift01.test1 : Swift01.TestEnum + 23

记住如下两条原则:

  • rip存储的是指令的地址
  • CPU要执行的下一条指令地址就存储在rip中
  • si 汇编单步调试,进入函数

分析如下

0x100001b5d <+2845>: movq   $0xa, 0x3b48(%rip)  
可以看到 rip的地址就是下一条的地址  0x100001b68
0x3b48 + 0x100001b68 = 0x1000056B0  把0xa 存入  0x1000056B0 q占用八个字节 以下依次类推
0x100001b68 <+2856>: leaq   0x3b41(%rip), %rax  0x100001b6f + 0x3b41 = 0x1000056B0 地址放入 rax
0x100001b6f <+2863>: movq   $0x14, 0x3b3e(%rip)  0x1000056B8 存 20
0x100001b7a <+2874>: movq   $0x1e, 0x3b3b(%rip)  0x1000056C0 存 30
0x100001b85 <+2885>: movb   $0x2, 0x3b3c(%rip)   0x1000056C8 
存 2 类型关联值  b 一个字节

看到对应的地址下存储的值
在这里插入图片描述

规律知识点
1.内存地址格式为 0x4bdc(%rip),一般是全局变量,全局区(数据段)
2.内存地址格式为 -0x78(%rbp),一般是局部变量,栈空间
3.内存地址格式为 0x10(%rax),一般是堆空间
4. 小括号里面放的都是内存地址
5. r开头: 64bit 8个字节
e开头: 32bit 4个字节
ax,bx,cx:16bit, 2个字节
ah,al:1个字节
bh,bl
movq 0xa %rax
0xa 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0

第一天结束,明天继续!!!!!!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 1 University students can understand innovation through learning from the past. 2. Students can better review by breaking down complex concepts into smaller components and studying the material in an organized way. 3. When learning from the past to understand innovation, it is important to focus on understanding the big picture and to not get bogged down in the details. ### 回答2: 1. 大学生如何理解温故而知新温故而知新是一种学习方法,它要求我们在学习新知识之前先回顾和巩固已经学过的知识。大学生理解温故而知新意味着要在学习新知识之前,先回顾和复习以前学过的相关知识或基础知识。通过温故,我们能够加深对已有知识的理解和记忆,从而更好地理解和掌握新的知识。 2. 学生如何更好地去复习? 学生要更好地复习,可以采取以下策略: 首先,制定一个合理的复习计划,将要复习的内容分配到不同的时间段,确保每个科目都有足够的时间。 其次,采用多种复习方法,如阅读教材、做练习题、参加讨论等,以帮助加深理解和牢固记忆。 另外,与同学或老师一起讨论复习内容,通过讲解和互动来加深理解。 此外,保持良好的学习习惯,比如及时复习、做好笔记等,能够帮助学生更好地掌握和复习知识。 3. 温故而知新的过程需要注意什么? 在温故而知新的过程中,需要注意以下几点: 首先,要有针对性,根据自己的学习需求和复习目标,选择性地回顾和复习相关知识点。 其次,要有系统性,将复习内容进行分类整理,形成一个清晰的知识框架,有助于加深理解和记忆。 另外,要关注重难点,重点复习那些相对较难或容易遗忘的知识点,加强对这些内容的学习和理解。 还要有耐心和恒心,温故而知新是一个持续的过程,需要长期坚持和不断巩固。 最后,要善于总结和归纳,通过整理和回顾复习过程中的笔记和练习,提炼出关键概念和思维模式,便于记忆和应用。 ### 回答3: 1. 大学生如何理解温故而知新? 大学生可以理解为通过回顾过去的知识和经验,来获取新的见解和理解。温故是指回顾已经学过的知识,了解其中的原理、概念和重要点。而知新则是指通过对新知识的学习,扩展和更新自己的知识体系。温故而知新相辅相成,是一个持续学习和发展的过程。 2. 学生如何更好地去复习? 学生可以通过以下方式更好地进行复习: - 制定合理的复习计划:根据时间安排和课程难度,合理分配复习时间,确保每个学科都有足够的复习时间。 - 多种复习方法结合:采用不同的学习方式,如阅读教材、做练习题、参与讨论、制作思维导图等,帮助巩固记忆和理解知识。 - 主动参与课堂:积极参与讨论和提问,与同学和老师交流,加深对知识的理解和记忆。 - 不断反思和总结:及时检查自己的复习情况,发现不足和问题,并及时调整学习方法和计划。 3. 温故而知新的过程需要注意什么? 在温故而知新的过程中,学生需要注意以下几点: - 有目的性地温故:针对具体的知识点或者问题进行回顾,明确自己的学习目标和重点。 - 理解和记忆结合:不仅要理解概念和原理,还要通过多次的复习和记忆,帮助信息在大脑中形成长期记忆。 - 理论联系实际:将学到的知识应用到实际情境中,加深对知识的理解和记忆。 - 及时巩固复习成果:通过做练习题、整理笔记、与同学讨论等方式,巩固复习的成果,确保知识掌握得更牢固。 - 长期持续学习:温故而知新是一个持续的过程,要保持学习的热情和动力,不断更新自己的知识体系。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值