0%

C++基础 注意点(一)

这里主要记录下我认为重要的点。

  1. 宏定义
    宏定义目的是防止头文件被重复引用。
    当编译器编译头文件时,判断当前宏是否被定义,如果没有定义,则定义宏,
    并编译头文件,否则跳过头文件。

    1
    #define __HEADER_H__	//宏定义

    #ifndef#pragma once的异同
    相同:
    都是为了避免同一个文件被include多次。

     
    1
    2
    3
    4
    #ifndef __HEADER_H__
    #define __HEADER_H__
    ...
    #endif
    1
    2
    #pragma once
    ...

    不同:
    #ifndef的方式受C/C++语言标准支持。它不光可以保证同一个文件不会被包含多次,
    也能保证内容完全相同的两个文件(或者代码片段)不会被不小心同时包含。
    由于编译器每次都需要打开头文件才能判定是否有重复定义,因此在编译大型项目时,
    #ifndef会使得编译时间相对较长,因此一些编译器逐渐开始支持#pragma once的方式。

    #prama once一般有编译器提供保证:同一个文件不会被包含多次。但是不能保证不同文件的
    相同内容不会被重复引用。但是对于大型项目,这个方式的编译速度会更快。同时,#pragma once
    兼容性不强。

  2. 引用头文件
    1-1. 使用<>引用系统头文件
    1
    #include <stdlib.h>
    系统会先在系统库文件目录下搜索头文件,它不会搜索当前工程下的目录。
    1-2. 使用""格式引用自定义头文件
    1
    #include "my_header.h"
    编译器会现在当前工程目录下搜索头文件,然后再搜索系统库文件目录。
  3. volatile关键字
    该关键字表示变量可以被某些意外的因素改变。
    当编译器遇到该关键字时,将不会对变量进行优化,从而提供对变量的直接访问。
    如果全局变量使用volatile关键字,程序每次访问都是从变量的内存地址访问。
    1
    2
    3
    4
    volatile int var = 1;
    _asm{
    mov ptr[ebp-4],10h
    }
  4. 变量存储类型
    C++语言中,变量有4种存储类型:extern,static,register,auto
    变量的声明:告知编译器变量的名称和数据类型。
    变量的定义:编译器为变量分配存储空间。
    4-1. extern存储类型
    extern关键字只声明不定义变量。通常在一个应用程序中包含多个文件,如果在一个文件中
    定义了一个全局变量,可能需要在其他文件中对其访问,可用extern关键字修饰其他文件中
    变量的声明,而定义源于前一个文件。
    eg:
    在一个文件中定义了一个全局变量var

    1
    int var=0;

    在其他文件中使用extern关键字声明全局变量var

    1
    extern int var=0;

    4-2. static存储类型
    静态变量类型,分为全局静态变量类型和局部静态变量类型。

    • 当使用static关键字标识一个局部变量时,该变量被分派在一个持久额存储区域,当函数调用结束时,
      变量并不被释放,依然保持其值。当下一次函数被调用时,将应用之前的变量值。
      局部静态变量只被初始化一次,但是它的作用域仍然是函数体内。
      可以认为静态变量是一个在函数调用后保持其值的局部变量。
    • 对于全局静态变量,它的作用域局限于本当前定义的文件,不能被其他文件使用extern关键字访问。
      可以认为全局静态变量只是半个全局变量。(不能被其他文件共享)

      eg:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      void TestStaticVar(){
      static int slocal=10;//定义一个局部静态变量
      printf("%d\n",slocal);
      ++slocal;
      }
      void main(){
      TestStaticVar();
      TestStaticVar();
      TestStaticVar();
      }

      将输出10,11,12。

      4-3. register存储类型
      使用register关键字定义变量,变量将被分派到CPU的寄存器中,因此访问速度很快。
      但是,register变量只能用于局部变量或作为函数的形式参数,而不能定义全局的register变量。

      4-4. auto存储类型
      全局变量、静态变量均为静态存储,而普通的局部变量属于动态存储。
      auto 关键字表示变量将被用动态存储。默认局部变量均属于auto类型。

  5. 实数和0的比较
    在比较实数和0时,尽量不要使用”==”,”!=”,而应该使用”<=”,”>=”运算符。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    void main(){
    float eps=0.0000001;//定义0的精度,相当于数学的无穷小
    float fvar = 0.00001;
    if(fvar>=-eps && fvar<=eps){
    printf("等于0\n",fvar);
    }else{
    printf("不等于0\n",10);
    }
    }
  6. 二维数组
    在定义二维数组时,如果需要提供全部元素的初始值,可以省略第一维的长度,
    但是不能省略第二维的长度。
    1
    int arr[][4]={1,2,3,4,5,6,7,8};
  7. 结构体的字节对齐问题
    编译器在为结构体变量分配变量空间时,保证下一个成员的偏移量为该成员数据类型长度的整数倍。
    1
    2
    3
    4
    5
    struct ByteAlign{
    double memOne;
    char memTwo;
    int memThree;
    };
    使用sizeof函数测试时,发现结构体ByteAlign的变量占用16个字节。
    分析:double占用8个字节,char占用1个字节,int占用4个字节。假设从0地址开始,
    memOne占用0,1,2,3,4,5,6,7。
    memTwo占用8。
    接下来为int型分配空间,需要空出9,10,11。
    memThree占用12,13,14,15。
  8. pair类型
    pair类型可以使两个相同或不同类型的变量关联在一起。
    在使用pair类型时,需要引入utility头文件。
    可以pair类型的成员,可以使用成员访问符firstsecond
    1
    2
    3
    4
    5
    6
    7
    8
    #include <utility>
    using namespace std;
    void main(){
    pair<int,char*> list(10,"C++");//定义pair类型变量list
    if(list.first==10 && list.second=="C++"){
    printf("I love C++ language!\n");
    }
    }
  9. const 关键字修饰指针类型
    • const在类型前,用户不能修改指针常量的数据,但是可以修改指向的地址,指针变量“只读”;
    • const在类型后,用户可以修改变量的值,但是不能修改指向的地址,指针变量固定指向地址;
    • const在类型前后都有,用户既不能修改指向的地址,也不能修改指向的数据。
      具体看下面的例子:
      1
      2
      3
      4
      5
      int var=10;				//定义一个整型常量
      const int* pvar=&var; //定义一个指针常量,对其初始化
      //*pvar=20; //错误代码,不能修改指针常量的数据
      int num=5; //再定义一个整型变量
      pvar=&num; //修改指向的地址
      1
      2
      3
      4
      5
      int ivar=0;				//定义一个整型常量
      int* const pvar=&ivar; //定义了一个指向整型的常量指针
      *par=20; //修改指针指向的数据
      int inum=5; //定义一个整型变量
      //pvar=&inum; //错误的代码,不能修改常量指针指向的地址
      1
      2
      3
      4
      5
      int ivar=10;			//定义一个整型变量
      const int* const pvar=&ivar;//定义一个常量指针常量
      //*pvar=20; //非法的代码,不能够修改pvar指向的数据
      int inum=10; //定义一个整型变量
      //pvar=&inum; //非法的代码,不能够修改pvar指向的地址
  10. 动态存储类型分配和释放
    newdelete搭配,mallocfree搭配。
    使用new运算符为数组分配空间时,不能对数组初始化,除非变量是一个对象,
    并且对象的类型(类)提供了默认的构造函数。
    此外,在释放使用new运算符为数组分配的空间时,需要使用delete []来释放。

    1
    2
    int* pvar=new int[5];
    delete [] pvar;

    对类对象类型的数组,必须这样做。

  11. 运算符的结合性和优先性

  12. 逗号表达式
    C++提供了一种特殊的表达式,即逗号表达式。比如:
    10*5,8*9
    逗号表达式的运算过程为: 先计算表达式1,再计算表达式2,依次类推。
    整个表达式的值,也就是最右边的表达式的值。
    注意:赋值符=的优先级高于逗号运算符,,一般要加括号。

    1
    2
    3
    int iret=2;
    iret=3*5,iret*4; //iret最后的值为15
    int var=(iret=3*5,iret*4);//var=60,iret=15;