Swift4.2:Nil cannot be assigned to type 'UnsafeMutablePointer'与Optional chaining

由于直接抱着对其他语言的认识来使用swift,这个问题苦恼了我很久,当我用关键词“swift 空指针”搜索也没有得到有用信息,直到我发现swift一定要对可能为空的变量做标记——Optional(?&!)。

Optional Chaining(可选类型)

https://docs.swift.org/swift-book/LanguageGuide/OptionalChaining.html

在C和Objective-C中不存在可选类型,可以也只能用传统方式处理空值,Swift引入可选类型专为处理值可能为空的情况。

声明

声明变量时后面加上?或!,他们的主要区别是用?更优雅。。。

The main difference is that optional chaining fails gracefully when the optional is nil, whereas forced unwrapping triggers a runtime error when the optional is nil.

传值

用!声明Optional变量意味着ta总是被forced unwraping(强制解析):传值时不用加!,如果运行中访问没有值的变量,程序就会崩溃。

用?声明的Optional变量在传值时有两种选择:使用时后面加??提供默认值以替代值可能为nil的情况,或者使用的时候后面加上!,那么情况就和上文一样了。

拓展

判断Optional变量是否为空,可以用if:

if var1 = nil{}

也可以申请一个局部变量(这里假设property1是Optional变量):

if let var1 = obj1.property1? {
    print("\(var1) exists.")
} else {
    print("Nothing.")
}

8086汇编实现对数字的存储、输出

目标

  • 设计一个INPUT子程序,使其能够接收用户输入的数字(以字符串的形式),并将数字存储到AX寄存器。
  • 设计一个DECOUT/BINOUT/HEXOUT子程序,使他们能够以十进制/二进制/十六机制的形式将AX中的数字输出到屏幕上。

分析

  • 对于INPUT子程序,我们需要调用DOS功能接收用户输入的字符串,将ASCII码转为数字,再依次乘以10、100...由于CPU执行指令时会占用AX,我们将积的累加和存储在内存变量中,最后一步复制到AX中。
  • 对于DECOUT子程序,我们对AX中的数除以进制数,将余数保存到内存中,循环此过程直至商为0,最后借用si寄存器变址寻址反向输出余数(转换为ASCII码)即可向用户展示AX中的数字。

INPUT

  • 调用DOS的0a号功能接收字符串
data segment
  str db 20h
  num db 0
  content db 20h dup(0)
data ends
...
mov dx,offset str
mov ah,0ah
int 21h
  • 外循环(ASCII码转数字,调用内循环)
data segment
  consequence dw 20h;存放结果
data ends
...
xor ax,ax;用异或清零
mov al,str+1;一共这么多字符,循环的范围是str[2]到str[si+1]
mov si,ax;用si计数

flag:xor ax,ax
mov al,str[si+1]
sub ax,30h;-30h将ascii码转数字
内循环
add consequence,ax
dec si
cmp si,0;si=0的时候不能再跳转了
jnz flag

mov ax,consequence
  • 内循环(将从右向左第n位数转换为n*10^(n-1),已知ax的内容是第(总位数+1-si)位数,其中si每次外循环-1,故乘以10的次数为(总位数-si);我们可以将总位数复制到cur内存变量,每次内循环-1,当cur=si,内循环结束):
data segment
cur dw 0;第几位
data ends
...

xor bx,bx
mov bl,num
mov cur,bx

mul10:cmp si,cur;如果外循环正处于第n位,内循环也处于第n位,不需要再乘以10
jz endd
mov bx,0ah
mul bx;ax*10,结果存放在dx|ax
dec cur
jmp mul10
endd:

DECOUT

  • DECOUT中BX的值可以决定输出的进制
binout proc;将ax的值以二进制显示
xor si,si;利用si寻址

mov bx,2
divv:cwd
div bx;余数在dx
add dl,30h;转位ascii码
mov num[si],dl;
inc si
cmp ax,0;商0即结束
jnz divv;商不为0的时候

mov cx,si
;上面多加了一次si
display: dec si
 mov dl,num[si]
mov ah,02h;调用字符显示
  int 21h
loop display
ret
binout endp

做MSSQL实验时的一些理解

数据库中有3张表,使用t-sql检索数据 s: sno学号, sname姓名, dept院系, age, sex
c: cno课程号, cname课程名, cpno, credit学分
sc: sno, cno, score

相关子查询的查询过程

select sno,cno,score from sc as sc1
where score<(select max(score) from sc as sc2 where sc2.sno=sc1.sno)
如果直接执行“select max(score) from sc”,我们将会得到sc表中score最大的那一行记录,但我们把s1与s2做sno相等的等值连接,情况有所不同。sc1.sno在查询过程中是变化的,我们可以认为数据库遍历了sc,sc1.sno等于遍历中的某一项的值。因此,这段代码将显示除了sno对应的最高成绩外的所有成绩。

当我们用EXISTS的时候我们在干嘛

select count(*) from sc where exists(select * from c where c.cno=sc.cno and cname='数据库原理')
go
select count(*) from sc,c where (c.cno=sc.cno and cname='数据库原理')
这两种查询得到的结果是相同的,我们可以认为exists仅仅用于表达嵌套子查询,增加了查询的可读性。

T-SQL拷贝表

create table s1(
sno varchar(10) not null,sname varchar(20),dept varchar(20),age int,sex char(4))
go
insert into s1(sno,sname,dept,age,sex) select sno,sname,dept,age,sex from s
有时候需要拷贝一份表,但我在网上查到了很多写法并不符合t-sql的语法,上面是可以使用的版本。我只找到了先按照表的结构新建表然后插入所有数据的办法。在sqlserver2017中可以正常运行,如果不能用,可能需要在末尾加where 1=1。

利用default约束插入数据

insert into s1(sno,sname,dept,age) values(125,'name','dept',16)
go
select * from s1
一开始我用了insert into s1 values(125,'name','dept',16)没有成功,好在及时悔悟,数据库管理系统并不能智能补缺。。。我们应该老老实实写上所有要加的属性。ps:default只在insert into起作用。

外键约束

alter table sc add constraint fkey foreign key(cno) references c(cno)'注释:sc表中cno的取值仅限于c表中cno的取值
A属性的取值范围是B属性已有取值的集合。或者说A中不同元素的集合属于B中不同元素的集合。

SQL自定义函数(存储过程)的语法

create proc calculate @n int '注释:@n int 是传入的参数,可以没有;create proc proc_name必须要有
as
declare @sum int,@i int '注释:声明变量,sql中变量前都加@
set @i=0 '为变量赋值
set @sum=1
while @i<@n'支持循环语句
begin
set @i=@i+1
set @sum=@sum*@i
end
print @sum
'ps:除了用set为变量赋值,也可以用select @variable= column_name from table_name where 条件
执行函数只需要'exec proc_name'

Mac下写8086汇编

前期准备

我们课上讲的是8086下的16位汇编,如此远古的操作系统导致我在配置环境时遇到了很多问题。起初打算在win7虚拟机下用masm和汇编ide写汇编,但编译不成功,因为masm新版已经不是16位了。据查,masm5.0版本支持16位汇编,但又发现了DOSBox,与其在win7虚拟机下模拟DOS,不如直接在Mac中运行DOS环境。

DOSBox下载:https://www.dosbox.com/

Step1、下载DosBox和masm5.0,将masm5.0放到用户文件夹内(避免权限不够)


Step2、在DOSBox中键入"mount c ~/masm5.0",将masm5.0文件夹挂载为C盘。~代表当前用户目录,即/Users/xxx

Step3、在Mac中将写好的汇编源文件xx.asm放到masm5.0目录下,依次键入"masm xx.asm;"与"link xx.obj;",就可以直接运行程序了 masm负责将汇编源文件翻译成obj文件,link将obj文件链接成exe

图1

#用批处理来完成Step3

Mac下使用文本编辑工具(vscode也有masm插件支持汇编高亮)在masm5.0目录下新建一个xx.bat,把命令按行写进去就好了,如:

masm xx.asm;
link xx.obj;
xx.exe

编写第一个汇编程序

https://www.guohere.com/659.html

如果你已经了解过指令系统寄存器,这些例子将向你介绍汇编源文件的基本结构:

data segment;定义数据段
  x db 'A'; define byte定义x为一个值为A的ASCII码的字节型变量
  y dw 30h; define word定义y为一个值为30h(48)的字型变量
  z dd 40h; define double word定义z为一个值为40h(64)的双字型变量
  a dw ?;定义一个变量
data ends;段结束的标记

stack1 segment para stack;不需要堆栈段可以不要这部分
  db 10h dup(0)
stack1 ends

code segment
assume cs:code,ds:data; assume伪指令用于确定段与段寄存器的关系,assume不会翻译成机器指令,但会存在于exe的文件头中,这会方便DOS重新分配内存时改变对应地址指针寄存器的值
start:mov ax,data;汇编后段名变成立即数,立即数不能直接赋值给段寄存器
  mov ds,ax;段寄存器将指向data数据段
  mov dl,x;显示字符前将字符移动到dl
  mov ah,02h;调用字符显示
  int 21h
  mov ah,4ch;4ch对应返回控制台子程序
  int 21h;根据ah确定子程序,自动跳转到子程序入口地址
 code ends
end start

#大小写转换

data segment;数据段
  errs db 'error!$'
data ends

stack1 segment para stack;堆栈段
  
stack1 ends

code segment;代码段
assume cs:code,ds:data
start:mov ax,data;程序起点
      mov ds,ax
input:mov ah,08h;控制台输入到al
      int 21h
      cmp al,'0';是否=0
      jz zero
      cmp al,'A';是否>=A,大于等于则cf=0,对应jnc
      jc err;<A且!=0的情况
      ;下面的情况>=A
      cmp al,5bh;是否<=Z,和Z的后一个字符比较,小于则cf=1,对应jc
      jc plus
      ;下面的情况>Z
      cmp al,'a'
      jc err
      cmp al,7bh
      jc minus
      jnc err
zero: mov dl,'0';移动到dl供显示
      mov ah,02h;字符显示
      int 21h
      mov ah,4ch;返回控制台
      int 21h
plus: add al,20h
      mov dl,al
      jmp show
minus: sub al,20h
       mov dl,al
       jmp show
show: mov ah,02h;字符显示
      int 21h
      loop input
err:  mov dx,offset errs;将errs首地址传送给dx
      mov ah,09h;召唤字符串
      int 21h;芝麻开门
      loop input

code ends;代码段结束
 end start