C语言瞎折腾之详细测试指针初始化

首先要说的是一个程序从编译到执行的时候有以下几个区:

1.栈区:这个 区主要存储的是程序中声明的局部变量(不包括static和全局变量),并且是向低地址增长,也就是多一个量,地址就向下减一位,然后栈区是我们操作不到的,由编译器来申请和释放的(当然,有一个alloca函数可以在栈上申请空间,但是栈的大小有限,和编译环境编译器有关),这个栈区和数据结构的栈意味一致,就是先进后出。这个空间大小有限,也就意味着我们在代码中不可能定义一个很大的数组如a[100000000000000000000]之类的,看了很多说法,有说windows下这个区是1M,有说不定的,总之少操作这个区的好。(据说这个区的变量在函数结束就销毁了,我们下面会做测试);

2.堆区:堆是我们可以操作的,一般使用的malloc / calloc等函数及new都在这区域上面分派空间,这个区域是向高地址增长,并且是以链表的形式存储,但是得我们自己用free/delete去销毁。(另外malloc/free和new/delete是有区别的,前者是C的函数,后者是C++运算符,见这个文章);

3.静态存储区:这个区存储着全局变量 / static声明的静态变量及常量,这个也是在编译器编译完的时候就确定了,只进行一次初始化(这里有一个问题就是:既然全局变量和常量都存储在这里,那全局变量是可以修改的,而常量是不能修改的,这时候编译器要怎么确定呢?);

4.代码区:存储二进制代码。

还有的文章里会提到一个BSS区,也叫未初始化数据区,是在Linux环境下提到的,存放的是未初始化的全局变量,形如:

char *p;
int a[10];

可以参考这篇文章,里面也提到了栈和堆的问题。

 

强调下我的环境是(OSX 10.9.4,GCC-4.8,64-bit)

先测试一下整型的,因为面试的时候问到了在一个函数中声明一个固定长度的整型数组,然后将数组名返回的问题。当然在写以下代码的时候,编辑器(sublime text 3)自带的插件会提示“address of stack memory associated with local variable 'a' returned”,也就是返回了一个函数局部变量的地址,这是非法的,因为按照常理,a在函数执行完就销毁了,即使返回对应栈上的地址,也不会有正确的内容。

int * func()
{
	int a[100];
	a[0]=1;
	a[1]=2;
	printf("the address of array a in func:%p\n",a);
	printf("the address of the last element of a in func:%p\n",a+99);
	printf("first two values of array a:%d,%d\n",a[0],a[1] );
	return a;
}
int main(int argc, char ** argv)
{
	int * test=NULL;
	printf("the address of pointer test in main:%p\n", &test);
	printf("the value of pointer test in main:%p\n", test);
	test=func();
	printf("the value of pointer test after func:%p\n", test);
	printf("first two value in test:%d,%d\n", test[0],test[1]);
	printf("the address of variable test after func:%p\n", &test);
	return 0;
}

 

输出的结果如下:

$ ./programStoreTest 
the address of pointer test in main:0x7fff510dcbd8
the value of pointer test in main:0x0
the address of array a in func:0x7fff510dca20
the address of the last element of a in func:0x7fff510dcbac
first two values of array a:1,2
the value of pointer test after func:0x7fff510dca20
first two values in test:2075216192,32767
the address of variable test after func:0x7fff510dcbd8

可以看到,在执行func之前,test指针的值是指向0的,执行了func后,test指针的值是指向func中a数组的首地址,这里是 0x7fff510dca20,然后在访问这个数组的第一个数和第二个数,发现已经输出错误的数值了,这是很符合规范的,也就是func函数声明的数组a是在栈上面的,当函数结束之后,栈上的地址虽然还在,但是已经被销毁了( 这里又可以引出的一个问题是,栈上的数据怎么个销毁的?也就是,地址还在那呢,数据咋没(变)了?这个最后补上我个人理解)( 另外在windows上实验的时候发现并不会出现这个现象,也就是栈区的两个值并没有被销毁,这个有待进一步分析),然后,我们可以知道,test这个指针所在的地址 0x7fff510dcbd8也是在栈区,而在func里a的最后一个数也就是a[99]的地址是 0x7fff510dcbac,按照函数的执行顺序,a数组是后于test指针进入栈区,而栈区是往小地址延伸,所以越后面的变量地址越小。做一个简单的计算,拿test指针的地址减去a的第一个元素的首地址,可以得到bd8-a20=1b8,也就是十进制的440,拿test指针减去a最后一个元素的地址,也就是bd8-bac=2c,就是十进制的44,剔除最后一个元素所占的空间,也就是说,从test指针的建立到func函数里a数组的建立之间,还有一些变量占据了这40byte的空间。为了谨慎起见,测试了一下,把test=func();前的俩个printf语句注释掉,输出结果如下:

$ ./programStoreTest 
the address of array a in func:0x7fff5988ba20
the address of the last element of a in func:0x7fff5988bbac
first two values of array a:1,2
the value of pointer test after func:0x7fff5988ba20
first two values in test:2075216192,32767
the address of variable test after func:0x7fff5988bbd8

可以看到后面a的地址、a最后一个元素的地址及test地址的相对位置都是没有变化的(也就是前面的高位地址都相同,最低三位的地址都是a20,bac,bd8),也就证明了printf语句不会对栈区产生影响(额,估计原理上就是不会影响的,不过还是测试下安心),OK,然后接下来的问题就是,我的那40byte到底哪去了,也就是看到剩这句test=func();了。换一下代码,把func里的内容放到main里去,就是执行以下代码,看看结果如何。

int main(int argc, char ** argv)
{
	int * test=NULL;
	printf("the address of pointer test in main:%p\n", &test);
	printf("the value of pointer test in main:%p\n", test);
	// test=func();
	int a[100];
	a[0]=1;
	a[1]=2;
	printf("the address of array a in main:%p\n",a);
	printf("the address of the last element of a in main:%p\n",a+99);
	printf("first two values of array a:%d,%d\n",a[0],a[1] );
	test=a;
	printf("the value of pointer test after been assigned:%p\n", test);
	printf("first two values in test:%d,%d\n", test[0],test[1]);
	printf("the address of variable test after been assigned:%p\n", &test);
	return 0;
}

输出结果如下:

 ./programStoreTest 
the address of pointer test in main:0x7fff59e32bd8
the value of pointer test in main:0x0
the address of array a in main:0x7fff59e32a40
the address of the last element of a in main:0x7fff59e32bcc
first two values of array a:1,2
the value of pointer test after been assigned:0x7fff59e32a40
first two values in test:1,2
the address of variable test after been assigned:0x7fff59e32bd8

这个时候test和a存在于同一个函数中,所以不存在a被销毁的问题,所以test[0]和test[1]的输出是正常的,分析一下地址情况,test的地址依旧是bd8结尾,而a数组这次是a40结尾,最后一个元素a[99]也编程了bcc结尾,算一算bd8-a40=198,也就是十进制的408,而bd8-bcc=c,也就是十进制12,这么看,test指针和a数组的最后一个数之间有8byte的距离(因为a的最后一个整型数也要占据4byte啦,所以是8byte),再输出下test指针的大小,添加以下代码:

printf("the size of pointer test:%d\n", sizeof(test));

再看输出:

$ ./programStoreTest 
the address of pointer test in main:0x7fff525d9bd8
the value of pointer test in main:0x0
the address of array a in main:0x7fff525d9a40
the address of the last element of a in main:0x7fff525d9bcc
first two values of array a:1,2
the value of pointer test after been assigned:0x7fff525d9a40
the size of pointer test:8
first two values in test:1,2
the address of variable test after been assigned:0x7fff525d9bd8

呵呵,这么一来清楚了一些,我的系统一个指针变量的长度是8byte,因为寻址空间是64bit的,当然,通过调整编译器的设
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值