代码:
- #include<iostream>
- usingnamespacestd;
- classA
- {
- private:
- intx1;
- intx2;
- public:
- A():x2(1),x1(x2++){}
- voidprint()
- {
- cout<<"x1="<<x1<<endl
- <<"x2="<<x2<<endl;
- }
- };
- intmain()
- {
- Aa;
- a.print();
- return0;
- }
疑:x1,x2最终被输出什么值呢?为什么?
解答:上机调试下会发现输出的结果是:x1是一个随机数,x2是1。为什么?因为在初始化列表中在给x1赋值为x2++时,这个x2并未初始化,也就是说x2里面什么东西也没装。也许你会问我不是在前面已经给x2赋值了吗,没错,但是有一个问题你忽略了,那就是初始化列表的赋值顺序是依照x1和x2的声明顺序的顺序来初始化的,也就是说在代码中,程序是先给x1赋值为x2++(此时x2并未初始化),再给x2赋值为1。还有初始化列表里的赋值,是在变量被声明时进行的,所以多使用初始化列表是可以提升程序效率的。
!!代码疑云系列由本人在天天唯C论坛下首发
代码:
- #include<iostream>
- usingnamespacestd;
- voidfoo(intp1,intp2,intp3)
- {
- cout<<"p1="<<p1<<endl
- <<"p2="<<p2<<endl
- <<"p3="<<p3<<endl;
- }
- intmain()
- {
- inti;
- cout<<"firstcall:"<<endl;
- i=0;
- foo(++i,++i,++i);
- cout<<"secondcall:"<<endl;
- i=0;
- foo(++i,i++,i++);
- return0;
- }
疑:两次调用foo函数分别输出了什么,为什么?
解答:按照cedel函数调用的约定,编译器使参数从左到右的入栈。第一次调用为什么p1,p2,p3的值全是3呢,原因在此,在foo被call之前三++i 操作将先被操作也就是连续自增了3次,最终结果i 的值是3,然后是编译器push(i),push(i),push(i)三次入栈,然后call到foo定义处依次出栈并相应地复制给了形参。第二次调用foo时,一开始与第一次一样先是计算三次++操作,但是所不同的是最后两个是i++,i++
刚没说到它们的计算顺序,编译器计算这些的顺序是由右到左的,也就是先i++,再i++,最后是++i,而运算i++是先取值再自增的,编译器会先把i (这时为0)存入寄存器(cpu中的存储器),再加1,然后计算下一个i++ 与前一次一样,所不同的是这次 i 的值是1,因为前面已加1 ,最后++i 。
!!代码疑云系列由本人在天天唯C论坛下首发
代码:
- #include<iostream>
- usingnamespacestd;
- intmain()
- {
- char*str1="string";
- char*str2="string";
- if(str1==str2)
- cout<<"str1issameasstr2";
- }
疑:str1 的值是否等于 str2 而输出字符串“str1 is same as str2”呢,为什么?
解答:是的 “str1 is same as srr2”,也就是说str1与str2指向了相同的内存地址,因为"string"是静态对象,是由编译器分配给他的内存空间,在代码中出现了两次,编译器并不会给他们分别分配空间,因为如果这样将会造成不必要的浪费。
代码:
- #include<iostream>
- usingnamespacestd;
- classA
- {
- };
- classB
- {
- chara;
- intb;
- };
- classC
- {
- voidfoo(){};
- };
- classD
- {
- virtualvoidfoo(){};
- };
- intmain()
- {
- cout<<sizeof(A)<<sizeof(B)<<sizeof(C)<<sizeof(D);
- return0;
- }
疑:结果是什么,为什么呢?
解答:sizeof(A)为1,而不是0,虽然空类没有任何成员变量,但其实体提供取地址操作,所以其内存空间不能为0。sizeof(B)为8,编译器在计算类体或结构体变量的地址时是通过字节对齐的方式进行的,也就是通过加多少偏移量来确认是那个变量。类的偏移量为其最大内存空间的类型的成员变量的长度,其最大内存空间类型成员是int,所以偏移量为4字节,即char a成员需要补上3个字节,才能让编译器准确高效地寻址,再加上int的4字节,sizeof(B)就是8了。sizeof(C)为1,类的成员函数在编译器编译时,其函数地址就已自动存放,所以不必为其分配额外的空间来存放他,所以它的长度跟空类一样为1。sizeof(D)为4,因为类D需要构造一虚函数列表来存放函数指针以实现动态调用,一个指针的长度占用4个字节,所以为4.
代码:
- #include<iostream>
- usingnamespacestd;
- classA
- {
- private:
- intvalue;
- public:
- A()
- {
- value=0;
- }
- voidcoutHello()
- {
- cout<<"hello"<<endl;
- }
- voidcoutValue()
- {
- cout<<value<<endl;
- }
- };
- intmain()
- {
- A*pA=NULL;
- pA->coutHello();
- pA->coutValue();
- return0;
- }
(感谢网友提供的题目)
疑:调用coutHello和coutValue方法有什么问题?
解答:
成员函数的地址在编译器编译时给出的,所以是已知的,根据thiscall约定,类的成员函数在编译时编译器会传入一个this指针,通过this指针指向成员变量,在调用couthello时并未用到this指针所以调用正常,而调用coutvalue时,value需要用到this指针,因为此时this是NULL指针,所以会发生内存报错。
代码:
头文件print_tools.h
- #include<stdio.h>
- voidprintStr(constchar*pStr)
- {
- printf("%s\n",pStr);
- }
- voidprinttInt(constinti)
- {
- printf("%d\n",i);
- }
头文件counter.h
- #include"print_tools.h"
- staticintsg_value;
- voidcounter_init()
- {
- sg_value=0;
- }
- voidcounter_count()
- {
- sg_value++;
- }
- voidcounter_out_result()
- {
- printStr("theresultis:");
- printtInt(sg_value);
- }
main.cpp
- #include"print_tools.h"
- #include"counter.h"
- intmain()
- {
- charch;
- counter_init();
- printStr("pleaseinputsomecharactors:");
- while((ch=getchar())!='$')
- {
- if(ch=='A')
- <spanstyle="white-space:pre"></span>counter_count();
- }
- counter_out_result();
- }
疑:以上代码编译时有何问题吗,是什么导致的呢?
解答:void printStr(const char*)和'void printtInt(int) 函数redefine了,道理很简单,因为在counter.h中已包含了print_tools.h,在main.cpp中又包含了print_tools.h头文件,也就是两次包含了print_toosl.h,所以也就发生了重定义错误,这是初学者常见问题,要避免这个问题,我们必须采取防范措施,就是使用预编译命令,如下作修改:
头文件print_tools.h
- #ifndefPRINT_TOOLS_H//如果未定义PRINT_TOOLS_H
- #definePRINT_TOOLS_H//则定义然后编译以下源代码
- #include<stdio.h>
- voidprintStr(constchar*pStr)
- {
- printf("%s\n",pStr);
- }
- voidprinttInt(constinti)
- {
- printf("%d\n",i);
- }
- #endif//结束//如此改头文件就只被编译器编译一次了,起到了防范重定义错误的作用
头文件counter.h
- #ifndefCOUNTER_H//
- #defineCOUNTER_H//
- #include"print_tools.h"
- staticintsg_value;
- voidcounter_init()
- {
- sg_value=0;
- }
- voidcounter_count()
- {
- sg_value++;
- }
- voidcounter_out_result()
- {
- printStr("theresultis:");
- printtInt(sg_value);
- }
- #endif//
在一个工程项目中头文件众多繁杂,采用这个方法可以很好的避免,所以每当我们定义一个头文件时要养成如此的良好习惯。
代码:
- #include<iostream>
- usingnamespacestd;
- classA
- {
- public:
- A()
- {
- Print();
- }
- virtualvoidPrint()
- {
- cout<<"Aisconstructed.\n";
- }
- };
- classB:publicA
- {
- public:
- B()
- {
- Print();
- }
- virtualvoidPrint()
- {
- cout<<"Bisconstructed.\n";
- }
- };
- intmain()
- {
- A*pA=newB();
- deletepA;
- return0;
- }
(感谢网友提供的题目)
疑:以上代码输出结果是?
输出结果如下:
A is constructed.
B is constructed.
解释:当new B()时,因为B继承了A,所以编译器首先调用A的构造函数,A的构造函数里是调用A类里的Print(),所以首先输出A is constructed ,然后调用的是B的构造函数,此时Print虚函数指针已指向B的void print(),所以输出B is constructed。
代码:
- #include<stdio.h>
- intmain()
- {
- inta=0,b=0;
- printf("%d\n",!a||++b||b++);
- printf("b=%d\n",b);
- printf("%d\n",!a&&++b&&b++);
- printf("b=%d\n",b);
- return0;
- }
疑:以上输出什么,为什么?
解答:int a=0,b=0;在在第一次printf语句中,!a为1,后面的++b和b++就不做了,所以第二次输出是0,第三次printf中是与运算,后面的++b和b++还要做的,所以第二次会输出b=2。
代码:
- #include<stdio.h>
- char*GetString1()
- {
- charp[]="HelloWorld";
- returnp;
- }
- char*GetString2()
- {
- char*p="HelloWorld";
- returnp;
- }
- intmain()
- {
- printf("GetString1returns:%s.\n",GetString1());
- printf("GetString2returns:%s.\n",GetString2());
- return0;
- }
(感谢网友提供的题目)
疑:以上输出结果是什么情况?
………………………………………………………………………………………………………………………………………………………
解答:结果是GetString1()是乱码,GetString2()是 “Hello World”。因为GetString1()返回的是是指向该函数作用域里的局部变量char p[]的首内存空间,而当函数返回到主函数时,该内存空间将会被释放,所以P指向了一个无效的内存空间。而GetString2()中的p是指向全局对象(字符串“Hello
World”)的,所以是“HelloWorld”.
- #include<stdio.h>
- #include<string.h>
- structTStudent
- {
- longintID;
- union{
- charname[10];
- charmingzi[11];
- };
- chardept[17];
- };
- intmain()
- {
- TStudentstu;
- strcpy(stu.name,"zh");
- strcpy(stu.dept,"computerscience");
- printf("student`smingziis:%s\n",stu.mingzi);
- printf("student`snameis:%s\n",stu.name);
- printf("student`sdepartmentis:%s\n",stu.dept);
- }
疑:以上代码输出结果是什么,为什么?
……………………………………………………………………………………………………………………………………
参考解答:
union{
char name[10];
char mingzi[11];
};
定义这个就代表name[10]和mingzi[11]共享同一块内存。这里顺便提一下,mingzi[11]的空间大于name[10],所以申请空间的时候以union里面最大的字段为准。假如
union
{
char name[10];
char mingzi[11];
}UN_NAME;
那么sizeof(UN_NAME) 应该等于11。即取最大的字段的空间。当然这里不涉及字节对齐问题。
所以当strcpy(stu.name,"zh");执行这句的时候,mingzi[11]的内存空间也被赋值了"zh",所以最后打印两者的结果是一样的。
代码:
- #include<stdio.h>
- #include<stdlib.h>
- voidswap(int*pa,int*pb)
- {
- inttemp;
- temp=*pa;
- *pa=*pb;
- *pb=temp;
- }
- intmain()
- {
- chara[]="BACD";
- swap((int*)&a[0],(int*)&a[1]);
- printf("afterswap:\n");
- printf("%s",a);
- system("pause");
- return0;
- }
疑:以上c程序调用swap函数后能否得到预期的结果?结果是?
……………………………………………………………………………………………………………………………………………………………………………………
解释:不能,在32位机中,a为char型数组,各元素占用1字节内存空间,而在swap函数声明里的pa和pb为int 型指针,也就是说编译器会把pa,pb解析为指向连续四字节空间的指针,在进行各种运算时就是对pa、pb所指向的四字节进行运算,那么以上程序的pa、pb指向哪里呢?通过打印它们发现pa和pb的地址相差为1,pa小于pb,因为pa,pb对应a数组的地址所以实际上pa所指的四个字节是a数组下标第0,1,2,3的四个元素,而pb指向a数组下标为1,2,3,4的四个元素。图示:
pa对应a数组编号: 0 1 2 3
pa所指内存空间: | ‘B’ | ‘A’ | 'C' | 'D' |
pb对应a数组编号: 1 2 3 4
pb所指内存空间: | ’A‘ | 'C' | 'D' |'\0' |
所以在swap里,首先temp存储了*pa为[B,A,C,D],执行*pa=*pb后pa就变为[A,C,D,\0],此时a数组就变为[A,C,D,\0,\0], 然后执行*pb=temp,结果pb=[B,A,C,D],a数组又变为[A,B,A,C,D]。所以打印a字符串数组的最终结果是:ABACD然后后面跟着一堆乱码。
总结:强制转换指针变量类型是不明智的选择,除非你能确保转换后所指向的内存空间大小与没转换后是一样的,如果不是将会带来灾难性的后果,导致程序中其它变量被串改或由于指向了某段未经声明的内存空间而变为野指针。
分享到:
相关推荐
自《如月疑云》上映以来,这部电影以其别具一格的叙事方式、扣人心弦的悬疑情节以及蕴含其中的幽默元素,成为了不少影迷热烈讨论的话题。它不仅是一部让人捧腹大笑的喜剧片,更是一次思维的盛宴,提供了丰富的推理...
这可能是通过植入恶意代码或者利用系统漏洞实现的。一旦APP获取了音频数据,它可以分析用户的消费习惯,甚至可能在用户谈论特定商品时推送相关广告,这被称为“定向广告”。尽管这听起来像是科幻电影的情节,但在...
绑架疑云是一个涉及到犯罪侦查、社区安全监控、人际交往和心理应对等多个知识点的案例。在这个案例中,我们可以深入探讨以下几个方面的内容: 1. 犯罪侦查知识:案例中刑警李福生在接到孙子失踪的消息后,没有慌乱...
六年级语文纸团疑云PPT教案学习.pptx
上述代码首先将中文字符串按照“编码1”转换成字节,然后再将这些字节按照“编码2”转换回字符串。这种方法适用于没有内置编码选择功能的串口工具,通过程序代码的转换来确保正确的中文显示。 解决串口乱码问题的...
本周电子行业的焦点集中在了三安光电的财务疑云上,这一事件引发了整个LED产业的深思,仿佛整个产业正处在凛冽的寒冬之中。我们来深入探讨一下这个话题。 首先,三安光电作为国内LED行业的领军企业,其财务状况一直...
《IT经理世界》中国联通G网升级疑云.doc
汽车整车行业:特斯拉专题报告-疑云疏散,否极泰来
《如月疑云》是一部融合了推理与喜剧元素的电影,全长108分钟,以其独特的叙事手法和出人意料的情节转折赢得了观众的喜爱。影片最初被介绍为一部悬疑片,可能会让部分观众产生抵触心理,然而,观影后的体验却打破了...
电子行业周报:三安财务疑云发酵,LED产业寒冬凛冽.pdf
随着互联网技术的飞速发展,电商平台的推荐系统变得越来越智能化,能够根据用户的行为和偏好提供个性化服务。然而,用户在享受个性化推荐的便利的同时,也日益关注其个人隐私和数据安全问题。本文通过几个案例,分析...
在PCB设计中,反射是影响信号完整性和系统性能的关键因素。反射通常发生在信号线上的阻抗不连续处,例如连接器、过孔、走线宽度变化或端接不匹配等。这些不连续性会导致信号的能量部分反射回源头,产生信号失真,...
食品饮料行业:消费税疑云消散,继续看好白酒啤酒-1209-中信建投-35页.pdf
线下逆袭疑云:淘品牌纠结的“独立战争” 淘品牌,源自电商平台,特别是淘宝,以其独特的商业模式和快速崛起,一度成为电商领域的亮点。然而,近年来,一些淘品牌开始尝试开设线下实体店,试图打破纯电商模式,实现...
【步步高】2014-2015学年高中物理 2.1-2.2 拨开黑体辐射的疑云 涅槃凤凰再飞翔课件 沪科版选修3-5
违约研究系列专题之三:康而不实,美难掩瑕,康美药业的疑云与启示-0603-招商证券-12页.pdf
苹果公司于2023年9月宣布其新款Apple Watch系列实现了碳中和,这是一个备受瞩目的里程碑,尤其是在全球智能手机市场下滑的背景下。然而,这一声明伴随着一些争议,因为供应链中的部分供应商碳排放量并未显著下降,...
当前,食品饮料行业特别是白酒和啤酒板块,一直受到投资者的密切关注。近期,消费税政策的变化为行业带来了新的...随着消费税政策疑云的消散,食品饮料行业有望继续保持稳定的增长趋势,并为投资者带来持续的投资回报。
小学教案