《c primer plus》c语言学习笔记整理(十)-数组与指针

第十章 数组与指针
1.一维数组
(1)初始化数组
1)使用符号变量表示数组大小,这是我们推荐的做法,便于后期修改
2)把数组设置为只读,可用const声明与初始化数组,一旦声明为const,便不可以给其再赋值。只能从数组中检索值。
3)对于其他一些存储类别的变量和数组,如果在声明时未初始化,编译器会自动把它们的值设置为0。
3)使用数组前必须初始化,不然会得到垃圾数值
4)本章数组属于自动存储类别,意思是这些数组在函数内部声明,且声明时未使用关键字static
5)当初始化列表的值少于数组元素个数,编译器会把剩余元素初始化为0
6)如果初始化数组省略方括号中的数字,编译器会根据初始化列表中的项数确定数组大小。
7)如果初始化列表的项数多余数组元素个数,编译器会直接视为错误。
8)指定初始化器:可以初始化指定的数组元素(c99标准),第一,如果指定初始化器后面有更多的值,那么后面的值被用于初始化元素后面的元素,第二, 如果再次初始化指定的元素,那么最后的初始化将会取代之前的初始化。第三,编译器会把数组大小设置为足够装得下初始化的值。
(2)赋值
1)c不允许把数组作为一个单元赋值给另一个数组
2)除了初始化以外,也不允许使用花括号列表的形式赋值
3)编译器不会检查数组下标,使用越界的下标会导致程序改变其他变量的值。不同编译器运行结果可能不同。(非法越界)可能导致程序终止或者异常
4)数组元素的编号从0开始。最好是在声明数组时使用符号常量来表示数组大小;
5)数组大小指定 :方括号只能使用整型常量表达式(包括sizeof),但是const值(c++不同)不是,且表达式的值必须大于零。
2.多维数组
(1)初始化
1)初始化可以省略内部花括号,只保留最外面的一层括号,只要初始化的数值个数正确,效果与含有花括号相同。
2)如果初始化的数值不够,则按照先后顺序逐行进行初始化,直到用完所有的值,后面没有值初始化的元素统一初始化为零。
3)如果多维数组内部某个列表中的数值个数超过了数组每行的元素个数,会出错,但是这并不影响其他行的初始化.如果每个列表中初始化元素的个数小于数组每行元素个数,将只会初始化相应的列表前面的个数,后面多余的元素个数将默认被初始化为0.
3.指针与数组
(1)在c中,指针加1指的是增加一个存储单元,这是为什么声明指针所指向类型的原因之一,只知道地址不够,计算机要知道存储对象需要多少字节。
(2)如果要在被调函数中改变主调函数的变量,则应该传递函数的地址,利用指针改变函数的变量。
(3)指针的值是它所指向对象的地址
(4)在指针前面使用运算符可以得到该指针所指向对象的值
(5)间接运算符的优先级高于+,所以
dates+2与*(dates+2)不一样。
4.函数、数组、指针
(1)使用指针形参
1)只要在函数原型或函数定义头中, 才可以用int ar[]代替int ar;
2)int ar[]只能用于声明形式参数
3)使用指针形参的两种方法:一是使用一个指针形参标识数组的开始, 用一个整数形参表明待处理数组元素的个数(指针形参也表明了数组中的数据类型);二是传递两个指针,一个指明数组开始处,一个指明数组结束处。
4)一元运算符
与++优先级相同,但是结合律是从右向左,所以pi++先进行++运算,再进行计算。
(2)指针表示法和数组表示法:
处理数组的函数实际上用指针作为参数,但是在编写这样的函数时,可以选择是用数组表示法还是指针表示法。
5.指针操作
1)赋值:int *p1; a = 5; p1 = &a;
2)解引用:*运算符给出指针指向地址上储存的值;
3)取址;指针变量也有自己的地址和值,用&运算符
4)指针与整数相加:整数与指针指向类型的大小相乘,然后把结果与初始地址相加
5)递增指针:递增指向数组元素的指针可以让指针移动至数组下一个元素。
6)指针减去一个整数:该整数将乘以指针指向类型的大小(以字节为单位),然后用初始地址减去乘积
7)递减指针:
8)指针求差:一般是同一个数组的不同元素,通过计算求出两个元素的距离(单位与数组类型的单位相同),若是不同数组可能会得到一个值,或者导致运行错误。
9)比较:前提是两个指针都指向相同类型的对象;
10)编译器不会检查指针是否指向数组元素,C只能保证指向数组任意元素的指针和指向数组后面第一个位置的指针有效。但是,如果递增或递减一个指针后超出了这个范围,则是未定义的。即使指针指向数组后面一个位置是有效的,也能解引用这样的越界指针。
11)创建一个指针时,系统只分配了储存指针本身的内存,并未分配储存数据的内存,因此在使用指针前,必须先用已分配的地址初始化它,不要解引用未初始化的指针。
6.const的用法(保护数组中数据)
(1)对形式参数使用const
1)处理基本类型的函数,一般都是直接传递数值,因为函数使用的是原始数据的副本,这样就不会意外修改原始数据,保证数据的完整性。
2)当程序需要在函数中改变该数值时,应该传递指针;
3)对于数组,必须传递指针,这样效率更高,因为如果按值传递数组,必须分配足够的空间储存数组的副本,然后把原数组所有数据拷贝到新的数组中去。
4)把数组的地址传递给函数,让函数直接处理原数组更加高效。
5)如果函数的意图不是修改数组中的数据内容,那么在函数原型和函数定义中声明形式参数时应使用关键字const;
6)const并不会要求原数组是常量,而是该函数在处理数组时将其视为常量,保护数组中元素不被修改,如果需要修改数组,那么声明函数形参可不使用const(这种情况最好声明const)。
(2)const其他用法
1)指向const指针场经常用于函数形参中,表明该函数不会使用指针改变数据,例如:const double *pc,这种情况,将const数据或非const数据的地址初始化为指向const的指针或为其赋值是合法的;
2)只能把非const数据的地址赋给普通指针;例如:const double locked[4] = {1,2,3,4}
double *p; p = locked:(这种赋值方式是无效的,因为不这样的话,通过指针就可以修改const数组中的数据)
3)c标准规定,使用非const标识符修改const数据,导致的结果是未定义的;
4)可以用于声明并初始化一个不能指向别处的指针 例如:rate[3] = {1,2,3};double * const pc = rate ,但是可以改变初始化所指向地址的值。
5)创建指针时候可以使用const两次,该指针既不能更改他所指向的地址,也不能修改地址上的值。例如: const double const pi
7.指针与多维数组
(1)指向多维数组的指针(int zippo[4][2])
1)zippo是数组首元素的地址,与&zippo[0]的值相同,zippo【0】本身是一个内含两个整数的数组,zippo【0】的值与首元素第一个整数的地址(&zippo[0][0])的值相同;zippo占用两个int类型大小的地址,zippo【0】是一个占用一个int大小对象的地址;
2)解引用一个指针或者在数组名后使用带下标的【】运算符,得到引用对象代表的值
zippo是地址的地址,必须解引用两次才可以得到原始值,地址的地址或指针的指针就是双重间接的例子。
3)如果程序恰好使用一个指向二维数组的指针,而且要通过该指针获取值的时候,最好用简单的数组表示法,而不是指针表示法。
4)int (pz)[2] 指向多维数组的指针形式
(2)指针的兼容性
1)把const指针赋给非const指针不安全,因为这样可以使用新的指针改变const指针指向的数据, 编译器在编译代码时,可能会发出警告,执行这样的代码是未定义的,但是把非const指针赋给const指针没问题。
2)C++允许声明数组大小的时候使用整数,但是C却不允许,C++的指针检查更加严格
3)C++不允许把const指针赋值给非const指针,但是c却可以这么做,但是这种行为是未定义的。
(3)函数与多维数组
1)形式:void somefunction(int (pt)[4]) 或 void somefunction(int pt[][4]);
2)第二对花括号不可以省略,不让编译器不知道如何处理。
8.变长数组
1)变长数组必须是自动存储类别,这意味着无论在函数中声明还是作为函数形式参数声明,都不可以使用static或extern存储类别说明符,而且不能在声明中初始化他们(C11作为可选特性)
2)变长数组的变不是指可以修改已创建数组大小,而是一旦创建了变长数组,大小保持不变,这里的变指的是:在创建数组时可以指定数组的维度。
3)在形参列表中必须在声明ar之前先声明这两个形参,c99/c11标准规定可以省略原型中的形式参数名,但是这种情况下,必须用
号代替省略的维度,
int sum2d(int, int, int ar[
][
])
4)在函数定义的形参列表中声明的变长数组并未实际创建数组,变长数组实际上是一个指针,这说明变长数组形参的函数实际上是在原始数组中处理数组,因此可以修改传入的数组。
5)变长数组C90标准不允许在生命的时候使用const变量,数组大小必须是给定的整型表达式或者整型组合,c实现可以扩大整型常量表达式的范围,所以可能允许使用const,但是这种代码可能无法移植
6)c99/11标准允许声明变长数组的时候使用const变量,所以该数组必须是声明在块中的自动存储类别
7)变长数组允许动态内存分配,这说明可以在程序运行时指定数组大小,普通c数组都是静态内存分配,也就是说在编译时确定数组大小。

你可能感兴趣的:(C语言)