【问题标题】:why can not malloc in global but can use inline function for malloc [duplicate]为什么不能在全局中进行malloc但可以对malloc使用内联函数[重复]
【发布时间】:2020-10-17 04:39:17
【问题描述】:

您好,我有一个调用 malloc 的测试代码如下:

#include <stdio.h>
#include <stdlib.h>
int *p;// = (int*)malloc(sizeof(int));
int main() {
//...
}

当然,当使用error: initializer element is not constant 编译时,这段代码会失败,我已经引用了这个问题:Malloc function (dynamic memory allocation) resulting in an error when it is used globally。他们说我们必须在函数中使用malloc()。但是,如果我将代码更改为:

#include <stdio.h>
#include <stdlib.h>
int *p;
static int inline test_inline(int *x) {
    printf("in inline function \n");
    x = (int*)malloc(sizeof(int));
    return x;
}
test_inline(p);
int main(){
//...
}

正如inline函数的定义:“内联函数是那些定义很小的函数,在发生函数调用的地方被替换。函数替换完全是编译器的选择。”所以这意味着我们可以将上面示例中的内联函数 test_inline 替换为其中的代码,这意味着我们在 global 中调用了 malloc() ?问题一:inlinemalloc() 有错吗?

问题 2:在我给出的关于 malloc function dynamic 的链接中,有一个答案说“不仅是 malloc,你不能像你在这里调用的那样调用任何函数。你只能在那里将函数声明为全局或本地函数”但我看到我们仍然可以在全局和全局中调用函数,我们不仅可以初始化如下声明:

#include <stdio.h>
#include <stdlib.h>
int b;
b = 1;
int test() {
    printf("hello");
}
test();
int main() {
//...
}

所以这意味着在全局中我们仍然可以声明、初始化和调用函数。但是当我们编译上面的代码时,它有一个警告 warning: data definition has no type or storage class 那么为什么我们有这个带有变量 b 的警告呢?我在这里看不到任何无关紧要的事情。通过test(); 行,我在main() 之外调用了一个函数,我知道这是没有意义的,因为我们从未运行test(),但我没有问题,仍然构建成功。所以回到关于malloc() 的问题1,我认为答案是“我们不能在全局中调用函数或不能初始化”,我认为这不是真的。有没有更合理的解释?

【问题讨论】:

  • malloc 是运行时操作。全局数据在编译时初始化,因此 - 在编译时malloc 没有意义。原因是编译器在创建可执行文件时会进行地址空间优化。全局数据往往存在于二进制文件的.rodata.bss.data 部分,而动态分配的内存来自堆。此外,当请求动态内存时,实际上是内核返回保留的堆空间作为系统调用的结果——所以再一次在编译时这样做是没有意义的。
  • 第二个 sn-p 编译纯属巧合。 C 编译器威胁 test();b=1; 作为声明/定义等同于 int test();int b=1; 作为旧版 C。由于这些与代码中的其他声明兼容,因此不会导致错误。把test的类型改成void就可以看到了。
  • @EugeneSh。刚刚将其放入答案中!

标签: c function malloc global inline


【解决方案1】:

请参考cmets。

#include <stdio.h>
#include <stdlib.h>
int b;
b = 1;  //this is only allowed, because the previous line is a tentative definition. [1]
int test() {
    printf("hello");
}
test();    // this is taken as a function declaration, not a function call [2]
int main() {
//...
}
  • 案例 [1]:

将代码更改为

    int b = 5; // not a tentative defintion.
    b = 1; // this assignment is not valid in file scope.

你会看到一个错误。

  • 案例 [2]:

如果函数的签名不同,您将再次看到错误。示例:尝试以下操作:

    float test( int x ) {
        printf("hello");
        return 0.5;
    }  //return changed to float, accepts an int as paramater.

    test();  //defaults to int and no parameter - conflict!!

这将产生冲突类型的错误。

因此,底线,没有赋值,函数调用 - 总而言之,没有需要在运行时执行的代码,可以放入文件范围。其背后的原因是,除非它包含在从 main() 调用的函数中,否则无法知道何时/如何执行它。

【讨论】:

  • 我已经编辑了我的代码并再次编译为您的评论,但正如您所说,它会出错。所以对于“全局调用函数,全局初始化变量”的问题,答案是“不能全局调用函数,只能调用作用域内的函数”和“只能声明和初始化变量,不能在其上分配或操作操作员”。我理解的是真的吗?
【解决方案2】:

您没有“全局”调用函数。

以你为例:

#include <stdio.h>
#include <stdlib.h>
int b;
b = 1;
int test() {
    printf("hello");
}
test();
int main() {
//...
}

在 C 类型中默认为 int。 所以线条

int b;
b = 1;

基本上是

int b;
int b = 1;

还有线条

int test() {
    printf("hello");
}
test();

只是

int test() {
    printf("hello");
}
int test(); // -> this is just a matching declaration

看看: https://godbolt.org/z/3UMQAr

(尝试将 int test() { ... 更改为 char test() { ... 会出现编译器错误,告诉您这些类型不匹配)

也就是说,你不能在那里调用函数。您的程序在运行时调用函数(尤其是malloc,它要求您的操作系统为您分配内存)。我在这里不是 C 专家,但据我所知,C 没有 constexpr 函数,这将是唯一的“例外”。

见:Compile-Time Function Execution

【讨论】:

    【解决方案3】:

    问题 1:这是关于 inline 或 malloc() 的错误
    种类:malloc 确实必须在函数中调用,但它所作用的变量可以声明为全局变量。即int *pointer = NULL;//global scope 然后pointer = malloc(someByteCount);//called within function。现在,指针仍然是全局的,但还有一个指向someByteCount 字节内存的内存地址。

    问题 2: 在 C 中,所有函数都定义在 .c 文件的同一级别,就像 main(void){...return 0}但是 所有函数(main(void) 除外)都必须在其他函数的{...} 内调用,所以简而言之,函数不能从全局空间调用。

    第二季度的插图:

    //prototypes
    void func1(void);
    void func2(void);
    void func3(void);
    
    int main(){
        int val = test_inline(p);//...
    }
    
    int main(void)
    {
        //legal
        func1();
        func2();
        func3();
        
        return 0;
    }
    //not legal
    func1();
    func2();
    func3();
    
    //definitions
    void func1(void)
    {
        return 0;
    }
    void func2(void)
    {
        return 0;
    }
    void func3(void)
    {
        return 0;
    } 
    

    您的示例语法错误(参见 cmets):

    int *p = NULL;//initialize before use
    static int inline test_inline(int *x) {
        printf("in inline function \n");
        x = (int*)malloc(sizeof(int));
        printf("%p\n", x);
        return 0;
        //return x;//function returns int, not int *
    }
    //...  test_inline(p);//must be called in a function
    int main(void){
        int val = test_inline(p);//function declaration returns int, not pointer
        return 0;
    
    }
    

    此代码可编译并运行,但正如 cmets 中所述,可能缺乏实用性。

    【讨论】:

      【解决方案4】:

      问题 1:inlinemalloc() 有问题吗?

      两者都没有。您对inline 的理解不正确。函数call 可以替换为函数definition 的内联扩展。首先,让我们修复函数定义,因为返回类型 int 与您实际返回的类型不匹配:

      static inline int *test_inline( int *x )
      {
        printf( "in inline function\n" );
        x = malloc( sizeof *x );
        return x; // x has type int *, so the return type of the function needs to be int *
      }
      

      如果你这样调用这个函数:

      int main( void )
      {
        int *foo = test_inline( foo );
        ...
      }
      

      编译器可以做的是将函数call替换为与以下等效的汇编语言:

      int main( void )
      {
        int *foo;
        do
        {
          printf( "in inline function\n" );
          int *x = malloc( sizeof *x );
          foo = x;
        } while( 0 );
        ...
      }
      

      这里没有“全局”发生。替换是在执行点(在main 函数的主体内),而不是在定义点。

      问题 2:在我给出的关于 malloc 函数动态的链接中,有一个答案说“不仅是 malloc,你不能像你在这里调用的那样调用任何函数。你只能在那里将函数声明为全局或本地函数”但我看到我们仍然可以在全局和全局中调用函数,我们不仅可以初始化如下声明:

      在代码中

      int test() {
          printf("hello");
      }
      test();
      

      test(); 行不是函数调用 - 它是(冗余且不必要的)声明。它不执行函数。

      以下是language definition 的一些摘录,以澄清其中的一些内容:

      6.2.4 对象的存储时长
      ...
      3     一个对象,其标识符声明时没有存储类说明符 _Thread_local,可以使用外部或内部链接,也可以使用存储类 说明符static,具有静态存储持续时间它的生命周期是整个执行 程序及其存储的值仅在程序启动之前初始化一次。

      加粗。在函数体之外声明的任何变量(例如您的第一个代码 sn-p 中的 p)都具有 static 存储持续时间。由于此类对象是在运行时之前初始化的,因此无法使用运行时值(例如函数调用的结果)对其进行初始化。

      6.7.4 函数说明符
      ...
      6     使用 inline 函数说明符声明的函数是内联函数。制作一个 内联函数建议尽可能快地调用函数。138)

      此类建议的有效程度由实施定义。139)
      138) 例如,通过使用通常的函数调用机制的替代方案,例如“内联” 替代''。内联替换不是文本替换,也不会创建新函数。 因此,例如,在函数体中使用的宏的扩展使用 它在函数体出现时的定义,而不是函数被调用的位置;和 标识符指的是正文出现的范围内的声明。同样,该函数具有 单个地址,不管除了外部出现的内联定义的数量 定义。

      139)例如,一个实现可能永远不会执行内联替换,或者可能只执行内联 在内联声明范围内对调用的替换

      这意味着inlined 代码的行为就像它仍然是一个单一的函数定义,即使它在整个程序的多个位置展开。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2012-09-02
        • 2015-05-21
        • 2019-06-11
        • 2014-05-06
        • 2019-12-03
        • 2019-02-17
        相关资源
        最近更新 更多