初入前端的北京某211大一非科班生(没错上学期还是日语生)的C语言自学笔记
本文约8k字,将介绍:编程基础 数据类型 表达式 语句与控制流 函数 数组,指针 结构体等会随着学习进度推进持续更新~
学习C语言的动机:大一上学期学完 html5 css3 javascript 后,程序对我要求掌握一定的数据结构与算法,而数据结构教材完全是以C语言讲解的,恰好本学期专业课有开展C程序设计语言,顺水推舟地就产生了这篇文章了。当然,绝大部分内容是基于我在csdn上阅读前人的经验所学习的,因此本文档可能更是一篇学习总结。
感谢你的阅读,让我们开始C程序设计语言入门的学习
编程
让计算机为解决某个问题 而使用某种 程序设计语言编写程序代码 并最终得到结果 的过程
计算机程序
计算机所执行的一系列的 指令集合
计算机语言
机器语言、汇编语言、高级语言
机器语言: 计算机最终执行的语言 由0和1组成的二进制数 二进制是计算机语言的基础
编程语言: 控制计算机的指令 有固定格式和词汇
分为汇编语言和编程语言
汇编语言: 和机器语言实质相同 都是直接对硬件操作 指令采用英文缩写的标识符
高级语言: C C++ Java C# Python PHP JavaScript Go语言 Objective-C Swift
编程语言和标记语言(html)区别
编程语言 很强的逻辑性和行为能力 诸如if else, for, while等具有逻辑性和行为能力的指令 主动
标记语言(html) 不用于向计算机发出指令 常用于格式化和链接 标记语言的存在是用来被读取 被动
翻译器
高级语言所编制的程序不能直接被计算机识别 必须经过转换才能被执行
翻译器:源代码——>机器语言 这一步也被称为 二进制化
数据类型
数据类型规定如何解读数据
-
int 整型 字节4 -215 ~ 215-1
-
char 字符型 字节1
-
short 短整型 字节2 -215 ~ 215-1
-
long 长整型 字节8 -231 ~ 231-1
-
unsigned int 无符号整型 字节4 0 ~ 216-1
-
float 单精度型 字节4 -3.4x10-38 ~ 3.4x1038
-
double 双精度型 字节8 -1.7x10-308 ~ 1.7x10308
-
long double 长双精度型 字节16 -1.2x10-4932 ~ 1.2x104932
-
void* 空类型 字节8
// c语言中不存在字符串变量 字符串只能存在于数组中
表达式
表达式是由**操作数(操作对象)和运算符(操作符)**组成的式子,表达式是指具有完整意义的计算机指令
表达式的分类:一元 二元 三元
基础运算
四则运算
比较运算(关系运算)
< > <= >= == !=
float\double 无法精确判等 存在精度问题
c语言中没有布尔型 真:非0 假:0
位(bit)运算
<< >> 位移运算(快于四则运算) 溢出则丢弃 左移后空缺补0
位运算不支持float\double
逻辑运算(&& || !)按位运算(& | !) 不一样 逻辑运算有真假(非0和0)
按位与(&): 十进制转换为二进制,上下比较,有0则0,两个都是1才1
按位或(~): 上下比较有1则1,两个都是0才0
按位异或(^): 上下比较,相同为0,不同为1
按位左移(<<): 二进制向左移一位,溢出丢弃,右边补零
按位右移(>>): 二进制向右移一位,右边丢弃,左边补原符号位
赋值表达式 += -= *= &= %=
三元表达式 a?b:c if a the b else c
部分单目操作符
&: 每次使用scanf函数时,总要在变量名前加上&,&变量a的意思是取变量a的地址,这是&的作用之一
~
: 对一个数的二进制按位取反
*
: 间接访问操作符(解引用操作符)
(类型): 强制类型转换
格式化输出语句 (占位输出)
将各种类型的数据按照格式化后的类型及指定位置从计算机上显示
printf("输出格式符", 输出项);
`// %d 带符号十进制整数
int a = 10;
printf("%d", a);
// %c 单个字符
char x = 'a';
printf("%c", x);
// %s 字符串
printf("%s", "string");
// %f 6位小数
float a = 1.23;
printf("%f", a);
格式符的个数要与变量、常量或者表达式个数一一对应
int a = 10;
float b = 7.56;
char x = 'c';
printf("%d, %f, %c", a, b, x);
类型:
d 有符号10进制整型
i 有符号10进制整型
u 无符号10进制整型
o 无符号8进制整型
x 无符号8进制整型
X 无符号16进制整型
f 单、双精度浮点数(默认保留6位小数)
e/E 以指数形式输出单、双精度浮点数
g/G 以最短输出宽度,输出单、双精度浮点数
c 字符
s 字符串
p 地址
标志和精度:
-
左对齐(默认是右对齐)
+
当输出值为正数时,在输出值前面加一个 + (默认不显示)
0
右对齐时,用0填充宽度(默认用空格填充)
空格
输出值为正数时,在输出值前面加上空格,为负数时加负号
#
对c/s/d/u类型无影响 对o类型在输出时加前缀o,对x类型输出时加前缀0x
printf("%.2f", a);
保留2位小数
printf("%.*f", n, a);
动态指定保留小数位数n
int a = 1;
int b = 10;
printf("a = |%5d|\n", a); // | 1|
printf("a = |%-5d|\n", a); // |1 |
printf("a = |%+d|\n", a); // |+1|
printf("a = |%05d|\n", a); // |00001|
printf("a = |% d|\n", a); // | 1|
printf("b = %#x\n", b); // 0xa
double c = 3.1415;
printf("c = %.2f\n", c); // 3.14
printf("c = %.*f\n", 3, c); // 3.142
Scanf函数
scanf函数用于接收键入的内容,是阻塞式函数(程序会停在scanf出现的地方,知道接收到数据才会执行后面的代码)
scanf运行原理:系统会将用户输入的内容先放入输入缓冲区,scanf方式会从输入缓冲区中逐个取出内容赋值给变量,如果输入缓冲区内容不为空,scanf会一直从缓冲区中获取,而不要求再次输入
// scanf("格式控制字符串", 地址列表);
int number;
scanf("%d", &number); // 接收一个整数
printf("number = %d\n", number); // 键入2就输出2,不键入输出0
int str;
scanf("%c", &str); // 接收一个字符
printf("str = %c\n", str);
int num2, str2;
scanf("%d,%c", &num2, &str2); // 键入 44,d
printf("%d,%c", num2, str2); // 打印 44,d
- 接收非字符和字符串类型时,空格、tab、回车会被忽略
- \n是scanf函数的结束符号,所以scanf函数中格式化字符串中不能出现\n
不可改变常量
常量: 在程序执行过程中,值不发生改变的量
c语言的常量分为 直接常量和符号常量
- 直接常量(字面量): 可以直接拿来使用无需说明的量
- 整型常量: 13、0
- 实型常量: 13.33
- 字符常量: ‘a’
- 字符串常量: “Hello World!”
-
**符号常量: 用一个标识符来表示一个常量。符号常量使用前必须先定义。**且不可被改变
由于预先对标识符进行了定义,在程序的预处理阶段, 编译器会将所有定义的标识符替换成相应的内容并生成 .i 文件
宏
// #define 标识符 常量值
// #define N 100
// 编译器在预处理程序时,会将程序中所有的N用100来替换。换言之,不同于const常量,define本质上是对文本内容的替换
// #define MAX 100 // 将 MAX 这个标识符和 100 这个数字关联起来
// #define REG register // 为 register 这个关键字,创建一个简短的名字REG
// #define STR "test_string" // 用 STR 这样一个名字来代替 test_string 这样一个字符串
#include <stdio.h>
#define PI 3.14
#define S(r) PI*r*r // 定义在主函数前 不要加分号
int main(void)
{
printf("area = %f", S(1+2)); // 3.14*1 + 2*1 + 2 = 7.14 直接把r替换成1+2不用加括号
// 这实际上,宏就是替换,并且是相当直接的替换
return 0;
}
#define定义标识 符的本质就是替换
define 定义表达式时要注意**“边缘效应”**
#define N 1+2
float a = N/2.0;
/*
按照常规做法,可能会认为结果是3/2 = 1.5
但是实际上,结果应该为 1+2/2.0 = 2.0
若想要实现3/2,则#define N (1+2)
即为避免边缘效应,一定要加!括!号!
*/
#define 机制包括了一个规定,允许把参数替换到文本中,这种实现通常称为宏(macro)或定义宏 (define macro)
#define 定义宏可分为两种
- 一种是不带参数的宏定义,这也就是第1小节提到的使用#define定义标识符
- 第二种是带参数的宏定义,其定义格式如下
// #定义 宏名(参数表) 内容
#define name( parament-list ) stuff
其中,parament-list是一个由逗号隔开的符号表,它们可能出现在stuff中
#define ADD(a, b) (a + b) // 宏的参数有两个a和b 这个宏所要实现的功能是将a和b相加
宏的使用和C语言中的函数类似,都需要进行传参。在main函数中两者的实现方式几乎是一样的,但具体的实现方式却很不一样
int ADD(int x, int y)
{
return x + y;
}
// #define ADD(a, b) a + b
int main()
{
// ...
}
宏定义的程序中,预处理器会将ADD(a, b)替换成(a + b)。宏定义是将a+b这样一个求和操作重新命名并置于main函数中,而ADD函数是通过函数调用来实现求和,使用了新的函数栈帧
#define替换规则:
- 在调用宏时,首先对参数进行检查,看看是否包含任何由#define定义的符号。如果是,它们首先被替换
- 替换文本随后被插入到程序中原来文本的位置。对于宏,参数名被它们的值所替换
- 最后再次对结果文件进行扫描,看看它是否包含任何由#define定义的符号,如果是,就重复上述过程
注意:
- 参数列表的左括号必须与name紧邻。如果两者之间有任何空白存在,参数列表就会被解释为stuff的一部分
- 宏参数和#define定义中 可以出现其他#define定义的符号
- 对于宏,不能出现递归
- 当预处理器搜索#define定义的符号的时候,字符串常量的内容并不被搜索
宏和函数的对比:
- 宏名全部大写,函数名不要全部大写
- 宏比函数在程序的规模和速度方面更甚一筹
- 函数的参数必须声明为特定的类型,所以函数只能在类型合适的表达式上使用。宏是类型无关的,不够严谨
- 每次使用宏的时候,一份宏定义的代码将插入到程序中。除非宏比较短,否则可能大幅度增加程序的长度
- 宏无法调试
自动类型转换
自动转换发生在不同数据类型运算时,在编译的时候自动完成
char转换为int遵循ASCII码中的对应值
- 字节小的可以向字节大的自动转换,但字节大的不能向字节小的自动转换
- char -> int int -> double char -> double
强制类型转换
通过定义类型转换运算来实现 一般形式为:
// (数据类型) (表达式)
其作用是把表达式的运算结果强制转换成类型说明符所表示的类型
注意:
- 数据类型和表达式都必须加括号。如把
(int)(x/2+y)
写成(int)x/2+y
则成了把x
转换成int
型之后再除2
再与y
相加了 - 转换后不会改变原数据的类型及变量值,只在本次运算中临时性转换
- 强制转换后的运算结果不遵循四舍五入的原则
运算符号
c语言中的运算符: 算术、赋值、关系、逻辑、三目运算符
算术运算符:+ - * / % ++ - -
注意:
- 除法中 两个数有一个为小数则结果为小数 如 9.0/2 = 4.500000
- 取余中 运算后的符号取决于被模数的符号 如(-10)%3 = -1,10%(-3) = 1
关系运算符: > >= == !=
逻辑运算符: &&逻辑与 ||逻辑或 !逻辑非
逻辑运算的值也是有两种分别为真和假,C语言中用整型的1和0来表示
循环结构之for循环
for(表达式1; 表达式2; 表达式3)
{
// 执行代码块
}
注意:
- 表达式123均可不写为空 但分号不能省
- 省略表达式2(循环条件) 或 表达式3(循环变量增减量) 死循环
如何获得一个数的百位十位个位
- 百位数: num/100 因为int整数型 765/100=7
- 十位数: num%100/10 765%100 = 65 65/10 = 6
- 个位数: num%10 765%10 = 5
break与continue
- 在没有循环结构的情况下,break不能用在单独的ifelse语句中
- 在多层循环中,一个break只跳出当前循环
switch
switch(表达式){
case 常量表达式1: 执行代码块1 break;
……
case 常量表达式n: 执行代码块n break;
default: 执行代码块n+1;
break;
}
- switch后面的表达式语句只能是整型或字符类型
- case后面允许有多个语句,可以不用{}括起来
- 各case和default子句的先后顺序可以变动,不会影响程序执行结果
- default子句可以省略不用
函数
定义: 函数是子程序,子程序是一个大型程序中的某部分代码,它相较于其他代码具备相对的独立性。一般会由输入参数并由返回值,提供对过程的封装和细节的隐藏
C语言提供了大量库函数,比如stdio.h提供输出函数
自定义函数的一般形式:
[数据类型说明] 函数名称 ([参数])
{
执行代码块;
return (表达式);
}
int main(){...}
- [] 包含的内容(数据类型说明)可以省略 省略了默认是int类型函数
- 参数省略表示该函数是无参函数,参数不省略表示该函数是有参函数
- 自定义函数尽量放在main函数前。如果要放在main函数后面的话,需要在main函数之前先声明自定义函数,声明格式为
[数据类型说明] 函数名称 ([参数])
函数调用
函数名 ([参数]);
- 对无参函数的调用可省略[ ]中的内容
- []中可以是常数,变量或其他构造类型数据及表达式,多个参数之间用逗号分隔
有参与无参
// 无参函数
[数据类型说明] 函数名称 ()
{
// ...
return 表达式;
}
// 有参函数
[数据类型说明] 函数名称 (参数列表) // 有参无参唯一区别在于()中多了 参数列表
{
// ...
return 表达式;
}
有参函数相对灵活,输出内容可以随着n的变化随意变动,只要在main函数中传递一个参数就可以了。而无参函数中输出的相对固定,当需要改动的时候还需要到自定义方法内改变循环变量的值
形参和实参
形参:目的是用来接收调用该函数时传入的参数。只有在被调用时才分配内存单元,在调用结束时即刻释放所分配的内存单元。因此,形参只有在函数内部有效
实参:调用时传递该函数的参数。实参可以是常量、变量、表达式、函数等。应预先用赋值等办法使实参获得确定值。
在参数传递,实参和形参在数量上、类型上、顺序上应严格一致,否则会发生类型不匹配的错误
#include <stdio.h>
int Function(int x) { // x是形参
return x*2;
}
int main() {
int x = 10; // 定义变量x
// 下面的x是实参 它其实是main函数中定义的变量
printf("x=%d\n", Function(x));
return 0;
}
函数的返回值
返回值:函数被调用后,执行函数体中的程序段所取得的 并返回给主调函数的值
- 函数的值只能通过return语句返回主调函数
- 函数值的类型和函数定义中函数的类型应保持一致,若不一致则以函数返回类型为准
- 没有返回值的函数,返回类型为void
- void函数中可以有执行代码块,但不能有返回值。void函数中如果有return语句,该语句只能起到结束函数运行的功能,其格式为
return;
递归函数
递归就是一个函数在它的函数体内调用自身。执行递归函数将反复调用其自身,每调用一次就进入新的一层。递归函数必须有结束条件
递归函数特点:
- 每一级函数调用时都有自己的变量,但是函数代码并不会得到复制。如计算5的阶乘时每递推一次变量都不同
- 每次调用时都会有一次返回,如计算5的阶乘时每递推一次都返回进行下一次
- 递归函数中,位于递归调用前的语句和各级被调用函数具有相同的执行顺序
- 递归函数中,位于递归调用后的语句的执行顺序和各个被调用函数的顺序相反
- 递归函数中必须有终止语句
因此,递归函数是 自我调用且有完成状态
// 递归demo 有5个人坐在一起,问第5个人多少岁?他说比第4个人大2岁。问第4个人岁数,他说比第3个人大2岁。问第3个人,又说比第2人大两岁。问第2个人,说比第1个人大两岁。最后 问第1个人,他说是10岁。请问第5个人多大?
// 程序分析:利用递归的方法,递归分为回推和递推两个阶段。要想知道第5个人岁数,需知道第4人的岁数,依次类推,推到第1人(10岁),再往回推。
#include <stdio.h>
int dfs(int n) {
return n == 1 ? 10 : dfs(n - 1) + 2;
}
int main()
{
printf("第5个人的年龄是%d岁", dfs(5));
return 0;
}
局部与全局
变量按作用域范围可分为局部变量和全局变量
- 局部变量(内部变量)作用域仅限于函数内,离开该函数后再使用这种变量是非法的
- 全局变量(外部变量)定义在函数外部的变量,它不属于任何函数,它属于一个源程序文件,其作用域是整个源程序
在所有函数之外声明的函数叫做全局变量
变量存储类别
C语言根据变量的生存周期来划分,可以分为静态存储方式和动态存储方式
- 静态存储方式:在程序运行期间分配固定的存储空间。静态存储区中存放了在整个程序执行过程中都存在的变量,如全局变量。静态存储是只读的,存放的是指令
- 动态存储方式:在程序运行期间根据需要进行动态分配存储空间。动态存储区中存放的变量是根据程序运行的需要而建立和释放的,通常包括:函数形式参数、自动变量、函数调用时的现场保护和返回地址等
C语言中存储类别又分为四类:
- 自动(auto)
- 静态(static)
- 寄存器的(register)
- 外部的(extern)
-
用关键字auto定义的变量为自动变量,auto可省略,auto不写则隐含定为“自动存储类别",属于动态存储方式
int fn(int a) { // 定义fn函数, a为参数
auto int b, c; // 定义b,c为 自动变量
}
-
用static修饰的为静态变量,如果定义在函数内部称为静态局部变量,定义在函数外部称为静态外部变量
#include <stdio.h> void fn() { static int i = 0; // static修饰局部变量 i++; printf("%d\n", i); } int main() { int i; for(i=0; i<10; i++) { fn(); } return 0; } // 输出: 1 2 3 ... 10 // 函数内的局部变量并没有每次调用都是为变量i开辟内存空间,而是将变量i的值保存了下来
由此可见:static修饰局部变量改变了变量的生命周期,让静态局部变量超出了作用域后依然存在,直到程序结束时,生命周期才结束
注意:静态局部变量属于静态存储类别,在静态存储区内分配存储单元,在程序整个运行期间都不释放;静态局部变量在编译时赋初值,即只赋初值一次;如果在定义局部变量时不赋初值的话,则对静态局部变量来说,编译时自动赋初值0(对数值型变量)或空字符(对字符变量)
另外,static修饰全局变量和修饰函数的时候,这个全局变量/函数只能在本源文件中使用,不能在其他源文件内使用
-
为了提高效率,C语言允许将局部变量的值存放在CPU的寄存器内,这种变量叫做“寄存器变量(register)”
void fn() { register int i; // 定义i为寄存器类型变量 }
只有局部自动变量和形式参数可以作为寄存器变量;不能定义任意多个寄存器变量;局部静态变量不能定义为寄存器变量
-
外部变量用extern声明,外部变量的意义是某函数可以调用在该函数之后定义的变量
#include <stdio.h> int main() { extern int x; // 这里声明使用的是外部全局变量 printf("extern x=%d\n", x); return 0; } int x = 100;
内部函数与外部函数
- 在C语言中不能被其他源文件调用的函数称为内部函数,内部函数由static关键字定义,又被称为静态函数,
static[数据类型]函数名([参数])
- static是对函数的作用范围的一个限定,限定该函数只能在其所处的源文件中使用,因此在不同文件中出现相同的函数名称的内部函数是没有问题的
- 在C语言中能被其他源文件调用的函数称为外部函数,外部函数由extern定义,形式同上
- 在没有指定函数的作用范围时,系统默认是外部函数,因此当需要定义外部函数时extern也可以省略
// 外部函数demo
// main.c
#include <stdio.h>
void main() {
extern void test(); // 调用在其他函数中定义的三个函数
...
}
// test.c
#include <stdio.h>
void test() {
// 定义外部函数test
}
数组
数组初始化
数组在程序中是一块连续的,大小固定并且里面的数据类型一致的内存空间,数组初始化的语法:
int arr[3] = {1, 2, 3};
int arr2[] = {1, 2, 3};
// 获取数组元素:arr[0];
- 数组初始化时,数组内元素个数 <= 声明的数组长度,并且多余的数组元素初始化为0
- 在声明数组后没有进行初始化的时候,静态(static)和外部(extern)类型的数组元素初始化元素为0,自动(auto)类型的数组的元素初始化值不确定
数组的遍历
int arr(3) = {1, 2, 3};
int i;
for (i=0; i<=2; i++)
{
printf("%d\n", arr[i]);
}
return 0;
- 循环变量不要超出数组的长度
- C语言的数组长度一经声明就是固定,无法改变
- C语言不提供计算数组长度的方法
// 获取数组长度
int length = sizeof(arr)/sizeof(arr[0]);
数组作为函数参数
数组可以由整个数组当作函数的参数,也可以由数组中的某个元素当作函数的参数
// 整个数组当作函数参数 即**把数组名称传入函数**中
#include <stdio.h>
void temp(int arr[]) { // 函数没有返回值或函数无参数 声明为void h
int i;
for (i=0; i<=4; i++) {
printf("%d\n", arr[i]);
}
}
int main() {
int arr[5] = {1, 2, 3, 4, 5};
temp(arr); // 传数组
return 0;
}
// 数组中的元素当作函数参数,即把数组中参数传入函数中
#include <stdio.h>
void temp(int arrValue) {
printf("%d\n", arrValue);
}
int main() {
int arr[5] = {1, 2, 3, 4, 5};
temp(arr[3]); // 传数组中的参数
return 0;
}
- 数组名作为函数实参传递时,函数定义处 作为接收参数的数组类型形参 既可以指定长度也可以不指定长度
- 数组元素作为函数实参传递时,数组元素类型必须与形参数据类型一致
例1:冒泡排序
// 冒泡排序
#include<stdio.h>
void Function(int arr[]) {
int i, j, length;
length = sizeof(arr);
printf("arr's length is %d", length);
printf("\nbefore: ");
for(i=0; i<length; i++) {
if(i != length-1) {
printf("%d, ", arr[i]);
} else {
printf("%d", arr[i]);
}
}
for(i=0; i<length-1; i++) {
for(j=0; j<=i; j++) {
if(arr[j]<arr[j+1]) {
int temp;
temp = arr[j];
arr[j] = arr[j+1];
arr[j+1] = temp;
}
}
}
printf("\nafter: ");
for(i=0; i<length; i++) {
if(i != length-1) {
printf("%d, ", arr[i]);
} else {
printf("%d", arr[i]);
}
}
}
int main()
{
int arrTest[] = {0, 5, 6, 2, 9, 4, 3, 1};
Function(arrTest);
return 0;
}
例2:数组查找
#include <stdio.h>
int getIndex(int arr[5], int value) { // value是要查询的元素
int i, index; // index将是查询元素的下标 元素不存在则赋值-1
for(i=0; i<5; i++) {
if(arr[i]==value) { // 某个元素==查询的元素
index = i; // 获得查询元素的下标
break;
}
index = -1;
}
return index;
}
int main() {
int arr[5]={3,14,64,2,8}; // 实参1
int value = 8; // 实参2
int index = getIndex(arr, value); // 调用函数并传参:声明返回值=函数(实参)
if(index!=-1)
printf("%d存在, 下标为%d\n", value, index);
else
printf("%d不存在\n", value);
return 0;
}
字符串与数组
C语言中无法直接定义字符串数据类型,但是我们可以使用数组来定义我们想要的字符串
// 定义
char stringName[length] = "字符串值";
char stringName[length] = {'字符1', '字符2', '字符n', '\0'};
// 输出
printf("%s", stringName);
puts(stringName);
- []中的length可以省略
- 采用第二种方式时最后一个元素必须是’\0’,'\0’表示字符串的结束标志
- 采用第二种方式时数组种不能写中文
字符常量和字符串常量
C语言中用单引号表示字符常量,双引号表示字符串常量
字符常量: 用一对单引号括起来的一个字符,如’a’, ‘9’, ‘!’ ,字符常量中的单引号只起定界作用,并不表示字符本身。一个字符常量是一个整数
字符串常量: 字符串常量是用双引号括起来的多个字符的序列,字符串本质上是多个字符组成的字符数组。在每个字符串常量的结尾,系统都会自动加一个字符’\0’作为该字符串的结束标识符,系统据此判断字符串是否结束
字符串函数
- strlen(s) 获取字符串长度
// 注意: 字符串中 汉字和字母的长度不一样
char str1[] = "栖夜";
char str2[] = {'i', 'j', 'k', '\0'};
printf("两者长度分别为%d\t%d\n", strlen(str1), strlen(str2)); // 6 3
-
strcmp(s1, s2) 把字符串转换成ASCII码后比较
返回值0 表示s1和s2的ASCII码相等
返回值1 表示s1比s2的ASCII码大
返回值-1 表示s1比s2的ASCII码小
char str1[] = "a";
char str2[] = "b";
printf("结果是%d\n", strcmp(str1, str2)); // -1
- strcpy(s1, s2) 字符串拷贝 拷贝后会覆盖原字符串且不能对字符串常量进行拷贝
char str1[] = "js";
printf("%s\n", strcpy(str1, "vue")); // vue
char str2[] = "C语言";
strcpy(str2, "C++");
printf("%s\n", str2); // c++
- strcat(s1, s2) 把字符串s2拼接到s1后。strcat在使用时s1与s2指的内存空间不能重叠,且s1要有足够的空间来容纳要复制的字符串
char str1[] = "hello ";
char str2[] = "world";
strcat(str1, str2);
printf("%s", str1); // hello world
多维数组
定义一个二维数组 其中第一个[3]表示第一维下标的长度 第二个[3]表示第二维下标的长度
int num[3][3] = {{1,2,3},{4,5,6},{7,8,9}};
多维数组初始化必须指定列的维数,因为系统会根据数组中元素的总数来分配空间,当知道元素总数以及列的维数时,会自动计算出行的维数
多维数组遍历(循环嵌套)
综合练习
#include <stdio.h>
#define N 10
// 打印分数
void printScore(int score[]) // void函数中不需要有返回值
{
int i;
printf("\n");
for(i=0; i<N; i++)
{
printf("%d ", score[i]);
}
printf("\n");
}
// 计算总分
int getTotalScore(int score[])
{
int sum = 0;
int i;
for(i=0; i<N; i++)
{
sum += score[i];
}
return sum;
}
// 计算平均分
int getAvgScore(int score[])
{
return getTotalScore(score)/N;
}
// 计算最高分
int getMax(int score[])
{
int max = -1;
int i;
for(i=0; i<N; i++)
{
if(score[i]>max)
{
max = score[i];
}
}
return max;
}
// 最低分
int getMin(int score[])
{
int min = 100;
int i;
for(i=0; i<N; i++)
{
if(score[i]<min)
{
min = score[i];
}
}
return min;
}
// 分数降序排序
void sort(int score[])
{
int i, j;
for(i=0; i<N-2; i++)
{
for(j=0; j<=i; j++)
{
if(score[j]<score[j+1])
{
int temp;
temp = score[j];
score[j] = score[j+1];
score[j+1] = temp;
}
}
}
printScore(score);
}
int main()
{
int score[N]={67,98,75,63,82,79,81,91,66,84};
int sum, avg, max, min;
sum = getTotalScore(score);
avg = getAvgScore(score);
max = getMax(score);
min = getMin(score);
printf("总分是: %d\n", sum);
printf("平均分是: %d\n", avg);
printf("最高分是: %d\n", max);
printf("最低分是: %d\n", min);
printf("------------成绩排名------------\n");
sort(score);
return 0;
}
更新于2023.3.14
(未完待续…下一章:指针)