一、单维数组
初印象: 顺序存储(连续空间),一些相同类型值的集合,有一维、二维...多维数组,数组名有讲究,数组做参数传递,下标有讲究。
回顾: 今天重新读了读《C和指针》第8章:数组,发现又有许多新收获,特此写本博文,记录下自己的受过,以防忘记。
二、数组名
①数组名:指向第一个元素的指针常量,(注:不是指针变量)。
程序在编译链接的过程中,直接给数组分配好了固定的空间,数组名就是一个指向这个空间的指针常量。
判断题:数组和指针是相同的。 (X) 理由:只有数组名在表达式中使用时,编译器才会为了它产生一个指针常量。
②有些时候数组名不是指针常量:
1)当数组名作为sizeof
操作符的操作数时,返回数组的长度。
2)当数组名作&
的操作数时候,返回指向数组的指针 而不是指向某个 指针常量 的指针。
&array[0]和array
值相等 但是前者是指针变量后者是指针常量(不能改变)。
③疑问: 在刚开始学习数组的时候,我总是存在这样一个疑问:为什么一般的变量直接用=
即可赋值,如a=b
,但是数组却不能直接int a[10];int b[10]; b=a;
直接赋值它不香吗?
④解答: 数组名只是指向第一个元素的常量指针,而不是所有元素。
三、数组下标
①下标引用和间接访问完全相同:array[i]<=>*(array+i)
②扩展: 下标引用可以作用于任意的指针,不仅仅是数组名。
判断题:i[array]
这种表达形式对吗? (√)理由:i[array]<=>array[i]
③指针和下标谁谁效率高: 用指针效率>=用下标的效率,但是不能差别不大,收益不高。
④警告: 下标不能越界,有的编译器为了提高效率,不会检查是否下标越界,所以要注意。
四、数组和指针
①区分:int a[5]
和int* b
区分a
和b
| |相同点|不同点1:被编译器区别对待 |不同点2:间接访问|不同点3:指针运算
|--|--|--|--|--|
|a |具有指针值、都可以间接访问和下标引用操作。 | 为数组声明一大片连续地空间|*a
是数组第一个元素|a++
非法 是指针常量
|b| 具有指针值、都可以间接访问和下标引用操作。|只为指针本身保留空间,不为int分配空间|*b
访问某个未知空间非法|b++
合法 是指针变量
②结合下图理解:
五、数组名做函数参数。
①回顾: 数组名是一个指向数组第1个元素的指针常量。
②调用形式: 设int array[10]
做函数function
的参数。
int function(int *array); //推荐使用,这里的array是指针,不和真正意义上的数组混淆
int function(int array[]);
③参数如何传递: 实际传递给函数的是指向数组起始位置的指针的一份拷贝,该指针同样指向数组起始位置。在函数内部对指针形参进行间接访问操作,实际访问的是原数组的元素。这个形参(拷贝的指针)不会改变实参,但是能改变它们指向的内容。(毕竟它们操作的都是一块内存)。
④实列: 把第2个参数中的字符串复制到第1个参数指定的缓冲区
void (char* buffer,const char* string)
{
while((*buffer++=*string++)!='\0');
}
⑤练习题: 下面代码中的array1
和array2
有区别吗?
void function(int array1[10])
{
int array2[10]
}
答案:指针和数组名的区别
array1是一个指针变量,它指向传给它的实参数组,它的值在函数中可以改变。在这个函数中没有为这个数组分配内存空间,并且不能保证传输的这个数组有10个值,指出数组数量是多余的。
aray2是一个 指针常量,它的值不能改变,它指向函数中分配的含有10个整型元素的数组的首元素。
六、多维数组
①存储方式: 行主序列,列主序列。要点:只要坚持使用同一种方法,这两种解释方法都可行。
②数组名: 一维数组名的值是一个指针常量,类型是: 指向元素类型的指针 多维数组的第一维的元素是宁外一个数组,类型是: 指向指针的指针
③下标: 下标应用仍然是一种间接访问表达式的一种伪装形式。下面通过实例来理解:
原数组:int matrix[3][10]
matrix[1][5]<=>*(*(matrix+1)+5)
matrix<=>matrix[0]
matrix+1<=>matrix[1]
*(matrix+1)<=>&matrix[1][0]
*(matrix+1)+5<=>&matrix[1][5]
④指向数组的指针
int array1[10],*p1=array1;//正确声明
int array2[3][10],*p2=array2;//错误声明
p1
声明是正确的,理由如下:p1
和array1
都指向array1
的第一个元素,并且都是指向整型的指针。p2
声明是错误的,理由如下:p2
是一个指向整型的指针,但是这里array2
是一个指向整型数组的指针,它们类型都不一致。array2
是一个由10个整型元素的指针。正确声明如下:
int array2[3][10];
int (*p2)[10]=array2;//p2指向了array2的第1行
那么如果要一个指针逐个访问整型元素而不是逐行在数组中移动,该如何声明呢??
int *p=&array[0][0];
int *p=array[0];
⑤多维数组作为函数参数
回顾一维数组做参数:
int function(int *array); //推荐使用
int function(int array[]);
多维数组和一维数组做参数实质上都是:传递指向数组第1个元素的指针。但是两者还是有区别的:
多维数组的每一个元素本身是宁外一个数组,编译器需要知道它的维数,以便为函数形参的下标表达式进行求值。
int function(int (*array)[10]);
int function(int array[][10]);
疑问: 第1维的长度可以不用填?
解答: 数组的长度是由编译器自动计算的,只有第1维才能根据初始化列表默认提供,剩下的几维必须显示地写出来。具体细节,不详细说明,总之这是编译器的事儿。
警告⚠: 编写1维数组形参的原型时,既可以写成数组形式,也可以写成指针形式。但是对于多维数组,只有第1维才有这两种选择。这种表示第二维数组的写法是不正确的:int function(int ** array);
这是指向整形的指针,而并非指向整形数组的指针。
七、指针数组
初印象: 首先指针和数组这两个名词组合在一起,那究竟是指针呢?还是数组?显而易见是:数组,其实你发现别的类型的叫法:整形数组、浮点型数组....猛然回头,指针也是一种类型。整形数组用于存整形数据,自然而然,指针数组,用来存指针。
讨论: int *p[10];
乍一看,有点复杂,我们假设它是个表达式,并对它进行求值。
(下标引用[]
)优先级高于(间接访问*
),所以先执行[]
,所以它是一个数组,随后执行*
得到一个整型值。再对数组的某元素执行间接访问*
后得到一个整型,所以p
是数组,它的元素类型是指向整形的指针。
八、一些实用技能
①计算数组的长度: 整个数组大小/每个元素大小=元素个数 sizeof(array)/sizeof(array[0]);
这里的数组名:array
此刻被sizeof
操作,所以它不是一个指针常量。
②区分char const* ptr
、const char* ptr
和 char * const ptr
: 参考路痴的旅行的博客char const* ptr
<==>const char* ptr
定义一个指向字符常量的指针,这里,ptr
是一个指向 char*
类型的常量,所以不能用ptr
来修改所指向的内容,换句话说,*ptr
的值为const
,不能修改。但是ptr
的声明并不意味着它指向的值实际上就是一个常量,而只是意味着对ptr
而言,这个值是常量。
相应技巧: 很多时候为了防止这个指针修改这个形参,我们就把它声明为:char const*
类型,让指针指向一个“假”的const
类型。char *const ptr
定义一个指向字符的指针常数,即const指针,不能修改ptr指针,但是可以修改该指针指向的内容。
练习题:
解释void function(int const a,int const b[])
中两个const
的区别:
答案:第1个参数是个标量,所以函数得到值的一份拷贝。对这份拷贝的修改并不会影响原先的参数,所以const关键字的作用并不是防止原先的参数被修改。
第2个参数实际上是一个指向整型的指针。传递给函数的是指针的拷贝,对它进行修改并不会影响指针参数本身,但函数可以通过对指针执行间接访问修改调用程序的值。const关键字用于防止这种修改。
③区分sizeof
和strlen
: 首先根本上来说,他们根本就是牛头不对马嘴,sizeof
是操作符而strlen
是库函数。在求字符串的长度的时候,strlen
不包括\0
,而sizeof
包括\0
。如代码:
char a[] = "wocao";
cout<<"strlen求得长度:" << strlen(a) << endl;
cout << "sizeof求得长度:" << sizeof(a) << endl;
④数组计算长度: 不管是几维数组,都只能省略第一维。