[英]How to share variables between two functions without declaring it a global variable in C language
我已經閱讀了很多答案,對此有很多意見,但我找不到可以回答我的問題的代碼(我找到了很多回答“如何通過聲明共享變量”的代碼) ”)
這是情況:
既然已經解決了,讓我寫一個例子。
file1.c
- 監控
void function(){
uint8_t varFlag[10]; // 10 devices
for (uint8_t i = 0; i < 10; i++)
{
while (timeout <= 0){
varFlag[i] = 1;
// wait for response. We'll know by the ack() function
// if response back from RX,
// then varFlag[i] = 0;
}
file2.c
- RX 端
// listening... once indicated, this function is called
// ack is not called in function(), it is called when
// there's notification that there is a received message
// (otherwise I would be able to use a pointer to change
// the value of varFlag[]
void ack(uint8_t indexDevice)
{
// indexDevice = which device was acknowledged? we have 10 devices
// goal here is to somehow do varFlag[indexDevice] = 0
// where varFlag[] is declared in the function()
}
您共享值或數據,而不是變量。 嚴格意義上,變量在運行時不存在; 只有編譯器知道它們(最多,使用-g
,它可能會在調試部分放置一些元數據,例如偏移量和本地類型 - 通常在生產代碼中剝離 - 可執行文件)。 鏈接器符號表(用於全局變量)可以並且經常被剝離到嵌入式發布的ELF二進制文件中。 在運行時,您有一些數據段,可能還有一個由調用幀組成的調用堆棧(在某些插槽中保存一些局部變量,即它們的值)。 在運行時,只有位置是相關的。
(一些嵌入式處理器對其調用堆棧有嚴格的限制;其他的 RAM 或暫存器內存有限;因此了解您所針對的實際處理器和ISA並了解您擁有多少 RAM 會很有幫助)
所以有一些全局變量保持這些共享值(可能通過一些指針和數據結構間接傳遞),或者通過參數傳遞這些值(可能間接,同樣......)。
因此,如果您想共享十個字節的varFlag[10]
數組:
看起來你不想聲明uint8_t varFlag[10];
作為全局(或static
)變量。 你確定你真的不應該(這十個字節必須放在某個地方,而且它們確實消耗了一些 RAM,也許在你的調用堆棧中......)?
將varFlag
(數組,作為參數傳遞時衰減為指針)作為參數傳遞,因此也許聲明:
void ack(uint8_t indexDevice, uint8_t*flags);
並從function
調用ack(3,varFlag)
...
或聲明一個全局指針:
uint8_t*globflags;
並在將varFlag
聲明為局部變量的函數開始時設置它(使用globflags = varFlag;
),並在該函數varFlag
清除 if (使用globflags = NULL;
)。
我建議您查看編譯器生成的匯編代碼(使用GCC,您可以使用gcc -S -Os -fverbose-asm -fstack-usage
....)進行編譯。 我還強烈建議您讓同事審查您的代碼...
附注。 也許你應該使用GCC或Clang/LLVM作為交叉編譯器,也許你的IAR實際上正在使用這樣的編譯器......
您不使用全局變量的論點:
與保持較小的內存大小有關,因此局部變量在范圍結束時消失,但全局變量保留。 局部變量作為輸出發送,因此我們立即丟棄它們,因為我們不需要它們
將生命周期與范圍混淆。 無論范圍(或可見性)如何,具有靜態生命周期的變量都會永久占用內存。 具有全局作用域的變量碰巧也是靜態分配的,但其他任何靜態變量也是如此。
為了跨上下文共享變量,它必須是靜態的,因此避免使用全局變量不會節省內存。 然而,還有很多其他更有力的論據來避免全局變量,您應該閱讀 Jack Ganssle 的A Pox on Globals 。
C 支持三級作用域:
其中第二個允許變量在同一源文件中的函數之間直接可見,而外部鏈接允許在多個源文件之間直接可見。 但是,在大多數情況下您希望避免直接訪問,因為這是全局變量基本問題的根源。 你可以使用訪問器函數來做到這一點; 要使用您的示例,您可以添加一個包含以下內容的 file3.c:
#include "file3.h"
static uint8_t varFlag[10];
void setFlag( size_t n )
{
if( n < sizeof(varFlag) )
{
varFlag[n] = 1 ;
}
}
void clrFlag( size_t n )
{
if( n < sizeof(varFlag) )
{
varFlag[n] = 0 ;
}
}
uint8_t getFlag( size_t n )
{
return varFlag[n] == 0 ? 0 : 1 ;
}
帶有關聯的頭文件3.h
#if !defined FILE3_INCLUDE
#define FILE3_INCLUDE
void setFlag( size_t n ) ;
void clrFlag( size_t n ) ;
uint8_t getFlag( size_t n ) ;
#endif
file1.c 和 file2.c 包含其中,以便它們可以通過訪問器函數訪問varFlag[]
。 好處包括:
varFlag[]
不能直接訪問關鍵的是,避免使用全局變量並不能節省您的內存——數據仍然是靜態分配的——因為你不能無所事事地獲得varFlag[]
必須存在的東西,即使它不可見。 也就是說,關於內部表示的最后一點確實提供了存儲效率的潛力,因為您可以將標志表示從 uint8_t 更改為單個位標志,而無需更改數據接口或訪問訪問代碼:
#include <limits.h>
#include "file3.h"
static uint16_t varFlags ;
void setFlag( size_t n )
{
if( n < sizeof(varFlags) * CHAR_BIT )
{
varFlags |= 0x0001 << n ;
}
}
void clrFlag( size_t n )
{
if( n < sizeof(varFlags) * CHAR_BIT )
{
varFlags &= ~(0x0001 << n) ;
}
}
uint8_t getFlag( size_t n )
{
return (varFlags & (0x0001 << n)) == 0 ? 0 : 1 ;
}
還有更多的機會來生成健壯的代碼,例如,您可以只公開讀取訪問器 (getter) 並隱藏它,以便除一個翻譯單元外的所有翻譯單元都具有只讀訪問權限。
將函數放入單獨的翻譯單元並使用static
變量:
static type var_to_share = ...;
void function() {
...
}
void ack() {
...
}
請注意,我說的是翻譯單元,而不是文件。 您可以執行一些#include
魔術(以盡可能最干凈的方式)以將兩個函數定義分開。
不幸的是,你不能在 C 中。
做這種事情的唯一方法是使用組裝。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.