本课目标
· 完成仓颉 SDK 的下载、解压与环境变量配置。
· 能使用 cjc 和 cjpm 验证安装是否成功。
· 掌握仓颉程序入口 main 的写法。
· 掌握变量 let 与 var 的区别。
· 认识基础类型:Int64、Float64、Bool、String、Unit。
· 掌握函数 func、命名参数和默认值。
· 掌握分支 if、循环 while、for-in。
· 理解区间 ... 与 ...= 的区别。
· 掌握字符串插值 ${表达式}。
· 能独立完成本课的选择题、判断题、填空题、改错题、输出预测题和编程题。
一、安装与环境变量配置
- 下载 SDK
bash
# 打开仓颉语言官网或华为开发者联盟,找到仓颉 SDK 下载页
# 选择对应平台:Windows、Linux 或 macOS
# 下载后解压到无中文、无空格的目录
# Windows 示例目录:C:\Cangjie
# Linux/macOS 示例目录:$HOME/cangjie
- Windows 环境变量配置
text
CANGJIE_HOME = C:\Cangjie # 设置仓颉根目录
Path 追加:%CANGJIE_HOME%\bin # 将 bin 目录加入 Path,使 cjc 可全局使用
配置完成后,需要打开新的命令行窗口,环境变量才会生效。
- Linux / macOS 环境变量配置
bash
export CANGJIE_HOME=$HOME/cangjie # 设置仓颉根目录
export PATH=$CANGJIE_HOME/bin:$PATH # 将 bin 目录加入 PATH
export LD_LIBRARY_PATH=$CANGJIE_HOME/lib:$LD_LIBRARY_PATH # Linux 动态库路径
export DYLD_LIBRARY_PATH=$CANGJIE_HOME/lib:$DYLD_LIBRARY_PATH # macOS 动态库路径
source ~/.bashrc # 如果使用 bash,使配置立即生效
source ~/.zshrc # 如果使用 zsh,使配置立即生效
- 验证安装
bash
cjc --version # 查看仓颉编译器版本,成功会输出版本号
cjpm --version # 查看仓颉包管理工具版本,成功会输出版本号
- 编译与运行第一个程序
bash
cjc hello.cj -o hello # 编译 hello.cj,生成可执行文件 hello
./hello # 运行程序,Linux/macOS 使用 ./
Windows 下:
bash
cjc hello.cj -o hello.exe # 编译生成 hello.exe
hello.exe # 运行程序
二、核心语法知识点与逐行注释示例
- 第一个仓颉程序
cangjie
// hello.cj // 文件名,扩展名为 .cj
main(): Int64 { // 程序入口,返回 Int64 类型
println("Hello, Cangjie!") // 输出一行文本,自动换行
return 0 // 返回 0,表示程序正常退出
} // main 函数结束
- main 无返回值
cangjie
main() { // 程序入口,无返回值
println("你好,仓颉") // 输出一行文本
} // main 函数结束
- 变量:let 与 var
cangjie
main() { // 程序入口
let name: String = "仓颉" // 定义不可变变量 name,类型 String
var count: Int64 = 10 // 定义可变变量 count,类型 Int64
count = 20 // 修改 count,合法
// name = "Java" // 编译错误:let 变量不可重新赋值
println("${name} 数量 ${count}") // 字符串插值,输出变量值
} // main 函数结束
- 基础类型
cangjie
main() { // 程序入口
let n: Int64 = 42 // 定义 64 位有符号整数
let f: Float64 = 3.14 // 定义 64 位浮点数
let ok: Bool = true // 定义布尔值
let s: String = "hello" // 定义字符串
println("${n}, ${f}, ${ok}, ${s}") // 输出所有变量
} // main 函数结束
- 类型推断
cangjie
main() { // 程序入口
let score = 95 // 初始值为整数,推断为 Int64
let pi = 3.14 // 初始值为小数,推断为 Float64
println("${score}, ${pi}") // 输出两个变量
} // main 函数结束
- 函数定义与调用
cangjie
func add(a: Int64, b: Int64): Int64 { // 定义函数 add,返回 Int64
return a + b // 返回两数之和
} // 函数结束
main() { // 程序入口
println(add(3, 5)) // 调用 add,输出 8
} // main 函数结束
- 命名参数
cangjie
func greet(name!: String): String { // 定义命名参数函数,参数名后加 !
return "你好,${name}" // 返回问候语
} // 函数结束
main() { // 程序入口
println(greet(name: "小明")) // 调用时必须写参数名 name:
} // main 函数结束
- 命名参数带默认值
cangjie
func greet(name!: String, greeting!: String = "你好"): String { // greeting 有默认值
return "${greeting},${name}" // 返回拼接结果
} // 函数结束
main() { // 程序入口
println(greet(name: "仓颉")) // 省略 greeting,使用默认值
println(greet(name: "开发者", greeting: "欢迎")) // 显式传入 greeting
} // main 函数结束
- if 分支
cangjie
main() { // 程序入口
let score = 85 // 定义成绩,推断为 Int64
if (score >= 90) { // 条件必须用圆括号
println("优秀") // 90 分及以上
} else if (score >= 60) { // 否则继续判断是否及格
println("及格") // 60 分及以上
} else { // 以上都不成立
println("不及格") // 低于 60 分
} // if 结束
} // main 函数结束
- while 循环
cangjie
main() { // 程序入口
var i = 1 // 定义循环变量 i
while (i <= 3) { // 当 i <= 3 时循环
println("while: ${i}") // 输出当前 i
i = i + 1 // i 自增
} // while 结束
} // main 函数结束
- for-in 循环
cangjie
main() { // 程序入口
for (j in 1..=3) { // 遍历闭区间 1 到 3
println("for: ${j}") // 输出当前 j
} // for 结束
} // main 函数结束
- 区间 ... 与 ...=
cangjie
main() { // 程序入口
for (i in 1..5) { // 左闭右开:1、2、3、4
println("左闭右开:${i}") // 输出当前 i
} // for 结束
for (i in 1..=5) { // 闭区间:1、2、3、4、5
println("闭区间:${i}") // 输出当前 i
} // for 结束
} // main 函数结束
- 字符串插值
cangjie
main() { // 程序入口
let a = 10 // 定义变量 a
let b = 20 // 定义变量 b
println("a + b = ${a + b}") // 在字符串中嵌入表达式结果
} // main 函数结束
- 注释
cangjie
// 这是单行注释 // 从 // 到行尾都是注释
/*
* 这是多行注释 // 多行注释开始
* 可以写多行内容 // 中间内容
*/ // 多行注释结束
main() { // 程序入口
println("注释演示") // 输出文本
} // main 函数结束
三、多元化习题、答案与解读
A. 选择题
题目1
仓颉程序的入口是?
A. func main()
B. main()
C. start()
D. init()
答案与解读:B。main 是仓颉约定的程序入口,顶层定义,不需要 func。
题目2
下列哪个关键字定义可变变量?
A. let
B. var
C. const
D. final
答案与解读:B。var 定义可变变量,let 定义不可变变量。
题目3
仓颉字符串插值的正确写法是?
A. "你好, $name"
B. "你好, {name}"
C. "你好, ${name}"
D. "你好, #name"
答案与解读:C。仓颉使用 ${表达式} 进行字符串插值。
题目4
仓颉的 if 条件必须用什么包裹?
A. 方括号
B. 圆括号
C. 花括号
D. 不需要
答案与解读:B。if 条件必须用圆括号,例如 if (x > 0) { }。
题目5
1...=5 表示哪些整数?
A. 1、2、3、4
B. 1、2、3、4、5
C. 1、3、5
D. 5、4、3、2、1
答案与解读:B。...= 是闭区间,包含两端。
题目6
仓颉定义普通函数使用哪个关键字?
A. function
B. def
C. func
D. fn
答案与解读:C。仓颉用 func 定义普通函数。
题目7
命名参数在定义时,参数名后应加什么符号?
A. ?
B. !
C. @
D. #
答案与解读:B。定义命名参数时参数名后加 !,调用时必须写参数名。
题目8
仓颉合法整数类型是?
A. int
B. Integer
C. Int64
D. number
答案与解读:C。Int64 是仓颉整数类型之一。
B. 判断题
题目1
仓颉源文件扩展名是 .cj。
答案与解读:正确。仓颉源文件使用 .cj 后缀。
题目2
let 变量初始化后可以再次赋值。
答案与解读:错误。let 不可变,重新赋值需使用 var。
题目3
main 函数必须用 func 关键字定义。
答案与解读:错误。main 是特殊入口,不需要 func。
题目4
if 条件可以省略圆括号。
答案与解读:错误。if 条件必须用圆括号。
题目5
println 输出内容后会自动换行。
答案与解读:正确。println 输出后换行,print 不换行。
题目6
仓颉是静态强类型语言。
答案与解读:正确。仓颉是静态强类型语言。
题目7
字符串中 ${} 可以嵌入表达式。
答案与解读:正确。${} 内可写变量或表达式。
题目8
Unit 表示无值,类似其他语言的 void。
答案与解读:正确。Unit 表示无值。
题目9
变量类型标注不可省略。
答案与解读:错误。有初始值时通常可推断类型,可省略标注。
题目10
for (i in 1...5) 会遍历 1 到 5。
答案与解读:错误。1...5 是左闭右开,遍历 1 到 4;1...=5 才遍历 1 到 5。
C. 填空题
题目1
仓颉程序入口是 ______ 函数。
答案与解读:main。
题目2
定义不可变变量用 ______,可变变量用 ______。
答案与解读:let、var。
题目3
仓颉定义普通函数使用 ______ 关键字。
答案与解读:func。
题目4
字符串插值语法是 ______。
答案与解读:${表达式}。
题目5
条件分支关键字是 ______。
答案与解读:if。
题目6
遍历 1 到 10 的 for-in 写法:for (i in ______) { ... }。
答案与解读:1...=10。
题目7
输出一行内容使用 ______。
答案与解读:println。
题目8
仓颉布尔类型是 ______,值有 ______ 和 ______。
答案与解读:Bool、true、false。
题目9
函数无返回值时,返回类型可写为 ______。
答案与解读:Unit。
题目10
命名参数在定义时参数名后加 ______。
答案与解读:!。
D. 代码改错题
题目1
cangjie
main() { // 程序入口
let a = 10 // 定义不可变变量 a
a = 20 // 错误:let 不能重新赋值
println(a) // 输出 a
} // main 结束
答案与解读:
cangjie
main() { // 程序入口
var a = 10 // 将 let 改为 var,允许重新赋值
a = 20 // 修改 a 的值
println(a) // 输出 a
} // main 结束
let 不可重新赋值,需要改为 var。
题目2
cangjie
main() { // 程序入口
if x > 0 { // 错误:条件缺少圆括号,x 未定义
println("正数") // 输出"正数"
} // if 结束
} // main 结束
答案与解读:
cangjie
main() { // 程序入口
let x = 1 // 补充定义变量 x
if (x > 0) { // 条件必须加圆括号
println("正数") // 输出"正数"
} // if 结束
} // main 结束
if 条件必须用圆括号;原代码中 x 也未定义。
题目3
cangjie
func add(a: Int64, b: Int64) { // 错误:缺少返回类型
return a + b // 返回 a + b
} // 函数结束
答案与解读:
cangjie
func add(a: Int64, b: Int64): Int64 { // 补充返回类型 Int64
return a + b // 返回两数之和
} // 函数结束
有 return 返回值时,应声明返回类型 : Int64。
题目4
cangjie
main() { // 程序入口
for (i in 1..5) { // 左闭右开,只遍历 1 到 4
println(i) // 输出 i
} // for 结束
} // main 结束
要求输出 1 2 3 4 5,如何修改?
答案与解读:
cangjie
main() { // 程序入口
for (i in 1..=5) { // 改为闭区间,遍历 1 到 5
println(i) // 输出 i
} // for 结束
} // main 结束
... 左闭右开,...= 才是包含右端点。
题目5
cangjie
func greet(name!: String) { // 定义命名参数函数
println("你好,${name}") // 输出问候语
} // 函数结束
main() { // 程序入口
greet("小明") // 错误:命名参数必须写参数名
} // main 结束
答案与解读:
cangjie
func greet(name!: String) { // 定义命名参数函数
println("你好,${name}") // 输出问候语
} // 函数结束
main() { // 程序入口
greet(name: "小明") // 命名参数调用时必须写参数名
} // main 结束
命名参数调用时必须写参数名 name:。
E. 输出预测题
题目1
cangjie
main() { // 程序入口
let a = 5 // a = 5
var b = 3 // b = 3
b = b + a // b 更新为 8
println("${a} + ${b} = ${a + b}") // 输出 5 + 8 = 13
} // main 结束
答案与解读:输出 5 + 8 = 13。
a = 5,b = 3 + 5 = 8,a + b = 13。
题目2
cangjie
main() { // 程序入口
var sum = 0 // 累加器初始为 0
for (i in 1..=5) { // 遍历 1、2、3、4、5
sum = sum + i // 将 i 累加到 sum
} // for 结束
println(sum) // 输出 15
} // main 结束
答案与解读:输出 15。
1 + 2 + 3 + 4 + 5 = 15。
题目3
cangjie
func isEven(n: Int64): Bool { // 定义判断偶数的函数
return n % 2 == 0 // 余数为 0 返回 true
} // 函数结束
main() { // 程序入口
let x = 7 // 定义变量 x
if (isEven(x)) { // 调用 isEven 判断
println("偶数") // 条件为 true 输出"偶数"
} else { // 否则
println("奇数") // 输出"奇数"
} // if 结束
} // main 结束
答案与解读:输出 奇数。
7 % 2 == 1,isEven(7) 返回 false,进入 else。
题目4
cangjie
main() { // 程序入口
var i = 3 // i 初始为 3
while (i > 0) { // 当 i 大于 0 时循环
println(i) // 输出当前 i
i = i - 1 // i 递减
} // while 结束
} // main 结束
答案与解读:输出:
text
3
2
1
while 循环,i 从 3 递减到 1。
题目5
cangjie
func makeMsg(name!: String, greeting!: String = "你好"): String { // 命名参数,greeting 有默认值
return "${greeting},${name}" // 返回拼接后的消息
} // 函数结束
main() { // 程序入口
println(makeMsg(name: "仓颉")) // 使用默认 greeting
println(makeMsg(name: "开发者", greeting: "欢迎")) // 显式传入 greeting
} // main 结束
答案与解读:输出:
text
你好,仓颉
欢迎,开发者
greeting 有默认值,第一次调用省略;第二次显式传入。
F. 编程题
题目1
定义两个 Int64 变量,输出它们的和、差、积、商。
答案与解读:
cangjie
main() { // 程序入口
let a: Int64 = 20 // 定义整数 a
let b: Int64 = 6 // 定义整数 b
println("和:${a + b}") // 输出和:26
println("差:${a - b}") // 输出差:14
println("积:${a * b}") // 输出积:120
println("商:${a / b}") // 输出整数商:3
} // main 结束
整数除法会截断小数部分,20 / 6 结果为 3。
题目2
编写函数 isPositive(n: Int64): Bool,判断是否为正数。
答案与解读:
cangjie
func isPositive(n: Int64): Bool { // 定义判断正数的函数
return n > 0 // 大于 0 返回 true,否则返回 false
} // 函数结束
main() { // 程序入口
println(isPositive(5)) // 输出 true
println(isPositive(0)) // 输出 false
println(isPositive(-3)) // 输出 false
} // main 结束
比较运算返回 Bool 类型。
题目3
用 for-in 计算 1 到 100 中所有偶数的和。
答案与解读:
cangjie
main() { // 程序入口
var sum = 0 // 偶数和初始为 0
for (i in 1..=100) { // 遍历 1 到 100
if (i % 2 == 0) { // 判断是否为偶数
sum = sum + i // 累加偶数
} // if 结束
} // for 结束
println("偶数和:${sum}") // 输出 2550
} // main 结束
输出 偶数和:2550。偶数序列 2+4+...+100,和为 2550。
题目4
编写函数 factorial(n: Int64): Int64,用 while 计算阶乘。
答案与解读:
cangjie
func factorial(n: Int64): Int64 { // 定义阶乘函数
var result = 1 // 结果初始为 1
var i = 1 // 循环变量从 1 开始
while (i <= n) { // 当 i <= n 时循环
result = result * i // 累乘
i = i + 1 // i 自增
} // while 结束
return result // 返回阶乘结果
} // 函数结束
main() { // 程序入口
println(factorial(5)) // 输出 120
} // main 结束
5! = 5 × 4 × 3 × 2 × 1 = 120。
题目5
编写函数 describeScore(score: Int64): String,90 及以上返回"优秀",60 及以上返回"及格",否则返回"不及格"。
答案与解读:
cangjie
func describeScore(score: Int64): String { // 定义成绩描述函数
if (score >= 90) { // 90 分及以上
return "优秀" // 返回"优秀"
} else if (score >= 60) { // 60 分及以上
return "及格" // 返回"及格"
} else { // 低于 60 分
return "不及格" // 返回"不及格"
} // if 结束
} // 函数结束
main() { // 程序入口
println(describeScore(95)) // 输出"优秀"
println(describeScore(72)) // 输出"及格"
println(describeScore(45)) // 输出"不及格"
} // main 结束
分段判断,返回对应字符串。
题目6
使用命名参数和默认值,定义 introduce(name!: String, age!: Int64 = 18, city!: String = "北京"): String,返回自我介绍。
答案与解读:
cangjie
func introduce(name!: String, age!: Int64 = 18, city!: String = "北京"): String { // 命名参数,带默认值
return "我叫${name},今年${age}岁,来自${city}。" // 返回自我介绍
} // 函数结束
main() { // 程序入口
println(introduce(name: "小明")) // 只传 name,使用默认 age、city
println(introduce(name: "小红", age: 20)) // 传 name、age,city 用默认
println(introduce(name: "小刚", age: 22, city: "上海")) // 全部传入
} // main 结束
命名参数可选择性传参,未传的使用默认值。
G. 开放综合题
题目1
用自己的话解释:为什么 main 函数不需要 func 关键字?
答案与解读:main 是仓颉约定的程序入口,编译器会特殊识别,因此不需要像普通函数那样写 func。普通函数必须用 func 定义。
题目2
简述值类型与引用类型的区别,并各举一个例子。
答案与解读:值类型赋值时复制值,修改副本不影响原值,例如 Int64、Bool、struct。引用类型赋值时复制引用,多个变量可能指向同一对象,例如 class、Array。本课只需建立初步概念。
四、知识点总结
· 程序入口:main 函数,顶层定义,不需要 func。
· 源文件:扩展名 .cj。
· 变量:let 不可变,var 可变;类型可推断。
· 基础类型:Int64、Float64、Bool、String、Unit。
· 函数:用 func 定义;参数写法为 名: 类型。
· 命名参数:定义时参数名后加 !,调用时写参数名。
· 默认值:命名参数可设默认值。
· 分支:if (条件) { ... } else { ... }。
· 循环:while、for-in、do-while。
· 区间:... 左闭右开,...= 闭区间。
· 字符串插值:${表达式}。
· 注释:// 单行,/* */ 多行。
· 编译运行:cjc hello.cj -o hello,然后 ./hello。
· 包管理:cjpm 可用于初始化、构建、运行项目。
五、易错点
· let 和 var 别混:需要修改就用 var,否则用 let。
· if 条件要括号:写 if (x > 0),不要写 if x > 0。
· 区间 ... 与 ...= 不同:1...5 是 1 到 4,1...=5 是 1 到 5。
· 命名参数调用要写名字:定义 name!,调用必须 name: 值。
· 函数有返回值要写返回类型:如 : Int64、: Bool、: String。
· 字符串插值用 {}:不是 name,也不是 {name}。
· 整数除法会截断:5 / 2 结果是 2。
· main 是特殊入口:不需要 func,但普通函数必须用 func。
六、本课通关清单
· 完成仓颉 SDK 下载、解压和环境变量配置。
· 能用 cjc --version 验证安装。
· 能写出并运行 main() 入口程序。
· 能正确使用 let 和 var。
· 能定义并调用普通函数、命名参数函数。
· 能使用 if、while、for-in 控制流程。
· 能使用 ${} 进行字符串插值。
· 能区分 ... 和 ...= 的区间含义。
· 能独立完成本课全部习题。