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相同类型的对象。

不要解引用未初始化的指针。

这可能不会出什么错,也可能会擦写数据或代码,或者导致程序崩溃。

posted @ 2022-01-30 15:30  buguai_lxw  阅读(42)  评论(0)    收藏  举报