如何在一个C源代码文件中从另一个C源代码文件中调用静态函数?

16
我想从另一个C源代码文件中调用一个在一个.c文件中定义的静态函数。但是,它总是显示“使用但从未定义的函数”。
static void bt_le_start_notification(void)
{
    WPRINT_BT_APP_INFO(("bt_le_start_notification\n"));
}

ble.h文件中
static void bt_le_start_notification(void);

当我尝试在main.c中调用bt_le_start_notification()时,编译器报错,显示"bt_le_start_notification"被使用但未定义。
在main.c中。
#include "ble.h"

void application_start( void )
{
  bt_le_start_notification();
}

我有什么遗漏吗?提前谢谢。

5
static void bt_le_start_notification(void); 如果你打算从另一个 C 文件 调用它,为什么要使用 static - dxiv
4
这就像说“我把我的贵重物品锁在银行保险库里。现在我想让所有人都可以公开访问我的贵重物品。我怎样才能让全世界的人都能够访问我的保险库?但是银行不肯让我这么做。” 你为什么一开始要将它们锁起来,如果你希望每个人都可以访问它们? - Lundin
1
@GabrielStaples 所以,通过添加类似于setter/getter的函数来扩展公共API以完成这项任务。 - undefined
@Lundin,我在这里的新回答中总结了我的发现。通过使用自定义包装器和设置器/获取器来扩展API是我首选的解决方案之一,我在回答中提出了这个方案。 - undefined
显示剩余2条评论
7个回答

25
 For restricting function access from other file, the keyword static is used 

访问静态函数仅限于文件内部,除非它们被声明为public。当我们希望限制外部世界对函数的访问时,我们必须将它们设置为静态。如果您想从其他文件访问函数,则应选择全局函数即非静态函数。


为什么我在使用gcc(Xcode)包含静态函数后仍然可以访问它?!但如果我使用inline,我就不能访问它。这是gcc或XCode的问题吗? - TomSawyer
它不仅仅是具有外部链接的“全局函数”。 - undefined

15

我同意Frodo和ANBU.SANKAR的看法。 如果你想在文件外调用静态函数,可以使用以下示例。

1.c

extern (*func)();
int main(){
(func)();
return 0;}

2.c

static void call1(){
printf("a \n");
}
(*func)() = &call1;

9
当编写自动化单元测试时,有时需要打破封装性,这是合理的做法。 - user7610
1
吹毛求疵:最好不要在C语言中使用()参数列表声明函数。与C++不同,这与(void)并不相同。 - undefined

9

静态函数具有内部链接,只能由同一文件中编写的函数调用。但是如果您想从另一个文件调用静态函数,我们在C语言中有一个技巧。请按照以下步骤操作: 1. 在 ble.c 中全局创建一个函数指针并定义它。

(void)(*fn_ptr)();
static void bt_le_start_notification(void)
{
    WPRINT_BT_APP_INFO(("bt_le_start_notification\n"));
    fn_ptr=bt_le_start_notification;

}

在 main.c 中,extern 函数指针。
 #include "ble.h"
extern fn_ptr;

void application_start( void )
{
  fn_ptr();
}

希望这对您有所帮助。


1
不必要的复杂性。如果一个函数不应该是翻译单元内部的,则不应将其定义为“static”。 - Michael Foukarakis
1
@MichaelFoukarakis 有些情况下,客户的专有代码应该被保留,并且应该在使用其代码的基础上展示改进,这就是您需要在另一个源文件中使用静态函数的地方,这就是 Vivek 技巧发挥作用的地方。 - VRU
我同意这两个评论,并且都点赞了。@VRU,我有类似的需求。请看我在这里的评论 - undefined

2
静态函数作用域是定义它的文件(即翻译单元)。

2
关键字static经常用于封装函数在定义的源文件中。因此,它不是用于从外部调用静态函数,即其他c文件。一个外部article[^]非常好地解释了这个话题。

引用:

静态函数很像Java或C++中的私有方法。私有方法是只由类使用且无法在外部使用的方法。在C中,我们可以声明一个静态函数。静态函数是只能在声明它的源文件中使用的函数。

因此,如果需要从外部调用该函数,则不将函数定义为static


1
你收到这个消息是因为你声明了该函数为静态的。因此,实现仅在你的 .c 文件内可见。尝试从你的 .h 和 .c 中删除 static,这应该允许你的函数被看到。

-1
如何从一个C源代码文件中调用另一个C源代码文件中的静态函数?
摘要:
1. 你不应该从另一个源文件中调用一个静态函数。静态意味着“我不应该在其他地方使用”。所以,如果你真的需要从另一个源文件中使用该函数或变量,请从其声明中删除static。 - 请参阅下面“详细信息”部分的顶部。
2. 但是,如果你真的需要(例如:用于单元测试,或者在对第三方库进行最小更改的同时修改它),请参阅下面标题为“强制在另一个文件中包含静态函数或变量的技术”的部分。
此答案还回答了以下问题:“何时在C或C++中使用extern?”以及“C和C++中的extern vs static”。
首先,通常情况下,你不应该在另一个文件中使用静态函数或变量。如果你有一个在其他源文件中使用的函数,它应该是非静态的,并且在头文件中声明,然后在需要它的源文件中包含该头文件。这是正确的设计模式。然后在使用该函数的每个源文件中包含该头文件。有关此示例,请参阅下面的“#include vs 前向声明”部分。
如果你有一个只在一个文件中使用的函数或变量,它应该被声明为静态,这样它就不会在该文件之外可见,无论如何。静态关键字给函数或变量提供了内部链接或者俗称的“文件作用域”,这意味着它只在声明它的文件内可见。
这些答案也是这样说的。
  • @Anbu.Sankar
  • @vivek thantho
  • @Umamahesh P
  • @Frodo
  • @Teodorico Levoff
  • 但是,我可以想到两种情况,你可能需要在另一个文件中包含一个static函数:

    你正在尝试对一个.c源文件中的私有静态函数进行单元测试。
    你正在尝试增强一些第三方库的代码,同时对该代码进行零或最小限度的更改。当第三方库代码处于活跃开发状态时,希望对第三方库代码进行零或最小限度的更改,这样你就可以轻松升级到未来版本的代码,而不必解开你对其副本所做的更改。以我的情况为例,来自我的评论
    我现在就处于这种情况。情况是我需要增强一些FreeRTOS内核代码以注入一些额外的调试功能。然而,为了使我的代码模块化并且不依赖于FreeRTOS内核,以便我可以轻松升级FreeRTOS版本,我需要从定义了该静态C函数的文件之外访问FreeRTOS [tasks.c内核文件]中的一个私有静态C函数。因此,我认为在尝试增强第三方库的代码时,但又不触及(修改)其源代码时,这种情况最常见。
    对于在.c文件中定义的私有静态函数进行单元测试是另一个合理的用例。
    所以,如果你真的需要在另一个文件中调用一个静态函数,这里有一些方法。
    背景知识:
    首先,一些基本知识:
    1. extern vs static
    关键字extern和static是彼此的对立面。extern表示“这个变量或函数在另一个文件中定义”,而static表示“这个变量或函数只在这个作用域内可见”。通常,静态变量的作用域在它所在的花括号{}内(通常是一个函数),但如果静态变量在所有函数之外定义,则它是一个静态全局变量,其作用域是整个文件。如果一个函数或变量是非静态的,则其作用域是整个程序中的所有文件,只要它们满足以下条件之一:
    将每个声明的头文件包含在其中,或者在要使用它们的文件中提前声明函数或变量。
    没有`static`的函数默认为`extern`,所以你不需要在它们前面显式地写`extern`。但是,如果你愿意的话,你可以这样做:
    // explicitly `extern`
    extern void foo(void);
    
    // same thing, but implicitly `extern`
    void foo(void);
    

    所以,在头文件中,当你写一个函数声明时,你不需要在它前面写上extern,因为它默认就是extern
    另一方面,变量默认情况下不是extern,所以如果你想让它们成为extern,你需要明确地在它们前面写上extern
    uint32_t u32;  // this statically allocates a new variable named `u32`, because
                   // `extern` is NOT implied by default
    
    extern uint32_t u32;  // this tells the compiler that `u32` is allocated and 
                          // defined in another file, but needs to be accessible 
                          // inside this file
    

    foo.c中的静态全局函数或变量:
    // this function is only visible inside this file since it is `static`
    static void foo(void) {
        // your code here
    }
    
    // this global variable is only visible inside this file since it is `static`
    static uint32_t u32;
    

    2. #include与前向声明
    假设您有以下源文件。您想在该文件之外使用函数foo()和变量u32:

    foo.c:

    // this function can be used **outside** this file, if forward declared in 
    // the file where it will be used, before it is used, since it is NOT `static`
    void foo(void) {
        // your code here
    }
    
    // this global variable can be used and shared **outside** this file, if 
    // forward declared in the file where it will be used, before it is used, 
    // since it is NOT `static`
    uint32_t u32;
    

    要访问另一个文件中定义的非静态函数或变量,你有两个选项:
    1. 在你想要使用它的文件中,像这样提前声明它的存在: main.c:
    // 函数`foo`的前向声明,这样你就可以在这个文件中使用它。 // - 记住:对于函数,`extern`是自动隐含的! void foo(); // 同样的事情,但是显式地使用`extern` extern void foo();
    // 变量`u32`的前向声明,在另一个文件中定义,所以你可以在这个文件中使用它。 // - 记住:对于变量,`extern`不是自动隐含的,所以你必须在这里**显式地**写上`extern`。 extern uint32_t u32;
    // 现在你可以在这个文件中调用foo(),并且使用来自另一个文件的变量`u32`。 int main() { foo(); printf("u32 = %u\n", u32); u32 = 123;
    return 0; }
    2. 使用`#include "foo.h"`语句更好: 然而,一种更常规和推荐的方法是将前向声明放入一个`.h`头文件中,然后在需要访问这些函数和变量的地方包含该头文件。
    这是一个示例`foo.h`头文件来实现这一点: foo.h:
    #pragma once // 前向声明在foo.c中定义的`foo()`函数;对于函数,`extern`在这里是自动隐含的,所以你不必写它。 void foo();
    // 前向声明在`foo.c`中分配和定义的`uint32_t u32`变量的存在。 extern uint32_t u32;
    然后,你只需要包含这个头文件,这些前向声明就会在编译时由预处理器复制到你的文件中: main.c:
    #include "foo.h"
    // 现在你可以在这个文件中调用foo(),并且像之前手动写前向声明一样使用来自另一个文件的变量`u32`! int main() { foo(); printf("u32 = %u\n", u32); u32 = 123;
    return 0; }

    评论/总结

    所以,这些都可以使用:

    main.c:

    // For both functions and variables
    #include "foo.h"      // most-often used / most-recommended
    
    // or, for functions:
    void foo();           // forward declaration with implicit `extern`
    // or
    extern void foo();    // forward declaration with optional explicit `extern`
    
    // or, for variables:
    extern uint32_t u32;  // forward declaration with **required** explicit `extern`
    
    
    // Now you can call foo() in this file, and also use variable `u32` from the 
    // other file.
    int main()
    {
        foo();
        printf("u32 = %u\n", u32);
        u32 = 123;
    
        return 0;
    }
    

    如果您尝试从另一个文件调用一个静态函数或变量,将会出现以下错误示例:
    在Ubuntu 11.4.0-1ubuntu1~22.04上使用gcc --version进行了测试。要在Windows上运行这些gcc构建命令,请使用MSYS2终端。请参阅我的完整设置说明:从头开始安装和设置MSYS2,包括将所有7个配置文件添加到Windows终端
    这对于帮助您理解在您自己的代码中遇到此问题时非常有帮助:
    如果您尝试从另一个文件进行前向声明或包含一个静态函数或变量,将会出现以下构建(链接器,ld)错误的示例:
    undefined reference to `counter'
    undefined reference to `print_incrementing_number'
    

    链接步骤是构建过程的最后一步,它尝试在已编译的目标文件.o中找到并匹配函数声明与其已编译的定义。如果一个函数或变量被构建为static,它具有"静态链接",这意味着它在定义的文件之外是不可见的,因此链接器无法找到它,从而导致出现此错误。
    链接器(ld程序)无法找到我的print_incrementing_number()函数和uint32_t counter变量。如果您想尝试运行命令,请查看我eRCaGuy_hello_world存储库中的提交11430a3cb3b298f26d3763e4a7224a7d610751c1C语言完整构建错误:
    eRCaGuy_hello_world/c$ gcc -Wall -Wextra -Werror -O3 -std=gnu17 static_extern_function_include__main.c static_extern_function_include__module.c -o bin/a && bin/a
    /usr/bin/ld: /tmp/ccKPOHqx.o: warning: relocation against `counter' in read-only section `.text.startup'
    /usr/bin/ld: /tmp/ccKPOHqx.o: in function `main':
    static_extern_function_include__main.c:(.text.startup+0x1b): undefined reference to `print_incrementing_number'
    /usr/bin/ld: static_extern_function_include__main.c:(.text.startup+0x21): undefined reference to `counter'
    /usr/bin/ld: static_extern_function_include__main.c:(.text.startup+0x37): undefined reference to `print_incrementing_number'
    /usr/bin/ld: static_extern_function_include__main.c:(.text.startup+0x3d): undefined reference to `counter'
    /usr/bin/ld: static_extern_function_include__main.c:(.text.startup+0x53): undefined reference to `print_incrementing_number'
    /usr/bin/ld: static_extern_function_include__main.c:(.text.startup+0x59): undefined reference to `counter'
    /usr/bin/ld: warning: creating DT_TEXTREL in a PIE
    collect2: error: ld returned 1 exit status
    

    完整的C++构建错误:
    eRCaGuy_hello_world/c$ g++ -Wall -Wextra -Werror -O3 -std=gnu++17 static_extern_function_include__main.c static_extern_function_include__module.c -o bin/a && bin/a
    /usr/bin/ld: /tmp/ccAn3aKq.o: warning: relocation against `counter' in read-only section `.text.startup'
    /usr/bin/ld: /tmp/ccAn3aKq.o: in function `main':
    static_extern_function_include__main.c:(.text.startup+0x19): undefined reference to `print_incrementing_number()'
    /usr/bin/ld: static_extern_function_include__main.c:(.text.startup+0x1f): undefined reference to `counter'
    /usr/bin/ld: static_extern_function_include__main.c:(.text.startup+0x33): undefined reference to `print_incrementing_number()'
    /usr/bin/ld: static_extern_function_include__main.c:(.text.startup+0x39): undefined reference to `counter'
    /usr/bin/ld: static_extern_function_include__main.c:(.text.startup+0x4d): undefined reference to `print_incrementing_number()'
    /usr/bin/ld: static_extern_function_include__main.c:(.text.startup+0x53): undefined reference to `counter'
    /usr/bin/ld: warning: creating DT_TEXTREL in a PIE
    collect2: error: ld returned 1 exit status
    

    这是产生错误的源代码:

    static_extern_function_include__module.c:

    #include <stdbool.h> // For `true` (`1`) and `false` (`0`) macros in C
    #include <stdint.h>  // For `uint8_t`, `int8_t`, etc.
    #include <stdio.h>   // For `printf()`
    
    static uint32_t counter = 0;
    
    static void print_incrementing_number()
    {
        printf("counter = %u\n", counter);
        counter++;
    }
    
    void foo()
    {
        print_incrementing_number();
    }
    

    static_extern_function_include__main.c:

    #include <stdbool.h> // For `true` (`1`) and `false` (`0`) macros in C
    #include <stdint.h>  // For `uint8_t`, `int8_t`, etc.
    #include <stdio.h>   // For `printf()`
    
    void print_incrementing_number(); // forward function declaration
    extern uint32_t counter;          // forward variable declaration
    
    // int main(int argc, char *argv[])  // alternative prototype
    int main()
    {
        printf("Hello World.\n");
    
        print_incrementing_number();
        printf("  counter = %u\n", counter);
        print_incrementing_number();
        printf("  counter = %u\n", counter);
        print_incrementing_number();
        printf("  counter = %u\n", counter);
    
        return 0;
    }
    

    构建命令:
    # in C
    gcc -Wall -Wextra -Werror -O3 -std=gnu17 static_extern_function_include__main.c static_extern_function_include__module.c -o bin/a && bin/a
    
    # in C++
    g++ -Wall -Wextra -Werror -O3 -std=gnu++17 static_extern_function_include__main.c static_extern_function_include__module.c -o bin/a && bin/a
    

    修复方法:强制将static函数或变量包含在另一个文件中的技巧

    1. 可能的解决方案:

    1. [Preferred choice if you intend the function and variable to be used elsewhere] remove the static keyword from the .c file:

      Go to static_extern_function_include__module.c and remove the static keyword from the function and variable declarations, so you have this:

      uint32_t counter = 0;
      
      void print_incrementing_number()
      {
          printf("counter = %u\n", counter);
          counter++;
      }
      

      Now it builds and runs. Here is my command and output:

      eRCaGuy_hello_world/c$ gcc -Wall -Wextra -Werror -O3 -std=gnu17 static_extern_function_include__main.c static_extern_function_include__module.c -o bin/a && bin/a
      Hello World.
      counter = 0
        counter = 1
      counter = 1
        counter = 2
      counter = 2
        counter = 3
      

      The build commands for all other techniques below are exactly the same as just above unless stated otherwise.

    2. [My preferred choice for enhancing a 3rd-party library] write a non-static wrapper function in the bottom of their .c file for access to static functions, and setter and getter functions for access to their static variables:

      At the bottom of their .c file, add your custom wrappers and setters/getters, like this:

      static_extern_function_include__module.c:

      // ------- their original code left as-is -------
      
      static uint32_t counter = 0;
      
      static void print_incrementing_number()
      {
          printf("counter = %u\n", counter);
          counter++;
      }
      
      void foo()
      {
          print_incrementing_number();
          // a bunch of other library code here
      }
      
      // ------- your custom wrappers and setters/getters -------
      
      void print_incrementing_number_wrapper()
      {
          print_incrementing_number();
      }
      
      void set_counter(uint32_t new_counter)
      {
          counter = new_counter;
      }
      
      uint32_t get_counter()
      {
          return counter;
      }
      

      In your .c file, you can now forward declare and use these custom wrappers and setters/getters.

      Even better, make a wrapper .h file too:

      static_extern_function_include__module_wrapper.h:

      #pragma once
      
      #include <stdint.h>  // For `uint8_t`, `int8_t`, etc.
      
      void print_incrementing_number_wrapper();
      void set_counter(uint32_t new_counter);
      uint32_t get_counter();
      

      And in your .c file, include and use the wrapper header:

      #include "static_extern_function_include__module_wrapper.h"
      
      #include <stdbool.h> // For `true` (`1`) and `false` (`0`) macros in C
      #include <stdint.h>  // For `uint8_t`, `int8_t`, etc.
      #include <stdio.h>   // For `printf()`
      
      // Using the wrapper
      int main()
      {
          printf("Hello World.\n");
      
          print_incrementing_number_wrapper();
          printf("  counter = %u\n", get_counter());
          print_incrementing_number_wrapper();
          printf("  counter = %u\n", get_counter());
          print_incrementing_number_wrapper();
          printf("  counter = %u\n", get_counter());
      
          return 0;
      }
      
    3. Add non-static pointers to their functions and variables in the bottom of their .c file:

      Instead of adding wrapper functions and setters/getters, you can use non-static pointer variables instead:

      static_extern_function_include__module.c:

      // ------- their original code left as-is -------
      
      static uint32_t counter = 0;
      
      static void print_incrementing_number()
      {
          printf("counter = %u\n", counter);
          counter++;
      }
      
      void foo()
      {
          print_incrementing_number();
          // a bunch of other library code here
      }
      
      // ------- your custom, non-static pointer variables -------
      
      typedef void (*void_void_func_ptr_t)(void);
      
      void_void_func_ptr_t print_incrementing_number_ptr = 
          print_incrementing_number;
      
      uint32_t *counter_ptr = &counter;
      

      Now, forward declare these extern pointer variables in your .c file, or make a header file like this:

      static_extern_function_include__module_ptrs.h:

      #pragma once
      
      #include <stdint.h>  // For `uint8_t`, `int8_t`, etc.
      
      typedef void (*void_void_func_ptr_t)(void);
      
      // forward pointer variable declarations
      extern void_void_func_ptr_t print_incrementing_number_ptr;
      extern uint32_t *counter_ptr;
      

      And in your .c file, include and use the pointer header:

      #include "static_extern_function_include__module_ptrs.h"
      
      #include <stdbool.h> // For `true` (`1`) and `false` (`0`) macros in C
      #include <stdint.h>  // For `uint8_t`, `int8_t`, etc.
      #include <stdio.h>   // For `printf()`
      
      // Using the pointers
      int main()
      {
          printf("Hello World.\n");
      
          print_incrementing_number_ptr();
          printf("  counter = %u\n", *counter_ptr);
          print_incrementing_number_ptr();
          printf("  counter = %u\n", *counter_ptr);
          print_incrementing_number_ptr();
          printf("  counter = %u\n", *counter_ptr);
      
          return 0;
      }
      
    4. [Least intrusive / best to minimize changes to someone else's library files] #include your custom .h or .c file in the bottom of their .c file

      If you want to keep the bottom of their .c file as clean as possible, then you can do that by adding a single #include statement at the bottom of their .c file, like this. Note that the extension doesn't really matter. You can use any extension you want. Here, I use _extension.c to indicate I am extending this module with my own customizations:

      // ------- their original code left as-is -------
      // ...
      
      // ------- your custom include line at the very bottom -------
      // Including a `.c` file is a bit unconventional, but it works just fine.
      // You can think of the pre-processor as a type of code generator since
      // it just copies and pastes stuff around.
      #include "static_extern_function_include__module_extensions.c"
      

      Now, put the necessary wrappers or pointers from the examples above into this extension file included above. Here, I'll use the wrapper functions and setters/getters from the 2nd example above:

      static_extern_function_include__module_extensions.c:

      void print_incrementing_number_wrapper()
      {
          print_incrementing_number();
      }
      
      void set_counter(uint32_t new_counter)
      {
          counter = new_counter;
      }
      
      uint32_t get_counter()
      {
          return counter;
      }
      

      Now in your main .c file, just do what you did for the 2nd example above: include and use the wrapper header again, like normal, like this:

      #include "static_extern_function_include__module_wrapper.h"
      
      #include <stdbool.h> // For `true` (`1`) and `false` (`0`) macros in C
      #include <stdint.h>  // For `uint8_t`, `int8_t`, etc.
      #include <stdio.h>   // For `printf()`
      
      // Using the wrapper
      int main()
      {
          printf("Hello World.\n");
      
          print_incrementing_number_wrapper();
          printf("  counter = %u\n", get_counter());
          print_incrementing_number_wrapper();
          printf("  counter = %u\n", get_counter());
          print_incrementing_number_wrapper();
          printf("  counter = %u\n", get_counter());
      
          return 0;
      }
      

      Note: as of later versions of FreeRTOS, this is now possible! They have even provided "hooks" to do this right inside their main library code! See here for example, in V10.6.1 in the bottom of tasks.c: https://github.com/FreeRTOS/FreeRTOS-Kernel/blob/V10.6.1/tasks.c#L5514-L5534:

      /* Code below here allows additional code to be inserted into this source file,
      * especially where access to file scope functions and data is needed (for example
      * when performing module tests). */
      
      #ifdef FREERTOS_MODULE_TEST
          #include "tasks_test_access_functions.h"
      #endif
      
      
      #if ( configINCLUDE_FREERTOS_TASK_C_ADDITIONS_H == 1 )
      
          #include "freertos_tasks_c_additions.h"
      
          #ifdef FREERTOS_TASKS_C_ADDITIONS_INIT
              static void freertos_tasks_c_additions_init( void )
              {
                  FREERTOS_TASKS_C_ADDITIONS_INIT();
              }
          #endif
      
      #endif /* if ( configINCLUDE_FREERTOS_TASK_C_ADDITIONS_H == 1 ) */
      

      I'll be using that now. Older versions of FreeRTOS did not have this. Search the FreeRTOS code also for configINCLUDE_FREERTOS_TASK_C_ADDITIONS_H. And, see this forum discussion: https://www.freertos.org/FreeRTOS_Support_Forum_Archive/January_2019/freertos_Retrieve_the_size_and_maximum_usage_of_the_stack_per_task_7ab5c6eb05j.html.

      FreeRTOS is doing exactly what I am explaining just above. If you do #define configINCLUDE_FREERTOS_TASK_C_ADDITIONS_H 1 in your custom FreeRTOSConfig.h file, then they are including your custom freertos_tasks_c_additions.h file at the bottom of their private tasks.c file. This way, your file gets access to all of the private, static functions and variables inside the kernel's tasks.c, thereby allowing you to inject your customizations into the FreeRTOS kernel without editing any kernel source code files. Brilliant!

      See also my detailed answer here: FreeRTOS: obtain the stack size (usStackDepth) value in words or bytes after calling xTaskCreate().

    5. [My preference for unit testing private static C code] #include the entire .c file of interest in the top of your .c file:

      To unit test or get access to a ton of private static functions and variables from a .c file, just include the .c file near the top of your .c file.

      The pre-processor will then copy/paste that entire included file into the top of your file. Imagine that has happened, and write your code from there.

      You can access all of the private static data in your .c file because it is in your .c file now.

      I won't show an explicit example, for brevity, but you get the point.

    2. 替代方案:

    以下是一些其他可供考虑的替代方案:

    1. static代码复制到您自己的函数中,放在您自己的文件中。

      例如:只需复制粘贴即可。然而,我通常不建议这样做,因为维护重复的代码是一种浪费时间的可怕行为,而且容易出现不同步的情况。

      如果函数只有3行且只有一个函数,那么可以这样做。但如果有4行或2个函数,最好选择上述其他选项之一,以避免重复的代码。

    2. 向他们的库提交一个拉取请求,将感兴趣的函数或变量设置为非static

      虽然这是一个很好的主意,但要注意,等待上游开发人员接受您的拉取请求可能需要几周、几个月甚至几年,或者永远也不会接受。因此,在此期间,您可以选择上述选项之一来完成自己的工作。

    3. 编写一个Bash或Python脚本,在编译时自动从他们的.c文件中删除static关键字,从而应用一个编译时补丁。

      这实际上是一个非常有效的解决方案,有时也是最佳选择。例如,您可以:

      1. 手动进行必要的更改,然后生成一个类似于git diff.patch文件,在构建时应用该文件,或者
      2. 编写一个自定义的查找和替换脚本,为您执行一些简单的更改,例如从您需要访问的变量和函数中删除static

      如果选择后一种选项,我建议您的构建系统首先复制该文件,并让构建脚本修改这个未跟踪的副本。这样,您的原始文件不会被修改,每次构建时git statusgit diff也不会变得“脏”(有未提交的更改)。

    另请参阅

    1. 什么时候在C++中使用extern

    2. 关于"翻译单元"是什么的评论:为什么静态变量对其他文件可见?

      即预处理后的源文件(包括所有已包含的文件)。- HolyBlackCat

      还有:

      我还建议你了解翻译单元,这是构建C++代码的核心概念。编译器只能处理翻译单元,它基本上是一个包含所有已包含的头文件(或源文件,在你的情况下)的单个源文件。- Some programmer dude

    3. 我的回答:FreeRTOS:在调用xTaskCreate()后获取堆栈大小(usStackDepth)的值(以字或字节为单位)


    网页内容由stack overflow 提供, 点击上面的
    可以查看英文原文,
    原文链接