CPrimerPlus第十章
数组和指针
数组的定义:数组由数据类型相同的一系列“元素”组成,需要使用数组时,通过声明数组告诉编译器数组中内含多少元素和这些元素的类型。
(C把数组看成是一种‘’派生类型“,因为数组是建立在其他类型的基础之上)
一:数组
1.初始化。
int powers[8]={1,3,5,4,6,8,7,5};
-
用以花括括起来的用逗号分隔的值列表来初始化数组。 如果部分初始化数组,剩下的元素就会被初始化为0。
-
可以省略方括号中的数字,让编译器自动匹配数组大小。
-
sizeof(powers)以字节为单位给出数组的大小,sizeof(powers[0])以字节为单位给出数组中一个元素的大小,数组元素的个数为sizeof(powers)
/ sizeof(powers[0])。
关于const:用const来修饰一个值,就把这个值限定为”只读“,若在初始化之后再修改这个值的话编译器会报错。
若没有对数组初始化或赋值,就引用数组元素的话,编译器使用的值是内存相应位置上的现有值,即一个不确定的数(这很恶心),这样程序可能会发生错误。
关于定义的数组的存储类别问题:
1 auto.是默认的自动存储类别。
2 static静态变量。分为静态局部和静态全局。
3 extern表示变量在这个块中的外部。
4 register寄存器变量
强调
C不允许把一个数组作为一个单元赋给另一个数组。
int arr1[]={1,2};
int arr2[2];
arr2=arr1;//错误
另外,除了在声明数组时可以初始化,之后都不可以初始化了,
int arr1[4];
arr1[4]={1,5,6,4};//错误
另外,不要数组越界。要确保下标有效。不同编译器处理数组越界结果可能不同。可能会报错,也可能提取一个内存值或覆盖它。
int arr1[2];
printf("%d",arr1[2]);
-858993460
VS2019输出了内存上的一个值。
另外,数组元素的编号从0开始。最好在声明数组时使用符号常量来表示数组的大小。
二:多维数组
无论是几维的数组,其实元素在内存上都是排成一行,并没有行与列之分,二维的行与列只不过是便于使用。另外,二维数组理解为主数组中的元素为数组,即主数组中包含副数组会好一点,更容易理解。行与列为了方便抛弃了本质。
主数组包含的是数组,副数组包含的是元素。
可以把一维数组想象成一行数据,把二维数组想象成一个数据表,把三维数组想象成一叠数据表。
三:指针和数组
计算机的硬件指令非常依赖地址,指针在某种程度上把程序员想要传达的指令以更接近机器的方式表达,因此使用指针更有效率。
(转换说明%p通常以十六进制显示指针的值,即地址)
在函数定义中,以下两种方式等价:
int sum(int *arr,int n)
{
//其他代码省略
}
int sum{int arr[],int n}
{
//其他代码省略
}
要记住一个概念,指针变量是一种新类型,不是int,不是double.系统用多少字节储存地址,指针变量的大小就是多少字节。
int *p1;
printf("%d",sizeof(p1));
4
我的电脑系统是用四个字节储存第地址。
1:指针和整数相加时,整数都会和指针所指向类型的大小(以字节为单位)相乘,再相加。
2:如果希望在被调函数中改变主调函数的值,必须使用指针。也可以使用全局变量。
3:只有程序想在函数中改变该数值,才会传递指针。对于数组则别无选择,必须传递指针,因为这样做效率高。如果一个函数接受的是数组的值而不是地址,则必须分配足够的空间来储存原数组的副本,然后把原来所有的数据拷贝至新的数组中。如果把数组的地址传递给函数,让函数来处理原数组则效率要高。
四:const的作用。
一般而言,如果编写的函数需要修改数据,在声明数组形参时不使用const;如果编写的函数不用修改数组,那么在声明数组形参时最好使用const.
void array(const double*ar,int n);
指向const的指针通常用于函数形参中,表明该函数不会使用指针改变数据。
- 总:const指针可以指向const和非const。普通指针只能指向非const。(const限定的是同一内容,值或地址)。
- 对于指针来说,const可以限定值 或 地址 或 都限定。
- 识别限定对象的方法:“近水楼台先得月“。
const double *p1;//不能通过p1修改指向的值。
double *const p2=&a;//不能修改p2的指向,但可以通过p2修改它指向的值。(必须在一开始初始化指向)
const double *const p3=&a;//不能通过p3修改对象的值,也不能修改p3的指向。
去掉数据类型,看谁离const近就限定谁。
五:声明行指针
int (*p1)[2];//p1是一个指向内含两个int值的数组。
int *p1[2];//p1是一个内含两个元素的数组,两个元素都为指针变量。
[ ]的优先级比*的高,所以造成了上面的不同。
六:变长数组
C规定,数组的维数必须是常量,不能用变量来代替。
但C99新增了变长数组,允许使用变量来表示数组的维度。
变长数组必须是自动存储类别,不能使用static或extern来修饰。
变长数组的变指的是在创建数组上时,可以使用变量来指定数组的维度,不是指可以修改已创建的数组的大小。
在函数定义声明中有两种方式声明变长数组:
int sum1(int rows,int cols,int ar[rows][cols]);
int sum2(int,int,ar[*][*]);//必须用星号来代替省略的维度。
注意:在函数定义的形式参数列表红声明的变长数组并未实际创建数组。变长数组名实际上依然是一个指针。
七:复合字面量。
即数组常量,由一个圆括号包起来的类型名后面跟着用花括号抱起来的初始化器列表所构成的一个后缀表达式。
- 省略数组名。
- 可以省略大小,例如
(int []){2,1,3}
- 同时创建同时使用,可以作为实参传递(好处时把信息传入函数前不必先创建数组,这是复合字面量的典型用法)
- 假如指针要指向一个数组,指向之后都不要用到数组名了,可以用复合字面量。
int *p1; p1=(int []){1,1,1};
- 复合字面量具有块作用域。