【C语言】C语言期末突击/考研--详解一维数组与字符数组

news2024/9/30 0:28:38

目录

​一、一维数组

1.数组的定义

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

二、数组访问越界与数组的传递

1.数组的访问越界

2.数组的传递

三、字符数组与scanf读取字符串

1.字符数组的初始化及传递

2.scanf读取字符串

四、gets函数与puts函数,str系列字符串操作函数

1.gets函数与puts函数

2.strlen-strcmp-strcpy函数

五、练习题及解析


一、一维数组

1.数组的定义

    为了存放鞋子,假设你把衣柜最下面的一层分成了10个连续的格子。此时,让他人帮你拿鞋子就会很方便,例如你可直接告诉他拿衣柜最下面一层第二个格子中的鞋子。同样假设现在我们有10个整数存储在内存中,为方便存取,我们可以借助C语言提供的数组,通过一个符号来访问多个元素
    某班学生的学习成绩、一行文字、一个矩阵等数据的特点如下:
    (1) 具有相同的数据类型
    (2) 使用过程中需要保留原始数据。
    C语言为了方便操作这些数据,提供了一种构造数据类型——数组。所谓数组,是指一组具有相同数据类型的数据的有序集合
一维数组的定义格式为:

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

例如:

int al10];

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


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

int n;

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

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

 /* 数组大小为0没有意义 */

① float a[0];

/* 不能使用圆括号*/
② int b(2)(3)

/* 不能用变量说明数组大小*/
③ int k=3, a[k];

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

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

下面介绍一维数组的初始化方法:

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

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

     不能写成:

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

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

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

定义a数组有10个元素,但花括号内只提供5个初值,这表示只给前5个元索赋初值,后5个元索的值为0。
     (3) 如果要使一个数组中全部元素的值为0,那么可以写为:

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

int a[10]={0};

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

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

二、数组访问越界与数组的传递

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;
}


    下图显示了代码运行情况。如下图所示,在第8行左键打上断点,然后单击“小虫子”按钮,在内存视图依次输入&j、&a、&i 来查看整型变量j、整型数组a、整型变量i的地址,即可看到三个变量的地址,这里就像我们给衣柜的每个格子的编号,第一格、第二格···…一直到柜子的最后一格。操作系统对内存中的每个位置也给予一个编号,对于 Windows 32 位控制台应用程序来说,这个编号的范围是从 0x00 00 00 00 到 OxFF FF FF FF,总计为2的32次方,大小为4G。这些编号称为地址(我们是64 位程序,地址显示的是64位)。

【图】代码运行情况1

     在变量窗口中输入sizeof(a), 可以看到数组a的大小为20字节,计算方法其实就是sizeof(int)*5:数组中有5个整型元素,每个元素的大小为4字节,所以共有20字节。访间元素的顺序是依次从a[0]到 a[4],a[5]=6、a[6]=7 均为访问越界,下图显示了代码运行情况,从中看出,执行到第 1行时,变量i的值被修改了,这就是访问越界的危险性——未对变量i赋值,其值却发生了改变!

【图】代码运行情况2

     数组另一个值得关注的地方是,编译器并不检查程序对数组下标的引用是否在数组的合法范围内。这种不加检查的行为有好处也有坏处,好处是不需要浪费时间对有些已知正确的数组下标进行检查,坏处是这样做将无法检测出无效的下标引用,一个良好的经验法则是:如果下标值是通过那些已知正确的值计算得来的,那么就无须检查;如果下标值是由用户输入的数据产生的,那么在使用它们之前就必须进行检查,以确保它们位于有效范围内。

2.数组的传递

#include <stdio.h>
//一维数组的传递,数组长度无法传递给子函数
//C语言的函数调用方式是值传递
void print(int b[],int len) {
    int length = sizeof(b);
    int i;
    for (i = 0; i < len; i++) {
        printf("%d\t", b[i]);
    }
    b[4] = 20;//在子函数中修改版数组元素
    printf("\n");
}
    //数组越界
    //一维数组的传递
#define N 6
int main(){
    int a[]={1,3,5,7,9,11};
    print(a,5);
    print("a[4]=%d\n",a[4]);//a[4]发生改变
    return 0;
}

    如下图1所示,在第18行点击向下箭头,进入 print 函数,这时会发现数组b的大小变为8字节,如下图2所示,这是因为一维数组在传递时,其长度是传递不过去的,所以我们通过 len 来传递数组中的元素个数。实际数组名中存储的是数组的首地址,在调用函数传递时,是将数组的首地址给了变量b(其实变量b 是指针类型,具体原理会在指针节讲解),在[b]的方括号中填写任何数字都是没有意义的。这时我们在print 函数内修改元索 b[4]=20,可以看到数组b的起始地址和 main 西数中数组a的起始地址相同,即二者在内存中位于同一位置,当数执行结束时,数组a中的元素 a[4]就得到了修改。

【图1】代码运行情况3

【图1】代码运行情况4

三、字符数组与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>
void print(char c[]){
    int i=0;
    while (c[i]){
        printf("%c",c[i]);
        i++;
    }
    print("\n");
}

//字符数组存储字符串,必须存储结束符“\0”

int main(){
   char c[5]={'h','e','l','l','o'};
   char d[5]="how";
   printf("%s\n",c);//会出现打印了乱码
   printf("%s\n",d);
   print(d);
   return 0;
}

    上例中代码的执行结果如下图所示。为什么对数组赋值"hello"却打印出乱码,这是因为printf通过%s打印字符串时,原理是依次输出每个字符,当读到结束符'\0'时,结束打印:


我们通过print函数模拟实现printf 的%s打印效果,当c[i]为'\0'时,其值是0,循环结束,也可以写为c[i]!='\0'。

2.scanf读取字符串

【例】scanf读取字符串

#include <stdio.h>

//scanf读取字符串使用%s

int main(){
   char c[10];
   char d[10];
   scanf("%s",c);
   printf("%s\n",c);
   scanf("%s%s",c,d);
   printf("c=%s,d=%s\n",c,d);
   return 0;
}

    scanf通过%s读取字符串,对c和d分别输入"are"和"you" (中间加一个空格),scanf在使用%s读取字符串时,会忽略空格和回车(这一点与%d和%f类似)。
输入顺序及执行结果如下图,

四、gets函数与puts函数,str系列字符串操作函数

1.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>

//gets一次读取一行

int main(){
   char c[20];
   gets(c);
   puts(c);
   return 0;
}

上例的运行结果如下图:

2.strlen-strcmp-strcpy函数

    str系列字符串操作函数主要包括strlen、strcpy、 strcmp、 strcat 等。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系列字符串操作函数,掌握每个函数的内部实现。


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

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

int mystrlen(char c[]){
    int i =0;
    while (c[i++]);
    return i-1;
}
//统计strlen统计字符串长度

int main(){
  int len;//用于存储字符串的长度
  char c[20];
  char d[100]="world";
  gets(c);
  puts(c);
  len=strlen(c);
  printf("len=%d\n",len);
  len= mystrlen(c);
  printf("mystrlen len=%d\n",len);
  strcat(c,d);
  strcpy(d,c);//c中的字符串赋值给d
  puts(d);
  printf("C?D %d\n",strcmp(c,d));
  puts(c);
  return 0;
}

    下图所示为我们输人"hello"后的执行结果,通过strlen函数计算的字符串长度为5,我们自己写的函数就是strlen函数的计算原理,即通过判断结束符来确定字符串的长度,stropy函数用来将字符串中的字符逐个地赋值给目标字符数组,例中我们将c复制给d,就是将c中的每个字符依次赋值给d,也会将结束符赋值给d。注意,目标数组一定要大于字符串大小,即sizeof()>strlen(c).否则会造成访问越界。


    strcmp函数用来比较两个字符串的大小,由于字符数组c中的字符串与d相等,所以这里的返回值为0。如果c中的字符串大于d,那么返回值为1;如果c中的字符串小于d,那么返回值为-1.如何比较两个字符串的大小呢?具体操作是从头开始,比较相同位置字符的ASCII码值,若发现不相等则直接返回,否则接着往后比较。例如,strcmp("hello" ,"how")的返回值是-1。即hello"小于"how", 因为第一一个字符h相等,接着比较第二个位置的字符,e的ASCII码值小于o的,然后返回-1。strcat函数用来将一一个字 符串接到另外一一个字符串的末尾。例中字符数组c中存储的是"hello",我们将d中的"world"与c拼接,最终结果为"hello hhy world".注意,目标数组必须大于拼接后的字符串大小,即sizeof(c)>strlen( "hello hhy world")。

五、练习题及解析

1、数组内两个元素可以存储不同的数据类型
        A正确        B错误

2、int mark[100];我们可以做mark[100]=3; 
        A正确        B错误

3、int a[10]= {0,1,2,3,4};定义a数组有10 个元素,但花括号内只提供5个初值,这表示只给
前5个元素赋初值,后5个元素的值为0。
        A正确        B错误

4、数组int arr[5];我们做arr[5]=20这个操作造成了访问越界
        A正确        B错误

5、访问越界是非常危险的,因为C和C++语言没有针对访问越界进行检测
        A正确        B错误

6、数组传递时,可以把自身长度传递给子函数
        A正确        B错误

7、在子函数中改变数组中某个元素的值,子函数结束后,数组内元素值发生变化
        A正确        B错误
 

8、char c[ 10]= {T,a','m',h','a',p'p'y' }这种初始化方式是常用的方式
        A正确        B错误

9、char c[5]我们可以放5个字符来使用
        A正确        B错误

10、char c[10];scanf("%s",c);读取字符串时会读取到空格和\n
        A正确        B错误

11、gets 一次可以读取一行,能够读取空格
        A正确        B错误

12、puts(c)等 价于print(“%s\n",c)
        A正确        B错误

13、strlen函数用于统计字符串长度,strcpy函数用于将某个字符串复制到字符数组中,
strcmp函数用于比较两个字符串的大小,strcat 函数用于将两个字符串连接到一起
        A正确        B错误

14、如果字符串没有结束符’\0’,也可以使用strlen 正确统计长度
        A正确        B错误

解析:

1、B        解释:数组内的元素必须存储相同的数据类型。

2、B        解释:数组下标从零开始,定义int mark[100],只能方位mark[0]到mark[99]

3、A        解释:初始化元素时,剩余的未赋值元素,会被初始化为零

4、A        解释: int arr[5]数组访问arr[0]到arr[4],5及以后的值都是访问越界

5、A        解释:我们的实例演示了访问越界会改变其他变量的值,因此非常危险

6、B        解释:数组传递时,只是把数组的起始地址传递给了子函数,长度不能传递给子函数,我们需要使用额外的变量,来把长度传递给子函数

7、A        解释:当我们把数组名传递给子函数后,在子函数内就可以访问数组的某个元素,同时可以进行修改

8、B        解释:字符数组的初始化最常用的方式是char c[10]="Iamhappy”这种方式

9、B        解释: char c[5]我们只能放4个字符,第五个字符需要放置结束符\0',否则prinft("%s\n",c);
输出会造成乱码

10、B        解释: scanf("%s",c); 会忽略空格和\n,因此无法读取到空格和\n

11、A        解释:如果要一次读一行,同时需要把空格读到字符数组中,那么就需要用gets 

12、A        解释:如果输出字符串,使用puts更加方便,注意puts只能用于输出字符串,不能输出其
他类型

13、A        解释:这个需要记住

14、B        解释: strlen 是通过结束符'\0'来判断字符串长度的,如果没有结束符,无法统计字符串长度

下一期:

【C语言】C语言期末突击/考研--指针(一篇就够)-CSDN博客

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1980015.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

「iOS」自定义Modal转场——抽屉视图的实现

「iOS」自定义Modal转场——抽屉视图的实现 文章目录 「iOS」自定义Modal转场——抽屉视图的实现前言错误尝试自定义Modal转场实现流程自定义动画类UIPresentationController 成果展示参考文章 前言 在仿写网易云的过程之中&#xff0c;看到学长之前仿写时实现的抽屉视图&…

Java面试题-集合类

目录 1、请简单介绍下 Java 的集合类吧。 Collection Set TreeSet和HashSet List ArrayList 和 LinkedList 数组和链表的区别 Java 的列表有哪些实现类&#xff1f; Vector Queue Map 能说下 HashMap 的实现原理吗&#xff1f; 能说下 HashMap 的扩容机制吗&#x…

达梦数据库的系统视图v$cachepln

达梦数据库的系统视图v$cachepln 达梦数据库的系统视图V$CACHEPLN的主要作用是提供缓存中SQL执行计划的信息&#xff0c;在 ini 参数 USE_PLN_POOL !0 时才统计。通过查询这个视图&#xff0c;用户可以获取到缓存中的执行计划及其相关信息&#xff0c;如SQL语句文本等。这有助…

JavaScript青少年简明教程:DOM和CSS简介

JavaScript青少年简明教程&#xff1a;DOM和CSS简介 DOM简介 DOM&#xff08;Document Object Model&#xff09;将文档表示为一个树形结构&#xff0c;其中每个节点都是一个对象&#xff0c;每个对象都有其自身的属性和方法。 通过对DOM的操作&#xff0c;开发者可以使用编…

Mojo 不安全指针 详解

该UnsafePointer类型创建对内存中某个位置的间接引用。您可以使用UnsafePointer来动态分配和释放内存,或指向由其他代码分配的内存。您可以使用这些指针编写与低级接口交互的代码,与其他编程语言交互,或构建某些类型的数据结构。但顾名思义,它们本质上是不安全的。例如,当…

各地级市能源消费总量、夜间灯光值数据(2000-2022年)

全国各地级市能源消费总量、夜间灯光值数据&#xff08;2000-2022年&#xff09; 数据年限&#xff1a;2000-2022年 数据格式&#xff1a;excel 数据内容&#xff1a;337个地级市能源消费总量、夜间灯光值数据&#xff0c;包括城市、省份、年份、夜间灯光值&#xff08;总和&am…

子比主题允梦美化插件全开源版本

在其他论坛看到的一款不错的子比美化插件&#xff0c;功能也比较全面&#xff0c;因为插件作者上学没有时间维护&#xff0c;现在开源给大家&#xff0c;插件本站未做测试&#xff0c;需要的朋友自行下载测试&#xff0c;如果有授权的话可以到允梦作者网站进行咨询。需要其他美…

Java高级面试题(二)-- JVM

Jvm虚拟机&#xff0c;运行在操作系统之上&#xff0c;编译执行java代码 1, 面试官&#xff1a;手绘一个类加载过程 补充&#xff1a; 这里的执行硬件 java 调用 c 指令 创建线程 &#xff0c;new thread()->start() 底层代码就是 native start0&#xff08;&#xff09;&…

Golang | Leetcode Golang题解之第321题拼接最大数

题目&#xff1a; 题解&#xff1a; func maxSubsequence(a []int, k int) (s []int) {for i, v : range a {for len(s) > 0 && len(s)len(a)-1-i > k && v > s[len(s)-1] {s s[:len(s)-1]}if len(s) < k {s append(s, v)}}return }func lexico…

选择文件鼠标右键自定义菜单

注册表路径 计算机\HKEY_CLASSES_ROOT\*\shell 效果 操作 1.定位 winr&#xff0c;输入regedit, 地址栏输入以下路径&#xff0c;并回车。 计算机\HKEY_CLASSES_ROOT\*\shell 2.在shell上右键&#xff0c;新建项 3右键新建字符串值&#xff0c;Icon,Position 4 右键新建c…

设备IP监听工具 | 网工工具

在工作中经常遇到设备IP客户遗忘了&#xff0c;或者销售不知道从哪借来的设备&#xff0c;IP都不知道 导致无法配置设备&#xff0c;普通工控机还有console&#xff0c;服务器就得接显示器接键盘看了 所以用python写了个小工具通过ARP发现设备IP地址&#xff0c;使用前需要安装…

《书生大模型实战营第3期》基础岛 第1关 :书生大模型全链路开源体系

文章大纲 简介更新性能基座模型对话模型 依赖使用案例通过 Transformers 加载通过 ModelScope 加载通过前端网页对话 InternLM 高性能部署推理1百万字超长上下文推理 智能体微调&训练评测标准客观评测长文评估&#xff08;大海捞针&#xff09;数据污染评估智能体评估主观评…

JavaScript基础(29)_事件对象、鼠标移动事件

事件对象 当事件的响应函数被触发时&#xff0c;浏览器每次都会将一个事件对象作为实参传递进响应函数&#xff0c;在事件对象中封装了当前事件相关的一切信息&#xff0c;比如&#xff0c;鼠标的坐标 、键盘哪个键被按下、鼠标滚轮滚动的方向。。。 鼠标移动事件&#xff08…

aspeed2600 GPIO分析与适配ipmitool power status, ipmitool power on/off

1.说明 本节以x86-power-control/src/power_control.cpp为基础&#xff0c;分析整个GPIO的调用流程&#xff0c;实现简单的ipmitool power on/off,ipmitool power status的管理。 1.资源:x86-power-control:https://github.com/openbmc/x86-power-control2.相关文件: meta-ph…

【redis 第八篇章】链表结构

一、数组和链表 1、数组 数组会在内存中开辟一块连续的空间存储数据&#xff0c;这种存储方式有利也有弊端。当获取数据的时候&#xff0c;直接通过下标值就可以获取到对应的元素&#xff0c;时间复杂度为 O(1)。但是如果新增或者删除数据会移动大量的数据&#xff0c;时间复…

AI辅助教育:九章大模型的数学辅导功能解析

1.简介 九章大模型是学而思为学习研发的模型&#xff0c;该模型对于数学做了很多专门的训练&#xff0c;在题目推荐方面做得比较好。 同时&#xff0c;这个模型也能支持上传图片&#xff0c;对图片内容进行分析&#xff0c;然后针对内容进行校对&#xff0c;推荐相识题目。 支…

用于完成个人搜索的反向图像搜索工具

简介&#xff1a; Infringement.report 提供了一个强大的反向图像搜索工具&#xff0c;称为 Raider。这对于网络安全人员和渗透测试人员来说&#xff0c;是一个不可或缺的工具。 主要功能&#xff1a; 反向图像搜索&#xff1a; 该工具允许用户通过图像进行搜索&#xff0c…

Bash Shell 脚本中的循环语句

文章目录 Bash Shell 脚本中的循环语句一、for 循环1.1 列表循环1.2 不带列表循环&#xff08;C 风格的 for 循环&#xff09; 二、案例示例2.1 打印 1-5 的数字2.2 打印 5 次 "hello world"2.3 打印 abcde2.4 输出 0-50 之间的偶数 三、应用技巧3.1 使用花括号和 se…

自注意力和位置编码

一、自注意力 1、给定一个由词元组成的输入序列x1,…,xn&#xff0c; 其中任意xi∈R^d&#xff08;1≤i≤n&#xff09;。 该序列的自注意力输出为一个长度相同的序列 y1,…,yn&#xff0c;其中&#xff1a; 2、自注意力池化层将xi当作key&#xff0c;value&#xff0c;query来…

【Nuxt】资源导入

public 通常用于存放静态资源。 assets 通常用于存放样式表、字体或者 svg 的资源。 图片资源 alias 推荐使用 ~。 <img src"/avatar1.png" alt"avatar1"/> <img src"/assets/images/unnamed.jpg" alt"unnamed"/><te…