我正在使用 ESP32 的示例项目,并将主 C 文件拆分为两个文件,main.c并wifi.c使用一个包含共享变量的头文件。
头文件:
#ifndef WIFI_TEST_H
#define WIFI_TEST_H
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/event_groups.h"
#include "esp_system.h"
#include "esp_log.h"
#include "esp_ota_ops.h"
#include "esp_http_client.h"
#include "esp_flash_partitions.h"
#include "esp_partition.h"
#include "esp_wifi.h"
#include "esp_event_loop.h"
#define EXAMPLE_WIFI_SSID CONFIG_WIFI_SSID
#define EXAMPLE_WIFI_PASS CONFIG_WIFI_PASSWORD
const char *TAG = "wifi_test";
/* FreeRTOS event group to signal when we are connected & ready to make a request */
EventGroupHandle_t wifi_event_group;
/* The event group allows multiple bits for each event,
but we only care about one event - are we connected
to the AP with an IP? */
const int CONNECTED_BIT = BIT0;
esp_err_t event_handler(void *, system_event_t *);
void initialise_wifi(void);
#endif
Run Code Online (Sandbox Code Playgroud)
该文件包含在 和main.c中wifi.c。编译时,我收到以下错误:
/project/build/main/libmain.a(wifi.o):(.data.TAG+0x0): multiple definition of `TAG'
/project/build/main/libmain.a(main.o):(.data.TAG+0x0): first defined here
/project/build/main/libmain.a(wifi.o):(.rodata.CONNECTED_BIT+0x0): multiple definition of `CONNECTED_BIT'
/project/build/main/libmain.a(main.o):(.rodata.CONNECTED_BIT+0x0): first defined here
Run Code Online (Sandbox Code Playgroud)
变量仅在 h 文件中声明。我认为这可能是由于包括警卫,但它也不起作用。我做了一次make clean以确保没有留下任何旧的东西,但仍然没有运气。
也许我只是忽略了一些微不足道的事情......
(采纳并扩展了一些程序员家伙的评论,将这个问题从未回答的问题列表中删除。我相信他不介意,但提议删除。)
您在头文件中定义变量;请注意缺少关键字extern。这里:
const char *TAG = "wifi_test";
const int CONNECTED_BIT = BIT0;
Run Code Online (Sandbox Code Playgroud)
这意味着它将在包含该头文件的每个翻译单元(简化为:代码文件)中定义。
即,您为每个包含代码文件获得一个定义,即多个定义,即多个定义。这就是链接器通过您引用的错误消息告诉您的内容。
而是在头文件中声明变量(使用 extern 关键字并且不进行初始化),即:
extern const char *TAG;
extern const int CONNECTED_BIT;
Run Code Online (Sandbox Code Playgroud)
然后,仅在单个源文件中定义(并初始化)变量,即:
/* only in one .c, not in a .h, that is the difference */
const char *TAG = "wifi_test";
const int CONNECTED_BIT = BIT0;
Run Code Online (Sandbox Code Playgroud)
这个独特的代码文件部分使定义单一,没有多个,使链接器保持满意。
同时,标头中的声明使标识符及其类型在编译所有其他代码文件时对编译器可见/已知。这样访问它们就成为可能。然后,由编译器(使用占位符)创建的访问代码变得可执行,以便它通过填充占位符的链接器从所有代码文件中始终访问相同的内容。
关于重新包含防护装置的旁注。
和
#ifndef WIFI_TEST_H
#define WIFI_TEST_H
Run Code Online (Sandbox Code Playgroud)
定义设置之前在行中检查的宏,有效地防止再次编译相同的代码,但前提是定义已知。然而,这又仅适用于同一代码文件。
目的是避免在一个代码文件中重复相同的事情,如果包含此标头(例如间接包含它的标头),则可能会发生这种情况。(这种特殊情况会另外创建一个循环包含...)
但这并不会阻止标头被包含到下一个代码文件中,因为编译器不知道在其他代码文件中定义的宏(或在其他地方包含的标头中) )。它不应该阻止这种情况,因为访问变量的所有代码文件都需要声明(而不是定义)。