一.数组的定义和初始化
1.数组的定义
数组的维数必须使用大于等于一的常量表达式来定义,此常量表达式只能包含整型字面值常量,枚举类型,或者用常量表达式初始化的整型,非const变量和到运行阶段才知道其值的const变量都不能用来定义数组的维数
/** ok 字面值常量 */
int array[10];
/** ok 枚举类型*/
enum ARRAYSIZE{
SIZE1 = 10,
SIZE2 = 12
};
int array2[SIZE1];
/** ok 字面值常量初始化的const整型*/
const unsigned int arraySize =10;
int array3[arraySize];
/** error 非const整型 */
int arraySize = 10;
int array[arraySize];//error: array bound is not an integer constant before ‘]‘ token int array[arraySize];
/** error 在运行时才知道数组的大小 */
const int arraySize = getSize();
int array[arraySize];
注:这样在运行时才知道维数的写法在g++上面编译没问题,但是在VC6上面编译不通过,所以还是建议不要这样写(可能是g++版本比VC6高出好多)
2.数组的初始化
- 在函数体外定义的内置数组,其元素都被初始化为0
- 在函数体内定义的内置数组,其元素无初始化
- 不管在函数内还是函数外定义的类类型数组,都将调用默认构造函数进行初始化(这里类类型一定要有缺省构造函数)
- 用{}括起来的初始化列表来显式初始化数组时,初始化列表内的元素个数不能大于数组的大小,否则编译报错(error: too many initializers)
- 用{}括起来的初始化列表显式初始化数组时,初始化列表的元素小于数组元素的个数,那么剩余的元素全都初始化为0
注意:数组的大小一旦确定就不能再更改,如果需要再想向输入里面插入元素,建议使用vector,或者操作内存,分配一块更大的内存(realloc)
3.数组的操作
数组的下标一般来说是size_t类型的,在我们编程过程中如果忘记检查数组的下标是否越界时,可能会造成“缓冲区溢出”
二.指针
什么是指针?指针用于指向对象!
1.指针的初始化和赋值的约束
对于指针的初始化可以使用下面四中类型的值:
- 0值的常量表达式(在定义指针的时候最好初始化为NULL,野指针很可怕)
- 类型匹配的对象的地址
- 另一个对象的下一个地址
- 同类型的另一个有效指针
把int类型的值赋值给指针是非法的,但是把字面常量值0或者是初始化为0(除0以外的都不行)的const整型赋值给指针就行!
/** ok */
const int myNull = 0;
int * ptr = myNull;
/** error 只可以为0*/
const int myNull = 1;
int * ptr = myNull;
/** error 类型不匹配 */
double dNumber;
int * ptr = &dNumber;
/** ok 强制的类型转换为相同类型的指针 */
double dNumber = 10.0;
//这里会内存截断(8字节->4字节),根据大端小端来指向dNumber在内存里面的高四字节或者低四字节,一旦对ptr解引用,得到的数据是不可预知的!
int * ptr = (int *)(&dNumber);
printf("%d\n",*ptr);
2.void *指针
C++提供了一种特殊的指针,他可以保存任何类型的指针
/** ok */
int number = 10;
double dNumber = 10.0;
void * ptr = &number;
ptr = &dNumber;
注意:void * 指针表明他与一内存地址相关,但是不知道这块内存的类型,所以我们不能对void *指针进行解引用操作,在解引用之前需要进行类型转换!
void * 指针只只支持下面几个有限的指针操作:
- 进行两个void * 指针的比较
- 向函数传递void * 指针或者从函数返回void *指针
3.指针和引用的区别
虽然指针和引用都可以间接访问一个值,但是他们之间还是有一些区别的:
- 引用总是指向某个对象的(一旦初始化,就永远绑定同一个特定对象),在定义引用时没有初始化是错误的,而指针可以不指向任何对象(赋值为NULL)
- 在给引用赋值的时候,修改的是引用所绑定的对象,而给指针赋值时,修改的是指针本身的值,指针指向的对象没有发生改变
4.使用指针访问数组
C++里面,指针和数组密切相关,特别是在表达式中使用数组名时,数组名会自动转化成为指向数组第一个元素的指针
指针和数组的下标操作:
我们知道数组的下标是size_t类型的,也就是unsigned类型的,但是对于指针来说也一定是这样的吗?下面我们来看一个例子:
int intArray[]={1,2,3,4,5};
int * ptr = &intArray[2];
for (int i = -2; i < 3; ++i)
{
printf("%d\n", ptr[i]);
}
这里的ptr下标为-1 -2 都正常使用,这里数组的使用采用一种相对寻址的方法,ptr地址+i的偏移,所以这里可以采用相对寻址
5.指向const的指针和const指针
5.1指向const对象的指针
指向const指针定义如下:
const int * ptr ;
表明ptr指向的对象的值不能修改
const对象一定要用指向const对象的指针来指向,而非const对象既可以用一般指针来指向也可以使用指向const对象的指针来指向,说的有点拗口,下面我们来看一些例子:
/** ok */
const int number = 10;
const int * ptr = &number;
/** error number为const对象,需要用const指针来指向 */
const int number = 10;
int * ptr = &number;//如果这里可以的话,那么就可以通过ptr来为const对象赋值,这是极其危险的!
/** ok */
int number = 10;
const int * ptr = &number;
int * ptr = &number;
下面注意一点:如果是void 类型的指针,那么不能用void *指向const对象,必须要使用const void * 才可以!
5.2const指针
const指针定义如下:
int * const ptr;
表明ptr本身的值不能修改
/** error ptr的值不能修改 */
int number = 0;
int * const ptr = &number;
ptr++;
/** ok 可以修改指向的对象的值*/
int number = 0;
int * const ptr = &number;
*ptr = 10;// number = 10;
5.3指向const对象的const指针
顾名思义,就是指向一个const的指针,指针自身的特性也是const类型的,指针指向的对象值不能改变,自身的指针值也不能改变
6.创建动态数组
创建动态数组一般规则大家知道,我们来看一下一些特殊情况:
int * ptr = new int[-1];
int * ptr = new int [0];
要是申请的数组的大小小于等于0会发生什么?
int * ptr=NULL; try { ptr = new int[-1]; } catch(std::bad_alloc e) { printf("%s\n", "bad alloc"); } printf("%p\n" , ptr);
运行结果为
bad alloc
00000000
抛出了bad_alloc 异常
那要是0呢?
int * ptr=NULL; try { ptr = new int[0]; } catch(std::bad_alloc e) { printf("%s\n", "bad alloc"); } printf("%p\n" , ptr);
运行结果为
00481818
表明在0的时候分配数组成功!那我们来对这个分配好的内粗操作
*ptr = 10;
printf("%d\n",*ptr );
输出10
操作正常!!!在《C++ Primer》上面写的是不能进行解引用操作!求解释???
7.多维数组
在《C陷阱与缺陷》里面提到过,C语言中没有所谓的多维数组,只不过是数组的类型是数组而已也就是说多维数组起始就是这样定义的:
typedef int array_t[3];
array_t array[2];
array就和定义为 array[2][3]一样的;