【问题标题】:Linking an extern static array of structs is not working correctly链接外部静态结构数组无法正常工作
【发布时间】:2015-06-05 18:17:56
【问题描述】:

我正在尝试链接静态定义的结构数组。我正在使用 extern 修饰符来做到这一点。当我打印出我的 extern 结构的内存地址时,它与它在可执行文件中的位置不同。

这是我所拥有的:

type.h:

typedef struct tableEntry {
     const char *my_str;
     void *my_addr;
     int myint;
} my_struct;

test.c:

include "type.h"

my_struct my_array[] = {
    {"hello", (void*)15, 5000},
    {"world", (void*)15, 3000},
    {"abtest", (void*)15, 2000},
};

ma​​in.c:

#include <stdio.h>
#include "type.h"

extern my_struct* my_array;

int main() {
    printf("array location - %p\n", my_array);
    printf("array entry 1 myint - %d\n", my_array[0].myint);
    printf("array entry 1 address - %p\n", my_array[0].my_addr);
    printf("array string location - %p\n", &(my_array[0].my_str));
    printf("array string - %s\n", my_array[0].my_str);
}

我这样编译程序:

gcc test.c main.c

当我运行我的可执行文件时,我得到以下输出:

array location - 0x4006be
array entry 1 myint - 29811
array entry 1 address - 0x6574626100646c72
array string location - 0x4006be
Segmentation fault (core dumped)

my_array 在 nm 输出中的地址:

0000000000601060 D my_array

如您所见,我的输出不是我所期望的,并且 my_array 未正确链接(nm 输出中的位置与实际程序打印的位置不同)。

注意:我不能在 main.c 中包含 test.c 文件。它必须是链接的。

【问题讨论】:

    标签: c arrays struct extern static-array


    【解决方案1】:

    改变

    extern my_struct* my_array;
    

    extern my_struct my_array[];
    

    您不能使用extern 将您的数组修改为指针。

    【讨论】:

    • 这很容易。谢谢!
    • @rost0031 你没有真正解决这个问题。请参阅奥拉夫的回答。
    • @banach-space:OP 似乎认为确实如此。
    • @rost0031 它确实修复了结果。可惜你不解释原因。
    • 我做到了。 OP 似乎知道如何处理数组和指针就好了。他只是不知道如何正确链接外部数组。几年前我遇到过同样的问题,我纠正了他在代码中唯一的问题,并在最后一句话中解释了原因。
    【解决方案2】:

    my_array 的地址的问题是你打印 value 指针包含。但是对于数组,您需要指针本身的地址(这将是数组第一个元素的地址)。然而,作为一个指针,它仍然被错误地声明(想知道为什么编译器没有抱怨)。

    短:

    • 结构 x *p; // p 是存储在 x 中的值:“它指向的地方”
    • 结构 x q[]; // q 是第一个条目的地址(== 数组的地址)。

    要获得相同的 p,您实际上必须采用&amp;p,但这将是指向指针的类型,而不是结构。这是数组和指针之间的区别之一。阅读standard了解详情。

    因此,使用与main.c 中的实现文件中相同的声明。实际上,您应该将声明打包到标头type.h 中(注意这很容易与标准标头type**s**.h 混淆)。当您编译“type.c”时,这也会生成关于声明和定义不匹配的警告。这实际上是标题最重要的用途之一。

    对于实际地址和逻辑地址之间的差异nm报告:文件在执行前被加载到RAM,必须重新定位 根据加载地址。文件中的地址实际上是相对于.data(已初始化变量)或.bss(未初始化,默认为0 个变量)部分的基地址,文件中每个部分通常为0。加载时,将 RAM 中的起始地址添加到这些相对地址中以获得实际地址。这是程序在加载后可能需要一些时间的原因之一(重定位可能相当复杂)。

    有一个例外:如果您让链接器将程序重定位到绝对地址,例如裸机(没有成熟的操作系统)嵌入式控制器,nm 将报告与运行时相同的地址或在调试器中。原因是链接器实际上包含了上述运行时加载器的工作。因此,代码必须加载到正确的地址范围,例如到闪存。

    【讨论】:

    • 那么,投反对票的人至少可以这么好心地发表评论吗?
    • nm 和代码报告与 rost0031 的修复类似的地址。这使您的最后一段无关紧要。 (顺便说一句,投反对票的不是我)。
    • @banach-space:我没有怀疑你。对于nm:相似与“不同”的即时通讯编程相同。 nm 不能显示相同的地址,除非它用于 ld 重新定位到绝对地址的嵌入式系统。但是没有给出这些信息(既不是标签也不是明确的)。
    • 这很公平,但我尝试了代码并运行nm,在两种情况下我都得到了相同的地址(在PC而不是嵌入式平台上)。我不太了解加载程序,但我怀疑由于 VMA(假设我们生活在 Linux 世界中),在链接期间为 .data 计算的重定位地址与执行期间的重定位地址相同。这样nmprintf() 显示相同的地址。
    • 可以。但实际上,随着.bss.data 大小和地址空间随机化的不同,这将如何改变?此外,这将如何使用统一的地址空间?也许 nm 已经为符号应用了某种重定位?无论如何,对于非绝对 ELF 部分,我实际上不会依赖它。
    猜你喜欢
    • 2013-12-13
    • 1970-01-01
    • 2012-12-27
    • 2014-12-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-03-21
    相关资源
    最近更新 更多