其他:
1 int&的作用
float a = 1.0f;
cout<<(int&)a;
结果是将a在内存中的存储数据拿出,按照读取整数的规则来读取并输出。
举例:
int c =0x12345679;
cout<<(char&)c;
输出是y,因为是按照char类型来读取,所有读取8位,就是读取c的低8位(79),因为16进制的79对应十进制的121,对应ascii为y。
如果想查看具体的二进制数,可以使用bitset
bitset<sizeof(int)*8> a(c);
cout<<a<<"\n";
bitset<8> d("00000010");
cout<<d<<"\n";
2地址的转换
unsigned int a = 0xAAAAAAA7;
unsigned char i = (unsigned char)a;
char* b = (char*)&a;
unsigned char* c = (unsigned char*)&a;
printf("%08x, %08x, %08x,%08x\n", a, i, *b, *c);
结果:
08x中8表示输出8位。
aaaaaaa7, 000000a7, ffffffa7,000000a7
说明:
第二句是将a转换为无符号字符,i只保留了a的低八位,前面的6个0是因为对应无符号数,如果输出数位超过该类型数表示的长度时,前面位都是补上0,来表示是正数。
对于有符号数,才根据符号位来决定补充的数位。
unsigned char test1 = 0x11;
unsigned char test2 = 0xaa;
char test3 = 0x11;
char test4 = 0xaa;
printf("%08x, %08x, %08x,%08x\n", test1, test2, test3, test4);
输出: 00000011,000000aa, ffffffaa,00000011
第三句是将a的地址转换为char型地址,并赋给b,所以*b输出的是a的低八位但是,它是有符号数,所以前面都是1。
第四句更加第二句和第三句可以知道结果。
3类的sizeof多大
参考资源:http://blog.csdn.net/hairetz/article/details/4171769
class Base
{
public:
Base();
virtual ~Base(); //每个实例都有虚函数表
void set_num(int num) //普通成员函数,为各实例公有,不归入sizeof统计
{
a=num;
}
private:
int a; //占4字节
char *p; //4字节指针
};
class Derive:public Base
{
public:
Derive():Base(){};
~Derive(){};
private:
static int st; //非实例独占
int d; //占4字节
char *p; //4字节指针
};
int main()
{
cout<<sizeof(Base)<<endl;
cout<<sizeof(Derive)<<endl;
return 0;
}
结果:
12
20
说明:
Base类里的int a、char *p各占4个字节,共8个字节,而虚析构函数virtual ~Base()需要维持一个虚函数表,占4个字节,所以一个12个字节。
Derive类首先要具有Base类的部分,也就是占12字节,int d、char *p,各占4字节
static int st不计入统计,所以一共是20字节。
注意如果一个类是空类,那么它占一个字节。
拓展:
在考虑在Derive里加一个成员char c;
class Derive:public Base
{
public:
Derive():Base(){};
~Derive(){};
private:
static int st;
int d;
char *p;
char c;
};
结果:
12
24
说明:
一个char c,本来是只增加1一个字节,但是为了对齐数据(一般是按照4个字节对齐),所以多增加了4个字节。
至此,我们可以归纳以下几个原则:
1.类的大小为类的非静态成员数据的类型大小之和,也就是说静态成员数据不作考虑。
2.普通成员函数与sizeof无关。
3.虚函数由于要维护在虚函数表,所以要占据一个指针大小,也就是4字节。
4.类的总大小也遵守类似class字节对齐的调整规则。
4指针快速转换
char *str = NULL;
char **p = &str;
*p = (char*) malloc( sizeof(char) *10 );
在理解char **p = &str时,要注意这样思考,无论p前面有多少个*,p都是与赋值作用符右侧的内容是一个级别的,就是p就是&str,那么**p就是**(&str)就是*str了。
5指针的加减
int a[3];
a[0] = 3; a[1] = 8;a[2] = 9;
int *p = a;
int *q = &a[2];
char *m = (char *) a;
char *n = (char *) &a[2];
int temp0 = (int)(q - p);
int temp1 = a[q - p];
int temp2 = (int)(n-m);
int temp3 = a[n-m-8];
temp0、temp1、temp2、temp3分别是多少
答案:
分别是2、9、8、3
说明:
需要注意的指针直接相减并不是地址相减,而是地址相减后除以该类指针增加1所移动的字节数。
6字符串常量
char str1[] = "abc";
char str2[] = "abc";
const char str3[] = "abc";
const char str4[] = "abc";
const char *str5 = "abc";
const char *str6 = "abc";
char *str7 = "abc";
char *str8 = "abc";
cout << ( str1 == str2 ) << endl;
cout << ( str3 == str4 ) << endl;
cout << ( str5 == str6 ) << endl;
cout << ( str7 == str8 ) << endl;
输出的结果是多少?
答案:
0
0
1
1
说明:
str1,str2,str3,str4是数组变量,它们有各自的内存空间。str5,str6,str7,str8是指针,它们指向相同的常量区域。
拓展:
下面的程序输出结果一致吗?
//程序段1
#include <stdio.h>
char* returnStr()
{
char *p="hello world!";
return p;
}
int main()
{
char *str=NULL;//一定要初始化,好习惯
str=returnStr();
printf("%s\n", str);
return 0;
}
//程序段2
#include <stdio.h>
char* returnStr()
{
char p[]="hello world!";
return p;
}
int main()
{
char *str=NULL;//一定要初始化,好习惯
str=returnStr();
printf("%s\n", str);
return 0;
}
答案:
不一致。
说明:
hello world!"是一个字符串常量,存放在静态数据区,没错,但是把一个字符串常量赋值给了一个局部变量(char []型数组),该局部变量存放在栈中,这样就有两块内容一样的内存,也就是说“char p[]="hello world!";”这条语句让“hello world!”这个字符串在内存中有两份拷贝,一份在动态分配的栈中,另一份在静态存储区。这是与前者最本质的区别,当returnStr函数退出时,栈要清空,局部变量的内存也被清空了,所以这时的函数返回的是一个已被释放的内存地址,所以打印出来的是乱码。如果函数的返回值非要是一个局部变量的地址,那么该局部变量一定要申明为static类型。
7引用类型
找出下列语句的错误
int a = 2;//1
int &b = 1;//2
const &c = 2;//3
int &d;//4
int &e = a;//5
int &f = e;//6
const int m =1;//7
int &n = m;//8
int *p =0;//9
Int *& q =p; //10
找出下列语句的错误
答案:
2句错,非const引用不能为字面值;
4句错,引用必须初始化;
6句错,引用对象不能是引用;
8句错,非const引用不能指向常量对象。
注意:const int a =1;和int const a =1;是一样的。
引用只能绑定一个对象,不能再次绑定,所以以后只能是赋值操作。
8 const
找出下面语句的错误
const int i = 1;//1
const int j = i;//2
const int m = sizeof(i);//3
const int * const n = 2;//4
const int * r = 2;//5
int * const s = 2;//6
int *p = r;//7
int *q = s;//8
int r = n;//9
答案:
第7句错,非const指针不能指向const变量;
第8句错,顶层const不影响赋值;
第9句错,底层const指针只能赋值为底层const指针。
9 constexpr
下面语句是否正确?
constexpr int a = get();//1
constexpr int *p = 1;//2
p = &a;//3
constexpr const *q = 3//
答案:
第1句不一定,是否正确取决于get()是否是constexpr类型的函数,该类函数在编译时就能获得结果。
第3句错,constexpr 将p限定为顶层指针,而非底层,所指向不能改变。
10 typedef
下面两类语句有什么不同
typedef char* pstring;
const pstring cstr =0;
And
const char *cstr = 0;
答案:
前面一类代码中个,cstr是常量指针。
11无符号数赋值
下列代码输出结果是多少:
unsigned char a = -1;//1
unsigned int b =10, c = 42;//2
int d =-42;//3
cout<<a<<endl<<b-c<<endl<<b+d<<endl;//4
答案:
255
232-32
10+(232-42)
说明:
1 -- unsigned char数范围是0-255,一个数如何不在这个范围内,赋给unsigned char后应该这样计算X+n*256,调整n使得这个结果在0-255之间,最终unsigned char存储的数就是这个调整后的结果。
2-- 两个无符号数相减,结果不在无符号数表示范围时,结果会自动转换为这个范围,转换方法和1中的一样。
3--当无符号数和有符号数相加时,有符号数要先转为无符号数。
12初始化语句
下列初始化语句不正确的是
int a = 0;//1
int b = {3.14};//2
int c(0.2);//3
double d = 0.0225;//4
int e{d};//5
答案:
第2句和第5句不对,花括号本身可以作为初始化语句,但是当数据存在信息丢失时不能通过编译,这样可以提前告知潜在的错误。
13声明与定义
下列变量是声明式还是定义式,声明和定义有什么不同?
extern int i;//1
extern int j = 0;//2
int m;//3
答案:
第2句为定义,显示初始化的声明都是定义。
说明:
定义出现一次,声明可以出现多次,而且当使用其他文件中的变量时必须事先声明。
14 输入函数
输入函数cin、cin.getline()、cin.get和getchar有什么区别?
下列代码有什么不妥之处吗?
cin.get(name,10);
cin.get(class,10);
答案:
上述代码中获取输入时,“第一次回车键”(就是换行符)会保存在输入队列中,导致第二次获取的值为空,即class为空。可以在两句之间加上get()获取一个字符,即回车键。
比如:
输入name时键入:wudi| (后面的|代表回车)
这是整个“wudi| ”都会进入缓存,但是只有“wudi”赋值给name,缓存中依然有回车。
程序执行到cin.get(class,10)时,发现缓存中还有数据,就不会再次提示输入,一直读取到一个回车位置,由于缓存中就一个回车,那么就把回车前面的内容(空的)赋值给class,这个时候回车还继续在缓存中,前面说过,只取回车前面的内容,回车不会读取,只是告诉程序遇到回车,后面的就不用看了,但是回车本身还是留下来。
拓展:
l cin,第一输入数据时,遇到换行符号(注意:是换行符,不是所有空字符)才将数据放入缓存。但是cin只接受非空字符(空格、制表符、换行符),就是说遇到空字符,自动跳到后面的字符。换行符号也是被留着缓冲区中。
l cin.getline()遇到换行符或者字符达到指定数目停止读取,不保存换行符(即使后面有getchar,也需要重新输入,因为回车键没用保留)。
l cin.get与cin.getline一致,只是保存换行符(保存到缓冲区中,如果后面跟有getchar,是不需要二次输入的,getchar将得到回车)。
getchar第一输入数据时,遇到换行符号(不是所有空字符)才将数据放入缓存,但是
在读取数据时可以取空白字符。
拓展:
解释下列程序的a、b和c的输出,分别有几次输入提示?
char a, b, c, d;
cin >> a;
while (a != '#')
{
cout << a;
cin >> a;
}
b = getchar();
c = getchar();
cout << endl << b<<endl<<c;
输入:1)abc#| (|代表回车)
2)abc #|
3)abc#a|
4)abc# |
答案:
1)a依次为abc,b为回车,运行到c = getchar()再次提示输入,有两次输入提示。
说明:
因为回车键按下时,缓存区内存才被赋值为cin或者getchar,回车键也会保存在缓冲区。
2)a依次为abc,cin会跳过空白字符(包括回车)。b为回车,运行到c = getchar()再次提示输入,有两次输入提示。
3)a依次为abc。b为a,c为回车,由于输入内容足够读取,所以只有一次输入提示。
4)a依次为abc。b为空格(即' '),c为回车,由于输入内容足够读取,所以只有一次输入。这反应了getchar可以读取空白字符。
15 char型指针
下列语句能输出地址吗?如果不能,怎样才能输出地址?p102
char * p = "bear";
cout<<p;
答案:
不能
说明:
因为如果指针类型是char类型,直接输出指针指向的内容,如果想输出地址,可以将指针强制转换为int*类型。
16 strcpy和strncpy
strcpy和strncpy有什么区别?那个更安全?
答案:
strncpy可以控制赋值的个数,会更安全。
说明:
如:
char food[10];
strcpy(food,"1234567891234");
food后面的内存将被部分输入内容覆盖。
而如果使用:
strncpy(food,"123456789123",9);
food[9]='\0';
注意:被赋值的对象中的'\0'未能被复制进去,所以需要手动添加'\0'。
17左值
什么是左值?下面代码有什么错误?
void test(int &a)
int a =1;
int &b = a;
test(2);
答案:
左值是可被引用的对象,包括普通的变量、指针、数组、结构体、引用变量。常量和表达式不是左值
非常量引用作为参数,实参必须是左值,引用非常量引用的初衷是可能修改变量本身的内容,如果实参不是一个左值,就无法修改了。
18显式初始化与隐式初始化
隐式实例化、显式实例化、显式具体化的异同?
答案:
l 隐士初始化
定义了模板而没有生成具体的函数,那么调用时就使用模板首次生成具体的函数定义,这就是隐式实例化。
l 显示初始化
template 返回值类型 函数名 (类型名1,类型名2);
提前生成了具体函数定义再调用函数,就属于显式实例化。
l 显式具体化
template<> 返回值类型 函数名 (类型名1,类型名2);
再调用函数时,使用的是显式具体化的函数定义,注意它已经是具体的定义了。
19 cout格式设置
下面代码输出分别是多少
double a=123.456789012345;//1
cout<<a<<endl;//2
cout<<setprecision(9)<<a<<endl;//3
out<<setprecision<<a<<endl;//4
cout<< setiosflags(ios∷fixed)<a<<endl;//5
cout<<setiosflags(ios∷fixed)<<setprecision(8)<<a;输出:123.45678901
cout<<setiosflags(ios∷scientific)<<a;输出:1.234568e+02
cout<<setiosflags(ios∷scientific)<<setprecision(4)<<a;输出:1.2346e02
答案:
123.456
123.456789
123.456
123.456789
其他补充:
1. include<string> 后,还是提示标示符string未定义
2. EOF怎么输入
3. 有符号数相加减溢出问题