怎么看在运行时我在C程序的内存加载布局时内存溢出

在 SegmentFault,学习技能、解决问题
每个月,我们帮助 1000 万的开发者解决各种各样的技术问题。并助力他们在技术能力、职业生涯、影响力上获得提升。
问题对人有帮助,内容完整,我也想知道答案
问题没有实际价值,缺少关键内容,没有改进余地
如下代码输出什么:
#include&stdio.h&
int main()
int *p=(int *)&b;
printf("%d %d",a,b);
在看国外一本linux方面的书,里面讲到c语言内存布局相关,书中说这个程序运行结果是1 2,可是我再我的电脑(mac)上运行时02,并且想不明白这是为什么.我目前仅有的思路如下:258的二进制形式指针p指向了字符b的起始地址,字符与整形的地址对比可以通过下面的代码得出:
#include &stdio.h&
int main()
char a = 0;
char b = 0;
char c = 0;
printf("%p\n",&a);
printf("%p\n",&b);
printf("%p\n",&c);
printf("%p\n",&d);
printf("%p\n",&e);
printf("%p\n",&f);
代码输出0x7fffc38df0df0x7fffc38df0de0x7fffc38df0dd0x7fffc38df0d80x7fffc38df0d40x7fffc38df0d0地址由高到低,所以我的理解是*p复制258(258的二进制形式)会去覆盖字符a b 原有的值,所以b的值是
a的值是,a的值是1,b的值是2.此时涉及到大端与小端方式,我通过如下程序判断出我电脑环境为小端方式:
#include &stdio.h&
#include &string.h&
union u_tag
c[sizeof(short)];
int main(int argc, const char * argv[])
un.s = 0x0102;
//打印数组c中地址关系
printf("%p
%p\n",&un.c[0],&un.c[1]);
if (sizeof(short) == 2) {
if (un.c[0] == 1 && un.c[1] == 2)
printf("big-endian\n");
else if (un.c[0] == 2 && un.c[1] == 1)
printf("little-endian\n");
printf("unknown\n");
printf("sizeof(short) = %lu\n", sizeof(short));
可是我实际运行中确实0 2.
答案对人有帮助,有参考价值
答案没帮助,是错误的答案,答非所问
估计你开了编译器优化。
我的环境:
Ubuntu 16.04 (64 bit)
不开优化直接编译,结果的确是 1 2;但一旦开了优化(-O1, -O2, -O3, -Og结果都一样,其中 -Og 会报错),结果就变成了 0 2。原因在于:编译器在编译阶段就已经将值算好了,你输出的其实是常量。
拿 -O1 优化的汇编代码举例(使用 gcc test.c -O1 -S 获得,这里只贴核心部分):
$.LC0, %esi
__printf_chk
%edi, %esi, %edx, %ecx 分别是 __printf_chk 的参数,%esi 指向 %s %s 的地址。
可以发现最后两个参数并没有访问栈区,而是直接使用了立即数 0 和 2,可见这两个值的计算在编译期就已经完成了。
答案对人有帮助,有参考价值
答案没帮助,是错误的答案,答非所问
答案对人有帮助,有参考价值
答案没帮助,是错误的答案,答非所问
理论上的结果是 1 2。
但是操作的内存是栈,这里涉及到栈内存检测的问题。输出 0 2 是因为运行时栈异常处理所致(a 保留原值)。
为了更好的展现原因下面首先贴 Visual Studio 的调试及选项信息。
图1,关闭栈检测,此时输出的是理论结果:1 2
图2,默认情况下(开启栈检测),结果是 0 2,并且报异常。这是默认情况下的结果。
图3 异常类型,图4 检查选项,影响输出的原因,栈检测选项。
这个异常在VS里是 “Run-Time Check Failure #2”。因为 VS 默认开启了栈检测,对栈内存的 “非法” 操作由异常接管,a 部分的改变未执行(保留原值)。所以默认情况下输出 0 2。
楼主使用的 GCC 也有类似的设定,当O1以上优化选项开启时便会开启栈内存的相关检查,不同版本的 GCC 的默认选项有差别。下面是 GCC 官方文档说明。
优化选项(O 会开启大量选项包括栈检测):
更多的 GCC 章节
答案对人有帮助,有参考价值
答案没帮助,是错误的答案,答非所问
其实你的那个对大小端的判断是错误的。Intel x86的CPU是小端的,这个毋庸置疑。除非你的Mac电脑是十几年前的老古董,用的PowerPC的CPU。
一,请贴上你uname -a的结果。二,为了避免多写的字节(实际写了4个字节),破坏栈帧。你在最前面在加入两个char变量。再试试。
另外注意,C语言标准并没有规定局部变量的内存顺序。栈只是一种常见的实现。
答案对人有帮助,有参考价值
答案没帮助,是错误的答案,答非所问
win10 + vs2017
运行报错:Stack around the variable 'b' was corrupted.
不过还是输出了,是: 0 2
感觉这个计算机本身有很大的关联,让别的大神解释吧。我只能提供这么多了。
该答案已被忽略,原因:同问题相关的咨询、建议,请使用评论功能完成~
同步到新浪微博
分享到微博?
关闭理由:
删除理由:
忽略理由:
推广(招聘、广告、SEO 等)方面的内容
与已有问题重复(请编辑该提问指向已有相同问题)
答非所问,不符合答题要求
宜作评论而非答案
带有人身攻击、辱骂、仇恨等违反条款的内容
无法获得确切结果的问题
非开发直接相关的问题
非技术提问的讨论型问题
其他原因(请补充说明)
我要该,理由是:
在 SegmentFault,学习技能、解决问题
每个月,我们帮助 1000 万的开发者解决各种各样的技术问题。并助力他们在技术能力、职业生涯、影响力上获得提升。C++程序运行时内存布局之----------this到底是什么?
先问一个问题,在C++里,成员函数里的this指针和调用此函数的对象地址总是一样的吗?如果你的回答是:不一定。那么至少你是个老手吧,下面的内容你就不用看了;如果你的回答是:是啊,那么强烈建议你看看下面的内容。 非静态成员函数,无论是不是虚函数,都隐藏了一个this指针参数。这个参数的目的就是给函数提供一个基地址,以便于函数体内能找到对象的成员变量。那非静态成员函数是如何根据this指针找到成员变量的呢?直接看例子吧 1没有虚表的情况#include &iostream&& #include &stdio.h&& & using namespace& & class A& {& public:& &&& int& &&& int& public:& &&& void F1()& &&& {& &&&&&&& this-&x = 1;& &&&&&&& this-&y = 2;& &&&&&&& cout&&&this指针得值是:&&&std::hex&&std::showbase&&this&&& &&& }& };& & & & int main(int argc, char** argv)& {& &&& A& &&& cout&&&a对象的地址是:&&&&a&&& &&& cout&&&a对象的大小是:&&&sizeof(A)&&& &&& cout&&&成员a.x的地址是: &&&&a.x&&& &&& cout&&&成员a.x的偏移是:&&&&A::x&&& &&& a.F1();& &&& cin&&& &&& return 0;& }& #include &iostream&#include &stdio.h&class A{public:public:void F1(){this-&x = 1;this-&y = 2;cout&&&this指针得值是:&&&std::hex&&std::showbase&&this&&}};int main(int argc, char** argv){Acout&&&a对象的地址是:&&&&a&&cout&&&a对象的大小是:&&&sizeof(A)&&cout&&&成员a.x的地址是: &&&&a.x&&cout&&&成员a.x的偏移是:&&&&A::x&&a.F1();cin&&return 0;}那么函数F1的实现伪代码为: *(this+&A::x-1) = 1; *(this+&A::y-1) = 2; 其中&A::x是成员x的偏移+1,&A::y是成员y的偏移+1,这可是C++基本语法的知识,希望你知道,呵呵。可这些偏移量是相对应那里的偏移呢,是对象地址吗,答案是NO。是相对于第一个成员变量的偏移,这对于有些对象也许没有差别,但是对于有虚表的类的对象,就有差别了。这些偏移在编译期间就是确定了的。对于本例在VC++2010下&A::x,值为1, &A::y,值为5。为什么不是0,4,请看《Inside The C++ Object Model》。所以,对于找到成员变量,需要进一步确定的只有this的值。程序运行结果如下:可见此例中,对象的地址与this指针的地址相同,内存图如下所示。2有一个虚表的情况#include &iostream&& #include &stdio.h&& & using namespace& & class A& {& public:& &&& int& &&& int& public:& &&& virtual void F1()& &&& {& &&&&&&& this-&x = 1;& &&&&&&& this-&y = 2;& &&&&&&& cout&&&this指针得值是:&&&std::hex&&std::showbase&&this&&& &&& }& };& & & & int main(int argc, char** argv)& {& &&& A* p = new A();& &&& cout&&&a对象的地址是:&&&p&&& &&& cout&&&a对象的大小是:&&&sizeof(A)&&& &&& cout&&&成员a.x的地址是: &&&&p-&x&&& &&& cout&&&成员a.x的偏移是:&&&&A::x&&& &&& p-&F1();& &&& cin&&& &&& return 0;& }& #include &iostream&#include &stdio.h&class A{public:public:virtual void F1(){this-&x = 1;this-&y = 2;cout&&&this指针得值是:&&&std::hex&&std::showbase&&this&&}};int main(int argc, char** argv){A* p = new A();cout&&&a对象的地址是:&&&p&&cout&&&a对象的大小是:&&&sizeof(A)&&cout&&&成员a.x的地址是: &&&&p-&x&&cout&&&成员a.x的偏移是:&&&&A::x&&p-&F1();cin&&return 0;} 此时函数F1的实现伪代码为: *(this+4+&A::x-1) = 1;& //+4是因为存在虚表指针*(this+4+&A::y-1) = 2;& //+4是因为存在虚表指针 程序运行结果如下:内存布局如下:结论:this的值和对象地址相同,成员变量偏移量不因虚表指针存在而改变。带虚表的类的成员函数对成员变量的寻址方式不同,增加一个+4。3单继承的情况#include &iostream&& #include &stdio.h&& & using namespace& & class A& {& public:& &&& int& &&& int& public:& &&& void F1()& &&& {& &&&&&&& this-&x = 1;& &&&&&&& this-&y = 2;& &&&&&&& cout&&&this指针得值是:&&&std::hex&&std::showbase&&this&&& &&& }& };& & class B : public A& {& public:& &&& int& public:& &&& virtual void F2()& &&& {& &&&&&&& cout&&&this指针得值是:&&&std::hex&&std::showbase&&this&&& &&& }& };& & int main(int argc, char** argv)& {& &&& B* pb = new B();& &&& cout&&&对象的地址为:&&&std::hex&&std::showbase&&pb&&& &&& pb-&F1();& &&& pb-&F2();& & &&& cin&&& &&& return 0;& }& #include &iostream&#include &stdio.h&class A{public:public:void F1(){this-&x = 1;this-&y = 2;cout&&&this指针得值是:&&&std::hex&&std::showbase&&this&&}};class B : public A{public:public:virtual void F2(){cout&&&this指针得值是:&&&std::hex&&std::showbase&&this&&}};int main(int argc, char** argv){B* pb = new B();cout&&&对象的地址为:&&&std::hex&&std::showbase&&pb&&pb-&F1();pb-&F2();cin&&return 0;} 运行结果:内存布局:结论:this指针的值受两个因素确定,一是对象的地址,二是定义成员函数的类。This指向的是对象内,定义该方法的类得subobject。4 多继承的情况先看A没有虚函数,B有虚函数的情况#include &iostream&& #include &stdio.h&& & using namespace& & class A& {& public:& &&& int& &&& int& public:& &&& void F1()& &&& {& &&&&&&& this-&x = 1;& &&&&&&& this-&y = 2;& &&&&&&& cout&&&this指针得值是:&&&std::hex&&std::showbase&&this&&& &&& }& };& & class B& {& public:& &&& int& public:& &&& virtual void F2()& &&& {& &&&&&&& cout&&&this指针得值是:&&&std::hex&&std::showbase&&this&&& &&& }& };& & class C : public A, public B& {& public:& &&& int& public:& &&& virtual void F2()& &&& {& &&&&&&& cout&&&this指针得值是:&&&std::hex&&std::showbase&&this&&& &&& }& &&& void F3()& &&& {& &&&&&&& cout&&&this指针得值是:&&&std::hex&&std::showbase&&this&&& &&& }& };& int main(int argc, char** argv)& {& &&& C* pc = new C();& &&& cout&&&对象的大小为:&&&sizeof(C)&&& &&& cout&&&对象的地址为:&&&std::hex&&std::showbase&&pc&&& &&& pc-&F1();& &&& pc-&F2();& &&& pc-&F3();& & &&& cin&&& &&& return 0;& }& #include &iostream&#include &stdio.h&class A{public:public:void F1(){this-&x = 1;this-&y = 2;cout&&&this指针得值是:&&&std::hex&&std::showbase&&this&&}};class B{public:public:virtual void F2(){cout&&&this指针得值是:&&&std::hex&&std::showbase&&this&&}};class C : public A, public B{public:public:virtual void F2(){cout&&&this指针得值是:&&&std::hex&&std::showbase&&this&&}void F3(){cout&&&this指针得值是:&&&std::hex&&std::showbase&&this&&}};int main(int argc, char** argv){C* pc = new C();cout&&&对象的大小为:&&&sizeof(C)&&cout&&&对象的地址为:&&&std::hex&&std::showbase&&pc&&pc-&F1();pc-&F2();pc-&F3();cin&&return 0;}结果:内存布局:再看,如果A,B都有虚函数的情况。代码:#include &iostream&& #include &stdio.h&& & using namespace& & class A& {& public:& &&& int& &&& int& public:& &&& virtual void F1()& &&& {& &&&&&&& this-&x = 1;& &&&&&&& this-&y = 2;& &&&&&&& cout&&&this指针得值是:&&&std::hex&&std::showbase&&this&&& &&& }& };& & class B& {& public:& &&& int& public:& &&& virtual void F2()& &&& {& &&&&&&& cout&&&this指针得值是:&&&std::hex&&std::showbase&&this&&& &&& }& };& & class C : public A, public B& {& public:& &&& int& public:& &&& virtual void F2()& &&& {& &&&&&&& cout&&&this指针得值是:&&&std::hex&&std::showbase&&this&&& &&& }& &&& void F3()& &&& {& &&&&&&& cout&&&this指针得值是:&&&std::hex&&std::showbase&&this&&& &&& }& };& int main(int argc, char** argv)& {& &&& C* pc = new C();& &&& cout&&&对象的大小为:&&&sizeof(C)&&& &&& cout&&&对象的地址为:&&&std::hex&&std::showbase&&pc&&& &&& cout&&&x的地址&&&&pc-&x&&& &&& cout&&&z的地址&&&&pc-&z&&& &&& pc-&F1();& &&& pc-&F2();& &&& pc-&B::F2();& &&& pc-&F3();& & &&& cin&&& &&& return 0;& }& #include &iostream&#include &stdio.h&class A{public:public:virtual void F1(){this-&x = 1;this-&y = 2;cout&&&this指针得值是:&&&std::hex&&std::showbase&&this&&}};class B{public:public:virtual void F2(){cout&&&this指针得值是:&&&std::hex&&std::showbase&&this&&}};class C : public A, public B{public:public:virtual void F2(){cout&&&this指针得值是:&&&std::hex&&std::showbase&&this&&}void F3(){cout&&&this指针得值是:&&&std::hex&&std::showbase&&this&&}};int main(int argc, char** argv){C* pc = new C();cout&&&对象的大小为:&&&sizeof(C)&&cout&&&对象的地址为:&&&std::hex&&std::showbase&&pc&&cout&&&x的地址&&&&pc-&x&&cout&&&z的地址&&&&pc-&z&&pc-&F1();pc-&F2();pc-&B::F2();pc-&F3();cin&&return 0;}结果:内存布局:结论:再一次验证了this指针的值的确定方法,this始终要保证指向定义了该成员函数的类得subobject。因为C++保证base class subobject与base class object完全对应,从而保证了成员函数能根据成员变量在定义了该变量的类中的偏移寻址。
看过本文的人也看了:
我要留言技术领域:
取消收藏确定要取消收藏吗?
删除图谱提示你保存在该图谱下的知识内容也会被删除,建议你先将内容移到其他图谱中。你确定要删除知识图谱及其内容吗?
删除节点提示无法删除该知识节点,因该节点下仍保存有相关知识内容!
删除节点提示你确定要删除该知识节点吗?C语言中内存分布及程序运行加载过程
http://toutiao.com/i6792833/
一个程序内存分配:
下图是APUE中的一个典型C内存空间分布图(虚拟内存)
int g1=0, g2=0, g3=0;
int max(int i)
int m1=0,m2,m3=0,*p_
static n1_max=0,n2_max,n3_max=0;
p_max = (int*)malloc(10);
printf("打印max程序地址\n");
printf("in max: 0xx\n\n",max);
printf("打印max传入参数地址\n");
printf("in max: 0xx\n\n",&i);
printf("打印max函数中静态变量地址\n");
printf("0xx\n",&n1_max); //打印各本地变量的内存地址
printf("0xx\n",&n2_max);
printf("0xx\n\n",&n3_max);
printf("打印max函数中局部变量地址\n");
printf("0xx\n",&m1); //打印各本地变量的内存地址
printf("0xx\n",&m2);
printf("0xx\n\n",&m3);
printf("打印max函数中malloc分配地址\n");
printf("0xx\n\n",p_max); //打印各本地变量的内存地址
if(i) return 1;
else return 0;
int main(int argc, char **argv)
static int s1=0, s2, s3=0;
int v1=0, v2, v3=0;
p = (int*)malloc(10);
printf("打印各全局变量(已初始化)的内存地址\n");
printf("0xx\n",&g1); //打印各全局变量的内存地址
printf("0xx\n",&g2);
printf("0xx\n\n",&g3);
printf("======================\n");
printf("打印程序初始程序main地址\n");
printf("main: 0xx\n\n", main);
printf("打印主参地址\n");
printf("argv: 0xx\n\n",argv);
printf("打印各静态变量的内存地址\n");
printf("0xx\n",&s1); //打印各静态变量的内存地址
printf("0xx\n",&s2);
printf("0xx\n\n",&s3);
printf("打印各局部变量的内存地址\n");
printf("0xx\n",&v1); //打印各本地变量的内存地址
printf("0xx\n",&v2);
printf("0xx\n\n",&v3);
printf("打印malloc分配的堆地址\n");
printf("malloc: 0xx\n\n",p);
printf("======================\n");
printf("======================\n");
printf("打印子函数起始地址\n");
printf("max: 0xx\n\n",max);
打印结果:
可以大致查看整个程序在内存中的分配情况:
可以看出,传入的参数,局部变量,都是在栈顶分布,
随着子函数的增多而向下增长.
函数的调用地址(函数运行代码)(高地址)
而malloc分配的堆则存在于这些内存之上,并向上生长
全局变量,静态变量都是在分配内存的低部存在(低地址)
程序如何装载的
2 编译结果:
file a.out 查看文件类型
a.out: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.32, BuildID[sha1]=0xd66ac36636c4fcfcbe395efb6bbd38c053e1c6c7, not stripped
ELF目标文件格式的最前端是ELF文件头(ELF
Header),
包含了描述整个文件的基本属性,如ELF版本、目标机器型号、程序入口地址等
图1做了简单的说明(Linux系统下的)
左边的是UNIX/LINUX系统的执行文件,右边是对应进程逻辑地址空间的划分情况。
我理解就是类似mmap函数 直接内存映射
1 Linux内核如何装载和启动一个可执行程序
http://www.cnblogs.com/bushifudongjing/p/5361805.html
2 &程序员的自我修养—链接、装载与库.pdf&
没有更多推荐了,for myself...C语言的代码内存布局详解 - fengyv - 博客园
http://www.cnblogs.com/fengyv
posts - 189, comments - 44, trackbacks - 0, articles - 0
图引自《C专家编程》
int&a&=&0;&&&
char&*p1;&&&
&&&&static&int&c&=0;&&&
&&&&int&b;&&&
&&&&char&s[]&=&&abc&;&&&
&&&&char&*p2;&&&
&&&&char&*p3&=&&123456&;&&&
&&&&p1&=&(char&*)malloc(10);&&
&&&&p2&=&(char&*)malloc(20);&&&

我要回帖

更多关于 运行内存怎么看 的文章

 

随机推荐