/*
volatile的作用是: 作为指令关键字,确保本条指令不会因编译器的优化而省略,且要求每次直接读值.
简单地说就是防止编译器对代码进行优化.比如如下程序:
XBYTE[2]=0x55;
XBYTE[2]=0x56;
XBYTE[2]=0x57;
XBYTE[2]=0x58;
对外部硬件而言,上述四条语句分别表示不同的操作,会产生四种不同的动作,
但是编译器却会对上述四条语句进行优化,认为只有XBYTE[2]=0x58(即忽略前三条语句,只产生一条机器代码)。
如果键入volatile,则编译器会逐一的进行编译并产生相应的机器代码(产生四条代码).
volatile关键字:
(1)用来同步,因为同一个东西可能在不同的存储介质中有多个副本,
有些情况下会使得这些副本中的值不同,这是不允许的,
所以干脆用volatile,让它只 有一个,没有其他的副本,这样就不会发生不同步的问题。
(2)防止编译器优化去掉某些语句,
// 假设0x560012300 为寄存器地址
#define INTPAND *(volatile unsigned int *)0x560012300
INTPAND = INTPAND; // 清中断
像编译器如果看到有INTPAND = INTPAND;这种看似无用的操作,如果没有volatile说明,编译器就很有可能会去掉INTPAND = INTPAND;
实际上有用的东 西,却被编译器当没用的东西优化掉了。
(3)当地址是io端口的时候,读写这个地址是不能对它进行缓存的,
这是相对于某些嵌入式中有cache才有这个。
比如写这个io端口的时候,如果没有这个 volatile,很可能由于编译器的优化,会先把值先写到一个缓冲区,
到一定时候 再写到io端口,这样就不能使数据及时的写到io端口,
有了volatile说明以后, 就不会再经过cache,write buffer这种,而是直接写到io端口,从而避免了读写 io端口的延时。
*/
/
/*
在C++面向对象编程语言中,以下阐述不正确的是:
正确答案: A D
A接口中可以用虚方法
B一个类可以实现多个接口
C接口不能被实例化
D接口中可以包含已经实现的方法
所谓的接口是指只包含纯虚函数的抽象类,和普通的抽象类含不一样。所以A不对,必须是纯虚函数。
然后B是正确的没有问题。
然后是C,刚才说接口是特殊的抽象类,抽象类的唯一作用就是创建派生类,不能定义抽象类的对象,所以C是正确的。
对于D,接口即只包含纯虚函数的抽象类,所以D是不对的。
*/
/
/*
下列关于bool,int,float,指针类型的变量a 与“零”的比较语句正确的有?
正确答案: A B D
A bool : if(!a)
B int : if(a == 0)
C float: if(a == 0.0)
D 指针: if(a == NULL)
由于计算机二进制表示浮点数有精度的问题,0.0(浮点double)实际上不是0,而是非常接近零的小数,所以C错!
在ANSIC C中定义了FLT_EPSILON/DBL_EPSILON/LDBL_EPSILON来用于浮点数与零的比较,
一般if(fabs(a)<FLT_EPSILON)或if(fabs(a)< DBL_EPSILON)就可以表示a是否“为0”。
而if(a==0.0)是永远不会成立的,达不到要求!
*/
/
/*
下面有关c++ traits,说法正确的有?
正确答案: A B C D
A 一个traits包括了enum、typedef、模板偏特化(template partial specialization)
B typedef定义了各个类的各自不同的类型定义
C 模板偏特化用于实现各个类的不同功能
D 当函数,类或者一些封装的通用算法中的某些部分会因为数据类型不同而导致处理或逻辑不同,traits会是一种很好的解决方案
*/
/
/*
如下哪一段代码不能给地址0xaae0275c赋值为1?()
正确答案: B 你的答案: B (正确)
A volatile int *p=(int *)0xaae0275c;*p=1
B (volatile int *)0xaae0275c[0]=1
C volatile int *p=(int *)0xaae0275c;p[0]=1
D *(volatile int *)0xaae0275c=1
*/
/
/*
若有以下程序
struct st{ int n; struct st * nest; };
struct st a[3]={ 5,&a[1],7,&a[2],9, '\0' },*p;
p=&a[0];
则以下选项中值为6的表达式是
正确答案: D
A p->n
B (*p).n
C p->n++
D ++(p->n )
*/
/
/*
若有以下程序
#include <stdio. h>
main( )
{
int a = 0,b = 0,c = 0,d;
c = (a + = b,,b + = a); / * 第4行 * /
d = c; / * 第5行 * /
; / * 第6行 * /
;printf("%d,%d,%d\n",a,b,c);/ * 第7行 * /
}
编译时出现错误,你认为出错的是
正确答案: A
第4行
第5行
第6行
第7行
第4行逗号表达式中间的第二个表达式为空,是不合法的,可以去掉写成a + = b,b + = a,
也可以在里面补一个表达式,如a + = b,a,b + = a。所以选择A选项。
*/
/
/*
下面代码不能正确输出hello的选项为
#include<stdio.h>
struct str_t{
long long len;
char data[32];
};
struct data1_t{
long long len;
int data[2];
};
struct data2_t{
long long len;
char *data[1];
};
struct data3_t{
long long len;
void *data[];
};
int main(void)
{
struct str_t str;
memset((void*)&str,0,sizeof(struct str_t));
str.len=sizeof(struct str_t)-sizeof(int);
snprintf(str.data,str.len,"hello");//VS下为_snprintf
____________________________________;
____________________________________;
return 0;
}
正确答案: B
A struct data3_t *pData=(struct data3_t*)&str; printf("data:%s%s\n",str.data,(char*)(&(pData->data[0])));
B struct data2_t *pData=(struct data2_t*)&str; printf("data:%s%s\n",str.data,(char*)(pData->data[0]));
C struct data1_t *pData=(struct data1_t*)&str;printf("data:%s%s\n",str.data,(char*)(pData->data));
D struct str_t *pData=(struct str_t*)&str; printf("data:%s%s\n",str.data,(char*)(pData->data));
pData->data[0]不是数组的地址,而是数组的首元素,相当于是原结构体里面的char,因此必须取地址之后再强制转换成char *类型。
snprintf(),为函数原型int snprintf(char *str, size_t size, const char *format, ...)。
将可变个参数(...)按照format格式化成字符串,然后将其复制到str中
*/
/
/*
假定CSomething是一个类,执行下面这些语句之后,内存里创建了__6__个CSomething对象。
CSomething a();
CSomething b(2);
CSomething c[3];
CSomething &ra = b;
CSomething d=b;
CSomething *pA = c;
CSomething *p = new CSomething(4);
CSomething a();// 没有创建对象,这里不是使用默认构造函数,而是定义了一个函数,在C++ Primer393页中有说明。
CSomething b(2);//使用一个参数的构造函数,创建了一个对象。
CSomething c[3];//使用无参构造函数,创建了3个对象。
CSomething &ra=b;//ra引用b,没有创建新对象。
CSomething d=b;//使用拷贝构造函数,创建了一个新的对象d。
CSomething *pA = c;//创建指针,指向对象c,没有构造新对象。
CSomething *p = new CSomething(4);//新建一个对象。
*/
/
/*
下述有关虚函数和纯虚函数说法错误的是?
正确答案: C
A被virtual关键字修饰的成员函数,就是虚函数
B在基类中实现纯虚函数的方法是在函数原型后加“=0” virtual void funtion1()=0
C同时含有纯虚拟函数的类称为抽象类,它可以被实例化,但是对象不可以调用纯虚函数
D使用纯虚函数的意义是在很多情况下,基类本身生成对象是不合情理的
*/
/
/*
下面有关友元函数与成员函数的区别,描述错误的是?
正确答案: D
A友元函数可以让本类和友元类对象调用
B友元函数和类的成员函数都可以访问类的私有成员变量或者是成员函数
C类的成员函数是属于类的,调用的时候是通过指针this调用的
D友元函数是有关键字friend修饰,调用的时候也是通过指针this调用的
*/
/
/*
使用printf函数打印一个double类型的数据,要求:输出为10进制,输出左对齐30个字符,4位精度。以下哪个选项是正确的?
正确答案: C
A %-30.4e
B %4.30e
C %-30.4f
D %-4.30f
-: 左对齐
30: 最小字段宽度
.4: 精确度保留小数4位
f: double精度浮点数
e: 科学计数法
*/
/
/*
分析一下程序的运行结果:
class CBase
{
public:
CBase(){cout<<”constructing CBase class”<<endl;}
~CBase(){cout<<”destructing CBase class”<<endl;}
};
class CSub : public CBase
{
public:
CSub(){cout<<”constructing CSub class”<<endl;}
~CSub(){cout<<”destructing CSub class”<<endl;}
};
void main()
{
CSub obj;
}
结果:
constructing CBase class
constructing CSub class
destructing CSub class
destructing CBase class
子类对象生成时:先调用父类的构造函数,然后在调用子类的构造函数; 析构时相反
*/
/
/*
char * p= new char[100]
p在栈上 ,new出来的在堆上
*/
/
/*
在int b[ ][3] = {{1},{3,2},{4,5,6},{0}};中,sizeof(b) = ?
正确答案: D
A 4
B 12
C 28
D 48
b为4行3列的二维数组,12*4 = 48
*/
/
/*
int a=0;
class someClass{
int b;
static int c;
};
int main(){
int d=0;
someClass *p=new someClass();
return 0;
}
关于以上代码中的变量在内存中的存储位置描述不正确的是()
正确答案: B
A b存在堆区
B c存在堆区
C d存在栈区
D a存在全局变量区
1、栈区(stack)— 由编译器自动分配释放 ,存放为运行函数而分配的局部变量、函数参数、返回数据、返回地址等。
2、堆区(heap) — 一般由程序员分配释放, new, malloc之类的,若程序员不释放,程序结束时可能由OS回收 。
3、全局区(静态区)(static)— 存放全局变量、静态数据、常量。程序结束后由系统释放。
4、文字常量区 — 常量字符串就是放在这里的。程序结束后由系统释放。
5、程序代码区 — 存放函数体(类成员函数和全局函数)的二进制代码。
a,全局变量,全局区;
b,成员变量,堆区;(我是这样理解的,因为对象要申请,会放在堆区,所以类的成员也就是放在堆区)
c,静态变量,静态区;
d,方法变量,栈区;
p,方法变量,栈区,指向堆区的对象。
*/
/
/*
以下关于内联函数,说法正确的是:
正确答案: A B C
A一般用于加快程序执行速度
B可能减小可执行文件大小
C可能增加可执行文件大小
D以上说法都不正确
inline函数中如果有局部变量,同时,传参给inline函数时使用表达式传参,这样,编译器在处理时会产生很多临时对象,
当inline函数多次被调用时,会产生大量的扩展码,从而增加程序大小;
如果inline函数被编译器接受,同时又没有产生以上这些副作用,那么同非内联函数相比,
inline函数是用函数体直接替换,省去了函数调用的开销,加快程序执行速度。
*/
/
/*
x为整型,请用位运算实现x%8.
答案x&7
*/
/
/*
unsigned int a= 0x1234; unsigned char b=*(unsigned char *)&a; 在32位大端模式处理器上变量b等于()?
正确答案: A
0x00
0x12
0x34
0x1234
unsigned int a= 0x1234; 其中int是4字节, 大端存储 ,补齐16进制表示为: 0x00 00 12 34
unsigned char b=*(unsigned char *)&a; 由于大端存储, 所以上述int a变量的最低地址存储的是
十六进制表示中最左边的1字节, 为0x00.
*/
/
/*
写出下列程序在X86上的运行结果
struct mybitfields
{
unsigned short a : 4;
unsigned short b : 5;
unsigned short c : 7;
} test
void main(void)
{
int i;
test.a = 2;
test.b = 3;
test.c = 0;
i = *((short *)&test);
printf("%d\n", i);
}
正确答案: B
A 30
B 50
C 60
D 20
这里的冒号相当于分配几位空间,也即在定义结构体的时候,分配的成员a 4位的空间, b 5位,c 7位,一共是16位,正好两个字节。下面画一个简单的示意:
变量名 位数
test 15 14 13 12 11 10 9 |8 7 6 5 4 |3 2 1 0
test.a | |0 0 1 0
test.b |0 0 0 1 1 |
test.c 0 0 0 0 0 0 0 | |
在执行i=*((short *)&test); 时,取从地址&test开始两个字节(short占两个字节)的内容转化为short型数据,即为0x0032,再转为int型为0x00000032,即50
*/
/
/*
instanceof运算符能够用来判断一个对象是否为:
正确答案: C
A一个类的实例
B一个实现指定接口的类的实例
C全部正确
D一个子类的实例
instance是java的二元运算符,用来判断他左边的对象是否为右面类(接口,抽象类,父类)的实例
*/
/
/*
用C++语法来看,下列的哪个赋值语句是正确的?
正确答案: A B C D
A char a=12
B int a=12.0
C int a=12.0f
D int a=(int)12.0
在VS 2010中的运行结果显示
B、C选项有警告:
从“double”转换到“int”,可能丢失数据
从“float”转换到“int”,可能丢失数据
D选项没有警告,不过显然是因为显示转换的缘故,其实它也是从“double”转换到“int”,可能丢失数据
A肯定是没错的,如果写成char a = '12'会有警告: 从“int”到“char”截断
*/
/
/*
针对以下代码,
const char str1[]=”abc”;
const char str2[]=”abc”;
const char *p1 = “abc”;
const char *p2 = “abc”;
判断下列说法哪个是正确的:______。
正确答案: A 你的答案: F (错误)
A str1和str2地址不同,P1和P2地址相同。
B str1和str2地址相同,P1和P2地址相同。
C str1和str2地址不同,P1和P2地址不同。
D str1和str2地址相同,P1和P2地址不同。
E 4个地址都相同
F 4个地址都不相同。
*/
/
/*
若有以下定义和赋值语句,则与&s[i][j]等价的是()
int s[2][3] = {0}, (*p)[3], i, j;
p = s;
i = j = 1;
正确答案: C
A *(*(p+i)+j)
B *(p[i]+j)
C *(p+i)+j
D (*(p+i))[j]
int (*p)[3] 表示一个指针,指向一个含有三个元素的数组;
p=s,表示p指向了数组s的第一行,
p+1 表示现在指针指向了数组s的第二行;
*(p+1)表示数组s第二行第一个元素的地址;
*(p+1)+1 表示数组s第二行第二个元素的地址;
*/
/
/*
math.h的abs返回值()
正确答案: C
A不可能是负数
B不可能是正数
C都有可能
D不可能是0
c++中的函数申明为 int abs(int num);
例如:
#include<iostream>
#include<cmath>
using namespace std;
int main(){
int a=0x80000000,b=0x7fffffff;
cout<<a<<endl<<abs(a)<<endl<<abs(b);
}
-2147483648
-2147483648
2147483647
正常情况下,
num为0或正数时,函数返回num值;
当num为负数且不是最小的负数时,函数返回num的对应绝对值数,即将内存中该二进制位的符号位取反,并把后面数值位取反加一;
当num为最小的负数时(即0x80000000),由于正数里int类型32位表示不了这个数的绝对值,所以依然返回该负数。
这就是设计这个库函数的时候为什么把返回值设置为int而不是unsigned的原因
*/
/
/*
内联函数在以下场景中最有用的()
正确答案: C 你的答案: C (正确)
A当函数代码较长且多层嵌套循环的时候
B当函数中有较多的静态变量的时候
C当函数代码较小并且被频繁调用的时候
D以上都不对
内联函数只适合于只有1~5行的小函数。对一个含有 许多语句的大函数 ,
函数调用和返回的开销 相对来说微不足道,所以也没有必要用内联函数实现。
在内联函数内不允许用循环语句和开关语句。否则编译将该函数视同普通函数。
内敛函数不适于while switch 这样复杂结构,且语句最好在1-5条这样的小型函数上. 递归不能定义为内联.
*/
/
/*
class A
{
public:
A ():m_iVal(0){test();}
virtual void func() { std::cout<<m_iVal<<‘ ’;}
void test(){func();}
public:
int m_iVal;
};
class B : public A
{
public:
B(){test();};
virtual void func()
{
++m_iVal;
std::cout<<m_iVal<<‘ ’;
}
};
int main(int argc ,char* argv[])
{
A*p = new B;
p->test();
return 0;
}
正确答案: C
A 1 0
B 0 1
C 0 1 2
D 2 1 0
E 不可预期
F 以上都不对
涉及到两个方面:
1.C++继承体系中构造函数的调用顺序。
2.构造函数中调用虚函数问题,在构造或析构函数中调用虚函数会执行与之所属类型相对应的虚函数版本。
C++继承体系中,初始化时构造函数的调用顺序如下
(1)任何虚拟基类的构造函数按照他们被继承的顺序构造
(2)任何非虚拟基类的构造函数按照他们被继承的顺序构造
(3)任何成员对象的函数按照他们声明的顺序构造
(4)类自己的构造函数
据此可知 A*p = newB;先调用A类的构造函数再调用B类的构造函数。
构造函数中调用虚函数,虚函数表现为该类中虚函数的行为,即在父类构造函数中调用虚函数,
虚函数的表现就是父类定义的函数的表现。
原因如下:
假设构造函数中调用虚函数,表现为普通的虚函数调用行为,即虚函数会表现为相应的子类函数行为,并且假设子类存在一个成员变量int a;
子类定义的虚函数的新的行为会操作a变量,在子类初始化时根据构造函数调用顺序会首先调用父类构造函数,那么虚函数回去操作a,
而因为a是子类成员变量,这时a尚未初始化,这是一种危险的行为,作为一种明智的选择应该禁止这种行为。
所以虚函数会被解释到基类而不是子类。
B 继承自 A, 先 A 构造函数,输出 0 ,
然后 B 构造函数, B 的 test 继承自 A ,然后找 func 函数找到 B 自己的func,所以输出 1 ,
然后 p->test() 再执行一次,输出 2
*/
C++小题(六)
最新推荐文章于 2022-07-14 14:18:09 发布