C++梁哥笔记day2 | 我的日常分享

C++梁哥笔记day2

一、语法的增强

  1. 以下代码在C语言中好使,在C++中会报错
    1
    2
    3
    4
    5
    6
    7
    #include<stdio.h>
    int main(){
    int a = 10;//有赋值--->定义
    int a;//同名且没有赋值--->声明
    printf("a=%d\n",a);
    return 0;
    }
  1. C语言中,虽然有警告但还是可以编译运行,在C++中则不行。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    //i没有写类型可以传任意类型
    int fun1(i){
    printf("%d\n",i);
    return 0;
    }
    //i没有写类型可以传任意类型
    int fun2(i){
    printf("%s\n",i);
    return 0;
    }
    //没有写参数,代表可以传任何类型的实参
    int fun3(){
    printf("fun3\n");
    return 0;
    }
  2. 更严格的类型转换
    在C++中不同类型变量一般是不能直接赋值的,需要进行相应的强制类型转换。C语言中编译器可以编译通过。
    1
    2
    3
    4
    5
    6
    enum COLOR{GREEN, RED, YELLOW};
    int main(){
    COLOR mycolor = GREEN;
    mycolor = 10;//C++中编译出错
    return 0;
    }

二、对结构体的增强

  1. C语言中定义结构体变量需要加上struct关键字,C++中不需要。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    struct stu{
    int num;
    char name[32];
    };
    int main(){
    //C语言中必须加struct,不然会报错
    struct stu bob= {100"bob"};
    //C++语言中,可不加struct
    stu lucy = {200"lucy"};
    return 0;
    }
  2. C语言中的结构体只能定义成员变量,不能定义成员函数。C++既可以定义成员变量,也可以定义成员函数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
struct stu{
int num;
char name[32];
void func(){
cout<<"我是结构体中的func"<<endl;
}
};
int main(){
stu lucy = {100,"lucy"};
cout<<lucy.num<<endl;
//调用结构体中的成员函数(成员方法)
lucy.func();
return 0;
}

运行结果:
图片

三、bool类型

标准C++的bool类型有两种内建的常量true(转换为整数1)和false(转换为整数0)表示状态。
这三个名字都是关键字。
bool类型占1字节大小,给bool类型赋值时,非0值会自动转换为true(1),0会自动转换为false(0)
案例1:

1
2
3
4
5
6
void test1(){
bool flag = true;
cout<<"sizeof(bool)="<<sizeof(bool)<<endl;
cout<<"true:"<<true<<endl;
cout<<"false:"<<false<<endl;
}

运行结果:
图片

C语言中也有bool类型,在C99标准之前是没有bool关键字的,C99增加了bool类型,包含头文件,stdbool.h就可以使用C++一样的bool类型。

四、三目运算符的增强

  1. C语言中三目运算符表达式返回值为数据值,为右值,不能赋值。
    案例2:
    1
    2
    3
    4
    5
    void test2(){
    int a=10,b=20;
    printf("%d\n",a>b?a:b);
    //a>b?a:b=100;//错误 不能作为左值 相当于20=100
    }
  2. C++中三目运算符表达式返回值为变量本身(引用),为左值,可以赋值。
    案例3:
    1
    2
    3
    4
    5
    6
    7
    8
    void test3(){
    int a=10,b=20,c=0;
    c=a>b?a:b;
    //cout<<a>b?a:b;<<endl;这样写会报错
    cout<<c<<endl;
    a>b?a:b=100;//正确 整体返回的是变量b 相当于b=100
    cout<<"b="<<b<<endl;
    }
    运行结果:
    图片

五、const详解

1、const概述

const单词字面意思是常数、不变的。它是c/c++中的一个关键字,是一个限定符,它用来限定一个变量不允许改变,它将一个对象转换成一个常量

2、C语言中的const

常量的引进是在c++早期版本中,当时标准c规范正在制定。那时,尽管c委员会决定在c中引入const,但是,他们c中的const理解为“一个不能改变的普通变量”,也就是认为const修饰的应该是一个只读变量,既然是变量那么就会给const分配内存,并且c中const是一个全局只读变量,c语言中const修饰的只读变量是外部连接的,也就是说其他文件也可以使用。
const int arrSize = 10;
int arr[arrSize];
看似是一件很合理的代码,但是是错误的,因为arrSize占用某块内存,所以c编译器不知道它在编译时的值是多少。

  1. 在C语言中
    1
    2
    3
    4
    5
    6
    7
    8
    9
    const int a = 10; //不要把a看成一个常量
    //a的本质是变量,只是它是一个只读的变量
    /* !!!!!!重要!!!!!!!
    *只是不能通过变量名直接改变空间的内容
    *但是可以通过其地址来修改其空间内容
    *前提是要地址对应的空间区域可读可写
    *例如,定义在全局的const 变量在文字常量区
    *其空间区域都不可修改,即使知道其地址也不能修改
    */
    案例4:
    1
    2
    3
    4
    5
    6
    7
    void test4(){
    const int num=10;
    int *p=(int *)&num;
    printf("num=%d\n",num);
    *p=200;
    printf("num=%d\n",num);
    }
    运行结果:
    图片

总结:

  1. const修饰全局变量data,变量名只读,对应的内存空间在文字常量区(只读),不能通过data的地址,修改空间内容。
  2. const修饰局部变量num,变量名只读,对应的内存空间在栈区(可读可写),可以通过data地址,间接地修改空间内容。
  3. const修饰的变量是外部连接,其他文件也可以使用。

3、C++中的const深入理解

(1) 在C++中,出现在所有函数之外的const作用于整个文件(也就是说对其他文件不可见),默认为内部连接。
如果要在其他文件使用只读的全局变量 必须在定义的时候加extern将变量转换成外部连接
(2) 在C++中,一个const不必创建内存空间,而在c中,一个const总是需要一块内存空间。在c++中,是否为const常量分配内存空间依赖于如何使用。一般说来,如果一个const仅仅用来把一个名字用一个值代替(就像使用#define一样),那么该储存区空间不必创建。如果储存空间没有分配内存的话,在进行完数据类型检查后,为了代码更加有效,值也许会折叠到代码中。不过,取一个const地址,或者把它定义为extern,则会为该const创建空间。
案例5:

1
2
3
4
5
6
7
8
9
void test5(){
const int num = 10;
//num = 100;//错误 num只读
cout<<"num="<<num<<endl;
int *p = (int *)&num;
*p = 2000;
cout<<"*p="<<*p<<endl;
cout<<"num="<<num<<endl;
}

运行结果:
图片
想想明明就改成2000了,*p=2000,为什么num尽然还是10呢?

1、对于基础数据类型,也就是const int a = 10;这种,编译器会把它放到符号表中,不分配内存,当对其取地址时才会分配内存。

这样的话,上面的案例5的现象就很好解释了,当我们对其取地址时,系统给它分配了空间,这个空间的原始内容是10,然后我们将其改为2000,但是符号表中,num对应的值还是10,所以当我们输出num的值时,num的值是从符号表里面取的,所以输出的num仍然是10,但是系统为num分配的空间*p的值是2000。

运行结果:
图片

2、对于基础数据类型,如果用一个变量初始化const变量,例如const int a = b;那么也是会给a分配内存,而且是直接开辟空间,不会把a放到符号表中。

案例6:

1
2
3
4
5
6
7
8
9
void test6(){
int t=10;
const int num = t;
cout<<"num="<<num<<endl;
int *p = (int *)&num;
*p = 2000;
cout<<"*p="<<*p<<endl;
cout<<"num="<<num<<endl;
}

运行结果:
图片

3、对于自定数据类型,比如类对象,那么也会分配空间,且不会放到符号表中

案例7:

1
2
3
4
5
6
7
8
9
10
11
12
13
struct person{
char name[32];
int age;
};
void test7(){
const person lucy={"lucy",18};
person *p=(person *)&lucy;
cout<<"name="<<lucy.name<<",age="<<lucy.age<<endl;
strcpy(p->name,"bob");
p->age=20;
cout<<"name="<<p->name<<",age="<<p->age<<endl;
cout<<"name="<<lucy.name<<",age="<<lucy.age<<endl;
}

运行结果:
图片

六、尽量以 const 替换#define

在旧版本 C 中,如果想建立一个常量,必须使用预处理器”#define MAX 1024;我们定义的宏: MAX 从未被编译器看到过,因为在预处理阶段,所有的 MAX 已经被替换为了 1024,于是 MAX 并没有将其加入到符号表中。当我们使用这个常量获得一个编译错误信息时,可能会带来一些困惑,因为这个信息可能会提到 1024,但是并没有提到 MAX.如果 MAX 被定义在一个不是你写的头文件中,你可能并不知道 1024 代表什么,也许解决这个问题要花费很长时间。解决办法就是用一个常量替换上面的宏。

const int max= 1024;

const 和 #define 区别总结:

  1. const 有类型,可进行编译器类型安全检查。#define 无类型,不可进行类型检查.
  2. const 有作用域,而#define 不重视作用域,默认定义处到文件结尾,如果定义在指定作用域下有效的常量,那么#define 就不能用。

案例8:宏没有类型,const有

  1. 宏常量没有类型,所以调用了 int 类型重载的函数。const 有类型,所以调用希望的 short 类型函数?
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    #define PARAM 128
    const short param = 128;
    void func(short a){
    cout << "short!"<< endl;
    }
    void func(int a){
    cout <<"int"<< endl;
    }
    void test8(){
    func(PARAM);
    func(param);
    }
    运行结果:
    图片

案例9:宏作用域是当前整个文件,const作用域以定义情况决定
2. 宏不重视作用域

1
2
3
4
5
6
7
8
9
10
11
void my_func(){
//作用范围 当前的{}内
const int num = 10;

//作用范围 从当前位置到文件结束
#define MY_NUM 10
}
void test9(){
//cout<<"my_num="<<my_num<<endl;//报错,my_num未定义
cout<<"MY_NUM="<<MY_NUM<<endl;//正确编译
}

运行结果:
图片

问题:宏常量可以有命名空间吗?
没有。
案例10:
3. 宏不能作为命名空间内的成员

1
2
3
4
5
6
7
8
namespace MySpace{
#define NUM 1024//属于文件 不属于MySpace
}
void test10(){
//cout<<MySpace::NUM<<endl;//错误
//int NUM = 100;//命名冲突
cout<< NUM << endl;
}

总结:

  1. 在C++中,const定义的变量默认是内部连接,要把它变成外部连接要在定义的时候加extern,在其他文件使用的时候声明就行了。
  2. const int data = 10;//data先放入符号表
    如果对data取地址,系统才会给data开辟空间
  3. const int a = b;//b是变量名 系统直接给a开辟空间,而不放入符号表
  4. const修饰自定义数据时,系统为自定义数据开辟空间,不放入符号表。
  5. 尽量以 const 替换#define