以下为牛客网C/C++专项刷题:
1、有以下程序:
#include
#include
void fun( char *s )
{
char a[10];
strcpy ( a, "STRING" );
s = a ;
}
main( )
{
char *p= "PROGRAM" ;
fun( p );
printf ( "%s\n ", p) ;
}
程序运行后的输出结果是(此处□代表空格)?
STRING
STRING□□□□
STRING□□□
PROGRAM
KEY:D
解答:改变非指针变量,需要指针,来传递其地址,改变一级指针需要二级指针来接受其地址。
2、以下代码的执行结果是()。
int main(){
int i=-2147483648;
return printf("%d,%d,%d,%d",~i,-i,1-i,-1-i);
}
0,2147483648,2147483649,2147483647
0,-2147483648,-2147483647,2147483647
2147483647,2147483648,2147483649,2147483647
2147483647,-2147483648,-2147483647,2147483647
KEY:D
解答:先补充一下:负数的反码: 符号位不变,其余各位按位取反;负数的补码:在其反码的基础上+1(符号位也参与计算)。
首先,-2147483648这个数为32位int所能表示的最小负整数,而如果原码为 1000 0000 0000 0000 0000 0000 0000 0000(表示-0) ,其反码应为数值位取反,符号位不变,即1111 1111 1111 1111 1111 1111 1111 1111;补码为反码+1,即为0000 0000 0000 0000 0000 0000 0000 0000 (最高位溢出,舍去)。
而+0 的原码、反码、补码均为 0 000 0000 0000 0000 0000 0000 0000 0000,
也就是说,如果用 1000 0000 0000 0000 0000 0000 0000 0000作为 -2147483648的原码,则会导致 -2147483648和0的补码表示一样,因此,计算机中规定用 1000 0000 0000 0000 0000 0000 0000 0000来作为 -2147483648的补码,以确保-2147483648~2147483647都有唯一的补码表示;而 -2147483648的原码,对不起,写不出来,这个数只有补码。原码只能表示 -2147483647~ 2147483647之间的数。
总结以上内容:正数的原码、反码、补码形式一致,负数的反码为原码的数值位取反,补码为反码+1也即是原码的数值位取反再+1,计算机中以补码表示数据和运算,而32位最小负整数的补码为 1000 0000 0000 0000 0000 0000 0000 0000。
然后回到本道题目的解答:
首先,求 ~i , i的补码为1000 0000 0000 0000 0000 0000 0000 0000,取反0111 1111 1111 1111 1111 1111 1111 1111,此为补码,符号位为0,表示正数,正数原码补码一致,因而该数即表示2^31-1,即2147483647 。(取反操作连同符号位一起取反)
然后,求 -i ,要对一个数值执行单目运算符 - 表示的是对该数取反然后再+1,也即是我们常说的求补运算,注意这里取反+1与原码求补码的区别!也就是求补运算与求补码是不一样的!
例子:(4位有符号整数)x=-4,原码为:1100;(补码)-x=~x+1,也即是 0011+0001=0100(4);而1100再求补码应是先数值位取反,即1011,然后+1,变成1100!注意这两者(求补与求补码)之间的区别。
本质上其实就是:在求补运算的时候,是按位取反再加1(取反也要包括符号位);求补码的时候,是数据位取反再加1(取反不包括符号位)。
题目中,i的补码为 1000 0000 0000 0000 0000 0000 0000 0000,取反+1,仍为 1000 0000 0000 0000 0000 0000 0000 0000,即 -2147483648。
求 1-i,我们已经求出-i的补码为1000 0000 0000 0000 0000 0000 0000 0000 ,加上1的补码即为 1000 0000 0000 0000 0000 0000 0000 0001,该补码表示的原码为1 111 1111 1111 1111 1111 1111 1111 1111,即为- 2147483647。
最后求-1-i -1的补码为1 111 1111 1111 1111 1111 1111 1111 1111,加上-i补码 1000 0000 0000 0000 0000 0000 0000 0000,得 0111 1111 1111 1111 1111 1111 1111 1111,即 2147483647。
另外补充一点,计算机中有符号数和无符号数的机器码(补码)是一样的,同一个二进制码按照有无符号输出结果不一样,例如本题中四个答案如果按照无符号整数输出,那么答案就是C。
3、32位机上根据下面的代码,问哪些说法是正确的?
signed char a = 0xe0;
unsigned int b = a;
unsigned char c = a;
a>0 && c>0 为真
a == c 为真
b 的十六进制表示是:0xffffffe0
上面都不对
KEY:C
解答:将char转换为int时,关键看char是unsigned还是signed,如果是unsigned就执行0扩展,如果是signed就执行符号位扩展。跟int本身是signed还是unsiged无关。
也就是说,短数据类型扩展为长数据类型,看的是需要扩展的数据类型,而不是扩展成的数据类型。
4、逻辑运算符两侧运算对象的数据类型()。
只能是0或1
只能是0或非0正数
只能是整型或字符型数据
可以是任何类型的数据
KEY:D
5、7&3+12的值是15。请问这句话的说法是正确的吗?
正确
错误
KEY:B
解答:+优先级高于&(按位与)。乘除运算符>加减运算符>左移右移运算符>关系运算符>位运算符>逻辑运算符>赋值运算符。
6、若有int w=1,x=2,y=3,z=4;则条件表达w 正确 错误 KEY:B 解答:三目运算符为左结合,w 运算符中,只有单目运算符,三目运算符,和赋值运算符为左结合,其余都是右结合。 7、下列哪种方法不能用于文本加密() RSA RC4 MD5 DES KEY:C 解答:RSA:由 RSA 公司发明,是一个支持变长密钥的公共密钥算法,需要加密的文件块的长度也是可变的,非对称算法; RC2和RC4:对称算法,用变长密钥对大量数据进行加密,比 DES 快; DES(Data Encryption Standard):对称算法,数据加密标准,速度较快,适用于加密大量数据的场合; MD5:严格来说不算加密算法,只能说是摘要算法。 8、将逻辑代码: 用表达式:return x & -2; 替代,以下说法中不正确的是( )。 计算机的补码表示使得两段代码等价 用第二段代码执行起来会更快一些 这段代码只适用于x为正数的情况 第一段代码更适合阅读 KEY:C 解答:代码的本意是返回偶数。计算机内部表示的整数的方式为补码,正数为自身,负数为符号位不变其余各位取反加1。理解本体分两部分: 当为奇数时,补码的最后一位比为1,与-2相与时其它位不变最后一位为1,此时根据负数到源码的转换规则,各位取反加1,此时相当于加上-1。 9、定义函数模板add,以下说法正确的有:() pfun2 = add失败,需要显示定义为pfun2 = add pfun1 = add pfun3 = add失败,add无法正确推导 pfun4 = add KEY:BC 解答:A:pfun2 = add的错误提示不是pfun2 = add,而是 no matches converting function 'add' to type 'const int (*)(int&, int&); B:pfun1的参数和返回值一致,可以自动推导,正确赋值; C:pfun3 = add赋值失败,失败原因和A一样; D:pfun4 = add显示的指定类型T为int型,赋值正确。 10、针对以下代码, 判断下列说法哪个是正确的(注意是地址):______。 str1和str2地址不同,P1和P2地址相同。 str1和str2地址相同,P1和P2地址相同。 str1和str2地址不同,P1和P2地址不同。 str1和str2地址相同,P1和P2地址不同。 KEY:A 解答:str1和str2毫无干系,他们都是字符常量数组,恰巧存储的值相同而已。 后面的两个“abc”存储在常量区,因为他们是常量,但是p1 p2存储在栈中。所以p1和p2的值不同,但是他们指向同一块静态存储区域,也就是"abc"的地址。 11、T是一个数据类型,关于std::vector::at 和 std::vector::operator[] 描述正确的是: at总是做边界检查, operator[] 不做边界检查. at 不做边界检查, operator[] 做边界检查. at和operator[] 是一样的 KEY:A 解答:(详情参看 《C++ Primer》(第5版) P310 ”下标操作和安全的随机访问“): 提供快速随机访问的容器(如:string、vector、deque 和 array)也都提供下标运算符(operator [ ])。 下标运算符接受一个下标参数,返回容器中该位置的元素的引用。给定下标必须保证”在范围内“(即,大于等于0,且小于容器的大小)。保证下标有效是程序员的责任,下标运算符并不检查下标是否在合法范围内。使用越界的下标是一种严重的程序设计错误,而且编译器并不检查这种错误。 也就是说:c[n]:返回c中下标为n的元素的引用,n是一个无符号整数。若n>=c.size(),则函数行为未定义。 如果希望确保下标是合法的,可以使用at成员函数。at成员函数类似下标运算符,但如果下标越界,at会抛出一个out_of_range异常。 也就是说:c.at(n):返回下标为n的元素的引用。如果下标越界,则抛出一个out_of_range异常。 12、以下对枚举类型名的定义中正确的是()。 enum a={sum,mon,tue}; enum a {sum=9,mon=-1,tue}; enum a={"sum","mon","tue"}; enum a {"sum","mon","tue"}; KEY:B 解答:枚举变量定义的方式和结构体、共用体类似: 13、通过一个对象调用虚函数时,C++系统对该调用采用( )。 动态联编 静态联编 不确定是哪种联编 函数重载 KEY:A 解答:静态联编:通过对象名调用虚函数,在编译阶段就能确定调用的是哪一个类的虚函数; 动态联编:通过基类指针调用,在编译阶段无法通过语句本身来确定调用哪一个类的虚函数,只有在运行时指向一个对象后,才能确定调用时哪个类的虚函数。 14、函数fun的声明为int fun(int *p[4]),以下哪个变量可以作为fun的合法参数() int a[4][4]; int **a; int **a[4] int (*a)[4]; KEY:B 解答:一个二维数组相当于一个数组指针。而fun函数是一个指针数组,归根结底就是一个数组,数组元素为指针。所以合法参数就需要是数组首元素的地址就行了,而首元素是一个指针,所以一个二级指针就行了。 也可以通过+1来确定。int a[4][4]和int (*a)[4]表示的数组指针,+1的话表示越过一行,间隔int[4];int **a表示的是指向指针的指针,+1的话表示越过一个指针,间隔int*;而题目的int *p[4]表示指针数组,+1的话表示越过一个指针,间隔int*。很明显,应该选择B。 15、求sizeof(s)。 16 32 20 24 KEY:A 解答:在存储信息时,不必用一个或多个字节,而只需要占几个二进制位就可以了。例如在存放一个开关量时,只有开和关两个状态,用一个二进制位即可。为了节省存储空间,并使处理简便,C语言允许在一个结构体中以位为单位来指定某成员所占内存长度,这种以位为单位的成员,称为“位段”或“位域”。 位段定义的一般形式为: 位段中数据引用的一般形式为: 需要注意的是: 16、下列关于makefile描述正确的有? makefile文件保存了编译器和连接器的参数选项 主要包含了五个东西:显式规则、隐晦规则、变量定义、文件指示和注释 默认的情况下,make命令会在当前目录下按顺序找寻文件名为“GNUmakefile”、“makefile”、“Makefile”的文件, 找到了解释这个文件 在Makefile不可以使用include关键字把别的Makefile包含进来 KEY:ABC 解答:makefile文件保存了编译器和连接器的参数选项,还表述了所有源文件之间的关系(源代码文件需要的特定的包含文件,可执行文件要求包含的目标文件模块及库等)。 创建程序(make程序),首先读取makefile文件,然后再激活编译器,汇编器,资源编译器和连接器以便产生最后的输出,最后输出并生成的通常是可执行文件。创建程序利用内置的推理规则来激活编译器,以便通过对特定CPP文件的编译来产生特定的OBJ文件。 Makefile里主要包含了五个东西:显式规则、隐晦规则、变量定义、文件指示和注释。 默认的情况下,make命令会在当前目录下按顺序找寻文件名为“GNUmakefile”、“makefile”、“Makefile”的文件,找到了解释这个文件。在这三个文件名中,最好使用“Makefile”这个文件名,因为,这个文件名第一个字符为大写,这样有一种显目的感觉。最好不要用 “GNUmakefile”,这个文件是GNU的make识别的。有另外一些make只对全小写的“makefile”文件名敏感,但是基本上来说,大多数的make都支持“makefile”和“Makefile”这两种默认文件名。 在Makefile使用include关键字可以把别的Makefile包含进来,这很像C语言的#include,被包含的文件会原模原样的放在当前文件的包含位置。 include的语法是: include GNU make 的工作方式: 参考文章:Makefile 使用总结、教你写Makefile(很全,含有工作经验的)。 17、在x86的机器上,int a=0xabcd1234,char b=((char*)&a)[0]请问b是多少()。 0xa 0x4 0xab 0x34 KEY:D 解答:x86是小端存储,即高位存储在高地址,低位存储在低地址。int a = 0xabcd1234;, 内存中 ab cd 12 34,b作为一个char,右边表达式指针指向为0x34。 高 --> 低 18、如下代码,result变量的输出结果是多少? KEY:11 解答:首先要明白变量初始化的顺序是其声明的顺序,跟初始化列表中的顺序无关。所以变量的初始化顺序为m_nFir(i++),m_nSec(i++),m_nThd(i++),&m_nFor(m_nThd); i初始值为1,所以经过初始化列表初始化以后m_nFir=1,m_nSec=2,m_nThd=3,m_nFor为m_nThd的一个引用。并且此时i的值为4。对象成员的初始化结束后,开始执行本类的构造函数。构造函数中执行语句m_nThd=i后,m_nThd=4,m_nFor是它的一个引用,自然值也为4。 输出结果m_nFir+m_nSec+m_nThd+m_nFor=1+2+4+4=11。 19、词法分析器用于识别() 句子 句型 单词 生产式 KEY:C 解答:编译的过程为:扫描(词法分析)、语法分析、语义分析、源代码优化、代码生成和目标代码优化。 源码 ->(扫描)-> 标记 ->(语法分析)-> 语法树 ->(语义分析)-> 标识语义后的语法树 ->(源码优化)-> 中间代码 ->(代码生成)-> 目标机器代码 ->(目标代码优化)-> 最终目标代码。 20、设顺序表的长度为n。下列算法中,最坏情况下比较次数小于n的是()。 寻找最大项 堆排序 快速排序 顺序查找法 KEY:A 解答:顺序表不是、不是、不是元素排列有序的线性表,而是指元素以顺序存储结构存储在相邻的物理存储单元上,元素之间没有直接大小关联。 2个数比较大小 比较1次即可找出最大,3个数比较两次即可找出最大。同理n个数最多比较n-1次即可找出最大。 21、有以下说明语句: 则下列错误的引用是( )。 w.no p->no (*p).no *p.no KEY:D 解答:.的优先级高于*。 22、能够把指定长度的字节序列插入到输出流中的函数是()。 put write cout print KEY:B 解答:write函数所在的头文件为 其中:handle 是文件描述符;buf是指定的缓冲区,即指针,指向一段内存单元;nbyte是要写入文件指定的字节数;返回值:写入文档的字节数(成功);-1(出错)。str是字符指针或字符数组,用来存放一个字符串;n是int型数,它用来表示输出显示字符串中字符的个数。 23、下列关于赋值运算符“=”重载的叙述中,正确的是 赋值运算符只能作为类的成员函数重载 默认的赋值运算符实现了“深层复制”功能 重载的赋值运算符函数有两个本类对象作为形参 如果己经定义了复制拷贝构造函数,就不能重载赋值运算符 KEY:A 解答:默认的赋值运算符、拷贝构造函数都是实现了“浅层复制”功能。 例如: 参考文章:C++基础---浅层及深层拷贝构造函数。 24、类 CBase 的定义如下: 在构造函数 CDerive 的下列定义中,正确的是() CDerive::CDerive(int a,int b,int c):x(a),y(b),z(c){} CDerive::CDerive(int a,int b,int c):CBase(a),y(b),z(c){} CDerive::CDerive(int a,int b,int c):CBase(a),CDerive(b),z(c){} CDerive::CDerive(int a,int b,int c):x(a),CBase(b),z(c){} KEY:B 解答:三个变量分别用于基类构造函数初始化(使用基类)、对象成员构造函数初始化(使用对象成员名称)、派生类普通成员初始化(使用对象成员名称)。 25、下列程序执行后的输出结果是()。 -32767 FFFE -1 -32768 KEY:C 解答:因为x是字符型,所以将0xFFFF赋值给x,x的值为0xFF,因为数据在内存中是其补码表示的,OxFF的真值是-1,所以显示的结果是-1,即正确答案为C。 26、假设A为抽象类,下列声明( )是正确的 A fun(int); A *p; int fun(A); A obj; KEY:B 解答:抽象类不能实例化, 不能参数类型和函数返回类型。但是,抽象类可以用作指针类型,因而B选项正确。 27、某二叉树的前序序列为ABCDEFG,中序序列为DCBAEFG,则该二叉树的后序序列为()。 EFGDCBA DCBEFGA BCDGFEA DCBGFEA KEY:D 解答:前序序列:根左右;中序序列:左根右;后序序列:左右根。也就是说,左右的顺序是不会变的,根的顺序依次挪位。 前序序列为ABCDEFG 先访问结点,再到左孩子,右孩子 中序序列为DCBAEFG 先左,再结,后右 A是结点 BCD是A的左孩子 EFG是A的右孩子 A下是B结点,接着C是B的左孩子,D是C的左孩子 EFG以此类推,画图辅助更快。 怎么分析比较快呢? 前序ABCDEFG,所以A一定为根结点,在看一下中序DCBAEFG,看A的位置,可得出,DCB三个一定是在A的左边。分析前序BCD,所以B一定为A左边的根结点,看一下中序DCB中B的位置,所以DC一定是在B的左边……依次分析。 28、在32位环境下,以下代码输出的是( )。 ABAB~A~A ABAB~B~A~B~A ABAB~B~A ABAB~A~B~A~B KEY:A 解答:在C++中,析构函数的作用是:当一个对象被销毁时,调用析构函数对类对象和对象成员进行释放内存资源。 当我们定义一个指向派生类类型对象指针时,构造函数按照从基类到派生类的顺序被调用,但是当删除指向派生类的基类指针时,派生类的析构函数没有被调用,只是调用了基类的析构函数,此时派生类将会导致内存泄漏。 我们需要将基类的析构函数声明为虚函数,此时在调用析构函数的时候是根据ptr指向的具体类型来调用析构函数,此时会调用派生类的析构函数。 也就是说: 此时返回值为:ABAB~B~A~B~A。 或者: 此时返回值为:ABAB~B~A~B~A。 29、有如下程序段: 请问执行结果是: 21 22 无限循环 程序崩溃 KEY:C 解答:题中浮点数比较使用的是while( x != 1.0 ),正确方法应该是:判断浮点数a,b是否相等: fabs(a-b) 30、变量 val 的内存地址位于: 已初始化数据段 未初始化数据段 堆 栈 KEY:B 解答:初始化的全局变量和静态变量存放在 data段(全局初始化区);未初始化的全局变量和未初始化的静态变量存放在bss段(全局未初始化区)。 其中BSS段的特点是:在程序执行之前BSS段会自动清0,所以未初始化的全局变量和静态变量在程序执行之前就已经变成0,但是并不是存放在初始化段,是因为存放在未初始化段才初始化为0的。 所以选B。 text和data段都在可执行文件中(在嵌入式系统里一般是固化在镜像文件中),由系统从可执行文件中加载;而bss段不在可执行文件中,由系统初始化。 一个程序本质上都是由 bss段、data段、text段三个组成的。 比如,在C语言之类的程序编译完成之后,已初始化的全局变量保存在.data 段中,未初始化的全局变量保存在.bss 段中。 text和data段都在可执行文件中(在嵌入式系统里一般是固化在镜像文件中),由系统从可执行文件中加载;而bss段不在可执行文件中,由操作系统初始化清零。 同时,bss段并不给该段的数据分配空间,只是记录数据所需空间的大小;data段则为数据分配空间,数据保存在目标文件中。 31、没用参数的两个函数是不能重载的.说法是否正确? 正确 错误 KEY:B 解答:C++不允许仅根据函数的返回类型重载函数名称;可以编写两个名称相同,参数也相同的函数,其中一个是const,另一个不是。例如: 32、设有定义:int x = 0,﹡p; 紧接着的赋值语句正确的是? *p = NULL; p = NULL; p = x; *p = x; KEY:B 解答:指针P没有指向某个地址,如果P初始化了的话,D就是对的。也就是说,申明*p之后p是一个空指针,只能p = &x,不能给空指针赋值。 33、下面程序应该输出多少? POINTERSTEW FERSTEPOINW NEWPOINTW POINTFIREST KEY:A 解答:c是一个指针数组,每个数组单元都是一个指针,指向一个字符串。即c[0]="ENTER"......c[3]="FIRST" 由于[]与*运算本质几乎是一致的,以下用[]替换*更容易理解。也就是:c[i]=*(c+i) c和c+i都是char*[]类型,它可以退化成char**类型,它正好是一个char**数组,cp[0]=c+3 .....cp[3]=c,引用后cp[0][0]=*(c+3)=c[3]="FIRST"。 cp是char**[]类型,它可以退化成char***类型,正好与cpp类型一致。 1>++ccp的值是cp+1 *++p=*(cp+1)=cp[1] **++p=*(cp[1])=c[2]="POINT" 2>运算符优先级决定运算先后关系:++ccp的值是cp+2 *++p=*(cp+2)=cp[2]=c+1 --*++p=c *--*++p=*(c+0)=c[0]="ENTER"再+3字符串指向"ER" 3>cpp的值为cp+2 cpp[-2]=*(cpp-2)=*(cp+2-2)=cp[0]=c+3再引用*(c+3)=c[3]="FIRST"字符串指到"ST" 4>cpp的值没变,cpp[-1]=*(cpp-1)=*(cp+2-1)=cp[1]=c+2再[-1]得*(c+2-1)=c[1]="NEW",+1字符创指针指到"EW"。 首先看 C,CP,CPP 之间的存储关系: 计算各个表达式结果的过程如下图: 34、在vs编译环境下,以下代码的运行情况: 编译不过 运行错误 abc cba KEY:D 解答:当用函数做实参时,编译器一般会根据参数传递顺序,先计算出函数的返回值,然后将返回值传递给原来的函数。 而函数的参数是通过栈传递的。因此参数从右往左入栈顺序是:printf("c"),printf("b"),printf("a")。依次计算出结果:cba。 35、下面程序的执行结果: 11111111 12121212 11112222 21212121 22221111 KEY:E 解答:这道题应该注意:指针类型加减时步长的问题。A大小为4;B大小为8。那么:当调用seta()方法的时候,由于传入的实参为B类型,大小为8,而形参为A类型,大小为4。 data[idx] 取 data + idx 处的元素,这时指针 data加1 的长度不是一个B长度,而是一个A长度,或者说是1/2个B长度。这时该函数中 data[0~3] 指向的是原 data[0].a,data[0].b,data[1].a,data[1].b, 由于隐式类型转换的缘故,data[0].a, data[0].b,data[1].a,data[1].b 处的值全部由于 data[idx].a = 2; 操作变为 2。 这道题如果改为void seta(B* data, int idx),那么形参中data指针加1步长为8,结果就是21212121。但是由于步长为4,所以结果就是 22221111。 36、有以下程序: 程序的输出结果是? 21.10 11.10 12.10 1.10 KEY:D 解答:本题考查把数组名作为函数参数,执行fun函数后,s的值并没有发生变化,仍然是指向a,所以输出结果为1.10,选项D正确。 37、std::vector::iterator重载了下面哪些运算符? ++ >> *(前置) == KEY:ACD 解答:迭代器当指针看,++指针自增,*取内容,==判断指针相等,<<没有。 38、有声明: 在下列选项中,正确的调用是( )。 int a=15; int n=fun6(&a); int a = 15; cout<<(&pf)(a); cout<<(pf)( 256 ); cout << pf( 256 ); KEY:CD 解答:对于函数指针来说,用它调用函数的方法可以有: 也就是说,fun6之前的指针符号可以使用,也可以不使用。 39、有如下C++代码: 那么输出为: barfoob_bar foobarb_bar barfoob_foo foobarb_fpp KEY:A 解答:一般不建议在构造函数或者析构函数中调用虚函数,因为在构造函数和析构函数中调用虚函数不会呈现多态性,也就是说在构造基类构造函数的时候,派生类的部分还没有构造,怎么谈去用虚函数实现动态绑定派生生类对象呢,所以构造B基类部分的时候,调用的基类的函数bar。 也就是说:在构造函数中调用虚函数时,只调用自己类中定义的函数(若自己类中没有定义,则调用基类中定义的函数),而不是调用派生类中重新定义的虚函数。 40、下面代码的输出结果是()。 父子进程中输出的num相同,num地址不相同 父子进程中输出的num不同,num地址相同 父子进程中输出的num相同,num地址也相同 父子进程中输出的num不同,num地址不相同 KEY:B 解答:虚拟地址空间。num地址的值相同,但是其真实的物理地址却不一样。linux下实现了一下,发现地址值真的一样。fork之后子进程复制了父进程的数据、堆栈。但是由于地址重定位器之类的魔法存在,所以,看似一样的地址空间(虚拟地址空间),其实却是不同的物理地址空间。同时可以验证c程序中输出的地址空间其实都是虚拟地址空间。 也就是说,本题问题就出在地址相不相同,如果按照两个进程各处在独自的虚拟进程地址空间分析的话,这个题很容易会选择第四个答案,但是Linux中的资源分配都是虚拟机制,也就是说,他们还是会共用一个虚拟的地址,但是映射到物理内存就可能会不一样。 41、下面代码的执行结果是()。 CAMPUS RECRUITING RECRUITING CAMPUS CAMPUS CAMPUS RECRUITING RECRUITING KEY:C 解答:本题的逻辑可以画出下面的图: printf(“%s”,**++ppp);即,ppp当前所指向的位置,再往下移一个位置,即pp的位置2,而pp的位置2指向的是p的位置2,p的位置2指向的是CAMPUS,所以先输出CAMPUS; printf(“%s”,*++*++ppp);这个语句等价于 printf(“%s”,*++(*++ppp));所以我们首先看,++ppp,第一个printf语句中ppp已经指向了pp的位置2,所以再往下移一个,指向了pp的位置3,而(*++ppp)则代表pp位置3所指向的内容,即p的位置1(pp的位置3指向的是p的位置1),在此基础上前面再加上一个++,则代表指针p在位置1的基础上再往下移动,即指针p的位置2,而p的位置2所指向的内容是CAMPUS,所以第二行输出的也是CAMPUS。 也就是说,p、pp、ppp都是指针,都是指向上一级的指针。加上*就是取上一级的值,不加就是上一级的地址。ppp的值为pp的地址,pp的值为p的地址,p的值就是字符串。 42、以下叙述中正确的是() 在C语言中,逻辑真值和假值分别对应1和0 关系运算符两边的运算对象可以是C语言中任意合法的表达式 对于浮点变量x和y,表达式:x==y 是非法的,会出编译错误 分支结构是根据算术表达式的结果来判断流程走向的 KEY:B 解答:A选项中,在C语言中,逻辑真值对应非0; C选项中,表达式:x==y 是合法的;D选项中,分支结构的流程走向是根据表达式的值,并不仅仅是算数表达式的值。因此B选项正确。 43、在16位C编译系统上,以下程序的输出结果是()。 y=│□□-43456│ y=│-□□43456│ y=│-0043456│ y=│-43456□□│ y=│□□-43456│ y=│-43456□□│ y=│-0043456│ y=│-□□43456│ y=│-43456□□│ y=│-43456□□│ y=│-0043456│ y=│□□-43456│ y=│-43456□□│ y=│-4345600│ y=│-0043456│ y=│□□-43456│ KEY:C 解答:注意一下区别: %-81d表示输出占8个空格的位置,左对齐,右边多余的位置补空格,故第一个y的输出为:│-43456□□│; %-081d等价于%-81d,因为不可能在右边空位上补0,所以第二个y的输出也为:│-43456□□│; %081d表示输出占8个空格的位置,右对齐,左边多余的位置补0,所以第三个y的输出为:│-0043456│; %+81d表示输出占8个空格的位置,右对齐,左边多余的位置补空格,必须输出正负号,所以第四个y的输出为│□□-43456│。正确答案应为C。 44、一张1024×640分辨率的图片,假定每个像素用16位色彩表示,用位图文件(bitmap)格式存储,则这张图片文件需要占用多大的存储空间____。 640KB 1280KB 2560KB 5120KB 10240KB KEY:B 解答:1024*640*16 bit = 1024*640*16/8 B = 1024*640*16/8/1024 KB = 1280KB。 45、What is the result of the following program? abdcccd abdd abcc abddcccd Access Violation KEY:D 解答:it1的前两个字符为ab没有异议,当it2的指针指向c时执行 it2++,运行后it2指向d,然后下一个字母不为c,所以it1的指针内复制为d,即此时it1为abd,之后遇到3个c,执行 it2++,直到it2指向d时才将d赋值给it1,也就是此时it1=abdd,但是接下来it2已经为空了,也就是“\0”,所以就不执行循环了,但是it1内本身后面还有cccd,前面的四个字母只是it2覆盖成abdd,所以最终的答案是abddcccd也就是D。 46、运行下面这段代码,会出现的情况是()。 hello world 显示为乱码 程序崩溃 hello KEY:C 解答:GetMemory(char *p);,这里*p是形参,是局部变量不能将malloc申请的地址指针返回,造成内存泄露更严重的是执行GetMemory(str);,后str依然为NULL,执行strcpy(str, 'hello world');就会出错。 47、在一个被调用函数中,关于return语句使用的描述,( )是错误的。 被调用函数中可以不用return语句 被调用函数中可以使用多个return语句 被调用函数中,如果有返回值,就一定要有return语句 被调用函数中,一个return语句可以返回多个值给调用函数 KEY:D 解答:被调用函数中,如果有返回值,就一定要有return语句。编译的时候并不出错,运行的时候会提示出错。 48、C++里面如何声明const void f(void)函数为C程序中的库函数。 static "C" extern "C" explict "C" register "C" KEY:B 解答:当它与 “C” 一起连用时,如:extern “C” void fun(int a , int b);,会告诉C++编译器在编译fun这个函数名是按着C的规则去翻译相应的函数名而不是C++的,C++的规则在翻译这个函数名时会把fun这个名字变得面目全非。 49、下面定义语句是不正确的: 50、以下程序的输出结果是()。 132 321 213 312 KEY:B 解答:printf自右向左压栈,但这种写法是未定义行为!!! int x = ...;
if (x % 2) {
return x - 1;
} else {
return x;
}
template
const char str1[] = "abc";
const char str2[] = "abc";
const char *p1 = "abc";
const char *p2 = "abc";
cout << (str1 == str2) << endl << (p1 == p2) << endl;
enum 枚举名
{
枚举值[=整型常量],
枚举值[=整型常量],
...
枚举值[=整型常量],
}枚举变量名列表;
struct s
{
int x: 3;
int y: 4;
int z: 5;
double a;
}
struct 位段结构名
{
类型说明符 位段名1:位段长度1;
类型说明符 位段名2:位段长度2;
类型说明符 位段名3:位段长度3;
类型说明符 位段名4:位段长度4;
};
位段变量.位段成员
#include
struct Worker
{
int no;
char name[20];
};
Worker w, *p=&w;
ssize_t write(int handle, void *buf, int nbyte);
ssize_t write(const char* str, int n);
A(const A &a) //浅层
{
x = a.x;
}
A(const A &a) //深层
{
x = new int; //开辟新的空间,复制构造对象的同时将成员指针变量保存在新空间中
*x = *(a.x); //读取旧对象的成员指针变量x地址处的数据并赋值给新对象的成员指针变量x所指向的内存区域
}
class CBase
{
int x;
public:
CBase(int n) { x=n; }
};
class CDerive:CBase
{
CBase y;
int z;
public:
CDeriver(int a, int b, int c);
};
void main()
{
char x = 0xFFFF;
printf("%d\n", x--);
}
#include
#include
#include
int i, n = 0;
float x = 1, y1 = 2.1 / 1.9, y2 = 1.9 / 2.1;
for ( i = 1; i < 22; i++ )
x = x * y1;
while ( x != 1.0 )
{
x = x * y2; n++;
}
printf( “ %d / n ”, n );
void func()
{
static int val;
...
}
#include
char *c[] = { "ENTER", "NEW", "POINT", "FIRST" };
char **cp[] = { c+3, c+2, c+1, c };
char ***cpp = cp;
int main(void)
{
printf("%s", **++cpp);
printf("%s", *--*++cpp+3);
printf("%s", *cpp[-2]+3);
printf("%s\n", cpp[-1][-1]+1);
return 0;
}
int f(int a, int b, int c)
{
return 0;
}
int main(){
return f(printf("a"),printf("b"),printf("c"));
}
class A{
public:
long a;
};
class B : public A {
public:
long b;
};
void seta(A* data, int idx) {
data[idx].a = 2;
}
int main(int argc, char *argv[]) {
B data[4];
for(int i=0; i<4; ++i){
data[i].a = 1;
data[i].b = 1;
seta(data, i);
}
for(int i=0; i<4; ++i){
std::cout << data[i].a << data[i].b;
}
return 0;
}
#include
int fun6(int);
int (*pf)(int) = fun6;
int i;
fun6(i);
(fun6)(i);
(*fun6)(i);
struct A{
void foo(){printf("foo");}
virtual void bar(){printf("bar");}
A(){bar();}
};
struct B:A{
void foo(){printf("b_foo");}
void bar(){printf("b_bar");}
};
A *p=new B;
p->foo();
p->bar();
int main(){
int pid;
int num=1;
pid=fork();
if(pid>0){
num++;
printf("in parent:num:%d addr:%x\n",num,&num);
}
else if(pid==0){
printf("in child:num:%d addr:%x\n",num,&num);
}
}
int main(void)
{
char *p[]={“TENCENT”,”CAMPUS”,”RECRUITING”};
char **pp[]={p+2,p+1,p};
char ***ppp=pp;
printf(“%s”, **++ppp);
printf(“%s”, *++*++ppp);
return 0;
}
void main()
{
long y=-43456;
printf("y=│%-81d│ y=│%-081d│ y=│%081dl│y=│%+81d│",y,y,y,y);
}
char* f(char *str, char ch) {
char *it1 = str;
char *it2 = str;
while (*it2 != '\0') {
while (*it2 == ch) { it2++; }
*it1++ = *it2++;
}
return str;
}
void main(int argc, char *argv[]) {
char *a = new char[10];
strcpy(a, "abcdcccd");
cout << f(a, 'c');
}
void GetMemory(char *p)
{
p = (char *)malloc(100);
}
void Test(void)
{
char *str = NULL;
GetMemory(str);
strcpy(str, "hello world");
printf(str);
}
float a=b=10.0;
main()
{
char s[]="123",*p;
p=s;
printf("%c%c%c\n",*p++,*p++,*p++);
}