C - 为什么不能在全局中使用 malloc,但可以为 malloc 使用内联函数



嗨,我有一个调用malloc的测试代码,如下所示:

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

当然,当使用error: initializer element is not constant编译时,这段代码会失败,我已经参考了这个问题:Malloc函数(动态内存分配)在全局使用时会导致错误。他们说我们必须在函数方面使用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的内联函数,这意味着我们在全局中调用了 malloc() ?问题1:这是错误的inline还是malloc()

问题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 的警告呢?我在这里没有看到任何无关紧要的事情。有了我在main()之外调用函数test();行,我知道这毫无意义,因为我们从不运行test()但我没有问题,重要的是建立成功。所以回到关于malloc()的问题1,我认为用"我们不能在全局中调用函数或者不能初始化"的答案,我认为这是不正确的。还有比这更合理的解释吗?

请参考评论。

#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()调用的函数中,否则无法知道何时/如何执行它。

你不是在"全局"调用函数。

以你为例:

#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函数,这将是唯一的"例外"。

请参见: 编译时函数执行

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

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

第 2 季度的插图:

//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;
} 

示例语法错误(请参阅注释):

int *p = NULL;//initialize before use
static int inline test_inline(int *x) {
printf("in inline function n");
x = (int*)malloc(sizeof(int));
printf("%pn", 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;
}

此代码可以编译并运行,但如注释中所述,可能缺乏有用性。

问题 1:这是错误的inline还是malloc()

也不。 你对inline的理解是不正确的。 函数调用可以替换为函数定义的内联扩展。 首先,让我们修复函数定义,因为返回类型int与您实际返回的类型不匹配:

static inline int *test_inline( int *x )
{
printf( "in inline functionn" );
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 );
...
}

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

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

这里没有"全球"发生任何事情。 替换是在执行点(在main函数的主体内),而不是在定义点。

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

在代码中

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

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

以下是语言定义的一些摘录,以澄清其中的一些内容:

6.2.4 物品的存储持续时间
...
     3 声明其标识符而不使用存储类说明符的对象_Thread_local,并且具有外部或内部链接或存储类 说明符static,具有静态存储持续时间它的生存期是 程序及其存储的值仅在程序启动之前初始化一次。

大胆补充道。 在函数主体外部声明的任何变量(例如第一个代码片段中的p变量)都有static存储持续时间。 由于此类对象是在运行时之前初始化的,因此无法使用运行时值(例如函数调用的结果)对其进行初始化。

6.7.4 函数说明符

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

这些建议在多大程度上有效,由执行决定。139)
138)例如,通过使用通常函数调用机制的替代方法,例如"内联" 替代''。内联替换不是文本替换,也不会创建新函数。 因此,例如,函数主体中使用的宏的扩展使用 它在函数体出现时的定义,而不是调用函数的位置;和 标识符是指正文所在的范围内的声明。同样,该函数具有 单个地址,无论除外部定义之外出现的内联定义数量如何 定义。

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

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

最新更新