数组与指针
一、数组
数组是由类型名、标识符和维数组成的复合数据类型,类型名规定了存放在数组中的元素类型,维数则指定数组中包含的元素个数。
数组的维数必须用值大于等于1的常量表达式定义。此常量表达式只能包含整型字面值常量、枚举常量或者用常量表达式初始化的整型const对象。非const变量以及要到运行阶段才知道其值的const变量都不能用于定义数组的维数。
C++虽然不允许定义长度为0的数组变量,但明确指出,调用new动态创建长度为0的数组是合法的。
1、数组的长度是固定的,与vector类型不同,数组不提供push_back或者其他的操作在数组中添加新元素。如果必须添加新元素,程序员必须自己管理内存,重新分配一个新的内存空间更大的数组,复制所有元素到新的数组。
2.数组如果没有显示提供元素初值,则数组元素会像普通变量一样初始化:
- 在函数体外定义的内置数组,其元素均初始化为0;
- 在函数体内定义的内置数组,其元素无初始化,其值是不确定的;
- 不管数组在哪里定义,如果其元素为类类型,则自动调用该类的默认构造函数进行初始化;如果该类没有默认构造函数,则必须为该数组的元素提供显式初始化。
3. 特殊的字符数组
字符数组既可以用一组由花括号括起来、逗号隔开的字符字面值进行初始化,也可以用一个字符串字面值进行初始化。
字符串字面值包含一个额外地空字符(null)用于结束字符串。
当使用字符串字面值来初始化创建的新数组时,将在新数组中加入空字符:
1 char ca1[] = {'C' , '+' , '+'}; // no null
2 char ca2[] = {'C' , '+' , '+' , ' '}; //explict null
3 char ca3[] = "C++"; // null terminator added automatically
ca1的维数是3,而ca2和ca3的维数则是4.使用一组字符字面值初始化字符数组时,一定要记得添加结束字符串的空字符。
4、数组元素也是从0开始计数,vector中使用vector::size_type作为下标类型,在数组中下标的正确类型是size_t。
1 const size_t array_size = 10;
2 intg ia[array_size];
3 for(size_t ix=0; ix != array_size; ++ix)
4 ia[ix] = ix;
5、导致安全问题的最常见原因是所谓的“缓存区溢出(buffer overflow)”错误,当没有检测下标引用了越出数组或者其他类似数据结构边界时容易出现这个错误。
二、指针
指针是指向某种类型对象的符合数据类型,是用于数组的迭代器:指向数组中的一个元素。
1、指针是什么:指针就是用于指向对象,指针提供对其所指对象的间接访问。指针保存的是另一个对象的地址。
2、取地址符用户于一个对象上,返回该对象的存储地址,取地址符只能用于左值,只有变量作为左值时才能取地址。
3、很多运行时错误都源于使用了未初始化的指针。
对大多数的编译器来说,如果使用未初始化的指针,会将指针中存放的不确定值视为地址,然后操纵该内存地址中存放的位内容。使用未初始化的指针相当于操纵这个不确定地址中存储的基础数据。因此,在对未初始化的指针进行解引用时,通常会导致程序崩溃。
如果可能的话,除非所指向的对象已经存在,否则不要先定义指针,这样可避免定义一个未初始化的指针。
如果必须分开定义指针和气所指向的对象,则将指针初始化为0. 因为编译器可检测出0值的指针,程序可判断该指针并未指向一个对象。
4、对指针进行初始化或者赋值只能使用以下四种类型的值:
- 0值常量表达式
- 类型匹配的对象的地址
- 另一个对象之后的下一个地址
- 同类型的另一个有效指针
5.具有void* 类型的指针可以保存任意类型对象的地址。
指针操作
6、指针和引用的比较
引用总是指向某个对象,定义引用时没有初始化是错误的;赋值行为的差别,给引用赋值修改的是该引用所管理的对象的值,而并不是使引用与另一个对象关联。引用一经初始化就始终指向这个特定对象。
7、指向指针的指针
指针本身也是可用指针指向的内存对象。指针占用内存空间存放其值,隐藏指针的存储地址可存放在指针中;
int ival = 1023;
int *pi = &ival;
int **ppi = π
8、指针的算术操作
指针的算术操作只有在原指针和计算出来的新指针都指向同一个数组元素,货指向该数组存储空间的下一单元时才合法,如果指针指向同一对象我们还可以在指针上加1从而获取指向相邻的下一个对象的指针。
9、下标和指针
在表达式中使用数组名,实际上使用的是指向数组第一个元素的指针。
int ia[] = {0,2,4,6,8};
int i = ia[0];
ia[0]是一个使用数组名的表达式,在使用下标访问数组时,实际上是指向数组元素的指针做下标操作。
int *p = &ia[2]; //ok:p points to the element indexed by 2
int j = p[1]; //p[1] == ia[3] ,ok:p[1] equivalent to *(p + 1) , p[1] is the same element as ia[3]
int k = p[-2]; //p[-2] == ia[0] ok:p[-2] is the same element as ia[0]
虽然下标为-2 ,但是被编译器解析为偏移量,因此相当于*(p - 2).
10.两个指针减法操作的结果是标准库类型ptrdiff_t的数据。与size_t类型一样,ptrdiff_t也是一种与机器相关的类型,在cstddef头文件中定义。size_t 是unsigned类型,而 ptrdiff_t则是signed整型。
ptrdiff_t n = ip2 - ip; // ok:distance between the pointers
11. C字符串的标准库函数
12.永远不要忘记字符串结束符null
在使用处理C风格字符串的标准库函数时,牢记字符串必须以结束符null结束:
1 char ca[] = {'C' , '+' , '+'}; // not null-terminated
2 cout<< strlen(ca)<<endl; // disaster: ca is not null-terminated
在这个例题中,ca是一个没有null结束符的字符数组,则计算的结果不可预料。
标准库函数strlen总是假定其参数字符串以null字符结束,当调用该标准库函数时,系统将会从实参ca指向的内存空间开始一直搜索结束符,知道恰好遇到null位置。strlen返回这一段空间中总共有多少个字符,无论如何这个数值不可能是正确的。
1 //用int型数组初始化vector对象
2
3 //输入数组元素
4 for(size_t ix = 0 ; ix != arr_size ; ++ix)
5 cin >> int_arr[ix];
6 //用int型数组初始化vector对象
7 vector<int> ivec(int_arr , int_arr + arr_size);
三、创建动态数组
数组类型的变量有三个重要的限制:数组长度固定不变,在编译时必须知道其长度,数组只在定义他的块语句中存在。可以在运行时动态的分配数组。可以在动态 确定数组的长度。c语言使用标准库的malloc和free在自由存储区中分配空间,C++使用new和delete实现该功能。
1、动态数组的定义
int *pia = new int[10]; // array of 10 uninitialized ints
只需指定类型和数组的长度,创建完成后new将返回指向数组第一个元素的指针。在自由存储区创建的数组是没有名字的,通过间接的访问堆中的对象。
2、初始化动态分配的数组
动态分配数组时,如果数组元素具有类类型,将使用该类的默认构造函数,初始化,如果是内置类型就无初始化。
string *psa = new string[10] ; // 定义10个空string的数组
int *pia = new[10]; // 定义10个未初始化的int数组
对于动态分配的数组,其元素只能初始化为元素类型的默认值,而不能像数组变量一样,用初始化类别为数组元素提供各不相同的初值。
3、动态空间的释放
动态分配的内存最后必须进行释放,否则,内存最终将会逐渐耗尽。c++为指针提供deletep[]表达式来释放所指向的数组空间。
delete []pia; //该语句回收pia所指向的数组,把相印的内存返还给自由存储区。
- Email系列(QQ邮箱 + 含附件的邮箱案例 + 项目实战)下
- 警告:Android P(禁用非官方API)
- 使用JavaScript访问XML数据
- 详解Android UI线程卡顿收集
- 浅谈差分约束问题
- JVM活学活用——类加载机制
- WebAssembly详解及其使用案例
- 30分钟精通快应用
- BZOJ2440: [中山市选2011]完全平方数(莫比乌斯+容斥原理)
- Dapper扩展之~~~Dapper.Contrib
- JVM活学活用——GC算法 垃圾收集器
- BZOJ1101: [POI2007]Zap(莫比乌斯反演)
- freeRTOS事件组学习
- 洛谷P2522 [HAOI2011]Problem b(莫比乌斯反演)
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- 如何保证集合是线程安全的? ConcurrentHashMap如何实现高效地线程安全?
- 如果MySQL的 InnoDB 文件的损坏,该如何手动恢复?
- 如何使用慢查询快速定位执行慢的 SQL?
- 如何使用 EXPLAIN 精准查看执行计划?
- MySQL怎么查看 SQL 的具体执行成本?
- Python 爬虫进阶必备 | 某外卖优惠平台内容加密参数分析
- pytest 自动化测试框架(二)
- Web | Django 与数据库交互,你需要知道的 9 个技巧
- 商机负责人与商机团队负责人不一致时更新团队负责人为商机负责人语句
- mysql常用语句集合(仅供工作日常学习参考)
- 新一代Notebook神器出现,Jupyter危险了!
- vmstat 监视内存使用情况
- Android开发重写Animation实现下拉图片后弹射回去效果示例
- Android 中RxPermissions 的使用方法详解
- Android构建Material Design应用详解