struct
struct 把若干字段打包成一块连续的值内嵌存储。CIN 的 struct 没有指针、没有方法、没有构造函数:StructName s 声明即得到一块全零的存储。
定义语法
struct 名称 {
类型 字段名
类型 字段名[固长维度]
...
}struct Point {
float x
float y
}
struct Rectangle {
Point top_left // 嵌套 struct: 值内嵌
Point bottom_right
float area
}字段之间以换行或 ; 分隔,字段可以带固长数组维度(int grades[5]),但数组元素必须是标量——Point items[3] 这类 struct 元素数组不能作字段,见 字段限制。struct 定义只描述布局,本身不产生存储;存储来自变量声明。
struct Point { int x
int y }
function main() -> int {
Point p
p.x = 3
p.y = 4
println(int_to_str(p.x) + "," + int_to_str(p.y)) // 3,4
return p.x
}成员访问:点号
成员访问统一使用 .,支持链式:
struct Point { float x
float y }
struct Rectangle {
Point top_left
Point bottom_right
}
function area(Rectangle r) -> float {
float w = r.bottom_right.x - r.top_left.x
float h = r.bottom_right.y - r.top_left.y
return w * h
}
function main() -> int {
Rectangle box
box.top_left.x = 0.0
box.top_left.y = 0.0
box.bottom_right.x = 3.0
box.bottom_right.y = 4.0
println("area = " + area(box)) // area = 12
return 0
}- 多层嵌套可以一直点下去:
box.bottom_right.x。 - 对非 struct 类型取成员会报
Member access on non-struct type: int。 - 字段名拼错会报
Struct X has no field Y(例如Struct P has no field y)。
嵌套 struct:值内嵌
嵌套 struct 的字段内嵌存储,不是指针——外层 struct 的槽数包含所有内层字段。
| 定义 | 说明 |
|---|---|
Point top_left | 内嵌一个完整 Point(两个 float 槽) |
int grades[5] | ✅ 标量元素的固长数组字段(值内嵌) |
Point corners[2] | ❌ 编译期报错:struct 元素固长数组不能作字段(见 字段限制) |
string name | 一个指针槽(字符串本身在堆上) |
struct Inner { int a
int b }
struct Outer {
Inner i1
Inner i2
int tag
}
function main() -> int {
Outer o
o.i1.a = 1
o.i2.b = 2
o.tag = 9
println(int_to_str(o.i1.a) + int_to_str(o.i1.b)
+ int_to_str(o.i2.a) + int_to_str(o.i2.b)
+ int_to_str(o.tag)) // 10029
return 0
}聚合初始化:花括号
声明 struct 变量时可以用花括号一次性给字段赋值,按字段声明顺序逐个对应:
struct P { int x
int y }
function main() -> int {
P p = {3, 4} // p.x = 3, p.y = 4
println(int_to_str(p.x) + "," + int_to_str(p.y)) // 3,4
return 0
}| 写法 | 说明 |
|---|---|
P p = {3, 4} | 按声明顺序写字段 |
P q = {9} | 字段不足时其余字段保持默认值(这里是 q.y == 0,因为对象已全零) |
Out o = { {5, 6}, 7} | 嵌套 struct 用嵌套花括号;内层也可以省略(摊平后按叶子字段顺序对应) |
P ps[2] = { {1, 2}, {3, 4} } | struct 数组:每个元素一个花括号 |
P ps[2] = {1, 2} | 元素不足时其余元素保持默认(全零),等价于只初始化 ps[0] |
字段不足是允许的(其余取默认值),初始化器过多是编译期错误:
Compiler error: Too many initializers for struct P: got 3, expected at most 2
Compiler error: Too many initializers for struct array ps: got 3, expected at most 2- 每个值都按目标字段类型转换:
int字段收到3.7会向零截断为3; - 全局 struct 的聚合初始化同样要求常量表达式,否则报
Non-constant global initializer: var;局部可以写任意表达式; - 初始化只作用于对象自身的槽,不改变值语义:
P b = {1, 2}得到的仍是一个与任何 其它对象互不影响的新对象。
顺序 = 字段声明顺序,没有指定初始化器
CIN 没有 C 那样的 {.y = 4} 具名初始化器,所以 {3, 4} 永远按 struct 里字段出现的 顺序对应。调整字段声明顺序会同时改变所有聚合初始化器的含义,跟 C 一样要注意。
值语义:整体赋值、传参、返回
| 操作 | 语义 | 示例 |
|---|---|---|
整体赋值 P b = a / b = a | 值拷贝(逐槽复制到各自对象),之后互不影响 | b.x = 7 不改 a.x |
| 作为函数参数 | 引用可见(同一块存储,不复制) | 函数内 p.x = 99 调用方可见 |
| 作为返回值 | 值拷贝返回:每次调用都是新对象 | 两次 make(4) 得到互不影响的两个对象 |
| 作为 struct 数组元素 | 每个元素是独立的完整 struct 对象 | P ps[3] 是 3 份完整 P;ps[1] = ps[0] 同样是值拷贝 |
| 作为数组参数元素 | 引用可见 | 函数内 ps[0].x = 1 可见 |
struct P { int x }
function main() -> int {
P a
a.x = 5
P b = a // 拷贝
b.x = 7
println(int_to_str(a.x) + " " + int_to_str(b.x)) // 5 7
return 0
}语义有别,别混淆
P b = a 是拷贝(写 b 不动 a);把 P 交给函数是引用(函数写 p 会动到 a)。 如果希望函数不改动调用方,请传标量字段,或在函数内先自行拷一份。
固长数组字段
字段可以是固长数组,数组元素是标量时值内嵌在 struct 里(_type_slots 会按 维度 × 元素槽数 展开)。
struct Student {
int grades[5] // 固长数组字段(值内嵌)
float gpa
}
function main() -> int {
Student s
for (int i = 0; i < 5; i = i + 1) {
s.grades[i] = 60 + i * 10
}
s.gpa = 88.5
println(int_to_str(s.grades[0]) + " "
+ int_to_str(s.grades[4]) + " "
+ float_to_str(s.gpa)) // 60 100 88.5
return 0
}多维固长数组字段同样支持:
struct Grid {
int cell[3][4] // 行主序
int rows
}struct 元素的固长数组字段不受支持
struct Bag { Point items[3] } 会在编译期直接失败(而不是留到运行期出错):
Compiler error: struct array field Bag.items is not supported (elements would need
separate objects); use a fixed array of struct variables instead原因是每个 struct 元素都是独立对象,没法内嵌在父对象的一段连续槽里。替代写法是把 数组提升为独立的 struct 数组变量,让每个元素各自成为完整对象,需要成组传递时再用 下标关联(或自己维护一个计数字段):
struct Point { int x
int y }
Point items[3] // 独立 struct 数组变量: 3 个完整的 Point
function main() -> int {
items[0].x = 1
items[0].y = 2
items[2].x = 5
println(int_to_str(items[0].x) + "," + int_to_str(items[0].y)
+ " " + int_to_str(items[1].x) + " " + int_to_str(items[2].x)) // 1,2 0 5
return 0
}标量数组字段(int grades[5]、int cell[3][4])不受影响,照常可用。
字段限制
| 允许的字段 | 说明 |
|---|---|
标量 int float bool string | string 本身只占一个指针槽 |
| 嵌套 struct | 值内嵌,占满内层全部槽 |
固长数组 T[n]、T[n][m](元素为标量) | 值内嵌,占 n × 元素槽数 |
| 不支持的用法 | 说明 |
|---|---|
struct 元素的固长数组字段 Point items[3] | 编译期报错 struct array field X.items is not supported;改用独立的 struct 数组变量(见上) |
变长指针数组字段 T[] | 官方限制,不要依赖(见 docs/CIN_GUIDE.md §14 限制 5);需要变长集合请用固长数组上限 + 长度字段 |
| 字段带初始化器 | 字段只声明类型与名字,没有默认初始化表达式 |
| 方法 / 构造函数 / 继承 | 语言不提供,用普通函数接收 struct 实现 |
取字段地址(&s.x) | 语言无取地址运算 |
变长指针数组字段
struct Bag { int[] items } 这类写法不在受支持范围内。请改写为定长容量 + 计数字段:
struct Bag {
int items[64] // 容量固定
int count // 实际使用数量
}实例化与默认全零
StructName 变量名 声明即分配存储,所有字段默认为类型默认值(int → 0、float → 0.0、bool → false、string → 空串、嵌套 struct 递归全零);这块存储由编译器显式清零,不是"碰巧是新的堆内存"。
struct Point { int x
int y
string label
bool visible }
function main() -> int {
Point p
println(int_to_str(p.x) + " " + int_to_str(p.y)) // 0 0
println("[" + p.label + "]") // []
println("visible=" + bool_to_str(p.visible)) // visible=false
return 0
}- 全局 struct 变量默认同样全零;要非零初值就写常量聚合初始化器 (
Point origin = {1, 2}),非常量表达式仍报Non-constant global initializer: var。 - 局部 struct 变量在进入所在块时分配对象并显式清零:编译器发出
MALLOC后逐槽写 0, 不依赖"堆恰好是新的"内存。因此局部 struct 的字段默认值可靠;局部标量与局部固长 数组则不然 —— 那两者是栈帧残留值(见 变量与作用域 · 默认值)。 离开块后对象释放。
struct Point { int x
int y }
function main() -> int {
Point p // 进入块时分配对象并清零
p.x = 1
return p.x
}完整示例:Point / Rectangle / Student
// shapes.cin —— 运行: codecin shapes.cin
struct Point {
float x
float y
}
struct Rectangle {
Point top_left
Point bottom_right
float area
}
struct Student {
string name
int age
int grades[5]
float gpa
}
// 参数是 struct: 引用可见
function compute_area(Rectangle r) {
float w = r.bottom_right.x - r.top_left.x
float h = r.bottom_right.y - r.top_left.y
r.area = w * h
}
// 返回值是 struct: 值拷贝
function make_student(string name, int age) -> Student {
Student s
s.name = name
s.age = age
for (int i = 0; i < 5; i = i + 1) {
s.grades[i] = 60 + (i + 1) * 5
}
s.gpa = 88.5
return s
}
function average(Student s) -> float {
float sum = 0.0
for (int i = 0; i < 5; i = i + 1) {
sum = sum + s.grades[i]
}
return sum / 5.0
}
function main() -> int {
Rectangle box
box.top_left.x = 0.0
box.top_left.y = 0.0
box.bottom_right.x = 4.0
box.bottom_right.y = 5.0
compute_area(box) // 引用可见, box.area 被写入
println("area = " + float_to_str(box.area)) // area = 20
Rectangle copy = box // 值拷贝
copy.top_left.x = 1.0
println("orig.x = " + float_to_str(box.top_left.x)
+ " copy.x = " + float_to_str(copy.top_left.x)) // 0 1
Student s = make_student("Ada", 18)
println(s.name + " age=" + int_to_str(s.age)
+ " g0=" + int_to_str(s.grades[0])
+ " g4=" + int_to_str(s.grades[4]))
println("avg = " + float_to_str(average(s))) // avg = 75
return int_to_str(box.area) == "20" ? 0 : 1
}预期输出:
area = 20
orig.x = 0 copy.x = 1
Ada age=18 g0=65 g4=85
avg = 75常见 struct 错误
| 错误信息 | 原因 | 修正 |
|---|---|---|
Struct P has no field y | 访问了不存在的字段 | 检查字段名拼写 |
Member access on non-struct type: int | 对 int/float 用 . | 只有 struct 变量能用 . |
Undefined variable: s | struct 变量未声明 | 先 Point s 声明 |
struct array field Bag.items is not supported | struct 元素数组不能作字段 | 改用独立的 struct 数组变量(见 字段限制) |
Too many initializers for struct P: got 3, expected at most 2 | 聚合初始化器多于字段 | 删掉多余的初始化器;缺省字段会自动取默认值 |
Non-constant global initializer: var | 全局 struct 用了非常量初始化 | 全局只写常量聚合初始化器,或在函数内赋值 |
Expected RBRACE ... at line N | 字段块括号不配对 | 检查 struct 定义 |