2019-02-28 计算机二级C语言程序设计之数组(上)

参考资料:
高教版《全国计算机等级考试二级教程——C语言程序设计》
《21天学通C语言》

数组的基本概念

数组是一组带索引的数据存储位置,各位置的名称相同,以不同的下标索引来区分。下标(也叫作索引)指的是数组变量名后面方括号中的数字。数组中的每个存储位置被称为数组元素

与其他C语言的变量类似,在使用数组前必须声明它。

一维数组的定义和一维数组元素的引用

一维数组的定义

当数组中的每个元素只带有一个下标时,称这样的数组为一维数组

在C语言中,定义一维数组的语句一般形式如下:

类型名 数组名[整型常量表达式],……;

例如:

int a[8];

在这里,int是类型名,a[8]就是一维数组说明符。

以上语句说明了以下几点:

  1. 定义了一个名为a的一维数组。
  2. 方括号中的8规定了a数组含有8个元素,它们是a[0],a[1],a[2]……
  3. 类型名int规定了a数组中每个元素都是整型,在每个元素中只能存放整型数。
  4. 每个元素只有一个下标。每个数组中第一个元素的下标总为0(称为数组下标的下界),因此,以上a数组中的最后一个元素的下标是7(称为数组下标的上界)。
  5. C编译程序将为a数组在内存中开辟名为a[0]-a[7]的8个连续的存储单元,可以用这些名字来直接引用各存储单元。

在一个定义数组的语句中,可以有多个数组说明符,它们之间用逗号隔开,如:

double w[22], v[100], u[5];

数组说明符和普通变量名可同时出现在一个类型定义语句中,如:

char c1, c2, carr[81];

注意:数组说明符的一对方括号中只能是整型常量或整型常量表达式。

声明数组时,可以用字面常量或通过#define创建的符号常量来指定元素的个数,因此以下两种声明方式等价:

#define MONTHS 12
int array[MONTHS];

int array[12];

注意:大部分编辑器都不允许用const关键字创建的符号常量来声明数组的元素。

以下语句在编译时会报错:

const int MONTHS = 12;
int array[MONTHS];

一维数组元素的引用

一维数组的引用形式如下:

数组名 [下标表达式]

例如,若有以下定义语句:

double x[8];

则x[0],x[j],x[i + k]都是对x数组中元素的合法引用形式。

  • 一个数组元素实质上就是一个变量名,代表内存中的一个存储单元。
  • 一个数组占有一串连续的存储单元。
  • 在C语言中,一个数组不能整体引用。数组名中存放的是一个地址常量,它代表整个数组的首地址。

一维数组的初始化

一维数组初始化的形式为:

int a[8] = {0,1,2,3,4,5,6,7};

以上语句给a[0]赋初值0,给a[1]赋初值1……给a[7]赋初值7。

当所赋初值少于所定义数组元素的个数时,将自动给后面的元素补以初值0。

对于字符型数组也同样补以初值0。例如,以下两个定义相等:

char c[5] = {'@'};
char c[5] = {'@', '\0', '\0', '\0', '\0'};

当所赋初值多与所定义数组元素的个数时,在编译时会报错。

通过赋初值定义数组的大小

C语言规定可以通过赋初值来定义数组的大小,这时数组说明符的一对方括号中可以不指定数组的大小。例如,以下两条语句等价:

int a[] = {0, 0, 0, 0, 0};
int a[5] = {0};

一维数组的使用

数组元素可用于程序中任何相同类型的非数组变量的地方,通过使用数组名和带方括号的数组下标来访问数组中的各元素。

例如,以下语句将89.95存储在第2个数组元素中:

expenses[1] = 89.95;

下面的语句将数组元素expenses[11]中存储的值的副本赋给expenses[10]数组元素:

expenses[10] = expenses[11];

程序中经常会将整型变量或表达式作为下标,或者甚至是另一个数组元素,例如:

float expenses[100];
int a[10];
//其他语句已省略
expenses[i] = 100;      //i是一个整型变量
expenses[2 + 3] = 100;  //expenses[2 + 3]相当于expenses[5]
expenses[a[2]] = 100;   //a[]是一个数组

如果有一个整型数组a[],其中元素a[2]中存储8,则以下两种写法的效果相同:

expenses[a[2]] = 100;

expenses[8] = 100;

一维数组和指针

一维数组和数组元素的地址

在C语言中,在函数体中或在函数外部定义的数组名可以认为是一个存放地址值的指针变量名,其中的地址值是数组第一个元素的地址,定义数组时的类型即是此指针变量的基类型,数组名是指向数组第一个元素的指针。

注意:这个指针变量中的地址值不可改变,也就是说不可以给数组名重新赋值,可以认为数组名是一个地址常量。

比如,定义:

float a[10], *p, x;

语句a = &x;或a++都是非法的,因为不能给a重新赋地址值。a将永远指向数组的首地址。

但是,可以声明一个指针变量并将其初始化以指向该数组。例如,下面的代码声明并初始化指针变量p_array,把array数组首元素的地址存储在p_array中:

int array[100];
int *p_array = array;
//其他代码已省略

因为p_array是一个指针变量,所以可以修改它的值让它指向别处,或指向array[]的其他元素。

可以用对数组名加一个整数的办法,来依次表达该数组中不同元素的地址。例如:

int k;
for(k = 0; k < 10; k++)
    p = a + k;

在循环中并没有改变a的值,但通过表达式a+k,逐一给出了a数组中每个元素的地址,使p依次指向a数组中的每一个元素。

可以通过以下循环从终端读入数据依次放入a数组中:

for(k = 0; k <10; k++)
    scanf("%d", a + k);

语句p = &a[0]和p = a都是合法的。这两条语句都使指针变量p指向数组a的首地址。

以下循环语句中,由于在进入循环时使指针变量p指向了a数组的首地址,p++将使指针变量p依次指向a数组中的每一个元素:

for(p = a, k = 0; k < 10; k++)
    p++;

下面的两条循环语句也可以从终端读入数据依次放入a数组中,这种操作在一些简单的OJ题目中比较常见:

for(p = a, k = 0; k < 10; k++)
    scanf("%d", p++);

for(p = a; p - a < 10; p++)
    scanf("%d", p);

例:

以下程序通过声明short、float、double类型的数组并且依次显示数组元素的地址,演示了不同类型数组的元素和地址之间的关系。

#include <stdio.h>

//声明一个计数器变量和三个数组
int chr;
short array_s[10];
float array_f[10];
double array_d[10];

int main(void)
{
    //打印表头
    printf("\t\tShort\t\tFloat\t\tDouble");
    printf("\n============================================================");
    //打印各数组元素的地址
    for(ctr = 0; ctf < 10; ctr++)
        printf("\nElement %d:\t%ld\t%ld\t%ld", ctr, &array_s[ctr], &array_f[ctr], &array_d[ctr]);
    printf("\n============================================================");
    
    return 0;
}

输出:

                Short           Float           Double
============================================================
Element 0:      13084064        13084000        13083904
Element 1:      13084066        13084004        13083912
Element 2:      13084068        13084008        13083920
Element 3:      13084070        13084012        13083928
Element 4:      13084072        13084016        13083936
Element 5:      13084074        13084020        13083944
Element 6:      13084076        13084024        13083952
Element 7:      13084078        13084028        13083960
Element 8:      13084080        13084032        13083968
Element 9:      13084082        13084036        13083976
============================================================

可以看出,相邻两个short类型的元素的间隔是2字节,相邻两个float类型的元素的间隔是4字节,相邻两个double类型的元素的间隔是8字节。

通过数组的首地址引用数组元素

a是a数组元素的首地址,a的值等于&a[0],则a + 1的值等于&a[1]……a+9的值等于&a[9]。

可以通过间接访问运算符“*”来引用地址所在的存储单元,因此对于数组元素a[0],可以用表达式*&a[0]来引用,也可以用*(a + 0)或*a来引用。

因此,可以通过以下语句逐个输出a中数组元素的值:

for(k = 0; k < 10; k++)
    printf("%4d", *(a + k));

此语句与以下语句等效:

for(k = 0; k < 10; k++)
    printf("%4d", a[k]);

通过指针引用一维数组元素

若有:

float a[10], *p, k;
p = a;      //或p = &a[0];

可以使用间接访问运算符,通过指针p来引用a数组中的元素。

对于数组元素a[0],可以用表达式*(p + 0),即*p来引用;同理,对于数组元素a[1],可以用表达式*(p + 1)来引用。

因此,可以通过以下语句逐个输出a数组元素的值:

for(p = a, k = 0; k < 10; k++)
    printf("%4d", *(p + k));

以上语句与以下语句等效:

for(k = 0; k < 0; k++)
    printf("%4d", a[k]);

以上两种写法都没有移动指针p。可以用以下两种方式逐步移动指针p来引用a数组中的每一个元素,逐个输出a数组元素的值:

for(p = a, k = 0; k < 10; k++)
    printf("%4d", *p++);

for(p = a; p - a < 10; p ++)
    printf("%4d", *p);

用带下标的指针变量来引用一维数组元素

若有:

int *p, s[10], i;       //0≤i<10
p = s;

已经知道:

  • 可以用&s[i]、s + i和p + i来表示s[i]的地址
  • 可以用s[i]、*(s + i)、*(p + i)来表示数组元素s[i]

所以,*(p + i)也可以用p[i]来表示。

事实上,在C语言中,方括号是一种运算符。

因此,当p指向s数组的首地址时,表示数组元素s[i]的表达方式有:

  1. s[ i ]
  2. *(s + i)
  3. *(p + i)
  4. p[ i ]

注意:s是不可变的,p中的地址是可变的。

函数之间对一维数组和数组元素的引用

数组元素作为实参

调用函数时,数组元素可以作为实参传递给形参,每个数组元素实际上代表内存中的一个存储单元,所以对于的形参必须是类型相同的变量。

数组名作为实参

数组名也可以作为实参传递,但数组名本身是一个地址值,所以对应的形参就应当是一个指针变量,其基类型必须与数组的类型一致。

在函数中,可以通过此指针变量来引用调用函数中对应的数组元素,从而对调用函数中对应的数组元素进行操作二改变其中的值。

例1(高教版《全国计算机等级考试二级教程——C语言程序设计》P117 例9.2):

编写程序,通过一个函数给主函数中定义的数组输入若干大于或等于0的整数,用负数作为输入结束的标志;调用另一个函数输出该数组中的数据。

#include <stdio.h>
#define M 100

void arrout(int *, int);    //函数说明语句,此函数用以输出数组中的值
int arrin(int *);           //函数说明语句,此函数用以给数组输入数据

int main(void)
{
    int s[M], k;
    k = arrin(s);
    arrout(s, k);
    return 0;
}

int arrin(int *a)
{
    int i = 0, x;
    scanf("%d", &x);
    while(x >= 0)
    {
        *(a + 1) = x;
        i++;
        scanf("%d", &x);
    }
    return i;
}

void arrout(int *a, int n)
{
    int i;
    for(i = 0; i < n; i++)
        printf(((i + 1) % 5 == 0) ? "%4d\n" : *(a + 1));    //根据i的值来确定不同的格式串
    printf("\n");
}

当数组名作为实参时,对应的形参除了是指针外,还可以有其他形式。上例中arrin函数的首部还可以写成以下形式:

int arrin(int a[])
int arrin(int a[M])

此外,通过指针引用数组元素时,除了可以用上例的*(a + i)外,也可以写出a[ i ]的形式。

数组元素地址作为实参

当用数组元素地址作为实参时,对应的形参也应当是基类型相同的指针变量。

例2(高教版《全国计算机等级考试二级教程——C语言程序设计》P119 例9.3):

编写函数,对具有10个元素的char类型数组,从下标为4的元素开始,全部设置星号“*”,保持前四个元素的内容不变。

#include <stdio.h>
#define M 10
#define B 4

void setstar(char *, int);
void arrout(char *, int);

int main(void)
{
    char c[M] = {'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J'};
    setstar(&c[4], M - B);
    arrout(c, M);
    return 0;
}

void setstar(char *a, int n)
{
    int i;
    for(i = 0; i < n; i++)
        *(a + i) = '*';
}

void arrout(char *a, int n)
{
    int i;
    for(i = 0; i < n; i++)
        printf("%c", a[i]);
        printf("\n");
}

输出:

ABCD******

setstar函数的首部还可以写成以下形式:

void setstar(int a[], int n)
void setstar(int a[M - B], int n)
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,047评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,807评论 3 386
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,501评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,839评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,951评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,117评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,188评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,929评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,372评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,679评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,837评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,536评论 4 335
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,168评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,886评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,129评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,665评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,739评论 2 351

推荐阅读更多精彩内容

  • 指针是C语言中广泛使用的一种数据类型。 运用指针编程是C语言最主要的风格之一。利用指针变量可以表示各种数据结构; ...
    朱森阅读 3,430评论 3 44
  • 参考资料:《全国计算机等级考试二级教程——C语言程序设计》《21天学通C语言》 变量的地址和指针 在程序中,一个变...
    NoelleMu阅读 1,472评论 0 1
  • 第十章 指针 1. 地址指针的基本概念: 在计算机中,所有的数据都是存放在存储器中的。一般把存储器中的一个字节称为...
    坚持到底v2阅读 1,066评论 2 3
  • 数组在程序设计中,为了处理方便, 把具有相同类型的若干变量按有序的形式组织起来。这些按序排列的同类数据元素的集合称...
    朱森阅读 3,911评论 2 13
  • 一年四季都燥热的卡斯小镇,今天格外热,热的人心惶惶…… 九月开学季,今天是珊珊初一开学的日子。一大早,妈妈刚做7好...
    徐小八阅读 194评论 0 0