C++:00.一些注意点

以前记录的一些注意点,暂时先放在这里

1、使用静态类型的编程语言是在编译时执行类型检查,而不是在运行时执行类型检查

2、C++ 完全支持面向对象的程序设计,包括面向对象开发的四大特性:封装 抽象 继承 多态

3、标准的 C++ 由三个重要部分组成:

  • 核心语言,提供了所有构件块,包括变量、数据类型和常量,等等。
  • C++ 标准库,提供了大量的函数,用于操作文件、字符串等。
  • 标准模板库(STL),提供了大量的方法,用于操作数据结构等。

4、用 extern 声明外部变量是不能进行初始化的,只是声明而不是定义,声明是不会为变量开辟内存空间的,自然无法对其进行初始化的操作

5、左值可以在等号左右,但右值只能在等号右边。

  • 左值在等号右边的应用例子:实际项目中,为了防止将==误写作=推荐将变量名写在右侧,编译器可以帮助寻找错误

6、全局变量是指在所有函数外部声明的变量,包括主函数

  • 全局变量从定义处开始至程序结束起作用,即全局变量存在有效作用域。如果它定义在了调用语句的后面,需要在该调用语句前加上extern对全局变量进行声明

局部变量是指在函数或一个代码块内部声明的变量,注意是代码块也可以,即一个{}

  • 在程序中,局部变量和全局变量的名称可以相同,但是在函数内,局部变量的值会覆盖全局变量的值。
  • 也可以通过域名::在函数中引用到全局变量,不加域名解析则引用局部变量

7、 字符 ‘0’:****char c = ‘0’; 它的 ASCII 码实际上是 48

​ **字符 ‘\0’**: ASCII 码为 0,表示一个字符串结束的标志。这是转义字符(整体视为一个字符)

8、不应把**true **的值简单的看成 1,把 false 的值简单的看成 0,而是真和假。

9、定义成 **const **后的常量,程序对其中只能读不能修改。因此必须在一开始定义的时候就完成初始化。下面这段语句两行都会报错

1
2
const int PI;
PI = 3.1415;

除非这个变量是用extern修饰的外部变量

1
2
3
const int A=10;       //正确。
const int A; //错误,没有赋初始值。
extern const int A; //正确,使用extern的外部变量。

10、预处理 #define 变量定义值以后,不能加分号,否则会连同分号一起直接替换掉定义的部分

11、两个整型数据相除结果依然是整型,会把小数部分直接舍去(不是四舍五入)

1
2
3
int a = 10;
int b = 3;
cout << a/b; //3

主函数

main 函数前面加上什么数据类型,比如: int void

main 函数的返回值是返回给主调进程,使主调进程得知被调用程序的运行结果。

标准规范中规定 main 函数的返回值为 int,一般约定返回 0 值时代表程序运行无错误,其它值均为错误号,但该约定并非强制。

如果程序的运行结果不需要返回给主调进程,或程序开发人员确认该状态并不重要,比如所有出错信息均在程序中有明确提示的情况下,可以不写 main 函数的返回值,写void main(){} ,这在一些检查不是很严格的编译器中,比如 VC, VS 等,void 类型的 main 是允许的

不过在一些检查严格的编译器下,比如 g++, 则要求 main 函数的返回值必须为 int 型,因此还是建议使用int作为返回值

区分main程序运行结果并以 int 型返回,是一个良好的编程习惯,尽量避免使用void main()这种写法

13、int main() 和 int main(void) 的区别?

int main(void) 指的是此函数的参数为空,不能传入参数,如果你传入参数,就会出错。

int main() 表示可以传入参数。

在 C++ 中 int main() 和 int main(void) 是等效的

C++的变量自动转换

强制转换规则很简单,(待转换类型)(表达式)。但自动转换需要遵循一定规则:

  1. 若参与运算量的类型不同,则先转换成同一类型,然后进行运算。

  2. 转换按数据长度增加的方向进行,以保证精度不降低。如int型和long型运算时,先把int量转成long型后再进行运算。

    • 若两种类型的字节数不同,转换成字节数高的类型

    • 若两种类型的字节数相同,且一种有符号,一种无符号,则转换成无符号类型

      1
      2
      3
      int a = 1;
      double b = 2.1;
      cout << "a + b = " << a + b << endl; //输出为a + b = 3.1,先将a转为double再加上2.1
  3. 所有的浮点运算都是以双精度进行的,即使仅含float单精度量运算的表达式,也要先转换成double型,再作运算。

  4. char型和short型参与运算时,必须先转换成int型。

  5. 在赋值运算中,赋值号两边量的数据类型不同时,赋值号右边量的类型将转换为左边量的类型。如果右边量的数据类型长度比左边长时,将丢失一部分数据,这样会降低精度

    1
    2
    3
    4
    int a=1;
    double b=2.5;
    a=b;
    cout << a; //输出为 2,丢失小数部分

typedef 声明

可以使用 typedef 为一个已有的类型取一个新的名字。

1
typedef type newname; 

下面的语句会告诉编译器,feet 是 int 的另一个名称:

1
typedef int feet;

现在,下面的声明是完全合法的,它创建了一个整型变量 distance:

1
feet distance;

关于 typedef 的几点说明:

  • typedef 可以声明各种类型名,但不能用来定义变量。
  • 用typedef只是对已经存在的类型增加一个类型名,而没有创造新的类型
  • 当在不同源文件中用到同一类型数据(尤其是像数组、指针、结构体、共用体等类型数据)时,常用 typedef 声明一些数据类型,把它们单独放在一个头文件中,然后在需要用到它们的文件中用 #include 命令把它们包含进来,以提高编程效率。
  • 使用 typedef 有利于程序的通用与移植。有时程序会依赖于硬件特性,用 typedef 便于移植。

有关typedef 与 #define 的区别

  • 执行时间不同

    • 关键字 typedef 在编译阶段有效,由于是在编译阶段,因此 typedef 有类型检查的功能。
    • #define 则是宏定义,发生在预处理阶段,也就是编译之前,它只进行简单而机械的字符串替换,而不进行任何检查。所以它没有typedef 来的稳健
  • 功能有差异

    • typedef 用来定义类型的别名,定义与平台无关的数据类型,与 结构体的结合使用等。

      比如定义一个叫 FALSE 的浮点类型,在目标平台一上,让它表示最高精度的类型为:

      1
      typedef long double FALSE; 

      在不支持 long double 的平台二上,改为

      1
      typedef double FALSE; 

      在连 double 都不支持的平台三上,改为:

      1
      typedef float FALSE; 

      也就是说,当跨平台时,只要改下 typedef 本身就行,不用对其他源码做任何修改。标准库就广泛使用了这个技巧

    • #define 不只是可以为类型取别名,还可以定义常量、变量、编译开关等。

  • 作用域不同

    • #define 没有作用域的限制,只要是之前预定义过的宏,在以后的程序中都可以使用。

    • typedef 有自己的作用域

      1
      2
      3
      4
      5
      6
      7
      8
      9
      void func1()
      {
      typedef unsigned int UINT;
      }

      void func2()
      {
      UINT uValue = 5;//error C2065: 'UINT' : undeclared identifier
      }
  • 二者修饰指针类型时,作用不同

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    typedef int * pint;
    #define PINT int *

    int i1 = 1, i2 = 2;

    const pint p1 = &i1; //p不可更改,p指向的内容可以更改,相当于 int * const p;
    const PINT p2 = &i2; //p可以更改,p指向的内容不能更改,相当于 const int *p;或 int const *p;

    pint s1, s2; //s1和s2都是int型指针
    PINT s3, s4; //相当于int * s3,s4;只有一个是指针。

    void TestPointer()
    {
    cout << "p1:" << p1 << " *p1:" << *p1 << endl;
    //p1 = &i2; //error C3892: 'p1' : you cannot assign to a variable that is const
    *p1 = 5;
    cout << "p1:" << p1 << " *p1:" << *p1 << endl;

    cout << "p2:" << p2 << " *p2:" << *p2 << endl;
    //*p2 = 10; //error C3892: 'p2' : you cannot assign to a variable that is const
    p2 = &i1;
    cout << "p2:" << p2 << " *p2:" << *p2 << endl;
    }

    结果:

    1
    2
    3
    4
    p1:00EFD094  *p1:1
    p1:00EFD094 *p1:5
    p2:00EFD098 *p2:2
    p2:00EFD094 *p2:5

宏定义 #define 和常量 const 的区别

类型和安全检查不同

宏定义是字符替换,没有数据类型的区别,同时这种替换没有类型安全检查,可能产生边际效应等错误;

const常量是常量的声明,有类型区别,需要在编译阶段进行类型检查

编译器处理不同

宏定义是一个”编译时”概念,在预处理阶段展开,不能对宏定义进行调试,生命周期结束与编译时期;

const常量是一个”运行时”概念,在程序运行使用,类似于一个只读行数据

存储方式不同

宏定义是直接替换,不会分配内存,存储与程序的代码段中;

const常量需要进行内存分配,存储与程序的数据段中

定义域不同

1
2
3
4
5
6
7
8
9
10
void f1 ()
{
#define N 12
const int n 12;
}
void f2 ()
{
cout<<N <<endl; //正确,N已经定义过,不受定义域限制
cout<<n <<endl; //错误,n定义域只在f1函数中
}

定义后能否取消

宏定义可以通过#undef来使之前的宏定义失效

const常量定义后将在定义域内永久有效

1
2
3
4
5
6
7
8
void f1()
{
#define N 12
const int n = 12;

#undef N //取消宏定义后,即使在f1函数中,N也无效了
#define N 21//取消后可以重新定义
}

是否可以做函数参数

宏定义不能作为参数传递给函数

const常量可以在函数的参数列表中出现

const关键字

constconstant的简写,只要一个变量前面用const来修饰,就意味着该变量里的数据可以被访问,不能被修改。也就是说const意味着只读(readonly)。

规则:const离谁近,谁就不能被修改;

const修饰一个变量,一定要给这个变量初始化值,若不初始化,后面就无法初始化。

本质:const在谁后面谁就不可以修改,const在最前面则将其后移一位,二者等效。

const关键字作用

  • 为给读你代码的人传达非常有用的信息,声明一个参数为常量是为了告诉用户这个参数的应用目的;
  • 通过给优化器一些附加信息,使关键字const也许能产生更紧凑的代码;
  • 合理使用关键字const可以使编译器很自然的保护那些不希望被修改的参数,防止无意的代码修改,可以减少bug的出现;

const关键字应用

  • 欲阻止一个变量被改变,可使用const,在定义该const变量时,需先初始化,以后就没有机会改变他了;
  • 对指针而言,可以指定指针本身为const,也可以指定指针所指的数据为const,或二者同时指定为const;
  • 在一个函数声明中,const可以修饰形参表明他是一个输入参数,在函数内部不可以改变其值;
  • 对于类的成员函数,有时候必须指定其为const类型,表明其是一个常函数,不能修改类的成员变量;
  • 对于类的成员函数,有时候必须指定其返回值为const类型,以使得其返回值不为“左值”。

C++:00.一些注意点
http://jswanyu.github.io/2021/10/02/Cpp/00.一些注意点/
作者
万宇
发布于
2021年10月2日
许可协议