C++基础语法知识点汇总(一)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/LuyaoYing001/article/details/81463394

Const 关键字

  • 定义普通变量时,只能初始化一次,不可再修改。
  • 定义指针变量时,在类型前则值不能改,在类型后则地址不能改;若两者都有,则都不能改。
int a = 2;
const int* p1 = &a; //值不可以修改,地址可以修改
int* const p2 = &a; //值可以修改,地址不可以修改
  • 在类中定义的const函数,不能修改类成员变量的值。
  • 在函数中的const形参,可以防止意外修改传进来的参数值。

结构体

  • C++中struct与class的唯一区别是:默认访问限定符为public,而class为private。
  • C中struct不允许有函数,内部成员变量访问权限只能是public,且不可以继承。
  • struct vs union:
    • (1)struct中各成员有各自的内存空间,struct所占用内存是各成员所占用内存之和;union中各成员共享一段内存空间,union所占用内存是最大变量或对象所占用内存(需要考虑内存对齐问题)。
    • (2)对struct不同成员赋值互不影响,而对于union不同成员赋值,原来成员的值将不复存在。

指针与数组

  • 严格地说,数组不是指针,指针也不能说是数组。指针仅在内存中代表一个地址,而数组是许多连续的内存块,多个类型相似的元素存储在其中。
  • 数组名变量是数组中首元素的地址。它表现得像一个不能被修改的常指针一样,但却并不是一个指针,比如当数组名作为sizeof操作符的操作数时,返回的是整个数组的大小,而不是指向数组的常指针的大小;当数组名作为&操作符的操作数时,返回的是一个指向数组的指针,而不是一个指向某个指针常量的指针,并且,“&常指针”不一定是合法的。
  • 数组指针是指向该数组首元素地址的一个指针变量,只是一个普通的变量,可以赋为任何的值。
  • 指针数组,表示一个数组,它的内容是一个个的指针。数组指针,表示一个指向数组的指针。一般情况下数组名的值是一个指针常量,是数组首元素的地址。对于一维整型数组,其为整型变量的 指针;对于二维整型数组,其为一维数组的指针。而数组名取地址所产生的是一个指向数组的指针。
    int arr1[4];
    int arr2[4][4];

    int *(a[4]); //定义指针数组,括号可以省略
    a[0] = &arr1[0];
    *a[0] = 1; 
    printf("arr1[0]=%d\n", *a[0]); //arr1[0]=1

    int (*b)[4];//定义数组指针,为长度4的一维int型数组的指针
    //b = arr1; //错误,arr1是int型变量的指针
    b = &arr1;  //正确,&arr1是长度4一维int型数组的指针
    b = arr2;   //正确,arr2是长度4一维int数组的指针
    //b = &arr2;//错误,&arr2是4x4二维int型数组的指针

    b = &arr1;
    (*b)[0] = 2; //小括号不可省略,因为中括号优先级>星号
    printf("arr1[0]=%d\n", (*b)[0]);//arr1[0]=2
  • 数组表示法array_name[index]是一种语法糖,编译器会翻译为*(array_name+index),array_name表示数组中第一个元素的地址, +index则表示用于指针运算的偏移量。在程序与数组交互的时候,可以直接用指针表示法代替数组表示法。

二维数组

  • 参数传递:在被调函数中对形参数组定义时,必须指定第二维(以及更高维)大小,第一维可以省略,如void Func(int array[][])是不合法的。如果在形参中不说明列数,则系统无法决定应为多少行多少列。
  • 如果传递的实参大于形参,则在函数中只对实参的部分进行运算,比如下列代码中Func只会访问到数组的3行10列:
void Func(int array[3][10]);
int a[5][10];
Func(a);
  • 对于静态二维数组而言,a[i][j],a[i*n+j]和*((int*)a+i*n+j)这些寻址方式是等价的,通过后一种寻址方式,我们也可以利用Func((int**a, int n))来实现二维数组的参数传递。
  • 对于动态二维数组而言,每个row内的内存地址是连续的,但是每个row之间的内存地址一般是不连续的,也就是说在寻址表达式中凡是出现n(每个row的长度,即列数)都是不正确的,比如a[i*n+j]和*((int*)a+i*n+j)这两种寻址方式都是非法的。a[i][j]无论二维数组是静态还是动态,都是正确的;除此之外,*(*((int*)a+i)+j)这种方式就等同于a[i][j](提示:连续运用两次语法糖array_name[index] = *(array_name+index)),都可以用。
  • 为什么*(*((int*)a+i)+j)可以寻址a[i][j]? 因为数组a中存贮的都是int型指针(int*),这些指针指向一个int数组,数组a中的每个元素就是指向每个row的首地址的指针,*((int*)a+i)就是解引用a[i]得到第i行的数组的首地址,加上偏移量j就拿到了元素a[i][j]的地址了。
  • 动态二维数组可以通过下列语句生成,注意释放内存的写法:
int **a;
a = new int*[num_row];
for (int i=0; i<num_row; ++i) {
  a[i] = new int[num_colume](); // 初始化为0
}
/* do something */
for (int i=0; i<num_row; ++i) {
  delete [] a[i];
}
delete [] a;
  • 动态二维数组的第二种写法,需要使用STL中的vector。
vector<vector(int)> a(num_row, vector<int>(num_column));

引用

  • 引用与指针的异同点
    • C++ 标准只规定了引用是什么样子的(用法),但没有规定引用该怎么实现,具体编译器对于引用的实现方式不尽相同。通常情况下,引用是通过指针实现的,不过提供了一种比指针更容易理解的语法,同时在编译时会进行优化,引用就类似于常量指针。
    • C++11 标准 § 8.3.2/4
      It is unspecified whether or not a reference requires storage.
      C++11 标准 § 8.3.2/5
      There shall be no references to references, no arrays of references, and no pointers to references.
    • 相同点:两者都是地址的概念,指针指向一块内存,它的内容是所指内存的地址,引用是某块内存的别名,反汇编得到的内部实现是只读指针,引用变量内存空间保存的是被引用变量的地址。
    • 区别点
      • a. 指针是一个实体,是独立存在的,而引用仅是个别名,是依附于所引用的对象而存在的。
      • b. 引用需要在定义时初始化,之后不可变,并且不能为空(NULL),而指针没有这些限制。
      • c. 通过引用访问比通过指针访问更方便,无需解引用(*),指针需要解引用。
      • d. sizeof运算符:sizeof(引用)返回的所指向变量(对象)的大小,sizeof(指针)返回的是指针本身的大小,即所指向变量或对象的地址的大小。
      • e. 自增运算符号(++):引用的++实际上是所指向变量或对象的++,而指针的++是内存地址的++,将指向指针之后的内存地址。
  • 按值传递(pass by value)和按引用传递(pass by reference)
    • 按值传递会生成实参的一份拷贝,传递大对象会有不可忽视的分配存储空间(内存)的开销。按引用传递可以避免大对象的拷贝开销,只会有引用的拷贝,相当于一个常量指针的拷贝开销,可以忽略。
    • 按引用传递时,形参作为实参的别名使用,对形参变量的操作就是对被引用变量或对象的操作。而按值传递时,对形参变量的操作只针对拷贝副本,不影响实参本身。
  • 将引用作为函数返回值的注意事项
    • 不能返回局部变量的引用,局部变量在函数返回后被销毁,会导致返回的引用失效,造成运行时错误。
    • 不能返回函数内部new分配出来内存的引用,没有正确释放会造成内存泄漏(memory leak)。
    • 可以返回类数据成员的引用,但最好是const,返回非常量引用会破坏封装。
    • 流操作符重载的返回值必须是引用,其他操作符不能返回引用。

猜你喜欢

转载自blog.csdn.net/LuyaoYing001/article/details/81463394