C语言学习

1.C语言基础认知

1.1、c语言是什么

        C语言是计算机编程语言的一种,编程语言用于人和机器交流。

        编程语言通过一系列的语法和语义规则来描述计算机程序的行为和逻辑,程序员使用编程语言编写程序后,计算机可以将程序转化为二进制指令(即机器码),并由CPU执行,CPU会按照指令的顺序依次执行每个指令。

1.2、语言发展历程

第一代 机器语言

机器语言是一组由0和1系列组成的指令码,这些指令码,是CPU制作厂商规定出来的,然后发布出来,程序员必须遵守。

这种语言计算机能识别的唯一语言,人类很难理解,“此时的程序员99.9%都是异类!”

第二代 汇编语言

汇编语言,用一些容易理解和记忆的缩写单词(如move、load)来代替一些特定的指令,汇编语言和机器自身的编程环境息息相关,推广和移植很难。

第三代 高级语言(相当于人类的信息时代)

高级语言摆脱了计算机硬件的限制,把主要精力放在了程序设计上,不再关注低层的计算机硬件。高级语言要被计算机执行,也需要一个翻译程序将其翻译成机器语言,而翻译工作由编译器或解释器完成。

1.3、c语言的由来

c语言最初是作为Unix系统的开发工具而发明的。(为了满足unix系统开发的需要而创建的)

C语言诞生于美国的贝尔实验室, 当时丹尼斯·里奇(Dennis MacAlistair Ritchie)和肯尼斯·蓝·汤普森(Kenneth Lane Thompson)一起开发了Unix操作系统。Unix是用汇编语言写的,依赖于硬件。为了程序的可读性和可移植性,他们决定使用高级语言重定义。但是当时的高级语言无法满足他们的要求,汤普森就在BCPL语言的 基础上发明了B语言。

1972年,丹尼斯·里奇(Dennis MacAlistair Ritchie)在B语言的基础上重新设计了一种新的语言,这种语言取代了B语言,称为C语言。

1973年,整个Unix系统都使用C语言重写。

丹尼斯·里奇 被称为C语言之父、Unix操作系统的关键开发者

1.4、c语言的特点

简洁:C语言的语法简单,语句清晰明了,使得程序易于阅读和理解。

高效:C语言的执行效率高,可以用于开发需要高性能的应用程序。

可移植:C语言可以在不同的硬件平台和操作系统上运行,具有较高的可移植性。

模块化:C语言支持函数和结构体等模块化编程方法,使得程序的复杂性得到有效控制。

标准化:C语言的语法和标准库已经被ISO(国际标准化组织)和ANSI(美国国家标准学会)标准化,具有广泛的应用和兼容性。

2.c语言的基础语法

2.1、第一个c语言程序

#include<stdio.h>     //头文件 
int main(){
    printf("hello world\n");
    return 0;
}

代码分析

1.include头文件
    #include <stdio.h>代表包含stdio.h这个头文件
使用C语言库函数需要提前包含库函数对应的头文件,如这里使用了printf()函数,需要包含stdio.h头文件,
头文件在c语言中后缀名为h的文件被称为头文件。
头文件就像一本书的目录,当我们想使用某本书里的内容时,只需把目录(头文件)引进来就可以了。
2.main函数
    程序的入口,一个程序有且只有一个main函数入口
    {}叫代码块,一个代码块内部可以有一条或者多条语句
    所有的可执行语句必须是在代码块里面
    C语言每句可执行代码都是";"分号结尾
    所有的#开头的行,都代表预编译指令,预编译指令行结尾是没有分号的  *************
3.printf函数
    printf是C语言库函数,功能是向标准输出设备输出一个字符串
    printf(“hello world\n”);
    \n的意思是回车换行
4.return语句
    return代表函数执行完毕,返回return代表函数的终止
    在main函数中return 0代表程序执行成功

2.2、c语言程序快捷键 

单行注释   //
多行注释  ctrl+shift+/ 
多行块注释  /**/

全部快捷键:编辑---高级  --- 去查快捷键


2.3、c语言代码规范

1、一个说明或一个语句独占一行,例如:包含头文件、一个可执行语句都要换行
2、函数体内要有明显缩进,按一下tab键为一个缩进
3、括号要成对写,如果要删除,也要成对删除
4、一句可执行语句的末尾一定要加分号

3.变量

3.1、语法规范

一、变量的命名规范
  1.不能以数字开头
  2.由数字、字母、下划线、$符号
  3.不能用关键字(32个)
  4.见名知意
  5.建议使用下划线命名法和驼峰命名法

3.2、c语言关键字

数据类型关键字(12个)
char(字符型) , short(短整型),int(整型),long(长整型), float(浮点型),double(浮点型,变量精度更高),   
unsigned(无符号数), signed(有符号数),struct(结构体, union(联合体又叫共用体), enmu(枚举),   
 void(空类型)--特殊不能定义变量,修饰函数的参数或返回值,表示没有

控制语句关键字(12个)
if,else,switch,case,default
for,do,while,break,continue,goto,return

存储类关键字(5个)
auto(以后一般不用), extern(外部的),register(寄存器), static, const

3.3、变量程序练习

#include<stdio.h>
int main() {
	int a;
	a = 10;
	printf("a=%d\n",a);
	printf("a=%p\n",&a);
	int b = 10;
	printf("b=%d\n",b);
	int a1, b1;
	a1 = 100, b1 = 100;
	printf("a1=%d b1=%d\n",a1,b1);
	return 0;
}
/*
1.变量定义:可以变量的量。在内存中分配的一块空间,用于存储数据变量名
2.变量定义前 必须有数据类型,程序运行过程中可以修改
3.两种方式:
(1)先定义 再赋值
(2)定义的同时即赋值变量初始化


变量声明和定义
  变量声明: extern 告诉编译器,我在别的地方定义过了,可以有很多次
  变量类型:变量的类型,分配内存,在一个区域只能定义一次
*/

运行结果

4.c语言的数据类型

4.1、数据类型

  1. 基本数据类型
  2. 构造数据类型
  3. 指针类型
  4. 空类型

 4.2、字符型 char

char表示为字符类型,用于存储单个字符,每个字符变量都是由8个bit位构成,在内存中就是1个字节。

相关特性:

(1)在给字符型变量赋值时,需要用一对英文半角格式的单引号(' ')把字符括起来。

(2)字符变量在内存单元存储时,是将与该字符对应的ASCII码放到变量的存储单元中。

(3)char的本质就是一个1个字节大小的整型

#include<stdio.h>
int main() {
    //char  字符类型   单引号括住  只能作用于一个字符 转义字符除外
    //单引号的作用:1、定义字符  2、取字符的Ascll值
    //注意:printf %c输出的是字符   %d输出的是ASCII值
    char ch = 'a';
    printf("%c\n", ch);//a
    printf("%d\n", ch);//97

    char ch2 = '0';
    printf("%d\n", ch2);//48 是字符'0'的ASCLL值
    printf("%c\n", ch2);//'0'
    char ch3 = '\0';
    printf("%d\n", ch3);//'\0' 计算机存的是 0

    int a = 65;
    printf("%c\n", a);//A
    printf("%d\n", a);//65

    //注意:'0'和'\0'的区别
    //字符0存的是 48   '\0'存的是0

    //字符的大小写转换
    //'a' 97 'b' 98 'c' 99 ...... 'z' 122
    // 'A' 65 'B' 66 'c' 67 ...... 'z' 90

    char ch4 = 'a';
    ch4 = ch4 - ('a' - 'A');//小写转大写
    printf("%c\n", ch4);
    char ch5 = 'A';
    ch5 = ch5 + ('a' - 'A');
    printf("%c\n", ch5);//大写转小写
    return 0;
}

/*
数据类型
基本数据类型 整型

构造数据类型
指针
空
*/

运行结果

4.3、布尔类型

布尔类型是一种处理逻辑的类型,其有两个值,分别是真(true)或假(false),它在内存中的长度一般只占用1个字节。

(1)早期C语言没有布尔类型数据,以0代表逻辑假,1代表逻辑真;

(2)C99标准定义了新的关键字bool,提供了布尔类型,或者也可以使用stdbool.h中的bool;

#include<stdio.h>  
#include<stdbool.h>  //stdbool.h库函数中提供了bool关键字,用来声明布尔类型
int main(int num) {
    //
    bool flag = true;
    printf("%d\n", flag);  //1

    flag = false;
    printf("%d\n", flag);  //0

    return 0;
}

/*
早期是没有布尔类型
0表示假,1表示真

*/

运行结果

 4.4、数据类型长度

存储单位说明:

术语

含义

bit(比特)

一个二进制代表一位,一个位只能表示0或1两种状态。数据传输是习惯以“位”(bit)为单位。

Byte(字节)

一个字节为8个二进制,称为8位,计算机中存储的最小单位是字节。数据存储是习惯以“字节”(Byte)为单位。

1b

1bit

1B

1Byte = 8bit

1KB

1KB = 1024B

1MB

1MB = 1024KB

1GB

1GB = 1024MB

1TB

1TB = 1024GB

1PB

1PB = 1024TB

……

……

基本数据类型的长度

数据类型的长度会受操作系统平台的影响,所以在不同平台下基本数据类型的长度是不一样的。

代码分析

#include<stdio.h>  
#include<stdbool.h>
int main(int num) {
    //sizeof()作用:返回对象在计算机内存中所占的字节数,返回64位(8bytes)无符号整型
    //所以占位符使用%lld 代开long long int (8bytes)       %zu  打印无符号整型 size_t
    printf("%zu", sizeof(size_t));//8   size_t 类型本身的大小
    //--------上面的了解
    printf("int 型的字节长度%lld\n", sizeof(int));//4
    printf("short int 型的字节长度%lld\n", sizeof(short int));//2
    printf("long int 型的字节长度%lld\n", sizeof(long int));//4
    printf("long long int型的字节长度%lld\n ", sizeof(long long int));// 8
    printf("char 型的字节长度%lld\n", sizeof(char));//1
    printf(" float 型的字节长度%lld\n", sizeof(float));//4
    printf(" double 型的字节长度%lld\n", sizeof(double));//8
    printf("long double 型的字节长度%lld\n", sizeof(long double));//8
    printf("bool 型的字节长度%lld", sizeof(bool));//1
    //ps:int在不同的编译环境下。字节有所不同,在TC(Turbo C)下2字节  在VC(Visual C++)下是4个字节
    //    在单片机开发中,int在8位的单片机中占2个字节  在32位单片机 中长度为4个字节 

    return 0;
}

/*
sizeof()     %zu  %lld  %zd  内存占8个字节   返回值是size_t类型
数据单位
1位 1b bit 二进制
1字节 1B=8b
void 类型 函数无参或函数无返回值
void fn(void){}
*/

运行结果

4.5、可移植类型

最开始我们介绍C语言是一门跨平台的编程语言,使用C语言编写的程序可以在不同的系统平台下运行,这里有一些前提,为了更好的兼容不同平台,我们在使用基本上数据类型的时候会采用可移植的类型,这些类型可以确保在不同的平台下稳定的运行

例如:32位平台下,int类型为4字节,其他平台可能是8字节,也可能是2字节,就会导致变量的值域不一样,导致程序运行结果有出入,所以我们会采用可移植的类型。

stdint.h和inttypes.h是C语言标准库中的头文件,主要用于提供可移植性的整数类型和宏定义。

stdint.h提供了标准的整数类型,这些类型在不同的系统和编译器之间是可移植的。这个头文件定义了以下几种类型的整数:

  1. int8_t,uint8_t: 8位有符号和无符号整数
  2. int16_t,uint16_t:16位有符号和无符号整数
  3. int32_t,uint32_t:32位有符号和无符号整数
  4. int64_t,uint64_t:64位有符号和无符号整数

此外,stdint.h还定义了一些与这些类型相关的宏,例如in,INT16_MIN等,这些宏表示对应类型的最大值 、最小值。

inttypes.h提供了更多的整数类型和函数。这个头文件定义了以下几种类型的整数:

  1. intmax_t:最大的整数类型
  2. uintmax_t:最大的无符号整数类型
  3. intptr_t:可以转换为指针的整数类型
  4. uintptr_t:可以转换为无符号整数的指针类型

此外,inttypes.h还定义了一些与这些类型相关的宏,例如UINTMAX_MAX,INTMAX_MAX等,这些宏表示对应类型的最大值。

代码分析

#include<stdio.h>
#include<stdint.h>
#include<inttypes.h>
int main() {
	int8_t a = 17;
	printf("%lld\n",sizeof(a));//1字节 占8位

	int8_t b = 0b10000000;
	printf("%d\n", b);
	printf("%d\n",INT8_MIN);//-128
	printf("%d\n", INT8_MAX);//127

	int16_t a1 = 17;
	printf("%lld\n", sizeof(a1));//2字节 
	return 0;

}

/*
可移植类型:
c语言有跨平台特点,可以在不同平台运行,兼容各种系统

*/

运行结果

 4.6、常量

在程序执行过程中,值不发生改变的量称为常量。

C语言的常量可以分为直接常量、自定义常量和系统定义常量3类

1、直接常量也称为字面量,可以直接拿来使用,无需说明,例如:

整型常量

100,200,-100,0

实型常量

3.14 , 0.125,-3.123f

字符型常量

'a', 'b', '1'

字符串常量

"a", "ab","12356"

注意:

1.不以f结尾的实型常量为double类型

2.以f结尾的实型常量为float类型

2.自定义常量有两种方法

  • #define定义的预处理常量,一般出现在函数体的外部
  •  const声明的常量 一般出现在函数体的内部

3.系统定义的常量

C语言提供了一些常量库,比如有:float.h、limits.h等,这些常量同样受操作系统平台的影响会在不同的平台下不一样。

float.h

limits.h

代码分析 

#include<stdio.h>
#define max 100
#include <float.h>
#include<limits.h>
int main() {


	printf("直接量%d\n",100);


	const int num = 100;
	printf("num=%d\n",num);
	printf("max=%d\n", max);


	printf("%e\n", FLT_MAX);
	printf("%e\n", FLT_MIN);
	printf("%d\n", CHAR_MAX);//127
	printf("%d\n", CHAR_BIT);
	return 0;
}
/*
常量:值不发生改变的量
(1)字面量  直接量
以f结尾   float类型
不以f结尾  double类型
(2)自定义常量
const定义  常常在函数内部
#define定义的预处理的常量  一般在函数外部,结尾不加;
(3)系统定义的常量
*/

 运行结果

 5.进制

5.1、二进制

十进制转进制 除n,倒序

1001 1001 ----八进制 (每三位一组) 0 231 ---十六进制 (每四位一组)0x99

  • 二进制是计算技术中广泛采用的一种数制。二进制数据是用0和1两个数码来表示的数
  • 它的基数为2,进位规则是“逢二进一”,借位规则是“借一当二”
  • 当前的计算机系统使用的基本上是二进制系统,数据在计算机中主要是以补码的形式存储的
  • 十进制转化二进制的方法:
  • 用十进制数除以2,分别取余数和商数,商数为0的时候,将余数倒着数就是转化后的结果

5.2、八进制

  1. 八进制,Octal,缩写OCT或O,一种以8为基数的计数法,采用0,1,2,3,4,5,6,7八个数字,逢八进1
  2. 一些编程语言中常常以数字0开始表明该数字是八进制
  3. 八进制的数和二进制数可以按位对应(八进制一位对应二进制三位),因此常应用在计算机语言中
  • 八进制和二进制互转:
  • 十进制转化八进制的方法:
  • 用十进制数除以8,分别取余数和商数,商数为0的时候,将余数倒着数就是转化后的结果

5.3、十六进制

  1. 十六进制(英文名称:Hexadecimal),同我们日常生活中的表示法不一样,它由0-9,A-F组成,字母不区分大小写
  2. 与10进制的对应关系是:0-9对应0-9,A-F(或a-f)对应10-15
  3. 十六进制的数和二进制数可以按位对应(十六进制一位对应二进制四位),因此常应用在计算机语言中
  • 十六进制和二进制互转:

  • 十进制转化十六进制的方法:
    • 用十进制数除以16,分别取余数和商数,商数为0的时候,将余数倒着数就是转化后的结果

5.4、c语言如何表示响应的进制数

十进制

以正常数字1-9开头,如15

八进制

以数字0开头,如017

十六进制

以0x或0X开头,如0xf

二进制

以0b或0B开头,如0b1111

代码分析

#include<stdio.h>
int main() {

    //进制
    //十进制赋值
    int a = 17;
    //八进制赋值
    int b = 017;
    //十六进制赋值
    int c = 0xf;
    //二进制赋值
    int d = 0b111;
    printf("a,b,c,d分别是:%d,%d,%d,%d", a, b, c, d);
    printf("a,b,c,d分别是:%d,%#o,%#x,%d", a, b, c, d);
    return 0;
}

运行结果

5.5、数值存储方式

计算机底层都是存储数据都是采用二进制,但二进制也有几种,比如:原码、反码、补码。接下来我们来看看他们之间的关系的意义作用.

原码十进制数按照:除二取余、倒序排列,得到的就是原码。

  • 10 -> 0000 1010
  • -10 -> 1000 1010
  • -1 -> 1000 0001
  • 1 -> 0000 0001

问题:原码在做计算的时候会出现一些问题,比如正负数的加法运算,以及零的问题。

●正负数加法 -1 + 1 = 0

1000 0001 + 0000 0001 ---------------- 1000 0010 -> -2 ?

●正负零 +0 和 -0 十进制数字0,占了两个二进制;

0000 0000 1000 0000

反码为了解决上面的问题,出现了反码,反码的计算规则如下

  • 正数的反码就是原码本身;
  • 负数的反码是按位取反(但符号位不变);

示例●1 -> 0000 0001 -> 0000 0001 ●-1 -> 1000 0001 -> 1111 1110

0000 0001 + 1111 1110 ----------------- 1111 1111

1111 1111 是运算完之后的结果,但要注意,这时还是反码,需要重新返回来:1000 0000 。反码解决了正负数加法问题,但正负零的问题还是存在。

补码正数的补码就是原码本身;负数的补码就是在反码的基础上+1;

● 1 -> 0000 0001 -> 0000 0001 -> 0000 0001 三码合一

●-1 -> 1000 0001 -> 1111 1110 -> 1111 1111

0000 0001 + 1111 1111 ---------------- 0000 0000 正负0: 0000 0000 1000 0000 ->1111 1111->0000 0000

补码在正负数加减法运算时没问题,也不会出现正负零占两个二进制。

6.输入输出

6.1、输出语句

  • 输出:将程序的运行结果输出到控制台或终端窗口中
  • printf语法格式:
  • printf("输出格式符",输出项)

printf("我今年%d岁了\n",age) //%d表示整型占位符

格式化占位符:

打印格式

对应数据类型

含义

%c

char

字符型,输入的数字按照ASCII码相应转换为对应的字符

%hd

short int

短整数

%hu

unsigned short

无符号短整数

%d

int

接受整数值并将它表示为有符号的十进制整数

%u

unsigned int

无符号10进制整数

%ld

long

接受长整数值并将它表示为有符号的十进制整数

%f

float

单精度浮点数

%lf

double

双精度浮点数

%e,%E

double

科学计数法表示的数,此处"e"的大小写代表在输出时用的"e"的大小写

%s

char *

字符串。输出字符串中的字符直至字符串中的空字符(字符串以'\0‘结尾,这个'\0'即空字符)

%p

void *

以16进制形式输出指针

%o

unsigned int

无符号8进制整数

%x,%X

unsigned int

无符号16进制整数,x对应的是abcdef,X对应的是ABCDEF

6.2、输入语句

注意使用scanf输入数据时,如果报如下错误:

这个错误信息提示你的代码中使用了 scanf 函数,这个函数可能存在安全问题,你可以考虑使用 scanf_s 函数来代替,以确保代码的安全性。

解决方法有两种:

1、将scanf改为scanf_s即可

2、在代码的最上行加入:#define _CRT_SECURE_NO_WARNINGS

  • 输入:接收用户输入的数据的过程
  • scanf语法格式:
  • scanf("格式化字符串",&变量1,&变量2,......);
    • 格式化字符串用于指定输入的数据类型及其格式
    • 变量1、变量2等是要读入的变量的地址
//#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main(){
int num;
printf("第一个输入数:");
/*scanf_s("%d",&num);*/
scanf_s("%d",&num);
printf("num=%d\n",num);
//输入多个值
int a, b; 
printf("第二个输入数:");
scanf_s("%d,%d",&a,&b);//用逗号分开
//scanf_s("a=%d b=%d\n",a,b);//用空格分开
printf("a=%d b=%d\n", a,b);

//占位符 打印格式
//char c = 'A';
//printf("c=%c\n",c);
//short s = 100;
//printf("s=hd\n",s);
//long a = 200;
//printf("a=%ld\n",a);
//float b = 3.14;
//printf("b=%f\n", b);
//double d = 3.14;
//printf("d=%lf\n", d);
地址
//printf("d=%p\n",d);
return 0;

}

/*
输出printf()
输入scanf_s()
打印格式 占位符
%c char 
%d int
%ld long int
%lld long long int
%f float
%lf double %.2lf 保留两位小数
%s char*
%p void* 十六进制地址(指针)
%o unsigned int  八进制


*/

注意使用scanf输入数据时,如果报如下错误:

这个错误信息提示你的代码中使用了 scanf 函数,这个函数可能存在安全问题,你可以考虑使用 scanf_s 函数来代替,以确保代码的安全性。

解决方法有两种:

1、将scanf改为scanf_s即可

2、在代码的最上行加入:#define _CRT_SECURE_NO_WARNINGS

7.运算符

7.1、算术运算符

运算符

术语

示例

结果

+

10 + 5

15

-

10 - 5

5

*

10 * 5

50

/

10 / 5

2

%

取模(取余)

10 % 3

1

++a

前自增

a=2; b=++a;

a=3; b=3;

a++

后自增

a=2; b=a++;

a=3; b=2;

--a

前自减

a=2; b=--a;

a=1; b=1;

a--

后自减

a=2; b=a--;

a=1; b=2;

7.2、赋值运算符

运算符

术语

示例

结果

=

赋值

a=2; b=3;

a=2; b=3;

+=

加等于

a=0; a+=2;

等同于 a = a + 2;

a=2;

-=

减等于

a=5; a-=3;

等同于 a = a - 3;

a=2;

*=

乘等于

a=2; a*=2;

等同于 a = a * 2;

a=4;

/=

除等于

a=4; a/=2;

等同于 a = a / 2;

a=2;

%=

模等于

a=3; a%=2;

等同于 a = a % 2;

a=1;

7.3、比较运算符

C 语言的比较运算中, “真”用数字“1”来表示, “假”用数字“0”来表示。

运算符

术语

示例

结果

==

相等于

4 == 3

0

!=

不等于

4 != 3

1

小于

4 < 3

0

>

大于

4 > 3

1

小于等于

4

0

>=

大于等于

4 >= 1

1

7.4、逻辑运算符

运算符

术语

示例

结果

!

!a

如果a为假,则!a为真;

如果a为真,则!a为假。

&&

a && b

如果a和b都为真,则结果为真,否则为假。

||

a || b

如果a和b有一个为真,则结果为真,二者都为假时,结果为假。

7.5、位运算符

常见的位运算符号有&、|、^、~、>>、

运算符

术语

示例

结果

&

按位与运算

011 & 101

2个都为1才为1,结果为001

|

按位或运算

011 | 101

有1个为1就为1,结果为111

^

按位异或运算

011 ^ 101

不同的为1,结果为110

~

取反运算

~011

100

左移运算

1010

10100

>>

右移运算

1010 >> 1

0101

7.5.1、按位与(&)运算:位与位进行比较,如果都为1,则为1,否则为0;

//按位运算符:针
/*1、按位 与运算   &  位与位进行比较 如果都为1,则为1,否则为0
* 例如:40 | 15 = 
* 40转成二进制: 0010 1000
* 15转成二进制: 0000 1111
* 
* 0010 1000
* 0000 1111
* ------------
* 0000 1000======>8     
*/
printf("%d\n", 40 & 15);//8

7.5.2、按位或(|)运算:位与位进行比较,如果都为0,则为0,否则为1;

/*
2、按位或|:  位与位进行比较  如果都为0则为0,否则为1
例如:40  | 15
    0010 1000
    0000 1111
    -----------
    0010 1111======>47
*/
printf("%d\n", 40 | 15);//47

7.5.3、按位异或运算:位与位进行比较,相同为0,不同为1;

/*3、异或运算^:位与位进行比较 相同为0 不同为1
* 例如:40 ^15
*    0010 1000
*    0000 1111
* --------------
*    0010 0111====>39
*/
printf("%d\n", 40 ^ 15);//39

7.5.4、按位取反运算:补码取反,再将取反后的补码转为原码;

ps:无符号的数据,取反后最高位为1,也不需要逆运算。

  • 对于无符号数据,取反就是简单的按位取反,取反后的值是一个有效的无符号数,且不需要通过逆运算来恢复原始值。

/**
 * 按位取反运算:补码取反,再将取反后的补码转为原码。
 *      1、正数取反:由于正数的原码和补码是相同的,取反的方式简便了些。
 *              补码(原码) -> 取反 -> 补码逆运算 -> 反码逆运算(符号位不变) -> 取反后的原码
 *      2、负数取反:
 *              原码 -> 反码 -> 补码 -> 取反 -> 取反后的补码即原码
 * 示例:
 *            原码(补码)  取反的补码   补码逆运算-1  反码逆运算
 *      ~40 = 0010 1000 -> 1101 0111 -> 1101 0110 -> 1010 1001 = -41
 *
 *            原码(补码)  取反的补码   补码逆运算-1  反码逆运算
 *      ~15 = 0000 1111 -> 1111 0000 -> 1110 1111 -> 1001 0000 = -16
 *
 *                原码         反码          补码          取反
 *      ~-15 = 1000 1111 -> 1111 0000 -> 1111 0001 -> 0000 1110 = 14
 */
printf("~40 = %d\n", ~40);
printf("~15 = %d\n", ~15);
printf("~-15 = %d\n", ~(-15));

7.5.5、左移运算符<<

将数字的二进制补码全部向左移动,空出来的位置补0,超出范围的二进制数丢弃;

有符号的数据左移后最高位如果为1,则需要进行逆运算;

注意事项:

  • 无符号的数据,左移后最高位为1,也不需要逆运算;
  • -128:1000 0000 特殊情况也不需要逆运算;

1.位运算符针对是整型数值做的运算

 示例:
     *      40 << 4 = 0010 1000 << 4 = 1000 0000 = -128 (特殊的不需要逆运算)
     *      41 << 4 = 0010 1001 << 4 = 1001 0000 = 1000 1111 = 1111 0000 = -112
     *       7 6 5 4 3 2 1 0
     *       1 0 0 1 0 0 0 0
     */

    int8_t p = 40;
    p <<= 4;    //  p = p << 4;
    printf("40 << 4 = %d\n", p);

7.5.6、右移运算符 >>

逻辑移位:将数字的二进制补码全部向右移动,空出来的位置补0,超出范围的二进制数丢弃

算术移位:左边移入的位由原先该值的符号位决定,符号位为1,则移入的位均为1,符号位为0,则移入的位均为0,这样能保证原数的正负形式不变

标准规定,无符号值执行的所有位移操作都是逻辑移位,对于有符号值,到底采用逻辑移位还是算术移位取决于编译器。

注意:

1. 如果不能整除,向下取整

2.右移运算符最好只用于无符号整数,不要用于负数。因为不同系统对于右移后如何处理负数的符号位方法,可能会得到不一样的结构

**
 * 示例:
 *      15 >> 4 = 0000 1111 >> 4 = 0000 0000 = 0
 *
 *      特殊情况,默认情况下计算机采用逻辑右移方式,即左侧空出来的位置补0,
        但如果是算术右移,那么左侧空出来的位置需要根据符号位的值来补位。
 *
 *      -15 >> 2 = 1000 1111 >> 2 (原码)
 *               = 1111 0000 >> 2 (反码,注意:负数反码符号位不变)
 *               = 1111 0001 >> 2 (补码)
 *               = 1111 1100 (右移后的补码,注意:算术右移左侧空出来的位置需要根据符号位来补位)
 *               = 1111 1011 (右移后的反码,补码转反码只需-1即可)
 *               = 1000 0100 (右移后的原码)
 *               = -4
 */

int8_t q = 15;
q >>= 4;
printf("15 >> 4 = %d\n", q);

int8_t m = -15;
m >>= 2;
printf("-15 >> 2 = %d\n", m);

 案例需求:

// 将变量a的第2位设置为1,其他位保持不变
uint8_t a = 0b10110011; // 0xb3;

// 将变量b的第2位、第6位设置为1,其他位保持不变
uint8_t b = 0b10110011; // 0xb3;

// 将变量c的第5位设置为0,其他位保持不变
uint8_t c = 0b10110011;  // 0xb3;

// 将变量d的第0~3位设置为0,其他位保持不变
uint8_t d = 0b11111111;  // 0xff;

// 将变量e的第2位取反,其他位保持不变
uint8_t e = 0b10110011;  // 0xb3;

// 将变量f取出8-15位
uint32_t f = 0x12345678;

方法一:

#include <stdio.h>
#include <stdint.h>
int main() {
    // 将变量a的第2位设置为1,其他位保持不变
    uint8_t a = 0b10110011; // 0xb3;
    a |= 0b00000100;
    printf("%#x\n", a);//0xb7      1011 0111
    /*
    总结 0位是从后向前查的    |  位与位进行比较,如果都为0,则为0,否则为1(只要有一个为1即为1);
    */

    // 将变量b的第2位、第6位设置为1,其他位保持不变
    uint8_t b = 0b10110011; // 0xb3;
    b |= 0b01000100;
    printf("%#x\n", b);//0xf7     1111 0111

    // 将变量c的第5位设置为0,其他位保持不变
    uint8_t c = 0b10110011;  // 0xb3;
    c &= 0b11011111;
    printf("%#x\n", c);//0x93   1001 0011
    /*
    按位与(&)运算:位与位进行比较,如果都为1,则为1,否则为0(只要有一个为0即为0);
    */

    // 将变量d的第0~3位设置为0,其他位保持不变
    uint8_t d = 0b11111111;  // 0xff;

    d &= 0b11110000;
    printf("%#x\n", d);//0xf0    1111 0000

    // 将变量e的第2位取反,其他位保持不变
    uint8_t e =       0b10110011;  // 0xb3;
    uint8_t mask = 0b00000100;
    e ^= mask;
    printf("%#x\n", e);//     0xb7,  0b10110111

    /*
    按位异或操作的特点是,当两个相应的二进制位相异时返回1,相同时返回0。
    因此,将 e 与这个掩码进行按位异或操作会将 e 的第2位取反。
    */

    // 将变量f取出8-15位
    uint32_t f = 0x12345678;

    // 将f右移8位,使得原本的第8位到第15位变为最低8位  
    // 然后用0xFF(即二进制的11111111)进行与操作,以保留最低8位  
    uint8_t result = (f >> 8) & 0xFF;

    printf("The bits 8-15 of f are: 0x%02X\n", result); //0x56

    return 0;
}

 方法二:

#include <stdio.h>
#include <inttypes.h>

int main() {
    // 将变量a的第2位设置为1,其他位保持不变
    uint8_t a = 0b10110011; // 0xb3;
    a |= (1 << 2);          // 或者 x = x | (1 << 2);
    printf("%02x\n", a);    // b7,  10110111

    // 将变量b的第2位、第6位设置为1,其他位保持不变
    uint8_t b = 0b10110011; // 0xb3;
    b |= (1 << 2 | 1 << 6);
    printf("%02x\n", b);    // f7,11110111

    // 将变量c的第5位设置为0,其他位保持不变
    uint8_t c = 0b10110011;  // 0xb3;
    c &= ~(1 << 5);
    printf("%02x\n", c);    // 93,10010011

    // 将变量d的第0~3位设置为0,其他位保持不变
    uint8_t d = 0b11111111;  // 0xff;
    d &= ~(1 << 0 | 1 << 1 | 1 << 2 | 1 << 3);
    printf("%02x\n", d);    // f0,11110000

    // 将变量e的第2位取反,其他位保持不变
    uint8_t e = 0b10110011;  // 0xb3;
    e ^= (1 << 2);
    printf("%02x\n", e);    // b7,  10110111

    // 将变量f取出8-15位
    uint32_t f = 0x12345678;
    uint32_t temp = (f & 0x0000ff00) >> 8;
    printf("%#x\n", temp);

    return 0;
}

7.6、运算符的优先级

  • 不同的运算符默认具备不同的优先级,符号较多不用记,现用现查就可以。
  • 当无法确定谁的优先级高时,加一个小括号就解决了。

优先级

运算符

名称或含义

使用形式

结合方向

说明

1

[]

数组下标

数组名[常量表达式]

左到右

--

()

圆括号

(表达式)/函数名(形参表)

--

.

成员选择(对象)

对象.成员名

--

->

成员选择(指针)

对象指针->成员名

--

2

-

负号运算符

-表达式

右到左

单目运算符

~

按位取反运算符

~表达式

++

自增运算符

++变量名/变量名++

--

自减运算符

--变量名/变量名--

*

取值运算符

*指针变量

&

取地址运算符

&变量名

!

逻辑非运算符

!表达式

(类型)

强制类型转换

(数据类型)表达式

--

sizeof

长度运算符

sizeof(表达式)

--

3

/

表达式/表达式

左到右

双目运算符

*

表达式*表达式

%

余数(取模)

整型表达式%整型表达式

4

+

表达式+表达式

左到右

双目运算符

-

表达式-表达式

5

左移

变量

左到右

双目运算符

>>

右移

变量>>表达式

6

>

大于

表达式>表达式

左到右

双目运算符

>=

大于等于

表达式>=表达式

小于

表达式

小于等于

表达式

7

==

等于

表达式==表达式

左到右

双目运算符

!=

不等于

表达式!= 表达式

8

&

按位与

表达式&表达式

左到右

双目运算符

9

^

按位异或

表达式^表达式

左到右

双目运算符

10

|

按位或

表达式|表达式

左到右

双目运算符

11

&&

逻辑与

表达式&&表达式

左到右

双目运算符

12

||

逻辑或

表达式||表达式

左到右

双目运算符

13

?:

条件运算符

表达式1?

表达式2: 表达式3

右到左

三目运算符

14

=

赋值运算符

变量=表达式

右到左

--

/=

除后赋值

变量/=表达式

--

*=

乘后赋值

变量*=表达式

--

%=

取模后赋值

变量%=表达式

--

+=

加后赋值

变量+=表达式

--

-=

减后赋值

变量-=表达式

--

左移后赋值

变量

--

>>=

右移后赋值

变量>>=表达式

--

&=

按位与后赋值

变量&=表达式

--

^=

按位异或后赋值

变量^=表达式

--

|=

按位或后赋值

变量|=表达式

--

15

逗号运算符

表达式,表达式,…

左到右

--

8.c语言数据类型转换

8.1、自动类型转换(隐式)

数据类型存在自动转换的情况.

自动转换发生在 不同数据类型 运算时,在编译的时候 自动完成

char类型数据转换为int类型数据遵循ASCII码中的对应值.

注意:

1、字节小的可以向字节大的自动转换以保证精确度不降低,但字节大的不能向字节小的转换

2、char类型可以转换为int,int可以转换为double,char可以转换为double,但反过来不行

自动类型转换 字节小的向字节大的转 ---隐式 编译器自动完成

(1)算术运算 不同类型必须转为同一种类型才能运算 其它类型转为最长类型再运算

(2)赋值运算 右边的必须转换成赋值左边的类型 (如果右边大于左边 出现截断或舍入)

  (3)  输出转换 以指定格式输出时 输出的类型和指定的类型不符时,会进行转换

注意:float 类型 可没有参与转换 (只有char int double 互转

#include<stdio.h>
int main() {
    //自动类型转换
        int a = 11;
    double b = a; //将a的值,11,转换为11.0000,再给b赋值 
    printf("b = %lf\n", b);
    printf("a = %d\n", a);
         printf("%lf\n", 12/3);//异常结果0.000000
        printf("%lf\n", 12/3.0);// 其中一个是浮点数才可以
     //自动类型转换  隐式类型转换 :编译器自动转换
     
    /*1、算术转换 进行算术运算时,不同类型的数据必须转换成同一数据类型才能运算,
          转换原则为:进行运算时,以表达式中最长类型为主,将其他数据类型均转换为该类型
     */  
    //int a = 10;
    //double b = 3.14;
    //printf("a+b=%lf\n", a + b); 
    //char ch = 'a';
    //printf("a+12=%d\n", ch + 12);//109

    //2、赋值转换
    //进行赋值操作时,赋值运算符右边的数据类型必须转换成赋值号左边的类型,
    //若右边的数据类型的长度大于左边,则要进行截断或舍入操作。
    char c = 'b';
    int i, result;
    i = 2;
    double d = 1.5;
    float m = 1.20000;    //从“double”到“float”截断    1.20000f
    result = c / i + (d * m); //分析result的类型  c/i=98/2 =49   1.5*1.2=1.8     49+1.8= 50   int result
         // "=":从“double”转换到“int”,可能丢失数据      
    printf("resault = %d\n", result);
 
       // 3、输出转换
    //在程序中,用printf以指定格式输出时,当要输出的数据类型与指定类型不符合时,会进行类型转换
    int a1 = 9;
    //printf("%f\n", a);//出错    0.000000
    float b1 = 90.5f;
    //printf("%d\n", b);//出错    0
    long c1 = 90;
    printf("%d\n", c);//正常转换
    char ch = 'a';
    printf("%d\n", ch);//正常
    //较长类型往较短类型转换时,不能超出较短类型的范围   
 //(-32,768 到 32,767 或 -2,147,483,648 到 2,147,483,647)    INT32_MAX     INT16_MAX
    //long d = 8000;
    //printf("%d\n", d); 
    return 0;
}

8.2、强制类型转换

强制类型转换是你通过定义类型转换运算来实现的

基本格式为: (数据类型)(表达式)

其作用:是把表达式的运算结果强制转换成类型说明符 所表示的类型

在使用强制转换时应注意以下问题:

  • 数据类型和表达式都必须加括号, 如把(int)(x/2+y)写成(int)x/2+y则成了把x转换成int型之后再除2再与y相加了。
  • 转换后不会改变原数据的类型及变量值,只在本次运算中临时性转换。
  • 强制转换后的运算结果不遵循四舍五入原则。

代码分析

#include<stdio.h>
int main() {
    //强制类型转换  语法:(类型)(变量)
    int a = 3;
    int b = 2;
    printf("%lf\n", (double)(a / b)); //1.000000  先转整再转double
    printf("%lf\n", (double)a / b); //1.500000
    printf("%lf\n", (double)(10 / 3));//3.00000000  出错  先转整再转double
    printf("%lf\n", (double)10 / 3);//3.333333

    //ASCLL表中  字符最大是127(字符占一个字节,8位  最大值是127)
    int c = 100;
    char d = (char)c;
    printf("%d\n", d);//正常


    int c1 = 200;//如果取大于127的值就会出错,数据可能会丢失
    char d1 = (char)c;
    printf("%d\n", d1);

    return 0;
}

9.分支结构

 9.1、单分支(简单的if语句)

简单的if语句基本结构如下:

单分支    if(条件判断)
               {
               执行代码块
               }

语义:如果表达式结果为真,执行其后的语句,否则不执行

注意:if()后面没有分号

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
int main() {
    //单分支:if简单语句
    //案例需求:定义个变量 年龄 用户输入年龄  如果年龄大于18,则允许进网吧 
    int age;
    printf("请输入您的年龄:");
    scanf("%d", &age);
    if (age > 18) {
        printf("可以进网吧");
    }

    return 0;

    
}

9.2、双分支

if(条件判断){
           条件成立:执行代码块1;
           }
        else{
           执行代码块2;
           }

 

 5、提示用户输入年龄,如果大于等于18.则告知用户可以查看,如果小于10岁,则告知不允许查看
 如果大于等于10岁.则提示用户是否继续查看 (yes、no)
如果输入的是yes则提示用户可以查看,否则提示不可以查看。
#include <stdio.h>
int main() {
    int age;
    int i;
    printf("请输入您的年龄:");
    scanf_s("%d", &age);
    if (age >= 18) {
        printf("您可以查看。\n");
    }
    else if (age < 10) {
        printf("不允许查看。\n");
    }
    else {
        printf("是否继续查看 (yes请输1 或 no请输0):");
        scanf_s("%d", &i);
        if (i==1) {
            printf("您可以查看。\n");
        }
        else {
            printf("不可以查看。\n");
        }
    }
    return 0;
}

9.3、多分支(多重if-else语句)

if(条件1){
        条件1成立:执行代码块1;
        }else if(条件2){
        条件1不成立,条件2成立:执行代码块2;
        }else if(条件3){
        条件1,2不成立,条件3成立:执行代码块3;
        }

1.输入成绩,划分成绩等级  >90 优秀   >80中   >70良   >60及格  <60  差
#include<stdio.h> 
#include<stdint.h>
int main() {
    int code;
    printf("请输入您的成绩:");
    scanf_s("%d", &code);
    if (code>=90) {
        printf("优秀");
    }
    else if(code >=80){
        printf("中");
    }
    else if (code >= 70) {
        printf("良");
    }
    else if (code >= 60) {
        printf("及格");
    }
    else if (code < 60) {
        printf("差");
    }
    return 0;
}

9.4、switch多分支语句

    switch(表达式的值){
      case value1:
          代码块1;
          break;
      case value2:
          代码块2;
          break;
           ......
     default:
     以上代码都不成立执行 代码块n

4.、做一个简易计算器    (实现 + -*/ % )
#include <stdio.h>
int main() {
    char operator;
    double num1, num2;
    printf("请输入运算符 (+, -, *, /, %): ");
    scanf_s("%c",&operator);
    printf("请输入两个操作数:如2,3\n ");
    scanf_s("%lf,%lf", &num1, &num2);

    switch (operator) {
    case '+':
        printf("%.2lf + %.2lf = %.2lf\n", num1, num2, num1 + num2);
        break;
    case '-':
        printf("%.2lf - %.2lf = %.2lf\n", num1, num2, num1 - num2);
        break;
    case '*':
        printf("%.2lf * %.2lf = %.2lf\n", num1, num2, num1 * num2);
        break;
    case '/':
        if (num2 != 0.0)
            printf("%.2lf / %.2lf = %.2lf\n", num1, num2, num1 / num2);
        else
            printf("除数不能为零。\n");
        break;
    case '%':
        if ((int)num2 != 0)
            printf("%d %% %d = %d\n", (int)num1, (int)num2, (int)num1 % (int)num2);
        else
            printf("除数不能为零。\n");
        break;
    default:
        printf("无效的运算符。\n");
    }

    return 0;
}

 10,循环结构

10.1、for循环

c语言中for循环的格式如下:

for(条件初始化;循环条件判断;循环迭代){
   循环体;
}


条件初始化
循环条件判断
条件成立
条件不成立,结束循环

循环迭代

执行思路:

1、执行表达式1,一般对循环变量做初始化

2、判断表达式2结果,为真,执行循环体,然后向下执行,为假,循环结束;

3、执行表达式3,一般是对循环变量进行操作

4、再次判断表达式的结果,一次循环往复,直至判断结果为假,跳出循环

注意:for循环表达式之间的分号,一定要写!

使用for循环结构,实现1+2+3+4+......+1000中所有偶数的累加和,所有数字的累加和
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdbool.h>
int main() {
    int sum_even = 0;
    int sum_all = 0;

    for (int i = 1; i <= 1000; i++) {
        sum_all += i;
        if (i % 2 == 0) {
            sum_even += i;
        }
    }
    printf("使用 for 循环:\n");
    printf("所有偶数的累加和: %d\n", sum_even);
    printf("所有数字的累加和: %d\n", sum_all);
 return 0;
}

10.2、while循环

语法格式:

while(条件){

循环体

}

执行思路:

当条件为假时,不执行循环体,否则会一直执行循环体

注意:循环体里一定要有退出条件,否则会陷入死循环

死循环:条件永远为真的循环,叫死循环

循环次数不明确,用while 

使用while循环结构,实现1+2+3+4+......+1000中所有偶数的累加和,所有数字的累加和
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdbool.h>
int main() {
    int sum_even = 0;
    int sum_all = 0;
    int i = 1;
    while (i <= 1000) {
        sum_all += i;
        if (i % 2 == 0) {
            sum_even += i;
        }
        i++;
    }
    printf("使用 while 循环:\n");
    printf("所有偶数的累加和: %d\n", sum_even);
    printf("所有数字的累加和: %d\n", sum_all);
 return 0;
}

10.3、do while循环

语法格式:

do{

循环体;

}while(条件)

执行思路:do...while循环是在执行循环体之后才检查条件,条件为真在执行一次,否则跳出循环

所以do...while循环,至少执行一次

使用do while循环结构,实现1+2+3+4+......+1000中所有偶数的累加和,所有数字的累加和
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdbool.h>
int main() {
    int sum_even = 0;
    int sum_all = 0;
    int i = 1;
    do {
        sum_all += i;
        if (i % 2 == 0) {
            sum_even += i;
        }
        i++;
    } while (i <= 1000);
    printf("使用 do-while 循环:\n");
    printf("所有偶数的累加和: %d\n", sum_even);
    printf("所有数字的累加和: %d\n", sum_all);
    return 0;
 return 0;
}

10.4、对比三种循环语句

三种循环在具体的使用场景上是有区别的:

1、在知道循环次的情况下,更适合使用for循环

2、不知道循环次数的情况下,适合使用while和do while循环

3、如果可能一次都不循环,考虑使用while循环

4、如果至少循环一次,考虑使用do while循环 

5、本质上来讲 while 和 do while循环是可以相互转换的

10.5、循环嵌套

多重循环就是在循环结构的循环体中又出现循环结构。

在实际开发中一般最多用到三层重循环。这里重点说一下双重循环

/*
案例2、双重for循环打印9 * 9乘法表
        1 *  1 = 1
    1 *  2 = 2  2 * 2 = 4
    1 *  3 = 3  2 * 3 = 6  3 * 3 = 9
    1 *  4 = 4  2 * 4 = 8  3 * 4 = 12  4 * 4 = 16
    1 *  5 = 5  2 * 5 = 10  3 * 5 = 15  4 * 5 = 20  5 * 5 = 25
    1 *  6 = 6  2 * 6 = 12  3 * 6 = 18  4 * 6 = 24  5 * 6 = 30  6 * 6 = 36
    1 *  7 = 7  2 * 7 = 14  3 * 7 = 21  4 * 7 = 28  5 * 7 = 35  6 * 7 = 42  7 * 7 = 49
    1 *  8 = 8  2 * 8 = 16  3 * 8 = 24  4 * 8 = 32  5 * 8 = 40  6 * 8 = 48  7 * 8 = 56  8 * 8 = 64
    1 *  9 = 9  2 * 9 = 18  3 * 9 = 27  4 * 9 = 36  5 * 9 = 45  6 * 9 = 54  7 * 9 = 63  8 * 9 = 72  9 * 9 = 81
*/
#include<stdio.h>
int main() {
for (int i = 1; i <= 9; i++) {
    for (int j = 1; j <= i; j++) {
        printf("%d * %d = %d  ", j, i, j * i);
    }
    printf("\n");
}
return 0;
}

10.6、跳转关键字(break continue goto)

10.6.1、break

循环的break说明:

某一条件满足时,不再执行循环体中后续重复的代码,并退出整个循环循环

需求:一共吃5碗饭,吃到第3碗吃饱了,就结束吃饭动作不再吃了

//break跳出循环
//需求:一共吃5碗饭,吃到第3碗不再吃了
#include<stdio.h>
int main() {
int i = 1;
while (i <= 5) {
    printf("这是我吃的第%d碗饭\n", i);;
    if (i == 3) {
        break;
    }
    i++;
}
return 0;
}

//换一种写法for循环
//for (int j = 1; j <= 5; j++) {
//    printf("这是我吃的第%d碗烦了\n", j);
//    if (j == 3) {
//        break;
//    }
}
//注意点:
//1、在没有循环结构的情况下,break不能单独出现在if-else语句中
//2、在多层循环中,一个break语句只跳出当前循环

10.6.2、continue

某一条件满足时,不再执行本次循环体中后续重复的代码,但进入下一次循环判断

○ while循环,continue 之前一定要修改计数器(条件变量),否则,导致死循环

● 需求:一共吃5个苹果,吃到第3个遇到虫子,这个跳过不吃,下一个继续

//continue  跳出本次循环 下一次循环正常进行
//案例需求:一共吃5个苹果,吃到第3个苹果遇到虫子,这个跳过不吃,继续吃下一个
#include<stdio.h>
int main() {
int a = 1;
while (a <= 5) {
    if (a == 3) {
        a++;
        continue;
    }
    printf("这是我吃的第%d个苹果了\n", a);
    a++;

}
return 0;
}


//for (int i = 1; i <= 5; i++) {
//    if (i == 3) {
//        continue;
//    }
//    printf("这是我吃的第%d个苹果了\n", i);    
//}

break语句与continue语句的区别是:

break是跳出当前整个循环,continue是结束本次循环开始下一次循环。

好比工作日上班,上班是个循环的动作,break就好比是离职,以后都不来上班了。而continue则好比是请假,假期结束,还的继续上班。

10.6.3、goto(了解)

● goto用于无条件跳转

○ 在一种情况下可以使用goto语句:从一组嵌套的循环中跳出

● goto语句可以导致代码不易理解和维护,并且容易引入不必要的错误。因此,除非必要,最好不要使用goto语句

格式 go 标号;

其中 标号 属于标识符 , 以 : 为标记,位于某语句前面

执行goto 语句后, 程序将跳转到指定标号处执行。这样可以随意控制转移到程序中的任意一条语句上,然后执行它。

11.函数

11.1、函数概述

  1. 函数是一种可重用的代码块,用于执行特定任务或完成特定功能
  2. 函数作用:对具备相同逻辑的代码进行封装,提高代码的编写效率,实现对代码的重用

11.2、函数的使用

 函数定义
void 函数名(int num) {
    函数体
}
int 函数名(int num) {
    函数体
}
 函数调用
函数名();

语法说明:

1、函数名左边的关键字 void 是函数的返回值,void代表函数没有返回值,int代表函数的返回值为整型。

2、函数名是与变量名一样,是标识符的一种,需要遵循标识符的命名规则

3、只定义函数,不调用,函数永远不会被执行

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>

//案例:定义一个函数求两个数的和

//函数的定义
void getSum() {
    int num1, num2;
    printf("请输入两个数:\n");
    scanf("%d %d", &num1, &num2);
    printf("两数的和是:%d", num1 + num2);
}

int main() {
    //函数的调用
    getSum();
    return 0;
}

11.3、函数的分类

11.3.1、函数有3中分类方式:

(1)从定义角度分类(即函数是谁实现的)

(2)库函数 (c 库实现的)

(3)自定义函数 (程序员自己实现的函数)

系统调用 (操作系统实现的函数)

(1)文件操作:如打开文件(open())、读取文件(read())、写入文件(write())和关闭文件(close())等。

(2)内存管理:如分配内存(malloc() 在用户空间实现,但可能间接使用系统调用)、释放内存(free())以及修改内存保护标志等。虽然 malloc() 和 free() 是库函数,但它们可能会使用系统调用来分配和释放内存。

(3)设备管理:如读取和写入设备文件、设置设备参数等。

11.3.2、从参数角度分类

(1) 有参函数:

函数有形参,可以是一个,或者多个,参数的类型随便 完全取决于函数的功能

(2) 无参函数:

函数没有参数,在形参列表的位置写个 void 或什么都不写

//函数的分类:
// 根据参数分类:定义时小括号里是形参   调用时小括号里是实参
//形参必须带类型,且以,分割
// 1、无参函数:形参什么都不写  或者是void
void say(void) {
    printf("大家好!!!");
}

//2、有参函数:形参可以有1个,也可以有多个,参数的类型随便
//调用时要传递实参
void func(int num) {
    printf("%d", num * 100);
}

int main() {
    //函数的调用
    say();
    func(19);//函数调用时传的是实参
    return 0;
}

11.3.3、从返回值的角度分类

(1). 带返回值的函数

在定义函数的时候,必须带着返回值类型,在函数体里,必须有 return

如果没有返回值类型,默认返回整型。

注意:如果把函数的返回值类型省略了,默认返回整型

函数内,return后边的内容不会被执行

在定义函数的时候,函数的返回值类型,到底是什么类型的,取决于函数的功能

(2)没返回值的函数

在定义函数的时候,函数名字前面加 void

注意:在函数的定义不能嵌套,即不能在一个函数体里定义另外一个函数,所有的函数必须是平行的

//2、根据有无返回值分类:
//1)有返回值的函数
//int getSum(int num1, int num2) {
//    printf("两数的和是:%d\n", num1 + num2);
//    return 0;
//}

//利用返回值:改写两数求和的函数
int getSum(int num1, int num2) {
    return num1 + num2;
}
//注意:如果函数没有返回值类型,就默认是int整型



//2)没有返回值的函数  函数名前加void
//例:求两个数的最大值
void getMax(int num1, int num2) {
    if (num1 >= num2) {
        printf("较大值是:%d\n", num1);
    }
    else {
        printf("较大值是:%d\n", num2);
    }
}


int main() {
    //函数调用
    //getSum(1, 2);
    //获取函数返回值  函数调用的结果就是返回值
    printf("%d\n", getSum(1,2));
    getMax(500, 90);

    return 0;
}

11.4、函数的声明

  1. 如果函数定义代码 没有放在函数调用的前面,这时候需要先做函数的声明
  2. 所谓函数声明,相当于告诉编译器,函数是有定义的,在别的地方定义,以便使编译能正常进行

注意:一个函数只能被定义一次,但可以声明多次

extern void test();//函数的声明

#include<stdio.h>
//函数的声明,相当于告诉编译器,函数是有定义的,在别的地方定义
// 函数的定义只能有一次,但声明可以有多次
//extern  void test();//函数的声明
//函数的声明  可以不带extern关键字
//void test();

int main() {
    int num=99;
    printf("%d\n", num);
    //函数的调用
    test();
}
//函数的定义
void test() {
    printf("我们要测试了!!!");
}

11.5、函数的案例练习

案例1

//定义一个函数,判断是不是质数,是返回1,不是返回0
#include <stdio.h>
int zhi(int num) {
	if (num <= 1) {
		return 0;
	}
	for (int i = 2; i <= num / 2; i++) {
		if (num % 2 != 0) {
			return 1;
		}
		else {
			return 0;
		}
	}
}
int main() {
	int a;
	printf("请输入数字:");
    scanf_s("%d",&a);
	int num = zhi(a);
	if (num==1) {
		printf("%d是质数",a);
	}else {
		printf("%d不是质数",a);
	}
	return 0;
}

案例2 

//定义一个函数,打印乘法口诀表,口诀表的行数和列数,用户输入, 
//  例如输入9,打印9*9乘法表,输入12打印12*12口诀表
#include<stdio.h>
void  mul(int a) {
	for (int i = 1; i <= a;i++) {
		for (int j = 1; j <= i;j++) {
			printf("%d * %d = %d ",j,i,j*i);
		}
		printf("\n");
	}
}
int main() {
	int n;
	printf("请输入数字:");
	scanf_s("%d", &n);
	printf("下面是%d*%d乘法表\n",n,n);
	mul(n);
	return 0;
}

案例3

//  用函数递归调用的方式,列出斐波那契数列的前20项
#include<stdio.h>
	int feiBo(int n) {
		if (n==0) {
			return 0;
		}
		else if (n==1) {
			return 1;
		}
		else {
			return feiBo(n - 1) + feiBo(n - 2);
		}
	}
int main() {
	int num;
	printf("请输入数字:");
	scanf_s("%d", &num);
	for (int i = 1; i <=num;i++) {
		int f3 = feiBo(i);
		printf("第%d项的值:%d\n",i, f3);
	}
	return 0;
}

 

案例4 

//超市迎国庆,回馈新老客户,消费满1000元,商品打9.5折;满500元,打9.8折,
// 根据用户消费金额,计算找零信息( 用函数实现)
#include <stdio.h>

// 计算单种商品的花费
double calCost(double price, int quantity) {
    return price * quantity;
}

// 计算总花费并应用折扣
double calTotalCost() {
    double totalCost = 0.0;
    double price;
    int quantity;
    char more;

    do {
        printf("请输入商品单价: ");
        scanf_s("%lf", &price);
        printf("请输入商品数量: ");
        scanf_s("%d", &quantity);

        totalCost += calCost(price, quantity);

        printf("是否还有其他商品?(y/n):");
        scanf_s(" %c",&more);
    } while (more == 'y' || more == 'Y');

    // 应用折扣
    if (totalCost >= 1000) {
        totalCost *= 0.95;
    }
    else if (totalCost >= 500) {
        totalCost *= 0.98;
    }

    return totalCost;
}

// 计算找零
double calChange(double totalCost, double amountPay) {
    return amountPay - totalCost;
}

int main() {
    double totalCost = calTotalCost();
    double amountPay;

    printf("总花费为: %.2f\n", totalCost);
    printf("请输入支付金额: ");
    scanf_s("%lf", &amountPay);

    double change = calChange(totalCost, amountPay);
    printf("找零为: %.2f\n", change);

    return 0;
}

 

10.6、局部变量和全局变量

局部变量

1.定义在函数内部的变量称为局部变量(Local Variable)

2.局部变量的作用域(作用范围)仅限于函数内部, 离开该函数后是无效的

3.局部变量的生命周期:

  • 离开该函数后,局部变量自动释放(函数执行完毕,自动释放)

注意:局部变量使用前,如果没有初始化,会报错

全局变量

1.在所有函数外部定义的变量称为全局变量(Global Variable),

2.它的作用域默认是整个程序,也就是所有的源文件,全局变量的生命周期是永久的,除非手动删除

3.过多的定义全局变量,会造成内存泄漏,工作中尽量避免过多的去定义全局变量

注意:全局变量,使用前如果没有初始化,它会默认初始化为0

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
extern void test();
int num2 = 999;
int main() {
    //局部变量:再函数内部定义的变量, 局部变量在函数执行结束后会自动释放
    int num = 99;//局部变量
    printf("%d\n", num2);
    test();
    return 0;
}
void test() {
    //printf("%d\n", num);//num局部变量,不可以在定义该变量的函数外部使用
    printf("%d\n", num2);//num2全局变量 可以在任何位置使用
}

 

12.数组

12.1、 数组的概念

12.1.1、 数组的概念

1.数组(Array):是多个相同类型数据,按照一定顺序排列的集合,并使用一个名字命名,并通过编号方式对这些数据进行统一管理。

2.数组中几个概念

            数组名

            下标(索引)

            元素

            数组的长度

注意:

1.数组是 C 语言中的一种数据结构,用于存储一组具有相同数据类型的数据。

2.数组中的每个元素可以通过一个索引(下标)来访问,索引从 0 开始,最大值为数组长度减 1。

3.数组是若干个相同类型的变量在内存中有序存储的集合。

12.1.2、数组的特点

  1. 数组中的元素在内存中是依次紧密排列的,有序的。
  2. 创㶳数组对象会在内存中开辟一整块连续的空间。占据的空间大小,取决于数组的长度和数组中元素的类型
  3. 我们可以直接通过下标(或索引)的方式调用指定位置的元素,速度很快。
  4. 数组,一旦初始化完成,其长度就是确定的。数组的长度一旦确定,就不能修改。
  5. 数组名中引用的是这块连续空间的首地址

ps: int型 每个元素 占4个字节 ,如果知道第一个元素的地址,就可以计算出后面元素的地址 (因为空间是连续的)

12.1.3、数组的分类

按元素类型分类:

1.字符数组

即若干个字符变量的集合,数组中的每个元素都是字符型的变量

char s[10]; s[0],s[1]....s[9];

2.短整型的数组

short int a[10];

3.整型的数组

int a[10];

4. 长整型的数组

lont int a[5];

5.浮点型的数组(单、双)

float a[6]; a[4]=3.14f;

double a[8]; a[7]=3.115926;

6.指针数组

char *a[10] int *a[10];

7.结构体数组

struct stu boy[10];

按维数(度)分类

一维数组:存储一组数据

二维数组:存储多组数据,相当于二维表,一行代表一组数据。每一行长度可以不同

三维数组,四维数组.....

12.2、一维数组定义

12.2.1、数组的定义方式1

数组通过变量名后加方括号表示,方括号里面是数组可以容纳的成员数量(即长度)

定义数组的语法格式:

类型 数组名[元素个数];

int arr[10];// 数组 arr ,里面包含10个成员,每个成员都是int 类型

或者

#define NUM 10

int arr1[NUM];

注意:声明数组时,必须给出数组的大小。

  • 数组名不能与其他变量名相同,同一作用域内是唯一的
  • 其下标从0开始,因此5个元素分别为arr[0] arr[1] arr[2] arr[3] arr[4]

12.2.2、数组元素的调用(增、改及取元素)

格式: 数组名[下标]

数组的下标从0开始,用int arr[10] 定义数组,则最大下标值为9,不存在数组元素arr[10]

arr[0]=13;// 对该位置数组元素进行赋值

int score=arr[0];// 调用此位置的元素的值

数组角标越界:

假设数组有n个元素,如果使用的数组的下标小于0,或者大于n-1,就是数组越界访问了,超出了数组合法空间访问。

C语言不做数组下标越界的检查,编译器也不一定报错,但是编译器不报错,并不意味着程序就是正确。

int arr[20];

arr[20]=51;

说明 :数组 arr只有20个成员,因此 arr[20]位置是不存在的。但是,引用这个位置并不会报错。赋值操作会导致紧跟在 arr后面的那块区域被赋值(这实际是其它变量的区域),因此不知不觉就更改了其它变量的值。

这很容易引发错误,而且难以发现。

12.2.3关于长度

数组的字节长度

sizeof运算符会返回整个数组的字节长度。

int arr[10]; printf("数组的字节长度为:%zd\n",sizeof(arr));//40

数组的长度

在定义数组时,需要指定数组中元素的个数,方括号中的常量表达式用来表示元素的个数,即数组长度。

由于数组成员都是同一个类型,每个成员的字节长度都是一样的,所以数组整体的字节长度除以某个数组元素的字节长度,就可以得到数组的成员数量。

//数组中元素的个数
int arrLen=sizeof(arr)/sizeof(arr[0]);
或
int arr[10];
int arrLen=sizeof(arr)/sizeof(int);

PS: sizeof返回值的数据类型是size_t,所以sizeof(a)/sizeof(a[0])的数据类型也是size_t,在printf()里的占位符,要用%zd或%zu. %lld 也行。

12.2.4、数据的遍历

将数组中的每个元素分别获取出来,就是遍历。 for循环与数组遍历是绝配。

需求:声明长度为10的int类型数组,给数组元素依次赋值为 0,1,2,3,4,5,6,7,8,9并遍历数组中所有的元素

#include<stdio.h>
int main() {
        int arr[10];
        //给数组中的每个元素赋值
        for (int i = 0; i < sizeof(arr) / sizeof(int); i++) {
            arr[i] = i;
        }
        //遍历数组中的元素
        printf("遍历数组中的元素:\n");
            for (int i = 0; i < sizeof(arr) / sizeof(int); i++) {
                printf("%d ", arr[i]);
            }
        printf("\n");
        return 0;
    }

12.2.5、数组定义方式2-数组的初始化

数组可以在声明时,使用大括号,同时对每一个成员赋值。

  • 全局数组:若不初始化,编译器将其初始化为零
  • 局部数组:若不初始化,内容为随机值
int arr[5]={10,20,30,40,50};
//等同于
int arr[]={10,20,30,40,50}//数组arr的长度,将根据大括号里面的值的数量,确定为3

变形:

int arr[5]={10,20,30};
//等同于
int arr[5]={10,20,30,0,0};

变形:

int arr[10]={0}

注意:int arr[3]={1,2,3,4};//报错

#include<stdio.h>
int list[9]; //全局数组
int main() {
    //数组的初始化,定义数组的同时,给其赋值
    int arr[5] = { 1,2,3,4,9 };//定义数组的同时,给元素赋值
    float arr2[4] = { 3.12,6.89 };//初始化前三个元素
    int arr4[3];
    printf("%d\n", arr[4]);
    printf("%lf\n", arr2[2]); //没有被初始化的赋值为0;
    printf("%d\n----", arr[90]);//获取数组元素时,超出数组元素个数时,是随机值

    printf("%d\n", arr4[0]);//局部数组不初始化,值为随机数
    printf("%d\n", list[1]);//全局变量不初始化,编译器将其视为0



    //数组初始化时,也可以不写数组个数
    int goods[] = { 90,78,56,34,12 };//定义一个数组,有5个元素
    return 0;
}

 12.3、一级数组的内存分析

12.3.1、 数组内存图

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

对应的内存结构:

说明:

数组名,记录数组的首地址,即a[0]的地址

数组的名个元素是连续分布的,假如a[0] 地址是0x1122, 则 a[1]的地址=a[0]的地址+int字节数(4)=0x1126,后面依次类推

#include<stdio.h>

int main() {
    int arr[8] = { 1,2,3 };
    //数组名是一个常量,代表的是第一个元素的地址
    printf("%p\n", arr);
    printf("%p\n", &arr[0]);

    //sizeof() 获取数组所占内存大小
    printf("%zd", sizeof(arr));  //32  因为数组有8个元素,每个元素都是int 占4个字节   8 *4 =32
    printf("%zd\n", sizeof(arr[0]));  //4
    //计算数组的元素个数
    printf("%zd", sizeof(arr) / sizeof(arr[0]));  //32/4=8 个元素
    return 0;
}

12.3.2 、注意事项

c语言规则,数组变量一旦声明,数组名指向的地址就不可更改。因为声明数组时,编译器会自动为数组分配内存地址,这个地址与数组名是绑定的,不可更改。

因此,当数组定义后,再用大括号重新赋值,是不允许的。下面的代码会报错。

错误1:

int num3[5];

nums={10,20,30,40,50};

// 使用在括号赋值时,必须在数组定义时赋值,否则编译器会报错。

错误2:

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

nums={10,20,30,40,50};// 报错

错误3:

int num3[100];

nums=NULL;

// 报错 相当于把地址给擦除了

错误4:下面的也导致不能将一个数组名赋值给另外一个数组名。

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

//写法一

int b[5]=a;//报错

//写法二

int b[5];

b=a;//报错 上面的写法 都会更改数组b的地址,导致报错。

12.3.3、变长数组

变长数组的根本特征是数组长度只有运行时才能确定 。它的好处是程序员不必在开发时,随意为数组指定一个估计的长度,程序员可以运行时为数组分配精确的长度。

以前的方法是一但定义后就不能改变,要想根据需要处理更多的数据。可以使用以下的方法

动态内存分配:使用malloc、calloc或realloc等函数来动态地分配和重新分配内存。

这允许你在运行时改变内存块(可以看作是一个动态数组)的大小。

方法一:

//c99标准中,支持变长数组    某些编译器可以不支持 变长数组
int length=10;
int arr7[length];
int arr8[length+5];

 方法二:

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>

int main() {
    int i, * arr;
    int n=0;
    printf("请输入要申请数组的个数:");
    scanf("%d", &n);

    //堆区申请 n*sizeof(int)空间   等价于int arr[n]
    arr = (int*)malloc(n * sizeof(int)); //字节的大小   在堆空间开辟的数组空间
    if (arr == NULL) {
        printf("申请空间失败!\n");
        return -1;
    }
    //给数组元素赋值
    for (i = 0; i < n; i++) {
        //给数组赋值
        arr[i] = i + 200;
    }

    //遍历获取数组元素
    for (i = 0; i < n; i++) {
        //输出数组元素
        //printf("%d\n", arr[i]);
        printf("%d\n", *(arr + i));
    }
    //释放堆区空间      使用完动态创建的数组后,一定要回收此数组的内存空间。 否则内存泄露
    free(arr);

    return 0;
}

12.4、一维数组的应用

12.4.1、数值型数组特征统计

这里的特征值涉及到:平均值、最大值、最小值、总和等

例1:定义一个int型的一维数组,包含10个元素,然后求出数组中的最大值,最小值,总和,平均值,并输出来。
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>

int main() {
    int arr[10] = {20,10,5,4,8,40,1,5,8,3};
    int max = arr[0];
    int min = arr[0];
    int sum=0;
    double avg;
    int arrLen = sizeof(arr) / sizeof(int);
    for (int i = 0; i < arrLen;i++) {
        if (max<arr[i]) {
            max = arr[i];
        }
    }
    printf("最大值为:%d\n", max);
    for (int i = 0; i < arrLen; i++) {
        if (min >arr[i]) {
            min = arr[i];
        }
    }
    printf("最小值为:%d\n", min);
    for (int i = 0; i < arrLen; i++) {
        sum += arr[i];
    }
    printf("总和为:%d\n", sum);
    avg = sum / 10.0;
    printf("平均值为:%lf\n", avg);
    return 0;
}

例2:评委打分
分析以下需求,并用代码实现: 
(1)在编程竞赛中,有10位评委为参赛的选手打分,分数分别为:5、4、6、8、9、0、1、2、7、3
(2)求选手的最后得分(去掉一个最高分和一个最低分后其余8位评委打分的平均值)

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>

int main() {
    int score[10] = { 5,4,6,8,10,0,1,2,7,3 };
    int max = score[0];
    int min = score[0];
    int sum=0;
    double avg;
    int scoreLen = sizeof(score) / sizeof(int);
    for (int i = 0; i < scoreLen; i++) {
        if (max < score[i]) {
            max = score[i];
        }
    }
    for (int i = 0; i < scoreLen; i++) {
        if (min > score[i]) {
            min = score[i];
        }
    }
    for (int i = 0; i < scoreLen; i++) {
        sum += score[i];
    }
    printf("总和为:%d\n", (sum-max-min));
    avg = (sum - max - min) / 8.0;
    printf("平均值为:%lf\n", avg);
    return 0;
}

12.4.2、数组的复制

由于数组名是指针,所以复制数组不能简单地复制数组名。

int a[3]={10,20,30};

int *b;

b=a;

上面的写法,结果不是将数组a复制给数组b,而是让a和b指向同一个数组。

正确的方式

  • 循环的方法
  • 用memcpy函数
#include <stdio.h>
#include <stdint.h>
//#include <string.h>
#define LENGTH 3
int main() {
    int a[LENGTH] = { 10,20,30 };
    int b[LENGTH];
         
    //复制数组a到数组b
    /*for (int i = 0; i < LENGTH; i++) {
        b[i] = a[i];
    }*/
 
 
    //使用memcpy函数复制数组a到数组b;
    memcpy(b, a, LENGTH * sizeof(int));
    //打印数组b的内容
    for (int i = 0; i < LENGTH; i++) {
        printf("%d ", b[i]);
    }
    printf("\n");
    return 0;
}

两种方式对比:

1. 循环复制:

优点: 简单直观,容易理解和实现。不需要引入额外的头文件。

缺点:需要编写循环代码来遍历数组并逐个赋值,相对而言可能稍显繁琐。不适用于复制大型数组或复杂数据结构。

2.memery函数复制:

优点:使用标准库提供的函数,可以实现快速且高效的内存复制。适用于大型数组或复杂的数据结构的复制。可以直接复制字节数,不需要遍历数组。

缺点:需要包含头文 件,对于简单的数组复制,可能有些过于繁重。

12.4.3、 数组元素的反转

实现思想:数组对称位置的元素互换。

方式1:

#include <stdio.h>
//#include <stdint.h>
//#include <string.h>
#define LENGTH 9
int main() {
    int a[LENGTH] = { 10,20,30,40,50,60,70,80,90 };
    //反转  a[i]  与a[LENGTH - 1 - i]
    for (int i = 0; i < LENGTH / 2; i++) {
        int temp = a[i];
        a[i] = a[LENGTH - 1 - i];
        a[LENGTH - 1 - i] = temp;
    }
        
    //打印数组b的内容
    for (int i = 0; i < LENGTH; i++) {
        printf("%d ", a[i]);
    }
    printf("\n");
    return 0;
}

方式2:

 #include <stdio.h>
#define LENGTH 9
int main() {
    int a[LENGTH] = { 10,20,30,40,50,60,70,80,90 };
    int i = 0;  // 首元素下标
    int j = 8;   // 尾元素下标
    int temp;

    while (i < j) {
        // 元素交换值
        temp = a[i];
        a[i] = a[j];
        a[j] = temp;
        // 位置移动
        i++;
        j--;
    }
    for (i = 0; i < LENGTH; i++) {
        printf("%d, ", a[i]);
    }

    return 0;
}

12.4.4、数组作为函数参数进行传递

数组作为参数的注意事项:

1、数组名作为函数实参时,函数定义处作为接收参数的数组类型形参既可以指定数组长度,也可以不指定数组长度

2、数组元素作为函数实参传递时,数组元素类型必须和形参数据类型一致

#include<stdio.h>
//定义一个函数,让数组逆置
int   getReverse(int a[], int length) {
    int i = 0;  // 首元素下标
    int j = length - 1;   // 尾元素下标
    int temp;

    while (i < j) {
        // 元素交换值
        temp = a[i];
        a[i] = a[j];
        a[j] = temp;
        // 位置移动
        i++;
        j--;
    }
    for (i = 0; i < length; i++) {
        printf("%d, ", a[i]);
    }
    return 0;

}
//定义一个函数,对数组元素求和******
int getSum(int a[5]) {
    int sum = 0;//用来存和
    for (int i = 0; i < 5; i++) {
        sum += a[i];
    }
    return sum;
}



//定义一个数组,用来求数组最大值
//函数封装的时候,如果不指定数组元素的个数,需要传递第2个参数,表示数组的长度*******
int getMax(int arr[], int length) {
    int max = 0;//用来存储最大值
    for (int i = 0; i < length; i++) {
        if (arr[i] >= max) {
            max = arr[i];
        }
    }
    return max;
}
//定义一个函数,将数组元素输出
void temp(value) {
    printf("%d\n", value);
}

int main() {
    //数组作为函数的参数进行传递:
    //案例1、封装个函数,进行数组求和
    int arr[5] = { 1,200,3,4,5 };
    getSum(arr);
    printf("数组元素的和是:%d\n", getSum(arr));


    //案例2、定义一个函数,求已有数组的最大值
    int length = sizeof(arr) / sizeof(arr[0]);
    printf("数组中最大值为:%d\n", getMax(arr, length));

    //案例3、定义一个函数,求数组逆置
    int len = sizeof(arr) / sizeof(arr[0]);
    getReverse(arr, len);

    //案例4、数组元素也可以作为函数参数进行传递
    temp(arr[1]);

    return 0;
}

  • 12
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
城市应急指挥系统是智慧城市建设的重要组成部分,旨在提高城市对突发事件的预防和处置能力。系统背景源于自然灾害和事故灾难频发,如汶川地震和日本大地震等,这些事件造成了巨大的人员伤亡和财产损失。随着城市化进程的加快,应急信息化建设面临信息资源分散、管理标准不统一等问题,需要通过统筹管理和技术创新来解决。 系统的设计思路是通过先进的技术手段,如物联网、射频识别、卫星定位等,构建一个具有强大信息感知和通信能力的网络和平台。这将促进不同部门和层次之间的信息共享、交流和整合,提高城市资源的利用效率,满足城市对各种信息的获取和使用需求。在“十二五”期间,应急信息化工作将依托这些技术,实现动态监控、风险管理、预警以及统一指挥调度。 应急指挥系统的建设目标是实现快速有效的应对各种突发事件,保障人民生命财产安全,减少社会危害和经济损失。系统将包括预测预警、模拟演练、辅助决策、态势分析等功能,以及应急值守、预案管理、GIS应用等基本应用。此外,还包括支撑平台的建设,如接警中心、视频会议、统一通信等基础设施。 系统的实施将涉及到应急网络建设、应急指挥、视频监控、卫星通信等多个方面。通过高度集成的系统,建立统一的信息接收和处理平台,实现多渠道接入和融合指挥调度。此外,还包括应急指挥中心基础平台建设、固定和移动应急指挥通信系统建设,以及应急队伍建设,确保能够迅速响应并有效处置各类突发事件。 项目的意义在于,它不仅是提升灾害监测预报水平和预警能力的重要科技支撑,也是实现预防和减轻重大灾害和事故损失的关键。通过实施城市应急指挥系统,可以加强社会管理和公共服务,构建和谐社会,为打造平安城市提供坚实的基础。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值