10.数组和指针
10.1 数组
数组由数据类型相同的一系列元素组成
声明数组告诉编译器数组中内含多少元素和这些元素的类型
int main(void)
{
float candy[365]; /* 内含365个float类型元素的数组 */
char code[12]; /*内含12个char类型元素的数组*/
int states[50]; /*内含50个int类型元素的数组 */
...
}
数组元素的编号从0开始
10.1.1 初始化数组
int main(void)
{
int powers[8] = {1,2,4,6,8,16,32,64}; /* 从ANSI C开始支持这种初始化 */
...
}
用以逗号分隔的值列表(用花括号括起来)来初始化数组,各值之间用逗号分隔。在逗号和值之间可以使用空格
要创建只读数组,应该用const声明和初始化数组。
const int days[MONTHS] ={31,28,31,30,31,30,31,31,30,31,30,31};
当初始化列表中的值少于数组元素个数时,编译器会把剩余的元素都初始化为0
如果初始化列表的项数多于数组元素个数,它会将其视为错误。
可以省略方括号中的数字,让编译器自动匹配数组大小和初始化列表中的项数
const int days[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31 };
10.1.2 指定初始化器(C99)
C99规定,可以在初始化列表中使用带方括号的下标指明待初始化的元素:
int arr[6] = {[5] = 212}; // 把arr[5]初始化为212
对于day[4]=30,31,30
相当于day[4]=10,day[5]=31,day[6]=30
int staff[] = {1, [6] = 4, 9, 10};
staff[]={1,0,0,0,0,4,9,10}
10.1.3 给数组元素赋值
C 不允许把数组作为一个单元赋给另一个数组,除初始化以外也不允许使用花括号列表的形式赋值。
#define SIZE 5
int main(void)
{
int oxen[SIZE] = {5,3,2,8}; /* 初始化没问题 */
int yaks[SIZE];
xen[SIZE]; /* 数组下标越界 */
yaks[SIZE] = {5,3,2,8}; /* 不起作用 */
10.1.4 数组边界
要确保程序中使用的数组下标在范围内,因为编译器不会检查出这种错误
使用越界的数组下标会导致程序改变其他变量的值。不同的编译器运行该程序的结果可能不同,有些会导致程序异常中止
10.1.5 指定数组的大小
最好是在声明数组时使用符号常量来表示数组的大小,这样做能确保整个程序中的数组大小始终一致。
声明数组时只能在方括号中使用整型常量表达式
int n = 5;
int m = 8;
float a1[5]; // 可以
float a2[5*2 + 1]; //可以
float a3[sizeof(int) + 1]; //可以
float a4[-4]; // 不可以,数组大小必须大于0
float a5[0]; // 不可以,数组大小必须大于0
float a6[2.5]; // 不可以,数组大小必须是整
float a7[(int)2.5]; // 可以,已被强制转换为整型常量
float a8[n]; // C99之前不允许
float a9[m]; // C99之前不允许
10.2 多维数组
int a [m] [n]
a是一个二维数组
10.2.1 初始化二维数组
for(int i =0;i<m;i++)
{
for(int j=0;j<n;j++)
{
a [i] [j]=1;
}
}
将a[m] [n]的每一个值初始化为1
10.2.2 其他多维数组
可以这样声明一个三维数组:
int box [10] [20] [30];
处理三维数组要使用3重嵌套循环,处理四维数组要使用4重嵌套循环。
10.3 指针和数组
flizny 和&flizny[0]都表示数组首元素的内存地址(&是地址运算符)。
指针加1指的是增加一个存储单元。对数组而言,这意味着把加1后的地址是下一个元素的地址,而不是下一个字节的地址
指针前面使用*运算符可以得到该指针所指向对象的值。
指针加1,指针的值递增它所指向类型的大小(以字节为单位)
dates + 2 == &date[2] // 相同的地址
*(dates + 2) == dates[2] // 相同的值
C语言标准在描述数组表示法时确实借助了指针。
也就是说,定义ar[n]的意思是(ar + n)。可以认为(ar + n)的意思是“到内存的ar位置,然后移动n个单元,检索储存在那里的值”。
10.4 函数、数组和指针
int sum(int * ar, int n)
第1个形参告诉函数该数组的地址和数据类型,第2个形参告诉函数该数组中元素的个数。
还可以在里面用sizeof(ar)/sizeof(ar[0])代表长度
这个时候函数名不代表首元素的地址
由于函数原型可以省略参数名,所以下面4种原型都是等价的:
int sum(int *ar, int n);
int sum(int *, int);
int sum(int ar[], int n);
int sum(int [], int);
在函数定义中不能省略参数名
int sum(int ar[], int n)
{
int i;
int total = 0;
for (i = 0; i < n; i++)
total += ar[i];
printf("The size of ar is %zd bytes.\n", sizeof ar);
return total;
}
这里ar代表数组首元素地址,所以长度为8byte
10.4.1 使用指针形参
#include <stdio.h>
int data[2] = { 100, 200 };
int moredata[2] = { 300, 400 };
int main(void)
{
int * p1, *p2, *p3;
p1 = p2 = data;
p3 = moredata;
printf(" *p1 = %d, *p2 = %d, *p3 = %d\n",*p1, *p2, *p3);
printf("*p1++ = %d, *++p2 = %d, (*p3)++ = %d\n",*p1++, *++p2, (*p3)++);
printf(" *p1 = %d, *p2 = %d, *p3 = %d\n",*p1, *p2, *p3);
return 0;
}
下面是该程序的输出:
*p1 = 100, *p2 = 100, *p3 = 300
*p1++ = 100, *++p2 = 200, (*p3)++ = 300
*p1 = 200, *p2 = 200, *p3 = 301
10.5 指针操作
赋值:可以把地址赋给指针。
解引用:*运算符给出指针指向地址上储存的值。
取址:和所有变量一样,指针变量也有自己的地址和值。
指针与整数相加:可以使用+运算符把指针与整数相加,或整数与指针相加。
递增指针:递增指向数组元素的指针可以让该指针移动至数组的下一个元素。
指针减去一个整数:可以使用-运算符从一个指针中减去一个整数。指针必须是第1个运算对象,整数是第 2 个运算对象。
指针求差:可以计算两个指针的差值。差值的单位与数组类型的单位相同。
比较:使用关系运算符可以比较两个指针的值,前提是两个指针都指向686相同类型的对象。
不要解引用未初始化的指针。
这可能不会出什么错,也可能会擦写数据或代码,或者导致程序崩溃。

浙公网安备 33010602011771号