念叨了这么多年的指针,咱来看看指针的定义:
指针(K&C): 指针是一种保存变量地址的变量. ps: 这里的指针是指的指针类型变量,简称指针或指针变量. ps: 注意!标准中把指针的类型称为 "(指向)T的指针" 在ANSI C89中有这样的描述: 1.指针类型可以由函数类型,对象类型或者不完整类型派生。 2.派生指针类型的类型称为引用类型,从引用类型构造指针类型的过程称之为指针类型的派生。 ps: 须将引用类型和引用(&)区分开。对于运算符*和&.
operator&
取地址符,&的操作数是左值,&操作的结果是指针类型的右值。operator*
解引用运算符,*的操作数是右值,*操作结果返回一个左值.void类型的指针
-void*即无类型指针
无类型指针可以保存任何类型对象的地址。 ps: 有此特性可以用于设计各种回调.指针与constraint.
类型限定包括const和volatile
限定词修饰其左词. 若不存在作词则修饰第一个右词. const int var = 100; //const 修饰int int const var = 200; //const 修饰int int const * p = &var;//const 修饰int int * const p = &var;//const 修饰int * Ps:其实就是c++中的顶层const和底层const之别.且看数组是如何定义的:
数组:数组是存储单一数据类型对象的集合. 数组由数组名(标识符),类型名和数组唯独组成. 数组的初始化:可以使用初始化列表{,}进行初始化。 关于多维数组:在物理内存上是先行结构的,在逻辑上是多维的.单独的数组和指针定义描述完,且看历史回光,50多年来多少爱恨纠葛(数组与指针的爱恨纠缠从1970的c就开始了吧).
数组与指针.
1.数组名:数组名是指向数组第一个元素(值得推敲)的地址的指针,这个指针是个常量. 2.sizeof, sizeof对数组名做运算时,虽然数组名是指针,但运算得到的结果是数组的长度(length of bytes) 3.&, 对于取址运算,&对数组名做取址运算,产生的结果是"指向一维或多维的数组的指针",而不是指针本身的地址. 4.指针数组,和数组指针. 后缀运算符的优先级高于前缀运算符. 标识符的第一次结合往往能够判断类型 ---->> int *p[10]; //元素类型为int*的指针数组p int (*p)[10]; //指向int[10]数组类型的指针p 5.数组的引用 ps:引用的本质是指针. int a[10][4];int (&r)[4] = a[1]; //OK int (&r)[4] = a[1]+1; //ERROR //对引用的复制必须是左值. //我没见过引用的数组, 初始化困难.动态内存分配.
1.首先浅谈下c++的内存结构. - 内存的管理策略: - 任意大小分配策略 - 固定大小分配策略 - 垃圾收集分配策略 - 内存管理的分层(一般存在很多层管理(内存池是其中一层的策略实现)) - 内核层提供基础的分配服务. - 编译器有自己的内存分配策略(dsp都可以配置) - c++的malloc-free和new-delete(建立在操作系统本地服务的基础上) - c++对内存的划分 - 栈(stack) - 栈存储自动变量 - 栈变量的分配和释放由编译器自动完成(运行时存在) - 栈是先进后出的存储结构. - 栈的大小 - linux默认栈大小是8MB - windows默认栈大小是2MB(1M) - 栈的大小是可以配置的. - 栈也是可以通过alloca函数动态分配的. - 堆 - 用户通过malloc-free和new-delete自行分配和释放. - 堆是动态分配的,运行时分配 - 可以使用RAII实现简单gc - 堆的大小是4G(32位进程地址空间,进程的堆) - 全局/静态存储区 - 全局变量和静态变量存在的地方 - 还可能细分未初始化全局/静态的bss区 - 常量存储区 - 这个区域存储常量,不允许修改 - 通过内存页的属性readonly的吗? 2.动态内存的分配 - 内存分配和释放 - 单个对象的分配和释放. - int *ptr = new int; - delete ptr; - 动态数组的创建 - 一维数组 - int *parr = new int[20]; - delete []parr; - 多维数组 - int (*parr)[20][50] = new int[30][20][50]; - delete []parr; - Ps: - int ***ptr = new int[2][3][4];//ERROR, 类型不同. - 按照整体的方法分配的多维数组在物理上是连续的。 - 多维数组也可以多次分维度分配,不过这样就不是连续的 ------------------------------------------------ #include<iostream> #include<memory> #include<string> #include <iterator> int main(int argc, char** argv) { //方案一,分维度分配. int** x = new int*[20]; for (int i = 0; i < 20; i++) *(x + i) = new int[20]; if (int(x + 1) == int(*x + 20)) { std::cout << "方案一动态数组在物理上是线性的." << std::endl; } //方案二, 整体分配. int(*ptr)[20][500] = new int[200][20][500]; if (int(ptr + 1) == int(*ptr + 20)) std::cout << "方案二动态数组在物理上是线性的." << std::endl; delete[]ptr; return 0; } ----------------------------------------------- print: 方案二动态数组在物理上是线性的. - 悬垂指针(野指针) - int p = new int; delete p; - 此时p为悬垂指针. - 解决方案, delete释放对对象后, p = nullptr; //OK. - 堆变量可以很方便的承担起线程通信的角色. - 堆是进程共享的资源, 独立于线程之外存在,所以可以很方便的实现线程共享. - 动态联编和静态联编 - 动态联编:运行时束定,不需要时可以不创建 - 静态联编:编译时束定,不管使用与否,编译后即存在. - delete的对称 - 如果释放动态数组时,忘记加[]可能是致命的, 也有可能造成内存不释放完,造成内存泄漏. - 具体的错误和内存管理器相关. - 初始化动态数组 - int *p = new int[20](); - //注意这里是对空的(),不允许提供初始化值; new int[20](0);//ERROR. - A *pa = new A(); - //对于类类型不允许使用除默认构造外的构造函数进行初始化. - malloc-free无法满足对象类型的动态分配.