工作需要近期接触 C++ 利用空闲时间整理输出一下个人总结,希望给一起学习的伙伴们分享一下学习历程,也是鞭笞自己不能有所放松.因个人能力有限,如有错误的地方或是意见还请留言指出,谢谢
C语言基础 (一)
1.C语言概述
1.1 第一个C语言程序:hello world
1.1.1 编写C语言代码:hello.c
#include <stdio.h> //包含头文件 std标准库 io入输出库
int main(void)
{
printf("hello world! \n");
return 0;
}
1.1.2 通过gcc编译C代码
编译命令格式:
gcc [-option1] ... <filename>
g++ [-option1] ... <filename>
- 命令、选项和源文件之间使用空格分隔
- 一行命令中可以有零个、一个或多个选项
- 文件名可以包含文件的绝对路径,也可以使用相对路径
- 如果命令中不包含输出可执行文件的文件名,可执行文件的文件名会自动生成一个默认名,Linux平台为a.out,Windows平台为a.exe
1.1.3 代码分析
- include头文件包含
- include的意思是头文件包含,#include <stdio.h>代表包含stdio.h这个头文件
- include< > 与 #include ""的区别:
- < >表示系统系统库
- " "标志自定义库
- main函数
- 一个完整的C语言程序,是由一个、且只能有一个main()函数(又称主函数,必须有)和若干个其他函数结合而成(可选)。
- main函数是C语言程序的入口,程序是从main函数开始执行。
- {}括号,程序体和代码块
- {}叫代码块,一个代码块内部可以有一条或多条语句
- C语言美剧可执行代码都是";"号结尾
- 所有的#开头的行都是预编译指令,预编译指令行结尾是没有分号的
- 所有的可执行语句必须是在代码块里面
- 注释
- // 行注释
- /* 内容 */ 块注释
- printf函数
- printf(“hello wotld\n”); // \n的意思是换行`
- return 语句
- return 代表函数执行完毕,返回 return 代表函数的终止
- 函数的返回值要与main定义前面的类型相对应,如代码中 return 0 和 int main()
1.2 system 函数
1.2.1 system 函数的使用
#include <stdlib.h>
int system(const char *command);
// 功能: 在已经运行的程序中执行另一个外部程序
// 参数: 外部可执行程序名字或命令
// 返回值: 成功:不同系统返回值不一样 失败:通常为 -1
示例代码:
#include <stdio.h>
#include <stdlib.h>
int main()
{
//system("calc"); //windows平台
system("ls"); //Linux平台, 需要头文件#include <stdlib.h>
return 0;
}
1.3 C 语言编译过程
1.3.1 C语言编译步骤
C代码编译成可执行程序经过4步:
1)预处理:宏定义展开、头文件展开、条件编译等,同时将代码中的注释删除,这里并不会检查语法
2)编译:检查语法,将预处理后文件编译生成汇编文件
3)汇编:将汇编文件生成目标文件(二进制文件)
4)链接:C语言写的程序是需要依赖各种库的,所以编译之后还需要把库链接到最终的可执行程序中去
1.3.2 gcc 编译过程
-
分布编译
预处理:gcc -E hello.c -o hello.i
编 译:gcc -S hello.i -o hello.s
汇 编:gcc -c hello.s -o hello.o
链 接:gcc hello.o -o hello_elf选项 含义
-E 只进行预处理
-S(大写) 只进行预处理和编译
-c(小写) 只进行预处理、编译和汇编
-o file 指定生成的输出文件名为 file -
一步编译
gcc hello.c -o demo
1.3.3 找找程序所依赖的动态库
-
Linux平台下,ldd可执行程序,如:ldd hello
-
Windows平台下,需要相应的软件(Depends.exe)
-
数据类型
2.1 常量与变量
2.1.1关键字
C的关键字共有32个
- 数据类型关键字(12个)
char, short, int, long, float. double. unsigned. signed, struct, union, enum, void - 控制语句关键字(12个)
if, else, switch, case, default, for, do, while, break, continue, goto, return - 储存类关键字(5个)
auto, extern, register, static, const - 其他关键字(3个)
sizeof, typedef, volatile
2.1.2 数据类型
数据类型的作用:编译器预算对象分配内存空间的大小.
数据类型
- 基本类型
- 整形: int, short, long
- 字符型: char
- 实型(浮点型): 单精度 float 双精度 double
- 构造类型
- 数组类型
- 结构类型 struct
- 联合类型 union
- 枚举类型 enum
- 指针类型 (char *, int * ,int **)
2.1.3 常量
常量:
- 在程序运行过程中,值不能被改变的量
- 常量一般出现在表达式或复制语句中
格式:
- const 数据类型 常量名
- "#difine 常量名 值
注意:通过#define定义的常量是根据值来匹配数据类型的,对于C语言 const修饰的常量是不安全的,可以通过指针来修改
2.1.4 变量
- 变量(又称"只读变量")
变量- 在程序运行过程中,值可以改变
- 变量在使用前必须定义,定义变量前必须有相应的数据类型
标识符命名规则: - 标识符不能是关键字
- 标识符只能由字母,数字,下划线组成
- 第一个字符必须为字母或下划线
- 标识符中字母区分大小写
变量特点: - 变量在编译时为其分配相应的内存空间
- 可以通过变量的名字和地址访问相应内存
- 声明和定义的区别:
-
声明变量不需要建立储存空间
-
定义变量需要建立储存空间
#include <stdio.h>int main()
{
//extern 关键字只做声明,不能做任何定义
//声明一个变量a,a在这里没有建立存储空间
extern int a;
a = 10; //err, 没有空间,就不可以赋值int b = 10; //定义一个变量b,b的类型为int,b赋值为10
return 0;
}
-
2.1.5 使用示例
#include <stdio.h>
#define MAX 10 //声明了一个常量,名字叫MAX,值是10, 常量的值一旦初始化就不能改变
int main()
{
int a; //定义课一个变量,类型为int,名字叫a
const int b = 10; //定义一个const常量,名为叫b,值为10
// b = 11; //err,常量的值不能改变
// MAX = 100; //err,常量的值不能改变
a = MAX;//将abc的值设置为MAX的值
a = 123;
printf("%d\n", a);
return 0;
}
2.2 进制
十进制 以正常数字1-9开头,如123
八进制 以数字0开头,如0123
十六进制 以0x开头,如0x123
二进制 C语言不能直接书写二进制数
#include <stdio.h>
int main()
{
int a = 123; //十进制方式赋值
int b = 0123; //八进制方式赋值, 以数字0开头
int c = 0xABC; //十六进制方式赋值
//如果在printf中输出一个十进制数那么用%d,八进制用%o,十六进制是%x
printf("十进制:%d\n",a );
printf("八进制:%o\n", b); // %o,为字母o,不是数字
printf("十六进制:%x\n", c);// %x 或 %X
return 0;
}
术语 含义
bit(比特) 一个二进制代表一位,一个位只能表示0或1两种状态。数据传输是习惯以“位”(bit)为单位。
Byte(字节) 一个字节为8个二进制,称为8位,计算机中存储的最小单位是字节。数据存储是习惯以“字节”(Byte)为单位。
WORD(双字节) 2个字节,16位
DWORD 两个WORD,4个字节,32位
1b 1bit,1位
1B 1Byte,1字节,8位
1k,1K 1024
1M(1兆) 1024k, 1024*1024
1G 1024M
2.3 计算机内存数值储存方式
2.3.1 原码
一个数的原码(原始二进制码)有以下特点
- 最高位作为符号位,0表示正,1表示负
- 其他数值部分就是数值本身绝对值得二进制数
- 负数的原码是在其绝对值的二进制数基础上,最高位变为1
十进制数 原码
+15 0000 1111
-15 1000 1111
+0 0000 0000
-0 1000 0000
原码表示法简单易懂,但是原码不便于加减运算
2.3.2 反码
- 对于正数,反码与原码相同
- 对于负数,符号位不变,其他部分取反(1变0,0变1)
十进制数 反码
+15 0000 1111
-15 1111 0000
+0 0000 0000
-0 1111 1111
2.3.3 补码
在计算机系统中,数值一律用补码来存储
补码特点:
- 对于正数,原码,反码,补码相同
- 对于负数,补码为他的反码加1
十进制数 补码
+15 0000 1111
-15 1111 0001
+0 0000 0000
-0 0000 0000
2.3.4 补码的意义
示例:计算9-6的结果
以原码方式相加:
十进制数 原码
9 0000 1001
-6 1000 0110
计算结果为 1000 1111( -15),不正确
以补码方式相加:
十进制数 补码
9 0000 1001
-6 1111 1010
计算结果为 1 0000 0011(最高位的1溢出,剩余8位二进制表示是3) 正确
在计算机系统中,数值一律用补码来存储,主要原因:
- 统一了零的编码
- 将符号位和其他位统一处理
- 将减法运算转变为加法运算(计算机中无减法操作)
- 两个用补码表示的数相加时,如果最高位(符号位)有进位,则进位被舍弃
2.4 sizeof 关键字
-
sizeof 不是函数,所以不需要包含任何头未见,他的功能是计算一个数据类型的大小,单位为字节
-
sizeof 的返回值为 size_t
-
size_t 类型在32位操作系统下湿unsigned int 是一个无符号的整数
#include <stdio.h>
int main()
{
int a;
int b = sizeof(a);//sizeof得到指定值占用内存的大小,单位:字节
printf(“b = %d\n”, b);size_t c = sizeof(a); printf("c = %u\n", c);//用无符号数的方式输出c的值 return 0;
}
格式:
- int value =sizeof(变量名);
- int value = sizeof(数据类型) sizeof 数据类型
注意:根据32位操作系统获取的数据类型大小
2.5 整形
2.5.1 整型变量的输出
#include <stdio.h>
int main()
{
int a = 123; //定义变量a,以10进制方式赋值为123
int b = 0567; //定义变量b,以8进制方式赋值为0567
int c = 0xabc; //定义变量c,以16进制方式赋值为0xabc
printf("a = %d\n", a);
printf("8进制:b = %o\n", b);
printf("10进制:b = %d\n", b);
printf("16进制:c = %x\n", c);
printf("16进制:c = %X\n", c);
printf("10进制:c = %d\n", c);
unsigned int d = 0xffffffff; //定义无符号int变量d,以16进制方式赋值
printf("有符号方式打印:d = %d\n", d);
printf("无符号方式打印:d = %u\n", d);
return 0;
}
2.5.2 整型变量输入
#include <stdio.h>
int main()
{
int a;
printf("请输入a的值:");
//不要加“\n”
scanf("%d", &a);
printf("a = %d\n", a); //打印a的值
return 0;
}
2.5.3 short, int, long, long long
数据类型 占用空间
short(短整型) 2字节
int(整型) 4字节
long(长整形) Windows为4字节,Linux为4字节(32位),8字节(64位)
long long(长长整形) 8字节
注意:
- 整型数据在内存中占的字节数与选择的操作系统有关.
- 当一个小的数据类型赋值给一个大的数据类型,不会出错,因为编译器会自动转化,但是当一个大的类型赋值给一个小的数据类型,那么可能丢失高位
2.5.4 有符号和无符号数区别
-
有符号数
有符号数是最高位为符号位,0代表正数,1代表负数定义: signed int a = -1089474374; //定义有符号整型变量a
-
无符号数
无符号数最高为不是符号位,而是数的一部分,无符号数不可能是负数.
定义:unsigned int a = 3236958022; //定义无符号整型变量a
当写程序要处理一个不可能出现负值的时候,一般用无符号数,这样可以增大数的表达最大值.
- 有符号和无符号整形取值范围
数据类型 占用空间 取值范围
short 2字节 -32768 到 32767 (-215 ~ 215-1)
int 4字节 -2147483648 到 2147483647 (-231 ~ 231-1)
long 4字节 -2147483648 到 2147483647 (-231 ~ 231-1)
unsigned short 2字节 0 到 65535 (0 ~ 216-1)
unsigned int 4字节 0 到 4294967295 (0 ~ 232-1)
unsigned long 4字节 0 到 4294967295 (0 ~ 232-1)
2.6 字符型: char
2.6.1 字符变量的定义和输出
字符型变量用于存储一个单一字符,在C语言中用 char 表示,其中每个字符变量都会占用 1 个字节。在给字符型变量赋值时,需要用一对英文半角格式的单引号(’ ')把字符括起来。
#include <stdio.h>
int main()
{
char ch = 'a';
printf("sizeof(ch) = %u\n", sizeof(ch));
printf("ch[%%c] = %c\n", ch); //打印字符
printf("ch[%%d] = %d\n", ch); //打印‘a’ ASCII的值
char A = 'A';
char a = 'a';
printf("a = %d\n", a); //97
printf("A = %d\n", A); //65
printf("A = %c\n", 'a' - 32); //小写a转大写A
printf("a = %c\n", 'A' + 32); //大写A转小写a
ch = ' ';
printf("空字符:%d\n", ch); //空字符ASCII的值为32
printf("A = %c\n", 'a' - ' '); //小写a转大写A
printf("a = %c\n", 'A' + ' '); //大写A转小写a
return 0;
}
2.6.2 自读变量的输入
#include <stdio.h>
int main()
{
char ch;
printf("请输入ch的值:");
//不要加“\n”
scanf("%c", &ch);
printf("ch = %c\n", ch); //打印ch的字符
return 0;
}
2.6.3 转义字符
转义字符 含义 ASCII码值(十进制)
\a 警报 007
\b 退格(BS) ,将当前位置移到前一列 008
\f 换页(FF),将当前位置移到下页开头 012
\n 换行(LF) ,将当前位置移到下一行开头 010
\r 回车(CR) ,将当前位置移到本行开头 013
\t 水平制表(HT) (跳到下一个TAB位置) 009
\v 垂直制表(VT) 011
\ 代表一个反斜线字符"" 092
’ 代表一个单引号(撇号)字符 039
" 代表一个双引号字符 034
? 代表一个问号 063
\0 数字0 000
\ddd 8进制转义字符,d范围0~7 3位8进制
\xhh 16进制转义字符,h范围09,af,A~F 3位16进制
2.6.4 数值溢出
当超过一个数据类型能够存放最大的范围时,数值会溢出.
最高位溢出和符号位溢出的区别:符号位溢出会导致数的正负发生改变,最高位的溢出会导致最高位丢失
2.7 实型(浮点型): float double
实型变量也可以成为浮点型变量,浮点型变量是用来储存小数数值的.在C语言中,浮点型变量分为两种:单精度浮点型(float) 双精度浮点型(double)double类型变量所表示的浮点数比float型变量表示的更精确.
数据类型 占用空间 有效数字范围
float 4字节 7位有效数字
double 8字节 15~16位有效数字
不以 f 结尾的常量是double类型,以f结尾的常量是float类型.
3.运算符与表达式
3.1 常用运算符分类
运算符类型 作用
算术运算符 用于处理四则运算
赋值运算符 用于将表达式的值赋给变量
比较运算符 用于表达式的比较,并返回一个真值或假值
逻辑运算符 用于根据表达式的值返回真值或假值
位运算符 用于处理数据的位运算
sizeof运算符 用于求字节数长度
3.2算术运算符
运算符 术语 示例 结果
-
正号 +3 3
-
负号 -3 -3
-
加 10 + 5 15
-
减 10 - 5 5
-
乘 10 * 5 50
/ 除 10 / 5 2
% 取模(取余) 10 % 3 1
++ 前自增 a=2; b=++a; a=3; b=3;
++ 后自增 a=2; b=a++; a=3; b=2;
– 前自减 a=2; b=–a; a=1; b=1;
– 后自减 a=2; b=a–; a=1; b=2;
3.3 赋值运算符
运算符 术语 示例 结果
= 赋值 a=2; b=3; a=2; b=3;
+= 加等于 a=0; a+=2; a=2;
-= 减等于 a=5; a-=3; a=2;
= 乘等于 a=2; a=2; a=4;
/= 除等于 a=4; a/=2; a=2;
%= 模等于 a=3; a%2; a=1;
3.4 比较运算符
C 语言的比较运算中, "真"用数字 1 表示,"假"用数字 0 表示.
运算符 术语 示例 结果
== 相等于 4 == 3 0
!= 不等于 4 != 3 1
< 小于 4 < 3 0
大于 4 > 3 1
<= 小于等于 4 <= 3 0
= 大于等于 4 >= 1 1
3.5 逻辑运算符
运算符 术语 示例 结果
! 非 !a 如果a为假,则!a为真;如果a为真,则!a为假。
&& 与 a && b 如果a和b都为真,则结果为真,否则为假。
|| 或 a || b 如果a和b有一个为真,则结果为真,二者都为假时,结果为假。
3.6 运算符优先级
优先级 运算符 名称或含义 使用形式 结合方向 说明
1 [] 数组下标 数组名[常量表达式] 左到右 –
() 圆括号 (表达式)/函数名(形参表) –
. 成员选择(对象) 对象.成员名 –
-> 成员选择(指针) 对象指针->成员名 –
-
2 - 负号运算符 -表达式 右到左 单目运算符
-
按位取反运算符 ~表达式
++ 自增运算符 ++变量名/变量名++
– 自减运算符 --变量名/变量名–
-
取值运算符 *指针变量
& 取地址运算符 &变量名
! 逻辑非运算符 !表达式
(类型) 强制类型转换 (数据类型)表达式 –
sizeof 长度运算符 sizeof(表达式) –
3 / 除 表达式/表达式 左到右 双目运算符
-
乘 表达式*表达式
% 余数(取模) 整型表达式%整型表达式
4 + 加 表达式+表达式 左到右 双目运算符
-
减 表达式-表达式
5 << 左移 变量<<表达式 左到右 双目运算符
右移 变量>>表达式
6 > 大于 表达式>表达式 左到右 双目运算符
= 大于等于 表达式>=表达式
< 小于 表达式<表达式
<= 小于等于 表达式<=表达式
7 == 等于 表达式==表达式 左到右 双目运算符
!= 不等于 表达式!= 表达式
8 & 按位与 表达式&表达式 左到右 双目运算符
9 ^ 按位异或 表达式^表达式 左到右 双目运算符
10 | 按位或 表达式|表达式 左到右 双目运算符
11 && 逻辑与 表达式&&表达式 左到右 双目运算符
12 || 逻辑或 表达式||表达式 左到右 双目运算符
13 ?: 条件运算符 表达式1?表达式2: 表达式3 右到左 三目运算符
14 = 赋值运算符 变量=表达式 右到左 –
/= 除后赋值 变量/=表达式 –
= 乘后赋值 变量=表达式 –
%= 取模后赋值 变量%=表达式 –
+= 加后赋值 变量+=表达式 –
-= 减后赋值 变量-=表达式 –
<<= 左移后赋值 变量<<=表达式 –
= 右移后赋值 变量>>=表达式 –
&= 按位与后赋值 变量&=表达式 –
^= 按位异或后赋值 变量^=表达式 –
|= 按位或后赋值 变量|=表达式 –
15 , 逗号运算符 表达式,表达式,… 左到右 –
3.7类型转换
数据有不同的类型,不同类型数据之间进行混合运算是必然涉及到类型的转换.
转换类型有两种方式:
- 自动转换(隐式转换):遵循一定规则,有变异系统自动完成.
- 强制类型转换:把表达式的运算结果前值转换成所需的数据类型.
类型转换的原则:占用内存字节数少的类型向占用内存字节数多的类型转换,以保证精度不降低.
3.7.1 隐式转换
#include <stdio.h>
int main()
{
int num = 5;
printf("s1=%d\n", num / 2);
printf("s2=%lf\n", num / 2.0);
return 0;
}
3.7.2 强制转换
强制类型转换指的是适应强制类型转换运算符,将一个变量或表达式转化成所需的类型,基本语法: (类型说明符) (表达式)
#include <stdio.h>
int main()
{
float x = 0;
int i = 0;
x = 3.6f;
i = x; //x为实型, i为整型,直接赋值会有警告
i = (int)x; //使用强制类型转换
printf("x=%f, i=%d\n", x, i);
return 0;
}
- 程序流程结构
4.1 概述
C 语言支持最基本的三种程序运行结构:顺序结构 选择结构 循环结构.
- 顺序结构: 程序按顺序执行,不发生跳转
- 选择结构: 依据满足条件,有选择地执行相应功能
- 循环结构: 依据条件是否满足, 循环多次执行某段代码
4.2 选择结构
4.2.1 if 语句
#include <stdio.h>
int main()
{
int a = 1;
int b = 2;
if (a > b)
{
printf("%d\n", a);
}
return 0;
}
4.2.2 if … else 语句
#include <stdio.h>
int main(void)
{
int a =1;
int b =2;
if(a>b)
{
printf("%d\n",a)
}
else
{
printf("%d\n",b)
}
return 0
}
4.2.3 if … else if … else
#include <stdio.h>
int main()
{
unsigned int a;
scanf("%u", &a);
if (a < 10)
{
printf("个位\n");
}
else if (a < 100)
{
printf("十位\n");
}
else if (a < 1000)
{
printf("百位\n");
}
else
{
printf("很大\n");
}
return 0;
}
4.2.4 三目运算符
#include <stdio.h>
int main()
{
int a = 10;
int b = 20;
int c;
if (a > b)
{
c = a;
}
else
{
c = b;
}
printf("c1 = %d\n", c);
a = 1;
b = 2;
c = ( a > b ? a : b );
printf("c2 = %d\n", c);
return 0;
}
4.2.5 switch
#include <stdio.h>
int main()
{
char c;
c = getchar();
switch (c) //参数只能是整型变量
{
case '1':
printf("OK\n");
break;//switch遇到break就中断了
case '2':
printf("not OK\n");
break;
default://如果上面的条件都不满足,那么执行default
printf("are u ok?\n");
}
return 0;
}
注意:
if条件语句执行效率差, switch 条件语句执行效率高,但是 if 可以判断一个区间 switch用来判断一个值, 三目运算符结构简单明了.
4.3 循环结构
4.3.1 while 语句
#include <stdio.h>
int main()
{
int a = 20;
while (a > 10)
{
scanf("%d", &a);
printf("a = %d\n", a);
}
return 0;
}
while 语句一般用于不确定循环次数的情况下
4.3.2 do … while 语句
#include <stdio.h>
int main()
{
int a = 1;
do
{
a++;
printf("a = %d\n", a);
} while (a < 10);
return 0;
}
do … while 语句无论条件是否满足.循环总执行一次
4.3.3 for 语句
#include <stdio.h>
int main()
{
int i;
int sum = 0;
for (i = 0; i <= 100; i++)
{
sum += i;
}
printf("sum = %d\n", sum);
return 0;
}
最常使用的循环语句,常用语已知循环出口的情况
4.4 跳转语句 break continue goto
4.4.1 break 语句
在 switch 条件语句和循环语句中可以使用 break 语句:
- 当他出现在 switch 条件语句中时, 作用是终止某个 case 并挑出 switch 结构.
- 当他出现在循环语句中,作用是跳出当前内循环语句.
- 当他出现在嵌套循环语句中,跳出最近的内循环语句.
break 不能直接用于if 的,但是如果在for 里面,是可以用的,就是如果达到条件,就退出for
#include <stdio.h>
int main()
{
int i = 0;
while (1)
{
i++;
printf("i = %d\n", i);
if (i == 10)
{
break; //跳出while循环
}
}
int flag = 0;
int m = 0;
int n = 0;
for (m = 0; m < 10; m++)
{
for (n = 0; n < 10; n++)
{
if (n == 5)
{
flag = 1;
break; //跳出for (n = 0; n < 10; n++)
}
}
if (flag == 1)
{
break; //跳出for (m = 0; m < 10; m++)
}
}
return 0;
}
4.3.2 continue 语句
再循环中,如果希望立即终止本次循环,并执行下一次循环,此时就需要食用 continue 语句.
#include<stdio.h>
int main()
{
int sum = 0; //定义变量sum
for (int i = 1; i <= 100; i++)
{
if (i % 2 == 0) //如果i是一个偶数,执行if语句中的代码
{
continue; //结束本次循环
}
sum += i; //实现sum和i的累加
}
printf("sum = %d\n", sum);
return 0;
}
注意: 使用 continue 时要考虑到循环变量自增或自减,避免死循环
4.3.3 goto 语句 (无条件跳转, 尽量少用)
#include <stdio.h>
int main()
{
goto End; //无条件跳转到End的标识
printf("aaaaaaaaa\n");
End:
printf("bbbbbbbb\n");
return 0;
}
5 .数组和字符串
5.1 概述
在程序设计中,为了方便处理数据,把具有相同类型的若干变量按有序形式组织起来称为数组.
数组就是在内存中连续的相同类型的变量空间,同一个数组所有的成员都是相同的数据类型,同时所有的成员在内存中的地址是连续的.
数组属于狗仔数据类型:
- 一个数组可以分解为多个数组元素:这些数组元素可以是基本数据类型或构造类型.
- 安数组元素类型的不同,数组可分为"数值数组,字符数组,指针数组,结构数组等
通常情况下,数组元素下标的个数也称为维数.根据维数的不同,可将数组分为一维数组,二维数组,三维数组等.通常情况下,我们将二位及以上的数组称为多维数组.
5.2 一维数组
5.2.1 一维数组的定义和使用
-
数组名符合标识符的书写规定
-
数组名不能与其他变量名相同
-
方括号" [ ] "中常量表达时表示数组元素的个数
-
定义数组时" [ ] "内嘴还是常量,使用数组时 " [ ] "既可以是常量也可以是变量
#include <stdio.h>
int main()
{
int a[10];//定义了一个数组,名字叫a,有10个成员,每个成员都是int类型
//a[0]…… a[9],没有a[10]
//没有a这个变量,a是数组的名字,但不是变量名,它是常量
a[0] = 0;
//……
a[9] = 9;int i = 0; for (i = 0; i < 10; i++) { a[i] = i; //给数组赋值 } //遍历数组,并输出每个成员的值 for (i = 0; i < 10; i++) { printf("%d ", a[i]); } printf("\n"); return 0;
}
5.2.2 一位数组的初始化
在定义数组的同时进行赋值,称为初始化.全局数组若不初始化,便一起将其初始化为零.局部数组若不初始化,内容为随机值.
int a[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };//定义一个数组,同时初始化所有成员变量
int a[10] = { 1, 2, 3 };//初始化前三个成员,后面所有元素都设置为0
int a[10] = { 0 };//所有的成员都设置为0
//[]中不定义元素个数,定义时必须初始化
int a[] = { 1, 2, 3, 4, 5 };//定义了一个数组,有5个成员
求出数组元素个数:
int (size_t) unsigned int 个数 = sizeof(数组名)/sizeof(数组元素 | 数组数据类型)
5.2.3 数组名
数组名是一个地址的常量,代表数组中首元素的地址.
#include <stdio.h>
int main()
{
int a[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };//定义一个数组,同时初始化所有成员变量
printf("a = %p\n", a);
printf("&a[0] = %p\n", &a[0]);
int n = sizeof(a); //数组占用内存的大小,10个int类型,10 * 4 = 40
int n0 = sizeof(a[0]);//数组第0个元素占用内存大小,第0个元素为int,4
int i = 0;
for (i = 0; i < sizeof(a) / sizeof(a[0]); i++)
{
printf("%d ", a[i]);
}
printf("\n");
return 0;
}
5.2.3 一维数组应用
-
一维数组的最值
#include <stdio.h>
int main()
{
int a[] = { 1, -2, 3,- 4, 5, -6, 7, -8, -9, 10 };//定义一个数组,同时初始化所有成员变量int i = 0; int max = a[0]; for (i = 0; i < sizeof(a) / sizeof(a[0]); i++) { if (a[i] > max) { max = a[i]; } } printf("数组中最大值为:%d\n", max); return 0;
}
-
一维数组的逆序
#include <stdio.h>
int main()
{
int a[] = { 1, -2, 3,- 4, 5, -6, 7, -8, -9, 10 };//定义一个数组,同时初始化所有成员变量int i = 0; int j = sizeof(a) / sizeof(a[0]) -1; int tmp; while (i < j) { tmp = a[i]; a[i] = a[j]; a[j] = tmp; i++; j--; } for (i = 0; i < sizeof(a) / sizeof(a[0]); i++) { printf("%d ", a[i]); } printf("\n"); return 0;
}
-
冒泡排序
#include <stdio.h>
int main()
{
int a[] = { 1, -2, 3,- 4, 5, -6, 7, -8, -9, 10 };//定义一个数组,同时初始化所有成员变量int i = 0; int j = 0; int n = sizeof(a) / sizeof(a[0]); int tmp; //1、流程 //2、试数 for (i = 0; i < n-1; i++) { for (j = 0; j < n - i -1 ; j++)//内循环的目的是比较相邻的元素,把大的放到后面 { if (a[j] > a[j + 1]) { tmp = a[j]; a[j] = a[j+1]; a[j+1] = tmp; } } } for (i = 0; i < n; i++) { printf("%d ", a[i]); } printf("\n"); return 0;
}
5.3 二维数组
5.3.1 二维数组的定义和使用
二维数组定义的一般形式是:
类型说明符 数组名 [常量表达式1][常量表达式2]
其中常量表达式1表示第一维下标的长度,常量表达式2表示第二维下标的长度.
-
命名规则同一维数组
-
int a[3][4]定义一个 3 行 4 列数组,数组名为 a 其元素类型为整形,该数组的元素个数为 3 * 4 个,二维数组 a 是按行进行存放的,先存放 a[0]行,在存放 a[1]行,并且每行有四个元素,也是一次存放的.
-
二维数组在概念上是二维的: 其下标在两个方向上变化,对其访问一般需要两个下标.
-
在内存中并不存在二位数组,二位数组实际的硬件存储器是连续编址的,也就是说内存中只有一维数组,即放完一行之后顺次放入第二行,和一维数组存放方式是一样的.
#include <stdio.h>
int main()
{
//定义了一个二维数组,名字叫a
//由3个一维数组组成,这个一维数组是int [4]
//这3个一维数组的数组名分别为a[0],a[1],a[2]
int a[3][4];a[0][0] = 0; //…… a[2][3] = 12; //给数组每个元素赋值 int i = 0; int j = 0; int num = 0; for (i = 0; i < 3; i++) { for (j = 0; j < 4; j++) { a[i][j] = num++; } } //遍历数组,并输出每个成员的值 for (i = 0; i < 3; i++) { for (j = 0; j < 4; j++) { printf("%d, ", a[i][j]); } printf("\n"); } return 0;
}
5.3.2 二维数组的初始化
//分段赋值 int a[3][4] = {{ 1, 2, 3, 4 },{ 5, 6, 7, 8, },{ 9, 10, 11, 12 }};
int a[3][4] =
{
{ 1, 2, 3, 4 },
{ 5, 6, 7, 8, },
{ 9, 10, 11, 12 }
};
//连续赋值
int a[3][4] = { 1, 2, 3, 4 , 5, 6, 7, 8, 9, 10, 11, 12 };
//可以只给部分元素赋初值,未初始化则为0
int a[3][4] = { 1, 2, 3, 4 };
//所有的成员都设置为0
int a[3][4] = {0};
//[]中不定义元素个数,定义时必须初始化
int a[][4] = { 1, 2, 3, 4, 5, 6, 7, 8};
5.3.3 数组名
数组名是一个地址的常量,代表数组中首元素的地址
#include <stdio.h>
int main()
{
//定义了一个二维数组,名字叫a
//二维数组是本质上还是一维数组,此一维数组有3个元素
//每个元素又是一个一维数组int[4]
int a[3][4] = { 1, 2, 3, 4 , 5, 6, 7, 8, 9, 10, 11, 12 };
//数组名为数组首元素地址,二维数组的第0个元素为一维数组
//第0个一维数组的数组名为a[0]
printf("a = %p\n", a);
printf("a[0] = %p\n", a[0]);
//测二维数组所占内存空间,有3个一维数组,每个一维数组的空间为4*4
//sizeof(a) = 3 * 4 * 4 = 48
printf("sizeof(a) = %d\n", sizeof(a));
//测第0个元素所占内存空间,a[0]为第0个一维数组int[4]的数组名,4*4=16
printf("sizeof(a[0]) = %d\n", sizeof(a[0]) );
//测第0行0列元素所占内存空间,第0行0列元素为一个int类型,4字节
printf("sizeof(a[0][0]) = %d\n", sizeof(a[0][0]));
//求二维数组行数
printf("i = %d\n", sizeof(a) / sizeof(a[0]));
// 求二维数组列数
printf("j = %d\n", sizeof(a[0]) / sizeof(a[0][0]));
//求二维数组行*列总数
printf("n = %d\n", sizeof(a) / sizeof(a[0][0]));
return 0;
}
5.4 多维数组(了解)
多维数组的定义与二维数组类似.
#include <stdio.h>
int main()
{
//int a[3][4][5] ;//定义了一个三维数组,有3个二维数组int[4][5]
int a[3][4][5] = { { { 1, 2, 3, 4, 5 }, { 6, 7, 8, 9, 10 }, { 0 }, { 0 } }, { { 0 }, { 0 }, { 0 }, { 0 } }, { { 0 }, { 0 }, { 0 }, { 0 } } };
int i, j, k;
for (i = 0; i < 3; i++)
{
for (j = 0; j < 4; j++)
{
for (k = 0; k < 5; k++)
{
//添加访问元素代码
printf("%d, ", a[i][j][k]);
}
printf("\n");
}
}
return 0;
}
5.5 字符数组与字符串
5.5.1 字符数组与字符串的区别
-
C 语言中没有字符串这种数据类型,可以通过char 的数组来代替
-
字符串一定是一个 char 的数组, 但 char 的数组未必是字符串
-
数字0(和字符 ‘\0’ 等价)结尾的 char 数组就是一个字符串, 但如果 char数组没有数字 0 结尾,那么就不是一个字符串, 只是普通字符数组, 所以字符串是一种特殊的 char 的数组.
#include <stdio.h>
int main()
{
char c1[] = { ‘c’, ’ ', ‘p’, ‘r’, ‘o’, ‘g’ }; //普通字符数组
printf(“c1 = %s\n”, c1); //乱码,因为没有’\0’结束符//以‘\0’(‘\0’就是数字0)结尾的字符数组是字符串 char c2[] = { 'c', ' ', 'p', 'r', 'o', 'g', '\0'}; printf("c2 = %s\n", c2); //字符串处理以‘\0’(数字0)作为结束符,后面的'h', 'l', 'l', 'e', 'o'不会输出 char c3[] = { 'c', ' ', 'p', 'r', 'o', 'g', '\0', 'h', 'l', 'l', 'e', 'o', '\0'}; printf("c3 = %s\n", c3); return 0;
}
5.5.2 字符串的初始化
#include <stdio.h>
// C语言没有字符串类型,通过字符数组模拟
// C语言字符串,以字符‘\0’, 数字0
int main()
{
//不指定长度, 没有0结束符,有多少个元素就有多长
char buf[] = { 'a', 'b', 'c' };
printf("buf = %s\n", buf); //乱码
//指定长度,后面没有赋值的元素,自动补0
char buf2[100] = { 'a', 'b', 'c' };
printf("buf2 = %s\n", buf2);
//所有元素赋值为0
char buf3[100] = { 0 };
//char buf4[2] = { '1', '2', '3' };//数组越界
char buf5[50] = { '1', 'a', 'b', '0', '7' };
printf("buf5 = %s\n", buf5);
char buf6[50] = { '1', 'a', 'b', 0, '7' };
printf("buf6 = %s\n", buf6);
char buf7[50] = { '1', 'a', 'b', '\0', '7' };
printf("buf7 = %s\n", buf7);
//使用字符串初始化,编译器自动在后面补0,常用
char buf8[] = "agjdslgjlsdjg";
//'\0'后面最好不要连着数字,有可能几个数字连起来刚好是一个转义字符
//'\ddd'八进制字义字符,'\xdd'十六进制转移字符
// \012相当于\n
char str[] = "\012abc";
printf("str == %s\n", str);
return 0;
}
5.5.3 字符串的输入输出
由于字符串采用了 ‘\0’ 标志, 字符串的输入输出将变得简单方便.
#include <stdio.h>
int main()
{
char str[100];
printf("input string1 : \n");
scanf("%s", str);//scanf(“%s”,str)默认以空格分隔
printf("output:%s\n", str);
return 0;
}
5.5.4 字符串追加
#include <stdio.h>
int main()
{
char str1[] = "abcdef";
char str2[] = "123456";
char dst[100];
int i = 0;
while (str1[i] != 0)
{
dst[i] = str1[i];
i++;
}
int j = 0;
while (str2[j] != 0)
{
dst[i + j] = str2[j];
j++;
}
dst[i + j] = 0; //字符串结束符
printf("dst = %s\n", dst);
return 0;
}
5.5.5 字符串处理函数
-
gets()
#include <stdio.h>
char *gets(char *s);
功能:从标准输入读入字符,并保存到s指定的内存空间,直到出现换行符或读到文件结尾为止。
参数:
s:字符串首地址
返回值:
成功:读入的字符串
失败:NULL
注意: 由于 scanf() 和 gets() 无法知道字符串的大小,必须遇到换行符或督导文件结尾为止才接收输入,因此容易导致自负数组越界的情况. -
fgets()
#include <stdio.h>
char *fgets(char *s, int size, FILE *stream);
功能:从stream指定的文件内读入字符,保存到s所指定的内存空间,直到出现换行字符、读到文件结尾或是已读了size - 1个字符为止,最后会自动加上字符 ‘\0’ 作为字符串结束。
参数:
s:字符串
size:指定最大读取字符串的长度(size - 1)
stream:文件指针,如果读键盘输入的字符串,固定写为stdin
返回值:
成功:成功读取的字符串
读到文件尾或出错: NULL
fgets()在读取一个用户通过键盘输入的字符串的时候,同时把用户输入的回车也做为字符串的一部分。通过scanf和gets输入一个字符串的时候,不包含结尾的“\n”,但通过fgets结尾多了“\n”。fgets()函数是安全的,不存在缓冲区溢出的问题。 -
puts()
#include <stdio.h>
int puts(const char *s);
功能:标准设备输出s字符串,在输出完成后自动输出一个’\n’。
参数:
s:字符串首地址
返回值:
成功:非负数
失败:-1 -
fputs()
#include <stdio.h>
int fputs(const char * str, FILE * stream);
功能:将str所指定的字符串写入到stream指定的文件中, 字符串结束符 ‘\0’ 不写入文件。
参数:
str:字符串
stream:文件指针,如果把字符串输出到屏幕,固定写为stdout
返回值:
成功:0
失败:-1 -
strlen()
#include <string.h>
size_t strlen(const char *s);
功能:计算指定指定字符串s的长度,不包含字符串结束符‘\0’
参数:
s:字符串首地址
返回值:字符串s的长度,size_t为unsigned int类型 -
strcpy()
#include <string.h>
char *strcpy(char *dest, const char *src);
功能:把src所指向的字符串复制到dest所指向的空间中,’\0’也会拷贝过去
参数:
dest:目的字符串首地址
src:源字符首地址
返回值:
成功:返回dest字符串的首地址
失败:NULL
注意:如果参数 dest 所致的内存空间不够大, 可能会造成缓冲溢出的错误情况. -
strncpy()
#include <string.h>
char *strcpy(char *dest, const char *src);
功能:把src所指向的字符串复制到dest所指向的空间中,’\0’也会拷贝过去
参数:
dest:目的字符串首地址
src:源字符首地址
返回值:
成功:返回dest字符串的首地址
失败:NULL
char dest[20] ;
char src[] = “hello world”;strncpy(dest, src, 5); printf("%s\n", dest); dest[5] = '\0'; printf("%s\n", dest);
-
strcat()
#include <string.h>
char *strcat(char *dest, const char *src);
功能:将src字符串连接到dest的尾部,‘\0’也会追加过去
参数:
dest:目的字符串首地址
src:源字符首地址
返回值:
成功:返回dest字符串的首地址
失败:NULLchar str[20] = "123"; char *src = "hello world"; printf("%s\n", strcat(str, src));
-
strncat()
#include <string.h>
char *strncat(char *dest, const char *src, size_t n);
功能:将src字符串前n个字符连接到dest的尾部,‘\0’也会追加过去
参数:
dest:目的字符串首地址
src:源字符首地址
n:指定需要追加字符串个数
返回值:
成功:返回dest字符串的首地址
失败:NULLchar str[20] = "123"; char *src = "hello world"; printf("%s\n", strncat(str, src, 5));
-
strcmp()
#include <string.h>
int strcmp(const char *s1, const char *s2);
功能:比较 s1 和 s2 的大小,比较的是字符ASCII码大小。
参数:
s1:字符串1首地址
s2:字符串2首地址
返回值:
相等:0
大于:>0
小于:<0char *str1 = “hello world”;
char *str2 = “hello mike”;if (strcmp(str1, str2) == 0) { printf("str1==str2\n"); } else if (strcmp(str1, str2) > 0) { printf("str1>str2\n"); } else { printf("str1<str2\n"); }
-
strncmp()
#include <string.h>
int strncmp(const char *s1, const char *s2, size_t n);
功能:比较 s1 和 s2 前n个字符的大小,比较的是字符ASCII码大小。
参数:
s1:字符串1首地址
s2:字符串2首地址
n:指定比较字符串的数量
返回值:
相等:0
大于: > 0
小于: < 0char *str1 = “hello world”;
char *str2 = “hello mike”;if (strncmp(str1, str2, 5) == 0) { printf("str1==str2\n"); } else if (strcmp(str1, "hello world") > 0) { printf("str1>str2\n"); } else { printf("str1<str2\n"); }
-
sprintf()
#include <stdio.h>
int sprintf(char *_CRT_SECURE_NO_WARNINGS, const char *format, …);
功能:根据参数format字符串来转换并格式化数据,然后将结果输出到str指定的空间中,直到出现字符串结束符 ‘\0’ 为止。
参数:
str:字符串首地址
format:字符串格式,用法和printf()一样
返回值:
成功:实际格式化的字符个数
失败: - 1char dst[100] = { 0 }; int a = 10; char src[] = "hello world"; printf("a = %d, src = %s", a, src); printf("\n"); int len = sprintf(dst, "a = %d, src = %s", a, src); printf("dst = \" %s\"\n", dst); printf("len = %d\n", len);
-
sscanf()
#include <stdio.h>
int sscanf(const char *str, const char *format, …);
功能:从str指定的字符串读取数据,并根据参数format字符串来转换并格式化数据。
参数:
str:指定的字符串首地址
format:字符串格式,用法和scanf()一样
返回值:
成功:参数数目,成功转换的值的个数
失败: - 1char src[] = "a=10, b=20"; int a; int b; sscanf(src, "a=%d, b=%d", &a, &b); printf("a:%d, b:%d\n", a, b);
-
strchr()
#include <string.h>
char *strchr(const char *s, int c);
功能:在字符串s中查找字母c出现的位置
参数:
s:字符串首地址
c:匹配字母(字符)
返回值:
成功:返回第一次出现的c地址
失败:NULLchar src[] = "ddda123abcd"; char *p = strchr(src, 'a'); printf("p = %s\n", p);
-
strstr()
#include <string.h>
char *strstr(const char *haystack, const char *needle);
功能:在字符串haystack中查找字符串needle出现的位置
参数:
haystack:源字符串首地址
needle:匹配字符串首地址
返回值:
成功:返回第一次出现的needle地址
失败:NULLchar src[] = "ddddabcd123abcd333abcd"; char *p = strstr(src, "abcd"); printf("p = %s\n", p);
-
strtok()
#include <string.h>
char *strtok(char *str, const char *delim);
功能:来将字符串分割成一个个片段。当strtok()在参数s的字符串中发现参数delim中包含的分割字符时, 则会将该字符改为\0 字符,当连续出现多个时只替换第一个为\0。
参数:
str:指向欲分割的字符串
delim:为分割字符串中包含的所有字符
返回值:
成功:分割后字符串首地址
失败:NULL
- 在第一次调用时: strtok ()必须给与参数 s 字符串
- 往后调用则见参数 s 设置成 NULL , 每次调用成功则返回指向被分割出片段的指针
char a[100] = "adc*fvcv*ebcy*hghbdfg*casdert";
char *s = strtok(a, "*");//将"*"分割的子串取出
while (s != NULL)
{
printf("%s\n", s);
s = strtok(NULL, "*");
}
- atoi()
#include <stdlib.h>
int atoi(const char *nptr);
功能:atoi()会扫描nptr字符串,跳过前面的空格字符,直到遇到数字或正负号才开始做转换,而遇到非数字或字符串结束符(’\0’)才结束转换,并将结果返回返回值。
参数:
nptr:待转换的字符串
返回值:成功转换后整数
类似类型有:-
atof(): 把一个小数形式的字符串转化为一个浮点数
-
atol(): 将有一个字符串转换为 long 类型
char str1[] = “-10”;
int num1 = atoi(str1);
printf(“num1 = %d\n”, num1);char str2[] = "0.123"; double num2 = atof(str2); printf("num2 = %lf\n", num2);
-