【问题标题】:Dynamic Memory Allocation with Static Pointer使用静态指针进行动态内存分配
【发布时间】:2017-06-18 08:45:26
【问题描述】:

有人可以向我解释一下为什么下面的代码是这样工作的。这里我已经将outd 初始化为文件code2.c 中的静态指针。然后我用malloc动态分配内存给它。从单独文件code1.c 中的主函数一次又一次地调用它,它看起来整个数组以静态方式运行,因为它保留了从一个函数调用到另一个函数调用的所有值,即使分配了数组的内存动态的。我期待像分段错误这样的事情。

code2.c

#include <stdio.h>
#include <stdlib.h>

static double *outd;

void init0(int len)
{
    int i;
    outd=malloc(sizeof(double)*len);
    for (i=0; i<len; i++)
    {
       outd[i]=0.0;
    }
}

void showarray(int len, int iterno)
{
    int i;
    printf("iteration %d, array is \n",iterno);
    for (i=0; i<len; i++)
    {
        outd[i]=outd[i]+iterno;
        printf("%.2f ",outd[i]);
    }
    printf("\n");
}

void deletearray()
{
    free(outd);
}

code1.c

#include <stdio.h>
#include <stdlib.h>

void init0(int len);
void showarray(int len, int iterno);
void deletearray();

int main(int argc,char* argv[])
{
    int i, len;
    len=5;

    init0(len);

    for (i=0; i<7; i++)
    {
        showarray(len,i);
    }

    deletearray();
}

编译,

$(CC) -c -O2 code1.c 
$(CC) -c -O2 code2.c 
$(CC) -o bb5 code1.o code2.o -lm

跑步

localhost:testfft avinash$ ./bb5
iteration 0, array is 
0.00 0.00 0.00 0.00 0.00 
iteration 1, array is 
1.00 1.00 1.00 1.00 1.00 
iteration 2, array is 
3.00 3.00 3.00 3.00 3.00 
iteration 3, array is 
6.00 6.00 6.00 6.00 6.00 
iteration 4, array is 
10.00 10.00 10.00 10.00 10.00 
iteration 5, array is 
15.00 15.00 15.00 15.00 15.00 
iteration 6, array is 
21.00 21.00 21.00 21.00 21.00 

【问题讨论】:

  • 你 malloc 一次,inti 一次(幸运的值,尽管没有初始化内容),释放一次并在循环中对数组内容做一些计数。除了计数之外,您希望数组何时发生变化?而且您只能从有权访问 outd 的文件进行访问,该文件是文件全局的,当然会保留正确分配的内存的地址。
  • 哦,是的,对不起,我忘了初始化内容。我预计会有一些冲突,因为数组被声明为静态但内存是动态分配的。抱歉,我不太明白。
  • 通过用简单的static int 替换指针来简化(当然不要取消引用它)。如果您以类似方式访问(初始化一次,打印时计数),您将看到它的行为方式相同。
  • allocated 对象的 lifetime 是明确的:直到你 free() it
  • @Yunnosch 不,实际上完全一样(静态存储),只是标识符的范围不同。

标签: c arrays pointers static malloc


【解决方案1】:

请在 cmets 和其他答案中找到技术答案。
我提供了代码(基于您的代码)来说明这些很好的解释。

为了实现该说明,我使用了各种静态(两种)、局部变量和全局变量。
(为了简单起见,我使用整数而不是指针。
差异可能是您突出困惑的答案的一部分,仅在于缓冲区是否已更改。关于发生了什么以及没有发生什么与此相关,有一些 cmets。)

code2.c:

#include <stdio.h>
#include <stdlib.h>

// defining a pointer which is only visible from this file,
// to later be initialised to the return value of malloc()
static double *outd;

// defining an int which is only visible from this file,
// to later be initialised to some integer value
static    int  outI;

// define an int which is only visible from this file,
// but has an identically named twin in the other file
// (there be dragons)
static    int unknownTwinI;

// defining a global int which is visible from main()
          int  globalI;

void init0(int len)
{
    int i;

    // initialise the pointer
    // to the address of a buffer reserved for use via this pointer
    outd=malloc(sizeof(double)*len);

    // initialise the memory inside that buffer
    for (i=0; i<len; i++)
    {
       outd[i]=0.0;
    }

    // initialise the int to a value
    outI = 0;

    // initialise the global int to a value
    globalI = 5;

    // initialise one of the two twins
    unknownTwinI = 6;
}

// show the content of the buffer referenced by the pointer
// and the value of the integer
void showarray(int len, int iterno)
{
    int i;

    // make a function local, non-static integer
           int locI =0; // this init happens every time the functin is executed
    // make a function-local, but static integer
    static int inI  =0; // this init happens before the first execution of the function

    printf("iteration %d, array is \n",iterno);
    for (i=0; i<len; i++)
    {
        outd[i]=outd[i]+iterno; // "counting up" array contents
        printf("%.2f ",outd[i]);// show
    }
    outI   = outI  + iterno;    // "counting up" file-local integer value
    printf(" outI:%d", outI);   // show

    inI    = inI   + iterno;    // "counting up" the function-local static integer
    printf(" inI:%d", inI);     // show

    locI   = locI  + iterno;    // "single increase" the function-local integer
    printf(" locI:%d", locI);   // show  

    globalI   = globalI  + iterno;    // "single increase" the function-local integer
    printf(" globalI:%d", globalI);    // show

    unknownTwinI   = unknownTwinI  + iterno;    // "single increase" the function-local integer
    printf(" unknownTwinI:%d", unknownTwinI);    // show

    // Note that nothing here frees the buffer
    // or changes the pointer (which would be highly questionable, thinking of the free() later

    printf("\n");


}

void deletearray()
{
    free(outd);
}

code1.c:

#include <stdio.h>
#include <stdlib.h>

void init0(int len);
void showarray(int len, int iterno);
void deletearray();

// declare the global integer, which is defined in code2.cabs
// (should be in a header.h, 
//  excuse me for taking a shortcut for only having two files to post)
extern int globalI;

// attempt to similarly declare some of the identifiers which cannot be accessed
extern double *outd;
extern    int  outI;
extern    int inI;
extern    int locI;

// define an int which is only visible from this file,
// but has an identically named twin in the other file
// (there be dragons)
static    int unknownTwinI;

int main(int argc,char* argv[])
{
    int i, len;
    len=5;

    // exception of an init outside of init0(),
    // this one targets the twin in THIS file here
    unknownTwinI =0;

    // pointer gets address, buffer gets values
    // integers (those not static to showarray) get values
    init0(len);

    for (i=0; i<7; i++)
    {
        // all kinds of counting gets done
        // (only during the first execution
        //  the local static int initially has the init value)
        showarray(len,i);

        // demonstrating that the global integer is accessable
        globalI = globalI * 2;

        // the showarray outputs the value of the twin in the other file,
        // attempting to resist/undo the cumulative changes done there
        unknownTwinI =0;
        // (resistance is futile)

        // these are forbidden accesses,
        //  with the warnings you get WITHOUT trying to declare them

        // outd=NULL; // 'outd' undeclared (first use in this function)
        // outI=0;    // 'outI' undeclared (first use in this function)
        // inI = 0;   // 'inI' undeclared (first use in this function)
        // locI =0;   // 'locI' undeclared (first use in this function)



        // these are the forbidden accesses again,
        //   with the warnings you get WITH trying to declare them

        // outd=NULL; // undefined reference to `outd'
        // outI=0;    // undefined reference to `outI'
        // inI = 0;   // undefined reference to `inI'
        // locI =0;   // undefined reference to `locI'
    }

    deletearray();

    return 0;
}

输出:

iteration 0, array is
0.00 0.00 0.00 0.00 0.00  outI:0 inI:0 locI:0 globalI:5 unknownTwinI:6
iteration 1, array is
1.00 1.00 1.00 1.00 1.00  outI:1 inI:1 locI:1 globalI:11 unknownTwinI:7
iteration 2, array is
3.00 3.00 3.00 3.00 3.00  outI:3 inI:3 locI:2 globalI:24 unknownTwinI:9
iteration 3, array is
6.00 6.00 6.00 6.00 6.00  outI:6 inI:6 locI:3 globalI:51 unknownTwinI:12
iteration 4, array is
10.00 10.00 10.00 10.00 10.00  outI:10 inI:10 locI:4 globalI:106 unknownTwinI:16
iteration 5, array is
15.00 15.00 15.00 15.00 15.00  outI:15 inI:15 locI:5 globalI:217 unknownTwinI:21
iteration 6, array is
21.00 21.00 21.00 21.00 21.00  outI:21 inI:21 locI:6 globalI:440 unknownTwinI:27

【讨论】:

    【解决方案2】:

    Yunnosch 的回答通过示例很好地解释了不同声明的效果,但我想从语言规范中添加一些背景知识,因为我认为这有助于理解很多。

    • C 中的标识符有作用域。范围确定此标识符引用与其关联的对象的区域。在 C 中,标识符的作用域是花括号 ({ ... })。

      if (1) {
          int i = 2;
          // i refers to an object holding the value 2 here
      }
      // i doesn't refer to any object
      

      任何大括号外的标识符都有文件范围,它们引用整个源文件中的对象。

    • C 中的对象可以有不同的存储持续时间。存储持续时间决定了一个对象存在多长时间并且可以被访问。您应该了解以下内容:

      • 自动:只要执行在其范围内,对象就会存在。这是不在文件范围中的任何变量的默认值。
      • 静态:对象在程序的整个执行时间内都存在。这是文件范围中声明的默认值。
      • 动态:对象由allocationmalloc() 和朋友)创建并一直存在,直到被手动释放(free())。
    • 最后,标识符可以有不同的链接。这决定了不同翻译单元之间的可见性。具有外部链接的标识符在其自己的翻译单元之外是可见的,具有内部链接的标识符则不可见。

      文件范围标识符的默认链接是外部


    有了这个理论背景,我们来看看static是什么意思。 static 是一个存储类,将存储持续时间设置为静态,链接设置为内部。

    example1.c:

    static int i;
    // static storage duration, internal linkage
    
    int foobar(void) {
        static int j = 5;
        // static storage duration, internal linkage
        return ++j;
        // returns one more at each call, starting from 6
    }
    

    与 example2.c:

    int i;
    // static storage duration, external linkage
    
    int foobar(void) {
        int j = 5;
        // automatic storage duration, internal linkage
        return ++j;
        // always returns 6, because with automatic storage duration,
        // j refers to a NEW object every time the function is entered
    }
    

    关于问题中的代码:指针具有 static 存储持续时间,因此它适用于整个程序。无论您 malloc() 是否存在,只要您不存在 free() 它(见上文:动态存储持续时间),因此您完全可以使用此代码。

    【讨论】:

      【解决方案3】:
      static double *outd;
      

      变量声明。

       outd=malloc(sizeof(double)*len);
      

      内存分配

      因为变量是静态的,所以在init0函数完成后, 记忆被保留。相反,如果变量 NOT 被声明为 静态的,那么,这将是内存泄漏,因为一旦 init0 完成, 动态分配的内存将会丢失。

      【讨论】:

        【解决方案4】:

        static 在 C(以及其他语言)中的含义是不同的,如果它用于函数、全局变量或局部变量。这个关键字并不表示变量是静态分配还是动态分配,如果您使用指针,您实际上选择了动态分配。

        -对于局部变量,静态关键字会改变该变量的生命周期;但是如果你在静态指针上调用malloc,这个会改变它的值。这里举个例子:

        void myfunction(){
        static int* p = NULL;
        
        if (p == NULL)
            p = (int*) malloc(sizeof(int) * 4);
        else
        {
            *(p) = 1;
            printf("address: %d, first element: %d", p, p[0]);
        }
        }
        
        int main()
        {
            myfunction();
            myfunction();
        
            return 0;
        }
        

        只有第二个myfunction 会打印地址和第一个元素。 (我知道我没有打电话给free

        • 如果将static 与函数或全局变量一起使用,则含义将完全改变:在这种情况下,它会改变该函数或全局变量的可见性。正如您在 cmets 中看到的,静态函数仅对同一翻译单元(目标文件)中的其他函数可见。对于全局静态变量几乎是一样的。

        【讨论】:

        • 这里再次说明:static含义与存储类始终相同,指定存储持续时间链接。由于文件范围和函数范围的默认值不同,您似乎看到了不同的效果。看我的回答,试图真正解释这一点。
        猜你喜欢
        • 2016-02-03
        • 2014-03-10
        • 1970-01-01
        • 1970-01-01
        • 2019-07-12
        • 2015-02-05
        • 2020-12-11
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多