【问题标题】:Vala string processing corrupts memory. Why and how to avoid?Vala 字符串处理会破坏内存。为什么以及如何避免?
【发布时间】:2026-02-26 01:35:01
【问题描述】:

我不确定我是在滥用 Vala 还是 GLib.Regex,因为我对这两者都是新手。我创建了一个最小的示例,它重现了错误。从下面的代码中,我希望它会打印六次a INPUTX b,并交替以sourceresult 为前缀:

public class Test
{
    public static void run( string src )
    {
        var regex = new Regex( "INPUT[0-9]" );
        for( int i = 0; i < 3; ++i )
        {
            stdout.printf( @"-- source: $src\n" );
            src = regex.replace( src, -1, 0, "value" );
            stdout.printf( @"-- result: $src\n\n" );
        }
    }

    public static void main()
    {
        Test.run( "a INPUTX b" );
    }
}

我根据the example in the docs 编写了这段代码。但是,在使用valac Test.vala --pkg glib-2.0 编译并运行后,我得到:

-- source: a INPUTX b
-- result: a INPUTX b

-- source: -- source: 
-- result: N�

-- source: -- source: 
-- result: PN�

我做错了什么?

【问题讨论】:

    标签: glib vala memory-corruption


    【解决方案1】:

    查看生成的 C 代码后,我得出结论,这与 Vala 相关:Vala 将 g_free 放在循环体的末尾,这释放了 g_regex_replace 返回的内存,即由src 引用。但是为什么 Vala 会这样做呢?

    原因是(see

    默认情况下,参数是无主的。

    因此,当我们将regex.replace 返回的string 对象分配给unowned string src 时,该引用为(see)

    未记录在对象中

    并且 Vala 编译器认为它可以安全地处理 - 虽然还不清楚,但为什么会发生这种情况,特别是在循环体的末尾。

    因此,直截了当的解决方案是将src 参数声明为owned

    【讨论】:

    • @JensMühlenhoff:感谢您的评论。但我认为这不是错误,是吗?
    • 我认为可能是,请看我现在发布的答案。
    • 要清楚,错误是编译器接受代码。你仍然需要修复你的代码。
    • 我不认为这是一个错误,因为编译器根据文档做了它应该做的事情。而是缺少一个功能。
    【解决方案2】:

    考虑一下这个(废话)代码:

    string foo (string s)
    {
        return s;
    }
    
    void run (string src)
    {
        var regex = new Regex( "INPUT[0-9]" );
        for( int i = 0; i < 3; ++i )
        {
            stdout.printf( @"-- source: $src\n" );
            //src = regex.replace( src, -1, 0, "value" );
            src = foo (src);
            stdout.printf( @"-- result: $src\n\n" );
        }
    }
    
    void main ()
    {
        run( "a INPUTX b" );
    }
    

    Vala 编译器(理所当然地)抱怨:

    test.vala:13.2-13.16: error: Invalid assignment from owned expression to unowned variable
            src = foo (src);
            ^^^^^^^^^^^^^^^
    

    因此,vapi 文件中的方法必须有所不同,因为它允许调用Regex.replace ()

    我在某个地方(在编译器或 vapi 中)闻到了错误,但我不确定。

    【讨论】:

    • VAPI 没问题;问题是编译器不会在它应该产生错误的时候产生错误。似乎是因为 Regex.replace 抛出异常(没有异常则生成错误)。