簡體   English   中英

如何在兩個函數之間共享變量而不用C語言將其聲明為全局變量

[英]How to share variables between two functions without declaring it a global variable in C language

我已經閱讀了很多答案,對此有很多意見,但我找不到可以回答我的問題的代碼(我找到了很多回答“如何通過聲明共享變量”的代碼) ”)

這是情況:

  • 使用嵌入式系統
  • 使用 IAR 工作台系統
  • STM32F4xx HAL 驅動程序
  • 聲明全局變量不是一個選項(編輯:與保持內存大小有關,因此局部變量在范圍結束時消失,但全局變量保留。局部變量作為輸出發送,因此我們將它們丟棄離開,因為我們不需要它們)
  • C語言
  • 如果這很重要:2 個 .c 文件和 1 個 .h 包含在兩個文件中

既然已經解決了,讓我寫一個例子。

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 ....)進行編譯。 我還強烈建議您讓同事審查您的代碼...

附注。 也許你應該使用GCCClang/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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM