第四章 数组

news2024/9/20 23:55:22

前言

学习方法

  1. 可以多看几遍视频
  2. 把上课的代码,自己加加注释,在自己写之前,可以画一个流程图
  3. 照着流程图把代码自己实现一遍

不要怀疑自己,不要遇到困难就觉得自己不行,遇到困难就解决困难,编程初学者都是这样一步一步走过来的。


在指针阶段会演示整型、浮点型、字符型传递(传递的含义就是把一个变量传递给对应的子函数。)

一维数组

数组的定义

所谓数组,是指一组具有相同数据类型的数据的有序集合。

数组具有以下特点:

  1. 具有相同的数据类型
  2. 使用过程中需要保留原始数据

一维数组的定义格式为:

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

例如,定义一个整型数组,数组名为a,它有10个元素。

int a[10];

声明数组时要遵循以下规则:

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

	//定义数组就是写一个变量名,后面加上方括号,方括号内写上整型常量
    //定义数组时,数组占据的空间大小就确定下来了
	int a[5] = { 1,2,3,4,5 };
}

使用符号常量,不需要掌握,知道可以这样写即可。

//符号常量
#define N 5
int main() {
	int a[N] = { 1,2,3,4,5 };
}

查看数组内存空间

看任何变量的内存,都是将该变量取地址,拖入内存窗口来看。 只有指针类型才能拖入内存窗口查看。带 * 号的表示是指针类型

在这里插入图片描述

在这里插入图片描述

赋初值

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

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

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

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

或者

int a[5] = {0};

但肯定是第二种写法为好(当赋初值的个数小于数组大小时,则后面的元素会被自动赋值为0)。

数组访问越界

了解一下:微软的编译器设计,不同的变量之间有8个字节的保护空间(Mac和Linux是没有的)。[这并不是标准C规定的]。

在这里插入图片描述

数组访问越界的错误提醒:

Run-Time Check Failure #2 - Stack around the variable ‘a’ was corrupted.

在这里插入图片描述

原理:

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

由上面的例子可知,访问越界会造成数据异常。

简单了解说明一下为什么C的执行效率比Java高:C语言中有指针,可以操作内存,因此效率高;而Java中没有指针,内存交给编译器管理(操作系统管理),提高了开发效率,但执行效率降低。

数组传递

自定义的函数必须写在main函数的上面

注意,自己写的其他方法要放在main方法的上面,否则编译不通

实现打印数组里的每一个元素,这里先将 “打印数组元素的方法” 中数组的大小写成固定数值。

//打印数组里的每一个元素
//自己写的print方法必须放在main方法的上面,否则编译不通
void print(int a[5]) {
	for (int i = 0; i < 5; i++)
	{
		printf("a[%d]=%d\n", i, a[i]);
	}
}

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

	print(a);
}

得到输出结果:

在这里插入图片描述

现在想在 “打印数组元素的方法” 中动态的得到数组的大小,进行如下尝试:

//打印数组里的每一个元素
void print(int a[5]) {
	for (int i = 0; i < sizeof(a) / sizeof(int); i++)
	{
		printf("a[%d]=%d\n", i, a[i]);
	}
}

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

	print(a);
}

而这次的输出结果是:

在这里插入图片描述

通过断点调试发现sizeof(a)=4,而与数组a的大小无关。无论传过来的数组多大,sizeof(a)始终等于4。原因在于数组在传递的时候数组元素是传递不过去的,只能传递数组的起始地址(实际上就是指针)

在这里插入图片描述

正确的写法是这样的,在print方法中新增一个参数表示数组的大小,调用该方法时传入数组的大小。[注:C语言中没有Java中的a.length方法]。

//打印数组里的每一个元素
void print(int a[],int len) {
	for (int i = 0; i < len; i++)
	{
		printf("a[%d]=%d\n", i, a[i]);
	}
}

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

	print(a,5);
}

//以下代码实现也是正确的
/*
//形参和实参可以不一样
void print(int b[],int len) { //b、len是形参
	for (int i = 0; i < len; i++)
	{
		printf("a[%d]=%d\n", i, b[i]);
	}
}

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

	print(a,5);//a是实参
}
*/

在子函数中修改数组元素

在子函数中可以直接修改数组元素,可以读可以写。

//打印数组里的每一个元素
//print是我们自定义的函数,C语言中没有print函数
void print(int b[],int len) {
	for (int i = 0; i < len; i++)
	{
		printf("a[%d]=%d\n", i, b[i]);
	}
	b[4] = 20;//在子函数中修改数组元素

}

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

	print(a,5);//调用函数
	printf("a[4]=%d\n", a[4]);
}

输出结果:

在这里插入图片描述

字符数组

初始化字符数组

字符数组就是专门用来存字符串的(初试和机试)。

初始化字符数组:以后都用第二种初始化字符数组的方式char d[10] = “hello”;

int main() {
	char c[10] = {'h','e','l','l','o'};//这样初始化字符数组太麻烦了

	char d[10] = "hello";//这样初始化字符数组也可以,而且简单

	//给字符数组存一个字符串,就用第二种初始化方式,简单。
	//字符数组就是专门用来存字符串的(初试和机试)
}

在这里插入图片描述

C语言规定字符串的结束标志为’\0’,读到\0就不会再输出了,而系统会对字符串常量自动加一个’\0’,为了保证处理方法一致,一般会认为地在字符数组中添加’\0’,所以字符数组存储的字符串长度必须比字符数组少1字节。例如char c[10]最长存储9个字符,剩余的1个字符用来存储’\0’。

在这里插入图片描述

ASCII码表中\0对应编码是0000 0000 即值是0,\0在内存中存的值就是0.

所以正确的初始化字符数组代码如下:初始化字符数组时,一定要让字符数组的大小比看到的字符串的长度多1

//初始化字符数组时,一定要让字符数组的大小比看到的字符串的长度多1
int main() {
	char c[6] = {'h','e','l','l','o'};//字符数组存储的字符串长度必须比字符数组少1字节
	char d[5] = "how";

	//%s :输出一串字符串
	printf("%s------%s", c, d);//printf的%s,对应后面要写的是字符数组名或者字符串常量

	//printf("%s", "Chinese");//printf的%s,后面写字符串常量的例子
}

向字符数组中读取字符串

scanf中%s不用加&取地址符。[原因在于数组名本身存储的就是数组的起始地址(指针)。当然这里写上&取地址符也不会错,但一般不写].

%s和%d,%f一样都会忽略空格和\n。

scanf读取我们输入的字符串后会自动添加结束符\0。

//向字符数组中读取字符串
int main() {
	char e[20];
	//scanf中%s不用加&取地址符
	scanf("%s", e);//%s和%d,%f一样都会忽略空格和\n。
	printf("%s\n", e);
}

在这里插入图片描述

同时读取多个:

int main() {
	char e[20],f[20];
	scanf("%s%s", e,f);//,当读取多个时%s%s中间不用加空格,因为%s和%d,%f一样都会忽略空格和\n。
	printf("%s---%s\n", e,f);
}

%s、%d、%f都会忽略空格和\n,因此在输入多个时中间不用加空格,而%c不会忽略空格和\n,所以在多个混合输入时有%c出现时要在 %c前面加一个空格。

在这里插入图片描述

如何把wangdao xue408 读到同一个字符数组中(包括那个空格),用scanf实现较为麻烦,因为需要用到正则,不需要掌握。用gets就很容易实现了,gets可以一次读一行,中间有空格也可以读到一个字符数组中。

字符数组的传递

自定义的函数必须写在main函数的上面

/*
在传递整型数组时不光要传递数组名,同时还要传递数组长度。而字符数组在传递时,只需传递数组名即可,
不用传递数组长度。因为字符数组最后一个是 '\0',C语言规定字符串的结束标志为'\0',所以当扫描到'\0'时
就知道应该结束了,可以以此来作为循环结束的判断条件。
*/
//我们把d称为形参
void print(char d[]) {
	int i = 0;
	/*
	由于\0在ASCII码表中对应的编码就是0,所以循环判断条件也可以简写为while (d[i]!=0);
	又C语言中一切非0值都是真,0为假,所以可以再进一步简写为while (d[i]);
	*/
	while (d[i]!='0')
	{
		printf("%c", d[i]);
		i++;
	}
	printf("\n");
}

int main() {
	//初始化字符数组时,一定要让字符数组的大小比看到的字符串的长度多1
	char c[10] = "hello";
	print(c);//我们把c称为实参,调用print函数时,是d=c (本质上就是赋值)
}

在这里插入图片描述

同整型数组一样,同样可以在子函数中修改字符数组元素。如下:

void print(char d[]) {
	int i = 0;
	while (d[i]!='\0')
	{
		printf("%c", d[i]);
		i++;
	}
	printf("\n");

	//在子函数中修改字符数组元素。修改字符数组中的字符串的内容,把首字母变大写。
	d[0] = d[0] - 32;//A:65  a:97   大写字母与小写字母的差是32
}

int main() {
	//初始化字符数组时,一定要让字符数组的大小比看到的字符串的长度多1
	char c[10] = "hello";
	print(c);
	printf("%s\n", c);
}

gets函数

gets函数只能读取字符串

使用scanf读取字符串会遇到一个问题,scanf通过%s读取字符串时,当遇到空格以后,就会匹配结束,这样没办法把一行带有空格的字符串存入到一个字符数组中。这个问题可以用gets解决。

gets函数的格式如下:

//char *str类型是字符指针
char *gets(char *str);

gets函数从标准输入中读取字符并把它们加载到str(字符串)中,直到遇到换行符(\n)或到达EOF。【所以gets可以一次读一行,空格也能读入】

输入“how are you”,共11个字符,可以看到gets会读取空格,同时可以看到我们并未给数组进行初始化赋值,但是最后有’\0’(因为字符串的结束标志是\0,只有遇到\0才会停止输出),这是因为gets遇到\n后不会存储\n,而是将其翻译为空字符’\0’。

在这里插入图片描述

gets读一个字符就将其填到字符数组中,当读到\n时不会将其填到字符数组中,当读到\n就会停止匹配,同时gets会给字符数组填结束符\0,所以可以理解为将\n翻译为了\0。

在这里插入图片描述

字符数组的数组名存的就是字符数组的起始地址,类型就是字符指针。而数组的起始地址就是第一个元素的地址,即有&c[0]==c。如下图:

在这里插入图片描述

好好理解下面这段话

char c[20]; c是一个字符数组,但是编译器给c内部存了一个值,c里面存的值的类型是字符指针。

我们在定义其他变量时如整型变量i、浮点型变量f、字符型变量,我们拿这些变量的值时直接拿这个变量名即可,如直接访问i。但在定义数组时如char c[20],拿数组里的每一个元素时不会再去访问数组名c,而只会去访问c[0]-c[19],这时候编译器就会把数组名c中填一个固定的值。

int main() {
	char c[20];//字符数组的数组名里存的就是字符数组的起始地址,类型就是字符指针
	gets(c);
    /*
    函数调用是值传递,是把c里存的值传给gets,c里的值类型是字符指针,符合gets函数的格式
    */
	puts(c);
}

puts函数

puts只能输出字符串,printf可以输出所有类型如整型、浮点型、字符型、字符串等。

puts函数的格式:

int puts(char *str);

str系列字符串操作函数

初试中如果不考字符串的操作,则一般用不到str函数;但如果初试中考了字符串的操作,则用到str函数的概率非常高。需要指出的是过去只有一年在初试科目中考察了字符串处理。但str函数在机试中很重要。

str系列字符串操作函数主要包括strlen、strcpy、strcmp、strcat等(可以查询C/C++函数大全)。strlen函数用于统计字符串长度,strcpy函数用于将某个字符串复制到字符数组中,strcmp函数用于比较两个字符串的大小【两个字符串比较,是比较对应字符位置的ASCII码。(从前往后依次逐个对比字符的ASCII码值)。】;strcat函数用于将两个字符串连接到一起。各个函数的具体格式如下所示:[注:当有const修饰表示此处可放字符串常量]

#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);

size_t就是int类型,当不知道是什么类型时可以用如下操作:

在这里插入图片描述

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

注意使用str系列函数需要使用#include <string.h>头文件

//strlen函数是得到字符数组里字符串的长度。
int main() {
	int a,b;
	char c[20] = "wangdao";
	a = sizeof(c);//使用这种方法得到的是字符数组的长度,输出结果a=20。
	printf("%d\n", a);
	b = strlen(c);//使用这种方法得到的是字符数组里面字符串的长度,输出结果b=7。
	printf("%d\n", b);
}

练习使用str系列函数:

int main() {
	/*
	strlen函数是得到字符数组里字符串的长度,同时字符数组里的结束符\0不计入总长度内
	*/
	char c[20] = "wangdao";
	printf("数组c内字符串的长度=%d\n", strlen(c));

	char d[20];
	strcpy(d, c);//strcpy是把一个字符串复制给另一个字符串,d赋给c
	/*
	strcpy的格式如下:char *strcpy(char *to,const char *from);
	当有const修饰表示此处可放字符串常量
	如放置字符串常量的例子:
	strcpy(d, "study");
	*/
	puts(d);

	/*
	strcmp:比较两个字符串的大小。
	格式:int strcmp(const char *str1,const char *str2);两个地方都有const修饰说明两个地方都能放字符串常量
	str1 > str2 返回值为1;str1 < str2 返回值为-1;str1 = str2 返回值为0
	两个字符串比较,是比较对应字符位置的ASCII码。(从前往后依次逐个对比字符的ASCII码值)。
	如比较"how"与"hello",先对比第一个字符,相等;再对比第二个字符,o的ASCII码大于e的ASCII码,所以how>hello
	*/
	int ret = strcmp("how", "hello");
	printf("两个字符串比较后的结果=%d\n",ret);

	/*
	strcat:拼接两个字符串。格式char *strcat(char *str1,const char *str2);
	strcat实现拼接的原理是将后面的填入前面的字符数组中。因此目标数组要能够容纳拼接后的字符串(还应包括一个结束符)
	*/
	strcat(c, "study");
	puts(c);
}

输出结果:

在这里插入图片描述

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

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

相关文章

php代码审计8之SSRF

文章目录 1、关注函数2、SSRF支持的协议2.1、http/https&#xff1a;2.2、file&#xff1a;2.3、dict&#xff1a;2.4、gopher&#xff1a;2.5、ftp/ftps&#xff1a;2.6、tftp&#xff1a;2.7、imap/imaps/pop3/smtp/smtps&#xff1a;2.8、telnet&#xff1a; 3、绕过3.1、读…

flask.send_file实现文件下载、文件传输和二进制流传输

文章目录 flask.send_file函数常用参数描述url直接下载文件flask.send_file文件传输本地文件传输二进制流传输 在使用flask框架时&#xff0c;我们有时需要向前端传输文件。或者需要用户访问一个url时直接下载文件。这时可以使用flask.send_file()函数来实现相关的操作。 flask…

HarmonyOS学习路之开发篇—设备管理(控制类小器件)

控制类小器件开发概述 基本概念 控制类小器件指的是设备上的LED灯和振动器。其中&#xff0c;LED灯主要用作指示&#xff08;如充电状态&#xff09;、闪烁功能&#xff08;如三色灯&#xff09;等&#xff1b;振动器主要用于闹钟、开关机振动、来电振动等场景。 运作机制 控…

Spring Boot 中的 @RefreshScope 注解是什么,原理,如何使用

Spring Boot 中的 RefreshScope 注解是什么&#xff0c;原理&#xff0c;如何使用 在 Spring Boot 中&#xff0c;RefreshScope 注解是一个非常有用的注解。它可以让 Spring Boot 应用程序在运行时重新加载配置。这意味着您可以在不停止和重新启动应用程序的情况下更改配置。在…

大宇无限:一家中国初创企业的出海之旅

如果一家中国企业想要拓展海外市场&#xff0c;那么应该如何开启自己的出海之旅&#xff1f; 大宇无限的出海之路 作为一家互联网科技领域的初创企业&#xff0c;大宇无限的出海之旅对那些想要拓展海外业务的中国企业来说&#xff0c;无疑有着极佳的学习价值和借鉴意义。 “大宇…

WebAssembly:让Istio变得更强大

1 Wasm为Envoy带来新的扩展性 Envoy是一个高性能、可编程的L3/L4和L7网络代理&#xff0c;许多服务网格和网关都采用Envoy作为数据面。 Envoy通过监听器&#xff08;Listener&#xff09;捕获网络数据包&#xff0c;根据数据包的内容匹配某个过滤器链&#xff08;Filter Chai…

工作方法 - 戒定慧

风动、旗动&#xff0c;以及两种冰球棍的摆法 戒。就是戒律&#xff0c;知道边界。 在一个公司的工作中&#xff0c;就是知道我们的核心目标&#xff0c;核心资源&#xff0c;核心用户都是什么。 不是什么热闹都去凑&#xff0c;没有资源去做的事情也去做&#xff0c;非核心用…

记录--多行标签超出展开折叠功能

这里给大家分享我在网上总结出来的一些知识&#xff0c;希望对大家有所帮助 前言 记录分享每一个日常开发项目中的实用小知识&#xff0c;不整那些虚头巴脑的框架理论与原理&#xff0c;之前分享过抽奖功能、签字功能等&#xff0c;有兴趣的可以看看本人以前的分享。  今天要分…

Squid代理服务器(传统代理、透明代理)

Squid代理服务器&#xff08;传统代理、透明代理&#xff09; 一、Squid代理服务器1、代理的工作机制2、代理的类型3、使用代理的好处 二、安装Squid服务2、修改 Squid 的配置文件3、Squid 的运行控制4、编写 Squid 服务脚本 三、构建传统代理服务器1、squid服务器2、web1的配置…

libevent实践02:读取标准输入的数据

对于libevent的知识一点不了解&#xff0c;通知编写测试代码&#xff0c;发现问题&#xff0c;解决问题&#xff0c;一步一步入门学习。 CMakeLists.txt文件&#xff1a; project(libevent_project) cmake_minimum_required(VERSION 3.8)message(STATUS "lkmao:CMAKE_SOU…

Redis高可用群集---搭建(主从、哨兵、Cluster)

目录 Redis 高可用集群Redis 主从复制Redis 哨兵模式Redis 集群模式 Redis 高可用集群 在web服务器中&#xff0c;高可用是指服务器可以正常访问的时间&#xff0c;衡量的标准是在多长时间内可以提供正常服务&#xff08;99.9%、99.99%、99.999%等等&#xff09;。 但是在Redi…

# YourKit Java Profiler 教程

YourKit Java Profiler 教程 文章目录 YourKit Java Profiler 教程本教程软件版本说明YourKitIntelliJ Idea 系统要求探查器体系结构探查器代理探查器用户界面 开始分析集成环境集成IntelliJ Idea 安装插件IntelliJ Idea分析 解决性能问题性能瓶颈优化内存使用内存泄漏垃圾分配…

数据结构 | 双向链表

一、数据结构定义 /* 链表结点 */ typedef int ListType; typedef struct node {ListType data; // 存放整型数据struct node* llink, * rlink; // 指向前驱和后继结点的指针 } *Node;/* 双向链表头结点 */ typedef struct headNode {struct node* head, * tail; // 指向双向链…

IP 扫描程序:轻松发现、扫描和跟踪 IP 空间

什么是 IP 扫描 IP 扫描是实时持续监控网络 IP 地址空间的过程。包括 ICMP ping 扫描和 SNMP 扫描在内的网络协议数量用于扫描网络中的 IP 地址。网络管理员依靠 IP 扫描程序轻松检查和管理 IP 地址空间。使用网络 IP 扫描程序进行 IP 扫描可查看 IP 地址空间利用率和性能。 …

C# 使用CefSharp控件 High DPI问题的解决

使用CefSharp控件&#xff0c;在部分高分辨率的电脑中&#xff08;显示缩放比例非100%&#xff0c;而是120%或者125%等&#xff09;会出现以下一些情况&#xff1a; 显示的页面中出现了黑边&#xff0c;且按钮定位也偏了&#xff0c;比如点击【图层】按钮&#xff0c;需要点击上…

selenium用法

一、请求库selenium selenium是一个自动化测试工具&#xff0c;而爬虫中使用它主要是为了解决requests无法直接执行JavaScript代码的问题。操作浏览器模拟人的行为。 下载浏览器驱动&#xff1a;以谷歌浏览器为例---->谷歌浏览器驱动&#xff08;版本号对应&#xff09; …

K8S应用生命周期管理

K8S应用生命周期管理. 1 应用周期管理1.1 资源对象1.1.1 基础知识1.1.2 资源属性 1.2 Pod基础1.2.1 Pod概述1.2.2 简单实践1.2.3 流程解读1.2.4 应用解析1.2.5 初始化容器1.2.6 Sidecar实践1.2.7 静态POD实践 1.3 Pod进阶1.3.1 Pod探测机制1.3.2 命令探测1.3.3 TCP探测1.3.4 HT…

C# +.Net医院检验科LIS系统源码 实验室信息系统源码

实验室信息系统&#xff08;Laboratory Information System&#xff0c;缩写LIS&#xff09;是一类用来处理实验室过程信息的软件。这套系统通常与其他信息系统比如医院信息系统&#xff08;HIS&#xff09;连接。实验室信息系统由多种实验室流程模块构成&#xff0c;这些模块可…

79-基于stm32单片机酒精浓度测量疲劳驾驶检测系统(程序+原理图+元件清单全套资料)...

资料编号&#xff1a;079 功能介绍&#xff1a;采用stm32单片机作为主控CPU&#xff0c;采用MQ3传感器采集酒精浓度&#xff0c;采用红外接触传感器感应驾驶员上车时间&#xff0c;OLED显示酒精浓度和驾驶时间&#xff0c;当酒精浓度超过阈值&#xff08;程序可调&#xff09;&…