v2byy
V2EX  ›  C

下面的代码为什么赋值之后不对

  •  
  •   v2byy · Sep 11, 2018 · 5091 views
    This topic created in 2848 days ago, the information mentioned may be changed or developed.

    https://i.loli.net/2018/09/11/5b977fcb2e371.png

    赋值之后:

    https://i.loli.net/2018/09/11/5b977fcb3515e.png

    func_name 为字符串"wait", 但是赋值之后却不是这个值

    相关声明如下:

    
    typedef struct _local_func {
    	const char *func_name;	// function name
    	lua_CFunction func_ptr;	// function pointer
    	const char *help_info;	// help information for this function
    } LOCAL_FUNC;
    
    LOCAL_FUNC g_local_func;
    
    
    Supplement 1  ·  Sep 12, 2018

    不好意思,g_local_func是一个array,笔误。

    LOCAL_FUNC g_local_func[];
    
    //context here
    bool init_plugins(lua_State* L){
        //...
        const char* func_name = g_local_func[i].func_name;
        lua_CFunction func_pt = g_local_func[i].func_ptr;
        const char* help_info = g_local_func[i].help_info;
    }
    
    
    

    我在c代码中定义了一个g_local_func结构体数组,然后定义了一个init_plugins方法,在c中调用这个方法时如上面图片所示,g_local_func是有值的,但是赋值给函数中的局部变量的时候,却无法正确获取到值。

    这个跟lua通过栈来传递参数应该没有关系吧,因为我是在c中调用的这个函数,g_local_func是一个全局的变量。

    Supplement 2  ·  Sep 12, 2018

    我把代码提取了一下。希望大佬解惑。

    file: TestStudent.cpp

    #include "stdafx.h"
    #include "TestStudent.h"
    
    extern const int NUM;
    extern Student allStudents[];
    
    
    void print_student(){
    	for (int i = 0; i < NUM; ++i){
    
    		auto first_name = allStudents[i].first_name;  //not working, why?
    		auto last_name = allStudents[i].last_name;  //last_name is NULL?
    
    		std::cout << first_name << " " << last_name << std::endl;
    	}
    }
    
    int _tmain(int argc, _TCHAR* argv[])
    {
    
    	print_student();
    
    	return 0;
    }
    
    

    file: source.cpp

    #include "stdafx.h"
    #include "TestStudent.h"
    
    const int NUM = 3;
    
    
    static Student* get_all_students(){
    
    	Student* ss = new Student[NUM];
    	Student* ptr = ss;
    
    	for (int i = 0; i < NUM; ++i){
    		
    		ptr->first_name = "test1";
    		ptr->last_name = "test2";
    		++ptr;
    	}
    
    	return ss;
    	
    }
    
    Student* allStudents = get_all_students();
    

    file: TestStudent.h

    #pragma once
    
    typedef struct _student{
    	const char* first_name;
    	const char* last_name;
    } Student;
    
    extern const int NUM;
    
    37 replies    2018-09-13 17:55:47 +08:00
    Dori
        1
    Dori  
       Sep 11, 2018
    踩踩
    coderluan
        2
    coderluan  
       Sep 11, 2018
    ->
    raysonx
        3
    raysonx  
       Sep 11, 2018 via Android
    代码看起来没有问题,不过我好奇的是 g_local_func 看起来是一个结构体变量,你是怎么应用下标的?
    kljsandjb
        4
    kljsandjb  
       Sep 11, 2018 via iPhone
    上下文感觉不够啊…看不出来,还有 g_local_func 是个结构体数组?不一样至少说明 func_name 指向的内存被破坏了吧?看看栈有没有被破坏或者如果你是分配在堆上的话,是不是内存管理出了问题😂
    bp0
        5
    bp0  
       Sep 11, 2018
    g_local_func 不是数组吧,怎么看都只是一个全局变量而已。去掉[i]试试看。
    si
        6
    si  
       Sep 11, 2018
    猜不到是怎么赋值的
    inoki
        7
    inoki  
       Sep 11, 2018 via Android
    lua interpreter 源代码?
    Deville
        8
    Deville  
       Sep 11, 2018 via iPhone
    只能看得出是 vs 2017....
    zyp0921
        9
    zyp0921  
       Sep 12, 2018
    这是 C 语言吧
    v2byy
        10
    v2byy  
    OP
       Sep 12, 2018
    @raysonx 笔误,已 append
    v2byy
        11
    v2byy  
    OP
       Sep 12, 2018
    @kljsandjb 恩,我是通过 malloc 来给 g_local_func 分配的内存,是这样的,我使用在 c 文件中声明并给 g_local_func 分配内存,然后在另外一个 c 文件中通过 extern 导入 g_local_func 变量,然后调用的这个方法。
    v2byy
        12
    v2byy  
    OP
       Sep 12, 2018
    @bp0 笔误了,已 append
    v2byy
        13
    v2byy  
    OP
       Sep 12, 2018
    @inoki 不是哦,export 一些函数给 lua 用而已。
    v2byy
        14
    v2byy  
    OP
       Sep 12, 2018
    @Deville 怎么看出 vs2017 的?

    @zyp0921 源代码是 c++
    wutiantong
        15
    wutiantong  
       Sep 12, 2018
    @v2byy 看起来不会有你所说的“赋值不对”的问题,建议你直接 printf(%s, %p)出来看看?
    tusj
        16
    tusj  
       Sep 12, 2018
    感觉是乌龙
    ipwx
        17
    ipwx  
       Sep 12, 2018 via iPhone
    用 *g_local_func
    raysonx
        18
    raysonx  
       Sep 12, 2018
    从楼主给出的上下文中看不出任何问题,怀疑:
    1. 调试器故障。
    2. 编译器故障。
    3. 多线程运行,全局数组被覆盖。
    v2byy
        19
    v2byy  
    OP
       Sep 12, 2018
    @tusj 请看下 append
    v2byy
        20
    v2byy  
    OP
       Sep 12, 2018
    @raysonx 没有这么复杂,感觉是我理解的有问题,但是不知道在哪?
    innoink
        21
    innoink  
       Sep 12, 2018 via Android   ❤️ 1
    你把 extern Student allStudents[]改成 extern Students* allStudents
    kidtest
        22
    kidtest  
       Sep 12, 2018   ❤️ 1
    file: TestStudent.cpp

    #include "stdafx.h"
    #include "TestStudent.h"

    extern const int NUM;
    extern Student allStudents[];

    最后一行修改为:extern Student* allStudents 即可。

    另外记得释放内存
    v2byy
        23
    v2byy  
    OP
       Sep 12, 2018
    @innoink
    @kidtest

    多谢,但是为什么会出现这个问题呢?
    innoink
        24
    innoink  
       Sep 12, 2018 via Android
    @v2byy 类型不匹配。
    innoink
        25
    innoink  
       Sep 12, 2018 via Android
    @v2byy 链接过程是不做类型检查的,你想一下数组和指针变量的区别,指针额外占用一块内存保存地址,你把他声明为数组的话,连接器找到 allStudents 这个符号,会认为这就是数组首地址,即认为这个符号后面的数据就是数组内容。
    innoink
        26
    innoink  
       Sep 12, 2018 via Android
    或者你想一下,如果是真的有个 allStudents[],比如有个 Students allStudents[10],那么你 extern Students allStudents[],和你以前的写法之间的区别。
    gnaggnoyil
        27
    gnaggnoyil  
       Sep 12, 2018
    您可能是 array to pointer decay 的受害者
    raysonx
        28
    raysonx  
       Sep 12, 2018 via Android
    @innoink 但这并不能解释为何那条本地变量的的赋值结果会发生变化。我怀疑是因为调试器解析符号的行为和链接器实际链接的行为不一致,导致调试器里看到的=后面的值是错误的。
    v2byy
        29
    v2byy  
    OP
       Sep 12, 2018 via iPhone
    @innoink 太感谢了
    v2byy
        30
    v2byy  
    OP
       Sep 12, 2018 via iPhone
    @raysonx 是不是其实调试器显示的 allStudent[i].first-name 其实是错的,赋值操作其实是对的
    raysonx
        31
    raysonx  
       Sep 12, 2018
    @v2byy 我的理解是:
    不能简单的说对不对吧。大概就是 allStudents 的声明和实现不一致(声明为数组,实际为指针)。

    调用方( TestStudent.cpp )只能看到头文件,看不到具体实现,把 allStudents 当成了数组,于是 allStudents[i]被编译为了指针的值向后偏移,即*(allStudents + i),造成访问越界。

    调试器的行为目测与编译器不一致,它把 allStudents 视为一个指针,将 allStudents[i]解析为 allStudents 所指向的数组再向后偏移即*(*allStudents + i)。

    总而言之,赋值号=后面的求值结果与你从调试器看到不一致。
    raysonx
        32
    raysonx  
       Sep 12, 2018
    忽然发现我哦上面 *(allStudents + i)和*(*allStudents + i)的写法也是有歧义的。我也恨死 array to pointer decay 这个特性了。应当把 allStudents 替换 source.cpp 中那个全局指针变量的地址。
    innoink
        33
    innoink  
       Sep 12, 2018
    调试器没有链接步骤,是按照语法树来的,找到 allStudents 所在的编译单元,从它的语法树找到类型
    真正到了链接步骤,语法树已经没了
    kljsandjb
        34
    kljsandjb  
       Sep 13, 2018 via iPhone
    看到你贴的代码了,指针变量的值是你需要的,你这样 extern 的话,就是这个变量本身了。数组可以退化成指针,那这个指针指向的是数组首地址,但是指针转为数组的话,这个时候,你 focus 的地方应该是这个指针指向的内存位置,而不是这个指针本身在内存的位置。
    kljsandjb
        35
    kljsandjb  
       Sep 13, 2018 via iPhone   ❤️ 1
    举个例子,int arr[N];这是个数组定义,然后 int *p = arr, 这个时候 p 指向的是数组首地址。强行转为 p[]的话,那这个 p 你希望是转换之前 p 指向的位置的地址,这样就显然矛盾了:)
    bp0
        36
    bp0  
       Sep 13, 2018   ❤️ 2
    数组可以退化成指针,指针不能进化成数组。

    借用 @kljsandjb #35 的例子。
    数组的名称“ arr ”就是数组的第一个元素的地址,编译器并不会给“ arr ”额外分配一个地址。而指针“ p ”本身是要占用一个地址空间的,“ p ”的地址空间中保存的内容是数组“ arr ”的首地址。

    当编译器编译 TestStudent.cpp 文件时,会按照数组的方式直接使用“ allStudents ”的地址作为组数的首地址。而在编译 source.cpp 时,会给指针“ allStudents ”分配一个地址,并让其指向你动态分配的数组的首地址。

    所以 TestStudent.cpp 文件中实际上是把指针“ allStudents ”的地址当成数组的首地址使用了,自然得不到正确的内容。

    那为什么调试器能看到正确的内容呢?因为调试器是解析的 elf 文件中的 debug 信息。在 debug 信息中 allStudents 就是一个指针而不是一个数组。所以可以读到正确的内容。
    Deville
        37
    Deville  
       Sep 13, 2018
    @v2byy iOS 端图片显示了主题色- -。 看着感觉比较像。。
    About   ·   Help   ·   Advertise   ·   Blog   ·   API   ·   FAQ   ·   Solana   ·   5287 Online   Highest 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 149ms · UTC 01:16 · PVG 09:16 · LAX 18:16 · JFK 21:16
    ♥ Do have faith in what you're doing.