不妨不妨,来日方长

不妨不妨,来日方长

一些需要知道的基础知识点:

在程序代码中是通过变量名对内存单元进行存取操作的,但是代码经过编译后将变量名转换为该变量在内存中的存放地址,对变量值的存取都是通过地址进行的。比如i+j的运算,如果i等于3,j等于4,程序是先根据变量名与地址的对应关系,找到变量i的地址,从第一个地址开始顺序读取四个字节数据放到CPU寄存器中;再找到j的地址,依次读取四个放到寄存器中,然后通过CPU的加法中断计算出结果。

在低级的汇编语言中都是直接通过地址来访问内存单元的,而在高级语言中才使用变量名访问内存单元,C语言作为高级语言却提供了通过地址来访问内存单元的方法,C++也继承了这一特性。地址可以形象地称为指针,意思是通过指针能找到内存单元,因为原本是通过地址找到内存单元的,所以一个变量的地址称为该变量的指针。如果有一个变量专门存放另一个变量的地址,它就是指针变量。在C++语言中,专门用来存放内存单元地址的变量类型,称为指针类型。

指针是一种数据类型,通常所说的指针就是指针变量,是专门用来存放地址的变量。而变量的指针说的就是变量在内存中的地址。变量地址在编写代码时无法获得,只有在程序运行时才可以得到。

指针跟常规的变量为赋值相同,没有具体指向的指针不会导致编译出错,但是可能会导致难以预料的错误,所以一旦定义指针,一定要让它有一个具体的指向,也就是说要有一个地址赋给它。

另外,定义指针变量的时候,跟其他常规变量为了区分,要加*号,但其实真正的指针变量是没有*号的,在使用的时候要注意。

指针进行运算其实就是地址进行运算,指针的加减运算是跟指针的类型有关的,比如int类型的指针加1,地址值并不是加1而是加4,因为int类型占四个字节。

※指针还可以指向空类型void。空类型指针可以接受任何类型的数据,例如void *p = NULL

这里有点问题,NULL代替空指针存在二义性,所以后来用nullptr来代替空指针。总之,只要记住nullptr代表空指针就可以了,而NULL在C++中都把它理解为0就可以了。

※const int *p = &i  表示这个指针是指向常量的指针,只能用来“读”内存数据,无法通过*p的方式更改内存的内容,即更改变量的值,但是可以改变自身的地址值,就是指向别的内存地址;

※int* const p = &i  表示这个指针是一个常量指针,什么意思呢,以为指针变量里存放的是地址,定义为常量指针的话,说明这个指针变量存放的地址值是不可以改变的,但是呢,可以通过*p的方式改变内存的数据;

※const int* const p = &i  表示这个指针是指向常量的常量指针,有点拗口,跟上面的对比,反正就是只能用来“读”内存数据,也无法通过*p的方式更改内存的内容,也无法改变自身的地址值。

指针和一维数组

一维数组和二维数组在内存中的存放结构都是线性的。数组第一元素的地址就是整个数组的存储首地址,该地址存放在数组名中,看吧,其实数组名就相当于一个指针变量了,里头存放的是数组的首地址。访问数组元素的方式有下标法和指针法。用数组名[i]这个方式可以访问数组内容,其实也可以通过*(a+i)的方式,当然我们也可以再单独定义一个指针变量,将数组的首地址存放到这个指针变量中,当然了,可以用&a[0]也可以直接用a即数组名,因为我们说过了数组名本来就存放的是数组的首地址。

*(p++)相当于a[i++],先对p进行*运算,再使p自增。

指针与二维数组

a[0]是二维数组第一个元素的地址,可以赋值给一个指针变量。

a代表二维数组的地址,通过指针运算符可以获取数组中的元素。

a+n表示第n行的首地址;

&a[0][0]既可以看做数组0行0列的首地址,还可以看做是二维数组的首地址;

&a[0]是第0行的首地址;

a[0]+n是第0行第n个元素的地址;

*(*(a+n)+m)表示第n行第m列元素;

*(a[n]+m)表示第n行第m列元素。

数组指针和指针数组

这个还没有理解好,尤其是数组指针,指针数组还好理解,就是存储指针变量的数组。

指针和字符数组

字符数组就是一个字符串,通过字符指针可以指向一个字符串,然后通过地址的加减实现读取。

传递地址

之前接触的函数都是实参传递进函数体后,生成的是实参的副本,函数体内改变副本的值并不会影响到实参,但是如果传递进去的是指针,即使是副本指针,指向的地址还是一样的,因此可以改变指针指向地址的内容。

指向函数的指针

指针变量也可以指向一个函数。一个函数在编译时被分配给了一个入口地址,这个地址可以理解为存放在函数名中,可以定义函数指针,指向这个函数,通过指针来调用函数。

比如:

int sum(int x, int y)

int *a(int, int);

a = sum;

调用的时候这样:

int c,d;

(*a)(c,d);

还可以定义指针函数,返回值是指针,也就是地址了。

空指针调用函数

空类型指针指向任意类型函数或者将任意类型的函数指针赋值给空类型指针都是合法的。使用空指针调用自身所指向的函数仍然按照强制转换的形式使用。

指针数组

构造数据类型

结构体

C++ struct结构体变量其实跟数组有点像,只不过数组是相同类型元素的集合,而结构体变量可以是不同类型数据的集合。

定义结构体变量有两种方式:1、在定义结构体的时候定义;2、定义完之后定义。

struct PersonInfo
{
    int index;
    char name[30];
    short age;
}XiaoMing;

或者
struct PersonInfo
{
    int index;
    char name[30];
    short age;
}
PersonInfo XiaoMing;

引用方式:1.使用成员运算符.来引用;2.定义结构体指针变量之后,使用指向运算符->来引用

结构体还可以进行嵌套,结构体的大小一般情况下都是结构体内成员的大小之和。

使用typedef可以给一个复杂的数据类型定义一个别名,比如int(*)(int i)很复杂,就可以用一个别名来代替。

枚举类型enum的应用

枚举类型就是用大括号将不同标识符放到一起,变量的值只能取自括号内的值,在定义时,编译器默认将标识符自动赋上整形常数。还可以自行修改整形常数的值,说白了就是一个标识符对应一个常数,用于一些判断或者什么,用起来方便一些。赋值的时候,不能直接赋整型数,但是可以通过强制类型转换来赋值。枚举类型的变量,实质其实是整型的数字,所以可以进行比较和运算。

结构体

结构体变量还可以做函数的参数,还可以使用结构体指针,使用结构体指针减少了时间和空间上的开销,能够提高程序的运行效率。

结构体还可以创建结构体数组,跟创建结构体变量一样,在定义结构体时创建或者定义完了之后用结构体名这种数据类型创建。可以定义结构体指针,指向数组,使用指向符号->进行读取,但是要注意,指针加1,指向的就是数组里的下一个结构体了。

共用体

共用体union数据类型其实和结构体很像,声明共用体数据类型变量和声明共用体变量一样,都有三种方式,前两种应该知道,第三种是省略了共用体数据类型变量名,在定义共用体之后接着声明变量。

要注意共用体跟结构体最大的区别在于内存方面。共用体变量所占的内存长度等于最长的成员的长度,一个共用体变量不能同时存放多个成员的值,某一时刻只能存放其实一个成员的值,这就是最后赋予他的值。

共用体的特点是:1.使用共用体变量的目的是希望用同一个内存段存放几种不同类型的数据,但是某个瞬间只能存放一种,而不是同时存放几种;2.能够被访问的是最后一个被赋值的变量,对新的赋值后原来的就失去作用了;3.共用体变量的地址和它的各成员的地址都是同一地址。4.不能对共用体变量名赋值,不能引用变量名来得到哦一个值,不能在定义共用体变量时对它初始化,不能作为函数参数。

自定义数据类型

跟前面的数据类型重命名一样的,使用typedef标识符进行类型的重命名,或者说是自定义数据类型,其实是为了写起来方便或者用特定的单词代表特殊的意思。

宏定义

使用#define 可以对程序中经常出现的参数进行宏定义,这样在之后写代码的时候就可以用宏定义替换,一定程度上减轻了写代码的复杂度,程序在编译的时候自动进行替换,比如PI,一般标识符习惯用大写字母表示,以和变量名进行区分,要注意宏定义不是C语言,不需要在后面加分号,还可以定义数组和运算,定义运算的时候,括号建议都加上,以防止出现错误。如果字符串长于一行,可以在该行末尾用反斜杠\来续行。一般来讲,定义只有,作用域即有效范围指的是定义命令之后到此源文件结束,但也可通过#undef命令终止宏定义的作用域。

12-17 11:52