必须知道的c++语法知识--1-18

来源:互联网 发布:php做九九乘法表 编辑:程序博客网 时间:2024/06/05 18:19

1 int&的作用 

2 地址的转换

3 类的sizeof多大

4 指针快速转换

5 指针的加减 

6 字符串常量 

7 引用类型

8 const 

9 constexpr 

10 typedef 

11 无符号数赋值

12 初始化语句 

13 声明与定义

14  输入函数 

15  char型指针 

16  strcpystrncpy 

17 左值

18 显示初始化与隐士初始化 

 

 

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的低八位,前面的60是因为对应无符号数,如果输出数位超过该类型数表示的长度时,前面位都是补上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);  

输出:  00000011000000aa,  ffffffaa00000011

第三句是将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  achar *p各占4个字节,共8个字节而虚析构函数virtual ~Base()需要维持一个虚函数表,占4个字节,所以一个12个字节

   Derive类首先要具有Base类的部分,也就是占12字节int  dchar *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];

temp0temp1temp2temp3分别是多少

答案:

分别是2983

说明:

   需要注意的指针直接相减并不是地址相减,而是地址相减后除以该类指针增加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  输入函数

输入函数cincin.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.getcin.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  strcpystrncpy

strcpystrncpy有什么区别?那个更安全?

 

答案:

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(iosfixed)<a<<endl;//5

cout<<setiosflagsiosfixed<<setprecision8<<a;输出:123.45678901

cout<<setiosflagsiosscientific<<a;输出:1.234568e+02

cout<<setiosflagsiosscientific<<setprecision4<<a;输出:1.2346e02

 

答案:

123.456

123.456789

123.456

123.456789

 

 

 

 

 

 

 

 

 

 

 

0 0
原创粉丝点击