静态指针的动态内存分配

时间:2017-06-18 08:45:27

标签: c arrays pointers static malloc

有人可以向我解释为什么下面的代码按照它的工作方式工作。这里我将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 

3 个答案:

答案 0 :(得分:1)

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

为了实现这个例子,我玩各种静态(两种),本地和全局变量 (我使用整数而不是指针,以保持简单 差异,可能是你突出的令人费解的答案的一部分,只是缓冲区是否改变。有关于发生了什么以及与此无关的事情的评论。)

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

答案 1 :(得分:1)

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中的对象可以具有不同的存储持续时间。存储持续时间确定对象存活多久并且可以访问。您应该了解以下内容:

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

    文件作用域标识符的默认链接是外部

有了这个理论背景,让我们看看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
}

vs 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
}

关于问题中的代码:指针具有静态存储持续时间,因此它适用于整个程序。无论你malloc()存在什么,只要你没有free()它(见上文:动态存储时间),所以你对这段代码完全没问题。

答案 2 :(得分:0)

如果将static用于函数,全局变量或局部变量,则其在{(以及其他语言)中的含义也不同。此关键字不表示变量是静态还是动态分配,如果使用指针,则实际选择动态分配。

- 使用局部变量,static关键字会更改该变量的生命周期;但是如果你在静态指针上调用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与函数或全局变量一起使用,则意义完全改变:在这种情况下,它会更改该函数或全局变量的可见性。正如您在注释中看到的那样,静态函数只能由同一翻译单元(目标文件)中的其他函数显示。对于全局静态变量几乎是一样的。