# 04 - 函数定义与调用 Viper 的函数定义使用 Python 的 `def` 语法,通过类型注解指定参数类型和返回类型。 ## 基本函数定义 ```python def add(a: t.CInt, b: t.CInt) -> t.CInt: return a + b ``` 等价 C 代码: ```c int add(int a, int b) { return a + b; } ``` ## 无返回值函数 ```python def print_msg(msg: t.CConst | t.CChar | t.CPtr) -> t.CVoid: serial.puts(msg) ``` 等价 C 代码: ```c void print_msg(const char* msg) { serial_puts(msg); } ``` ## 函数修饰符 通过返回类型的 `|` 组合添加函数修饰符: ### 导出函数 `export` 关键字控制函数的符号可见性——标记为 `export` 的函数不加 SHA1 前缀,裸名导出、全局可见。详见 [01-overview.md 中 t.CExport 深度解析](01-overview.md#tcexport-深度解析)。 ```python export def _start() -> t.CInt: return 0 ``` ### 外部函数声明 使用 `extern` 关键字表示仅声明不定义: ```python extern def isr0() -> t.CVoid: pass extern def isr1() -> t.CVoid: pass ``` 等价 C 代码: ```c extern void isr0(); extern void isr1(); ``` > 注明:`t.State` / `t.CExtern` / `t.CExport` 注解旧写法已废弃(`t.State` 仅 `t.CEnum` 内部临时使用)。 ### 内联函数 `t.CInline` 标记函数为内联函数。当前无调用点展开实现,`t.CInline` 仅作存储类标记;实际内联用 `@c.Attribute(t.attr.always_inline())`(期望后续支持)。详见 [01-overview.md 中 t.CInline 深度解析](01-overview.md#tcinline-深度解析)。 ```python def fast_add(a: t.CInt, b: t.CInt) -> t.CInt | t.CInline: return a + b ``` ### 宏函数 宏函数存在两种形式:`def f(...) -> t.CDefine`,或 `define` 软关键字 `define f(...)`(自动追加 `t.CDefine` 返回注解);存根保留完整函数体;调用点按编译期内联宏展开(TPC 已实现,Test/DefineTest 实证)。详见 [01-overview.md 中 t.CDefine 函数](01-overview.md#tcdefine-函数)。 > 注明:TransPyV 端目前仅识别返回注解为 i32,调用点展开尚未对齐(期望)。 ```python def MAKE_FLAG(bit) -> t.CDefine: return (1 << bit) ``` ### 静态函数 ```python def helper() -> t.CVoid | t.CStatic: pass ``` ## 函数属性装饰器 使用 `@c.Attribute` 添加 GCC `__attribute__` 属性: ```python @c.Attribute(t.attr.section(".text.startup"), t.attr.aligned(16)) def _start() -> t.CInt: return 0 ``` 等价 C 代码: ```c __attribute__((section(".text.startup"), aligned(16))) int _start() { return 0; } ``` ### 常用属性组合 ```python @c.Attribute(t.attr.always_inline()) def hot_path() -> t.CInt: return 0 @c.Attribute(t.attr.noreturn()) def panic(msg: t.CConst | t.CChar | t.CPtr) -> t.CVoid: serial.puts(msg) while True: pass @c.Attribute(t.attr.packed) ``` ## LLVM 函数属性 在返回类型注解中通过 `t.attr.llvm` 指定 LLVM 属性: ```python def foo() -> t.CInt | t.attr.llvm.nobuiltin | t.attr.llvm.nounwind: return 0 ``` 已支持:`always_inline` / `noinline` / `noreturn` / `pure` / `const` / `nounwind` / `noredzone` / `stackrealign` / `willreturn` / `mustprogress`;示例中的 `nobuiltin` 尚未映射(会被静默丢弃,期望支持)。 ## 多返回值(CReturn) 使用 `@c.CReturn` 装饰器实现多返回值,返回多个值的函数打包为 LLVM struct 返回,调用处用元组解包 `a, b = f()` 接收: ```python @c.CReturn(t.CInt, t.CInt) def divmod(a: t.CInt, b: t.CInt) -> t.CVoid: q: t.CInt = a // b r: t.CInt = a % b return q, r q, r = divmod(a, b) ``` 规则: 1. `CReturn` 中的类型数量决定返回值个数 2. 必须使用左右值(元组解包)赋值,返回值不能直接传入函数。 ## 指针参数 ```python def read_data(buf: t.CChar | t.CPtr, size: t.CSizeT) -> t.CInt: pass ``` 等价 C 代码: ```c int read_data(char* buf, size_t size); ``` ### 输出参数模式 使用 `c.Addr` 传递变量地址作为输出参数: ```python res: t.CInt = fat32.opendir("/", c.Addr(dp)) ``` 等价 C 代码: ```c int res = fat32_opendir("/", &dp); ``` 后续会使用隐式左值获取来减少 `c.Addr` 的直接使用。 ## 函数指针类型 使用 `t.Callable` 定义函数指针类型: ```python irq_handler_t: t.CTypedef | t.Callable[[], t.CInt] ``` 等价 C 代码: ```c typedef int (*irq_handler_t)(); ``` 带参数的函数指针: ```python callback_t: t.Callable[[t.CInt, t.CInt], t.CVoid] ``` ## 默认参数 Viper 支持函数默认参数值: ```python def create_window(x: t.CInt, y: t.CInt, w: t.CInt, h: t.CInt, title: t.CConst | str = "Window") -> t.CInt: pass ``` ## return 语句 ```python return 0 # return 0; return # return; (void 函数) return a, b # 多返回值(需配合 @c.CReturn) ``` ## 全局变量声明 函数外的变量声明为全局变量,函数内通过 `global` 关键字访问: ```python BootInfo: bootinfo | t.CPtr def init() -> t.CVoid: global BootInfo BootInfo = saved_rdi ``` 支持 func-in-func 嵌套函数。 ## Call Func 函数调用 函数调用最好通过顺序调用来实现 ```python def add(x: int, y: int, z: int): return (x + y) * z export def main() -> int: print(add(1, 2, 3)) # (1 + 2) * 3 = 9 ``` 这也是 C 中的标准实现方法,但实际上,你也可以乱序传参。 ```python # 仍然用刚才的 add 举例 export def main() -> int: print(add(z=3, x=1, y=2)) # (1 + 2) * 3 = 9 ``` 乱序传参或带参传参对于结构体的初始化同样有效。同时,你也能够给函数指定默认值。 ```python def add(x: int, y: int, z: int = 1): return (x + y) * z export def main() -> int: print(add(1, 2)) # (1 + 2) * 1 = 3 ``` 在结构体中也可以指定默认值。