堆变量是全局变量,堆变量的范围和生命周期是什么

时间:2016-12-25 16:18:44

标签: c scope initialization dynamic-memory-allocation lifetime

#include<stdio.h>
#include<conio.h>
#include<alloc.h>

int * makearray(int );
void readdata(int *,int );
void printdata(int *,int );

void main()
{
    int *a,num;
    clrscr();
    printf("enter the size of array\n");
    scanf("%d",&num);
    a=makearray(num);
    readdata(temp,num);
    printdata(a,num);
    getch();
}

int * makearray(int n)
{
    int *temp;
    temp=(int *)malloc(sizeof(int)*n);
    printf("address of temp is %x and value of temp is %d and first value of            temp is garbage i.e %d\n",&temp,temp,*temp);
    return temp;
}

void readdata(int *x,int n)
{
    for(n--; n>=0; n--)
    {
        printf("enter the value in cell[%d]\n",n);
        scanf("%d",x+n);
    }
    return;
}

void printdata(int *x,int n)
{
    for(n--; n>=0; n--)
        printf("the value in cell[%d] is %d",n,*(x+n));
    return;
}

在下面的代码中,我想了解堆变量的范围,如果在程序的持续时间内,为什么temp在程序中显示为未识别的符号?

另外,我想知道堆变量的生命周期和范围是什么。

另外我想知道,因为变量只是在我们返回指针temp时初始化时在内存中保留了一个空格a,但是之后temp会发生什么?它是保持初始化还是被释放。

2 个答案:

答案 0 :(得分:5)

我认为你混淆了两个概念,scope和生命。

引用C11,章节§6.2.1

  

对于标识符指定的每个不同实体,标识符是可见的(即,可以是   used)仅在名为 scope 的程序文本区域内。 [...]

  

对象的生命周期是存储期间程序执行的一部分   保证为它保留。存在一个对象,具有一个常量地址, 33)并保留   它在其整个生命周期中的最后存储价值。 [....]

这里的问题是,标识符temp具有函数makearray()的块范围。由temp(一个指针)保存的值由内存分配器函数返回,因此显然它有一个liferime,直到它被释放,但这并不意味着temp变量本身具有文件范围。

您已使用另一个变量a来存储makearray()的返回值,因此请使用它。

答案 1 :(得分:1)

Sourav Ghosh's answer在某些方面比这更好。特别是,如果您不是语言律师,可能更容易理解。我将扩展他所撰写的内容,并对问题中的代码提出一些(希望是建设性的)批评。

实际上,C没有“全局”变量的概念。事实上,C标准甚至没有使用术语“变量”(至少不是通常意义上的)。它使用“对象”这个词,并不完全清楚哪些对象是或不是“变量”。

正如Sourav的回答所说,范围生命周期是两回事。标识符的范围是C程序文本的区域,该标识符在该区域中可见;这是一个编译时的概念。对象的生存期是该对象存在的时间段;这是一个运行时的概念。

以下是您问题中的相关功能:

int * makearray(int n)
{
    int *temp;
    temp=(int *)malloc(sizeof(int)*n);
    // printf call skipped for now
    return temp;
}

tempint*类型的本地指针对象。名称temp仅在关闭}的声明中可见。指针对象的生命周期(具有自动存储持续时间)是封闭块的执行;函数返回时指针对象不再存在。 (return temp;完全有效;它返回对象值的副本,因此对象本身不再存在并不重要。)

malloc函数创建一个新对象。该对象没有自己的名称,因此它没有范围。它的生命周期从调用malloc开始,一直持续到通过调用free(或realloc显式释放存储,但我们可以忽略它),或者直到程序完成为止。

main中,您有:

a=makearray(num);
readdata(temp,num);

名称tempmakearray函数的本地名称,因此在main中不可见 - 并且指针对象在该点上甚至不存在。但您刚刚将值分配给a。你只需要改变

readdata(temp, num);

readdata(a, num);

现在让我们来看看代码中的其他一些问题。

#include<alloc.h>

这不是标准标题。 malloc函数在<stdlib.h>中声明。

void main()

有些编译器会接受这个,但它不符合标准。请改用int main(void)

temp=(int *)malloc(sizeof(int)*n);

不要投射malloc的结果。它返回类型void*的结果,可以隐式转换为您需要的任何指针类型。推荐的习语是:

temp = malloc(n * sizeof *temp);

使用sizeof *temp而非sizeof(int),您可以避免使用错误类型并悄悄地分配错误大小的风险。如果你发现它更具可读性,你可以写sizeof(*temp)而不是sizeof *temp;要么有效。

你应该检查malloc电话是否成功。如果malloc失败,则返回空指针。您可以添加以下内容:

if (temp == NULL)
{
    fprintf(stderr, "malloc failed\n");
    exit(EXIT_FAILURE);
}

像这样的小分配不太可能失败,但你应养成良好的习惯。

printf("address of temp is %x and value of temp is %d and first value of temp is garbage i.e %d\n",&temp,temp,*temp);

让我们把它分开:

printf("address of temp is %x\n", &temp);

打印指针对象temp的地址(这不是特别有用的信息,但好奇心是好事)。 %x需要unsigned int类型的参数。要打印指针值,请使用%p,这需要类型为void*的参数;如果您要打印的指针值是某种其他类型,则使用强制转换:

printf("address of temp is %p\n", (void*)&temp);

继续前进。

printf("value of temp is %p\n", (void*)temp);

再次,使用%p打印指针值。

printf("first value of temp is garbage, i.e., %d\n", *temp);

这没关系。 (严格来说,行为是未定义的,信息没有用,但好奇心再好一点。)

您有scanf的几个电话,并且您认为他们将成功读取有效数据。 scanf,如果成功,则返回其扫描的项目数。你应该检查一下。例如:

int count = scanf("%d",&num);
if (count != 1)
{
    fprintf(stderr, "scanf failed\n");
    exit(EXIT_FAILURE);
}

更强大的程序会采取一些纠正措施(比如再次询问),但现在终止任何错误都没问题,而且比悄悄地忽略错误更好。