C语言中变量何时分配空间?

小猫爱学习 2020-11-08 08:54:59
编译器为gcc
现象一:
这三段代码输出结果怎么解释?

#include <stdio.h>
#include <stdlib.h>
int main()
{
int b ;
int a ;
printf("%x\n",&a); //只输出a的地址,本人机器上为61FE1C
system("pause");
return 0;
}


#include <stdio.h>
#include <stdlib.h>
int main()
{
int b ;
int a ;
printf("%x\n",&b); //只输出b的地址,本人机器上也为61FE1C
system("pause");
return 0;
}


#include <stdio.h>
#include <stdlib.h>
int main()
{
int b ;
int a ;
printf("%x\t%x\n",&a,&b); //同时输出a,b的地址,本人机器上为61FE18 61FE1C
system("pause");
return 0;
}

为什么前面两次输出的地址会一样?
现象二:
这两段代码结果怎么解释?

#include <stdio.h>
#include <stdlib.h>
int main()
{
int b;
int a;
printf("a = %d\n",a); //输出a = 16
printf("b = %d\n",b); //输出b = 0
printf("%X\n",&a); //a的地址为61FE18
system("pause");
return 0;
}


#include <stdio.h>
#include <stdlib.h>
int main()
{
int b;
int a;
printf("a = %d\n",a); //输出a = 0
printf("b = %d\n",b); //输出b = 16
printf("%X\n",&b); //b的地址也为61FE18
system("pause");
return 0;
}

从a,b的值交换了可以看出a,b的地址交换了?为什么a,b的地址会交换呢?
现象三:
这两段代码结果怎么解释

#include <stdio.h>
#include <stdlib.h>
int main()
{
int b = 10000;
int a ;
printf("%X\n",&b); //b的地址是 61FE1C
system("pause");
return 0;
}


#include <stdio.h>
#include <stdlib.h>
int main()
{
int b = 10000;
int a ;
printf("a = %d\n",a);
printf("%X\n",&b); //b的地址是 61FE18
system("pause");
return 0;
}

这两次b的地址为什么变了呢?
...全文
1429 29 打赏 收藏 转发到动态 举报
写回复
用AI写文章
29 条回复
切换为时间正序
请发表友善的回复…
发表回复
「已注销」 2020-11-13
  • 打赏
  • 举报
回复
引用 24 楼 funnywhere 的回复:
[quote=引用 23 楼 lin5161678的回复:][quote=引用 22 楼 funnywhere的回复:][quote=引用 21 楼 lin5161678的回复:][quote=引用 20 楼 funnywhere 的回复:] 并没有优化掉,都在的。
那么你的依据呢 你凭什么说 b变量还在呢[/quote] 因为我找到正确答案了[/quote] 别扯 这只是黑盒测试 没什么能叫做答案[/quote] 那就不叫答案吧,因为我看到一篇介绍gcc如何进行内存分配的文章,可以说找到正确的解释了。[/quote] 局部变量分配汇编指令 Intel 是 sub esp,xxx,ARM处理器是 sub sp, sp, #xxx ,不管什么编译器,就这种方法,还正确答案,别逗
「已注销」 2020-11-13
  • 打赏
  • 举报
回复
引用 28 楼 火花20180731 的回复:
可执行文件相当于一个内存模板,系统会按照这个内存模板起一个进程,按模板中的内容为进程分配内存(给一个空闲的虚拟的地址区间,可与物理内存建立映射),并填充初始数据。第一次进程运行结束后,占用的内存被释放,用过的地址区间再次空闲。第二次进程运行再次从这个空闲地址开始分配,所以两次地址打印相同。
现代操作系统都是虚拟内存,进程间内存是互不相干的,跟第几次运行都没关系,即时同时运行也一样
「已注销」 2020-11-13
  • 打赏
  • 举报
回复
引用 29 楼 funnywhere 的回复:
[quote=引用 26 楼 赵4老师的回复:]理解讨论之前请先学会如何观察! 这辈子不看内存地址和内存值;只画链表、指针示意图,画堆栈示意图,画各种示意图,甚至自己没画过而只看过书上的图……能从本质上理解指针、理解函数参数传递吗?本人深表怀疑! 这辈子不种麦不收麦不将麦粒拿去磨面;只吃馒头、吃面条、吃面包、……甚至从没看过别人怎么蒸馒头,压面条,烤面包,……能从本质上理解面粉、理解面食吗?本人深表怀疑!! 提醒: “学习用汇编语言写程序” 和 “VC调试(TC或BC用TD调试)时按Alt+8、Alt+7、Alt+6和Alt+5,打开汇编窗口、堆栈窗口、内存窗口和寄存器窗口看每句C对应的汇编、单步执行并观察相应堆栈、内存和寄存器变化,这样过一遍不就啥都明白了吗。 (Linux或Unix下可以在用GDB调试时,看每句C对应的汇编并单步执行观察相应内存和寄存器变化。) 想要从本质上理解C指针,必须学习C和汇编的对应关系。” 不是一回事! 不要迷信书、考题、老师、回帖; 要迷信CPU、编译器、调试器、运行结果。 并请结合“盲人摸太阳”和“驾船出海时一定只带一个指南针。”加以理解。 任何理论、权威、传说、真理、标准、解释、想象、知识……都比不上摆在眼前的事实!
确实很有道理。[/quote] 老赵的话,十年前就是复制粘贴的,水了一辈子了。
  • 打赏
  • 举报
回复
如果你的代码中没有使用某个变量,通常会被编译器优化掉(取决于编译器优化级别),如果你只读使用,编译器会将其优化为常量嵌入到代码中(立即数),只有你对它进行了写操作或者取地址,编译器才会为它实际分配空间
小猫爱学习 2020-11-11
  • 打赏
  • 举报
回复
引用 23 楼 lin5161678的回复:
[quote=引用 22 楼 funnywhere的回复:][quote=引用 21 楼 lin5161678的回复:][quote=引用 20 楼 funnywhere 的回复:] 并没有优化掉,都在的。
那么你的依据呢 你凭什么说 b变量还在呢[/quote] 因为我找到正确答案了[/quote] 别扯 这只是黑盒测试 没什么能叫做答案[/quote] 那就不叫答案吧,因为我看到一篇介绍gcc如何进行内存分配的文章,可以说找到正确的解释了。
小猫爱学习 2020-11-11
  • 打赏
  • 举报
回复
引用 26 楼 赵4老师的回复:
理解讨论之前请先学会如何观察! 这辈子不看内存地址和内存值;只画链表、指针示意图,画堆栈示意图,画各种示意图,甚至自己没画过而只看过书上的图……能从本质上理解指针、理解函数参数传递吗?本人深表怀疑! 这辈子不种麦不收麦不将麦粒拿去磨面;只吃馒头、吃面条、吃面包、……甚至从没看过别人怎么蒸馒头,压面条,烤面包,……能从本质上理解面粉、理解面食吗?本人深表怀疑!! 提醒: “学习用汇编语言写程序” 和 “VC调试(TC或BC用TD调试)时按Alt+8、Alt+7、Alt+6和Alt+5,打开汇编窗口、堆栈窗口、内存窗口和寄存器窗口看每句C对应的汇编、单步执行并观察相应堆栈、内存和寄存器变化,这样过一遍不就啥都明白了吗。 (Linux或Unix下可以在用GDB调试时,看每句C对应的汇编并单步执行观察相应内存和寄存器变化。) 想要从本质上理解C指针,必须学习C和汇编的对应关系。” 不是一回事! 不要迷信书、考题、老师、回帖; 要迷信CPU、编译器、调试器、运行结果。 并请结合“盲人摸太阳”和“驾船出海时一定只带一个指南针。”加以理解。 任何理论、权威、传说、真理、标准、解释、想象、知识……都比不上摆在眼前的事实!
确实很有道理。
火花20180731 2020-11-11
  • 打赏
  • 举报
回复
可执行文件相当于一个内存模板,系统会按照这个内存模板起一个进程,按模板中的内容为进程分配内存(给一个空闲的虚拟的地址区间,可与物理内存建立映射),并填充初始数据。第一次进程运行结束后,占用的内存被释放,用过的地址区间再次空闲。第二次进程运行再次从这个空闲地址开始分配,所以两次地址打印相同。
  • 打赏
  • 举报
回复
以前的老赵又回来了~~~
赵4老师 2020-11-11
  • 打赏
  • 举报
回复
理解讨论之前请先学会如何观察! 这辈子不看内存地址和内存值;只画链表、指针示意图,画堆栈示意图,画各种示意图,甚至自己没画过而只看过书上的图……能从本质上理解指针、理解函数参数传递吗?本人深表怀疑! 这辈子不种麦不收麦不将麦粒拿去磨面;只吃馒头、吃面条、吃面包、……甚至从没看过别人怎么蒸馒头,压面条,烤面包,……能从本质上理解面粉、理解面食吗?本人深表怀疑!! 提醒: “学习用汇编语言写程序” 和 “VC调试(TC或BC用TD调试)时按Alt+8、Alt+7、Alt+6和Alt+5,打开汇编窗口、堆栈窗口、内存窗口和寄存器窗口看每句C对应的汇编、单步执行并观察相应堆栈、内存和寄存器变化,这样过一遍不就啥都明白了吗。 (Linux或Unix下可以在用GDB调试时,看每句C对应的汇编并单步执行观察相应内存和寄存器变化。) 想要从本质上理解C指针,必须学习C和汇编的对应关系。” 不是一回事! 不要迷信书、考题、老师、回帖; 要迷信CPU、编译器、调试器、运行结果。 并请结合“盲人摸太阳”和“驾船出海时一定只带一个指南针。”加以理解。 任何理论、权威、传说、真理、标准、解释、想象、知识……都比不上摆在眼前的事实!
lin5161678 2020-11-10
  • 打赏
  • 举报
回复
引用 22 楼 funnywhere的回复:
[quote=引用 21 楼 lin5161678的回复:][quote=引用 20 楼 funnywhere 的回复:] 并没有优化掉,都在的。
那么你的依据呢 你凭什么说 b变量还在呢[/quote] 因为我找到正确答案了[/quote] 别扯 这只是黑盒测试 没什么能叫做答案
老马何以识途 2020-11-10
  • 打赏
  • 举报
回复
只單獨輸出a或b,是不是意味著這時候另一個變量在整個程序生命期中沒有用到?比如:

#include <stdio.h>
#include <stdlib.h>
int main()
{
    int b ;
    int a ;
    printf("%x\n",&a);  //只输出a的地址,本人机器上为61FE1C
    system("pause");
    return 0;
}
這段程序中,b只在定義時出現一次。 這就是你要明白的其中一點:編譯器有優化功能,它掃描了整個程序發現b其實可以不要,於是就很可能被省略掉了。 那麽這個時候,不管你輸出的是a還是b,它的地址分配都是一樣的,即屬於該程序的變量空間只有一個整數。
小猫爱学习 2020-11-10
  • 打赏
  • 举报
回复
引用 21 楼 lin5161678的回复:
[quote=引用 20 楼 funnywhere 的回复:] 并没有优化掉,都在的。
那么你的依据呢 你凭什么说 b变量还在呢[/quote] 因为我找到正确答案了
lin5161678 2020-11-10
  • 打赏
  • 举报
回复
引用 20 楼 funnywhere 的回复:
并没有优化掉,都在的。
那么你的依据呢 你凭什么说 b变量还在呢
小猫爱学习 2020-11-10
  • 打赏
  • 举报
回复
引用 19 楼 老马何以识途的回复:
只單獨輸出a或b,是不是意味著這時候另一個變量在整個程序生命期中沒有用到?比如:

#include <stdio.h>
#include <stdlib.h>
int main()
{
    int b ;
    int a ;
    printf("%x\n",&a);  //只输出a的地址,本人机器上为61FE1C
    system("pause");
    return 0;
}
這段程序中,b只在定義時出現一次。 這就是你要明白的其中一點:編譯器有優化功能,它掃描了整個程序發現b其實可以不要,於是就很可能被省略掉了。 那麽這個時候,不管你輸出的是a還是b,它的地址分配都是一樣的,即屬於該程序的變量空間只有一個整數。
并没有优化掉,都在的。
小猫爱学习 2020-11-09
  • 打赏
  • 举报
回复
引用 13 楼 眼里有光~的回复:
函数内部的变量是局部变量,比如你int a,b,这种非静态局部变量是放在栈上的,栈区是操作系统来管理的,程序在运行的时候操作系统自动为局部变量分配空间,局部变量出了作用域以后,操作系统自动回收这部分空间,也就是说不需要由程序员自己去申请和释放这块内存。 你上面出现的情况,每次运行单输出a和b的地址有时是一样的,这是正常情况,程序本次运行操作系统可能将这个地址分配给a,下次运行操作系统将这个地址分配给b,操作系统怎么分配栈区地址其实根本不用程序员操心。
感觉跟操作系统关系不大。感觉是gcc编译器的某种策略,换成msvc就没有这个现象。
舒泱 2020-11-09
  • 打赏
  • 举报
回复
函数内部的变量是局部变量,比如你int a,b,这种非静态局部变量是放在栈上的,栈区是操作系统来管理的,程序在运行的时候操作系统自动为局部变量分配空间,局部变量出了作用域以后,操作系统自动回收这部分空间,也就是说不需要由程序员自己去申请和释放这块内存。 你上面出现的情况,每次运行单输出a和b的地址有时是一样的,这是正常情况,程序本次运行操作系统可能将这个地址分配给a,下次运行操作系统将这个地址分配给b,操作系统怎么分配栈区地址其实根本不用程序员操心。
skull07 2020-11-09
  • 打赏
  • 举报
回复
短时间内一样的地址巧合而已。
小猫爱学习 2020-11-09
  • 打赏
  • 举报
回复
引用 7 楼 qybao 的回复:
[quote=引用 6 楼 funnywhere 的回复:] 你再给a也赋个值10000,你会发现如果只单独输出a或b的地址,也是一样的。
你把你的代码和运行结果贴出来看看
#include <stdio.h>
#include <stdlib.h>
int main()
{
    int b = 10000;
    int a = 10000;
    printf("%X\n",&b);   //你确定轮流注释这两句打印语句运行出来的结果一样?a,b同时被使用,编译优化不会舍弃任何一个,内存地址肯定不同
    //printf("%X\n",&a);  //估计你代码哪里有问题,要不就是进程加载时不是重复使用原来的内存(这很正常)
    system("pause");
    return 0;
}
[/quote]
小猫爱学习 2020-11-09
  • 打赏
  • 举报
回复
轮流注释地址就是一样的啊。试了好多种方案都不知道怎么解释。要不我就不会发帖问了。
自信男孩 2020-11-09
  • 打赏
  • 举报
回复
程序加载到内存时才会分配内存,但是编译器会指定偏移量。比如数组会指定长度~
加载更多回复(12)

69,380

社区成员

发帖
与我相关
我的任务
社区描述
C语言相关问题讨论
社区管理员
  • C语言
  • 花神庙码农
  • 架构师李肯
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧