#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
会发生什么?它是保持初始化还是被释放。
答案 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;
}
temp
是int*
类型的本地指针对象。名称temp
仅在关闭}
的声明中可见。指针对象的生命周期(具有自动存储持续时间)是封闭块的执行;函数返回时指针对象不再存在。 (return temp;
完全有效;它返回对象值的副本,因此对象本身不再存在并不重要。)
malloc
函数创建一个新对象。该对象没有自己的名称,因此它没有范围。它的生命周期从调用malloc
开始,一直持续到通过调用free
(或realloc
显式释放存储,但我们可以忽略它),或者直到程序完成为止。
在main
中,您有:
a=makearray(num);
readdata(temp,num);
名称temp
是makearray
函数的本地名称,因此在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);
}
更强大的程序会采取一些纠正措施(比如再次询问),但现在终止任何错误都没问题,而且比悄悄地忽略错误更好。