【考研C语言】【王道】哭哭羊进阶史(一维数组与字符数组)2024.8.4

01.4.1  一维数组

1 数组的定义

为了存放鞋子,假设你把衣柜最下面的一层分成了 10个连续的格子。此时,让他人帮你拿鞋子就会很方便,例如你可直接告诉他拿衣柜最下面一层第三个格子中的鞋子。同样假设现在我们有 10 个整数存储在内存中,为方便存取,我们可以借助C语言提供的数组通过一个符号来访问多个元素

某班学生的学习成绩、一行文字、一个矩阵等数据的特点如下:
(1)具有相同的数据类型
(2)使用过程中需要保留原始数据

C语言为了方便操作这些数据,提供了一种构造数据类型--数组。所谓数组,是指一组具有相同数据类型的数据的有序集合

一维数组的定义格式为

类型说明符 数组名[常量表达式];

例如

int a[10];   //定义一个整型数组,数组名为 a,它有 10 个元素

声明数组时要遵循以下规则:
(1)数组名的命名规则和变量名的相同,即遵循标识符命名规则
(2)在定义数组时,需要指定数组中元素的个数,方括号中的常量表达式用来表示元素的个数,即数组长度。
(3)常量表达式中可以包含常量和符号常量,但不能包含变量,也就是说,C语言不允许对数组的大小做动态定义,即数组的大小不依赖于程序运行过程中变量的值。

以下是错误的声明示例(最新的C标准支持,但是最好不要这么写):

int n;
scanf("%d",&n);  //在程序中临时输入数组的大小
int a[n];

 数组声明的其他常见错误:

(1)float a[0]; // 数组大小为0没有意义
(2)int b(2)(3);  //不能使用圆括号
(3)int k = 3, a[k];  //不能用变量说明数组大小

2 一维数组在内存中的存储

语句 int mark[100];定义的一维数组 mark 在内存中的存放情况如下图所示,每个元素都是整型元素,占用4字节,数组元素的引用方式是“数组名[下标]”,所以访问数组 mark 中的元素的方式是 mark[0],mark[1],...,mark[99]。注意,没有元素 mark[100],因为数组元素是从0开
始编号的。

图   一维数组 mark 在内存中的存放

一维数组的初始化方法: 

(1)在定义数组时对数组元素赋初值。例如

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

 不能写成

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

(2)可以只给一部分元素赋值。例如

int a[10] = {1,2,3,4,5};

定义a数组有10个元素,但花括号内只提供只提供5个初值,这表示只给前5个元素赋初值,后5个元素的值为0。 

(3)如果要使一个数组中全部元素的值为0,那么可以写为

int a[10] = {0,0,0,0,0,0,0,0,0,0};  //不建议这么写

int a[10] = 10;

(4)在对全部数组元素赋初值时,由于数据的个数已经确定,因此可以不指定数组的长度。例如

int a[] = {1,2,3,4,5};  //机试可以,初试不建议

01.4.2  数组访问越界与数组传递

1 数组访问越界

下面借助一个数组的实例来掌握数组元素的赋值、访问越界。

【例】一维数组的存储及访问越界

#include <stdio.h>

//数组越界

int main() {
    int a[5]={1,2,3,4,5};  //定义数组时,数组长度必须固定
    int j=20;
    int i=10;
    a[5]=6;  //访问越界
    a[6]=7;  //访问越界会造成数据异常
    printf("i=%d\n",i);  //i并没有赋值,但是值却变化了
    return 0;
}

输出结果

注意:编译器并不检查程序对数组下标的引用是否在数组的合法范围内。这种不加检查的行为有好处也有坏处,好处是不需要浪费时间对有些已知正确的数组下标进行检查,坏处是这样做将无法检测出无效的下标引用。

一个良好的经验法则:如果下标值是通过那些已知正确的值计算得来的,那么就无须检查;如果下标值是由用户输入的数据产生的,那么在使用它们之前就必须进行检查,以确保它们位于有效范围内。 

2 数组传递

#include <stdio.h>

//子函数把某一个常用功能,封装起来的作用
//数组名传递到子函数后,子函数的形参接收到是数组的起始地址,因此不能把数组的长度传递给子函数

void print(int a[],int length)
{
    int i;
    for(i=0;i<length;i++)
    {
        printf("%3d",a[i]);
    }
    a[3]=20;
    printf("\n");
}

//main函数就是主函数
int main() {
    int a[5]={1,2,3,4,5};
    print(a,5);  //数组在传递给子函数时,它的长度传递不过去
    printf("a[3]=%d\n",a[3]);
    return 0;
}

输出结果

一维数组在传递时,其长度是传递不过去的,所以我们通过 len来传递数组中的元素个数。实际数组名中存储的是数组的首地址,在调用函数传递时,是将数组的首地址给了变量b,在b[]的方括号中填写任何数字都是没有意义的。这时在 print 函数内修改元素 b[4]=20,可以看到数组b的起始地址和 main 函数中数组a的起始地址相同,即二者在内存中位于同一位置,当函数执行结束时,数组a中的元素 a[4]就得到了修改。

01.4.3  字符数组与scanf读取字符串

1 字符数组初始化及传递

字符数组的定义方法与前面介绍的一维数组类似。例如

char c[10];

字符数组的初始化可以采用以下方式。

(1)对每个字符单独赋值进行初始化。例如

c[0] = 'I';
c[1] = ' ';
c[2] = 'a';
c[3] = 'm';
c[4] = ' ';
c[5] = 'h';
c[6] = 'a';
c[7] = 'p';
c[8] = 'p';
c[9] = 'y';

(2)对整个数组进行初始化。例如

char c[10] = {'I',' ','a','m',' ','h','a','p','p','y'};

但工作中一般不用以上两种初始化方式,因为字符数组一般用来存取字符串。通常采用的初始化方式是 

char c[10] = "hello";

因为C语言规定字符串的结束标志为0,而系统会对字符串常量自动加一个'\0',为了保证处理方法一致,一般会人为地在字符数组中添加'\0',所以字符数组存储的字符串长度必须比字符数组少1字节。

例如,char c[10]最长存储9个字符,剩余的 1个字符用来存储'\0'。

【例】字符数组初始化及传递

#include <stdio.h>

//模拟printf("%s",c)操作

void print(char d[])
{
    int i=0;
    while(d[i])  //当走到结束符时,循环结束
    {
        printf("%c",d[i]);
        i++;
    }
    printf("\n");
}

//如何初始化字符数组,字符串如何输出
//输出字符串乱码时,要去查看字符数组中是否存储了结束符'\0'

int main() {
    char c[6]= "hello";  //使用这种方式初始化字符数组
    char d[5]="how";
    printf("%s\n",c);  //使用%s来输出一个字符串,直接把字符数组名放到printf后面位置
    print(d);
    return 0;
}

输出结果

 

2 scanf读取字符串

【例】scanf读取字符串

#include <stdio.h>

//scanf读取字符串操作,会自动往字符数组中放结束符

int main() {
    char c[10];
    char d[10];
//    scanf("%s",c);  //字符数组名c中存储了数组的起始地址,因此不需要取地址
//    printf("%s\n",c);
    scanf("%s%s",c,d);
    printf("c=%s,d=%s\n",c,d);
    return 0;
}

输出结果

 

01.4.4  gets函数与puts函数

gets 函数类似于 scanf 函数,用于读取标准输入。前面我们已经知道 scanf 函数在读取字符串时遇到空格就认为读取结束,所以当输人的字符串存在空格时,我们需要使用 gets 函数进行读取。

gets 所数的格式如下:

char *gets(char *str);

gets 函数从STDIN(标准输入)读取字符并把它们加载到str(字符串)中,直到遇到换行符(\n)。如下例所示,执行后,输入"how are you",共 11 个字符,可以看到 gets 会读取空格,同时可以看到并未给数组进行初始化赋值,但是最后有'\0',这是因为 gets 遇到\n 后,不会存储\n,而是将其翻译为空字符'\0'。

puts 函数类似于 printf 函数,用于输出标准输出

puts 函数的格式如下

int puts(char *str);

函数 puts 把 str(字符串)写人STDOU(标准输出)。puts 会将数组c中存储的"how are you"字符串打印到屏幕上,同时打印换行,相对于 printf 函数,puts 只能用于输出字符串,同时多打印一个换行符,等价于 printf(“%s\n”,c)。

#include <stdio.h>

int main() {
    char c[20];
    gets(c);  //gets中放入我们字符数组的数组名即可
    puts(c);  //puts等价于printf("%s\n",c); puts内放的参数是字符数组名
    return 0;
}

输出结果

01.4.5  str系列字符串操作函数(strlen-strcmp-str)(初试没那么重要,对于机试更重要一些)

str 系列字符串操作数主要包括 strlenstrcpystrcmpstrcat 等。

strlen函数用于统计字符串长度;strcpy函数用于将某个字符串复制到字符数组中;strcmp 函数用于比较两个字符串的大小;strcat 函数用于将两个字符串连接到一起。

各个函数的具体格式如下:

#include <string.h>
size_t strlen(char *str);  //统计字符串长度
char *strcpy(char *to,const char *from)  //将某个字符串复制到字符数组中
int strcmp(const char *str1,const char *str2);  //比较两个字符串的大小
char *strcat(char *str1, const char *str2);  //将两个字符串连接到一起

对于传参类型char*,直接放入字符数组的数组名即可。

【例】str系列字符串操作函数的使用

#include <stdio.h>
#include <string.h>

int mystrlen(char c[])
{
    int i=0;
    while(c[i])  //找到结束符后,循环结束,从而得出字符串长度
    {
        i++;
    }
    return i;
}

int main() {
    int len;
    char c[20];
    char d[100]="world";
    char e[100];
    gets(c);
    puts(c);
    len=strlen(c);  //统计字符串的长度
    printf("len=%d\n",len);
    len= mystrlen(c);
    printf("my len=%d\n",len);
    strcat(c,d);  //把d中的字符串拼接到c中
    puts(c);
    strcpy(e,c);  //把c中的字符串复制到e中
    puts(e);
   
 //c大于“how",返回值是正值,相等是0,c小于”how",返回负值

    printf("c?d=%d\n",strcmp(c,"how"));
    return 0;
}

输出结果

练习题

1.int mark[100]; 我们可以做 mark[100]=3;
A.正确     B.错误
答案:B
解释:数组下标从零开始,定义int mark[100],只能 mark[0]到 mark[99]。

2.访问越界是非常危险的,因为C和 C++语言没有针对访问越界进行检测。
A.正确     B.错误
答案:A
解释:访问越界会改变其他变量的值,因此非常危险。

3.puts(c)等价于 printf("%s\n",c)
A.正确     B.错误
答案:A
解释:如果输出字符串,使用 puts 更加方便,注意 puts 只能用于输出字符串,不能输出其
他类型。

4.如果字符串没有结束符'\0',也可以使用 stren 正确统计长度。
A.正确     B.错误
答案:B
解释:strlen 是通过结束符'\0'来判断字符串长度的,如果没有结束符,无法统计字符串长度。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值