C ++ 学习之 C 语言基础 (一)

工作需要近期接触 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 代码分析

  1. include头文件包含
  • include的意思是头文件包含,#include <stdio.h>代表包含stdio.h这个头文件
  • include< > 与 #include ""的区别:
    1. < >表示系统系统库
    2. " "标志自定义库
  1. main函数
    • 一个完整的C语言程序,是由一个、且只能有一个main()函数(又称主函数,必须有)和若干个其他函数结合而成(可选)。
    • main函数是C语言程序的入口,程序是从main函数开始执行。
  2. {}括号,程序体和代码块
    • {}叫代码块,一个代码块内部可以有一条或多条语句
    • C语言美剧可执行代码都是";"号结尾
    • 所有的#开头的行都是预编译指令,预编译指令行结尾是没有分号的
    • 所有的可执行语句必须是在代码块里面
  3. 注释
    • // 行注释
    • /* 内容 */ 块注释
  4. printf函数
    • printf(“hello wotld\n”); // \n的意思是换行`
  5. 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 编译过程

  1. 分布编译
    预处理: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

  2. 一步编译
    gcc hello.c -o demo

1.3.3 找找程序所依赖的动态库

  1. Linux平台下,ldd可执行程序,如:ldd hello

  2. Windows平台下,需要相应的软件(Depends.exe)

  3. 数据类型

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 常量

常量:

  • 在程序运行过程中,值不能被改变的量
  • 常量一般出现在表达式或复制语句中

格式:

  1. const 数据类型 常量名
  2. "#difine 常量名 值
    注意:通过#define定义的常量是根据值来匹配数据类型的,对于C语言 const修饰的常量是不安全的,可以通过指针来修改

2.1.4 变量

  1. 变量(又称"只读变量")
    变量
    • 在程序运行过程中,值可以改变
    • 变量在使用前必须定义,定义变量前必须有相应的数据类型
      标识符命名规则:
    • 标识符不能是关键字
    • 标识符只能由字母,数字,下划线组成
    • 第一个字符必须为字母或下划线
    • 标识符中字母区分大小写
      变量特点:
    • 变量在编译时为其分配相应的内存空间
    • 可以通过变量的名字和地址访问相应内存
  2. 声明和定义的区别:
    • 声明变量不需要建立储存空间

    • 定义变量需要建立储存空间
      #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 有符号和无符号数区别

  1. 有符号数
    有符号数是最高位为符号位,0代表正数,1代表负数

    定义: signed int a = -1089474374; //定义有符号整型变量a

  2. 无符号数
    无符号数最高为不是符号位,而是数的一部分,无符号数不可能是负数.
    定义:unsigned int a = 3236958022; //定义无符号整型变量a

当写程序要处理一个不可能出现负值的时候,一般用无符号数,这样可以增大数的表达最大值.

  1. 有符号和无符号整形取值范围
    数据类型 占用空间 取值范围
    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类型转换

数据有不同的类型,不同类型数据之间进行混合运算是必然涉及到类型的转换.

转换类型有两种方式:

  1. 自动转换(隐式转换):遵循一定规则,有变异系统自动完成.
  2. 强制类型转换:把表达式的运算结果前值转换成所需的数据类型.

类型转换的原则:占用内存字节数少的类型向占用内存字节数多的类型转换,以保证精度不降低.

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;
}
  1. 程序流程结构

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 一维数组应用

  1. 一维数组的最值

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

    }

  2. 一维数组的逆序

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

    }

  3. 冒泡排序

    #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 字符串处理函数

  1. gets()
    #include <stdio.h>
    char *gets(char *s);
    功能:从标准输入读入字符,并保存到s指定的内存空间,直到出现换行符或读到文件结尾为止。
    参数:
    s:字符串首地址
    返回值:
    成功:读入的字符串
    失败:NULL
    注意: 由于 scanf() 和 gets() 无法知道字符串的大小,必须遇到换行符或督导文件结尾为止才接收输入,因此容易导致自负数组越界的情况.

  2. 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()函数是安全的,不存在缓冲区溢出的问题。

  3. puts()
    #include <stdio.h>
    int puts(const char *s);
    功能:标准设备输出s字符串,在输出完成后自动输出一个’\n’。
    参数:
    s:字符串首地址
    返回值:
    成功:非负数
    失败:-1

  4. fputs()
    #include <stdio.h>
    int fputs(const char * str, FILE * stream);
    功能:将str所指定的字符串写入到stream指定的文件中, 字符串结束符 ‘\0’ 不写入文件。
    参数:
    str:字符串
    stream:文件指针,如果把字符串输出到屏幕,固定写为stdout
    返回值:
    成功:0
    失败:-1

  5. strlen()
    #include <string.h>
    size_t strlen(const char *s);
    功能:计算指定指定字符串s的长度,不包含字符串结束符‘\0’
    参数:
    s:字符串首地址
    返回值:字符串s的长度,size_t为unsigned int类型

  6. strcpy()
    #include <string.h>
    char *strcpy(char *dest, const char *src);
    功能:把src所指向的字符串复制到dest所指向的空间中,’\0’也会拷贝过去
    参数:
    dest:目的字符串首地址
    src:源字符首地址
    返回值:
    成功:返回dest字符串的首地址
    失败:NULL
    注意:如果参数 dest 所致的内存空间不够大, 可能会造成缓冲溢出的错误情况.

  7. 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);
    
  8. strcat()
    #include <string.h>
    char *strcat(char *dest, const char *src);
    功能:将src字符串连接到dest的尾部,‘\0’也会追加过去
    参数:
    dest:目的字符串首地址
    src:源字符首地址
    返回值:
    成功:返回dest字符串的首地址
    失败:NULL

    	char str[20] = "123";
    	char *src = "hello world";
    	printf("%s\n", strcat(str, src));
    
  9. strncat()
    #include <string.h>
    char *strncat(char *dest, const char *src, size_t n);
    功能:将src字符串前n个字符连接到dest的尾部,‘\0’也会追加过去
    参数:
    dest:目的字符串首地址
    src:源字符首地址
    n:指定需要追加字符串个数
    返回值:
    成功:返回dest字符串的首地址
    失败:NULL

    char str[20] = "123";
    char *src = "hello world";
    printf("%s\n", strncat(str, src, 5));
    
  10. strcmp()
    #include <string.h>
    int strcmp(const char *s1, const char *s2);
    功能:比较 s1 和 s2 的大小,比较的是字符ASCII码大小。
    参数:
    s1:字符串1首地址
    s2:字符串2首地址
    返回值:
    相等:0
    大于:>0
    小于:<0

    char *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");
       }
    
  11. 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
    小于: < 0

    char *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");
       }
    
  12. sprintf()
    #include <stdio.h>
    int sprintf(char *_CRT_SECURE_NO_WARNINGS, const char *format, …);
    功能:根据参数format字符串来转换并格式化数据,然后将结果输出到str指定的空间中,直到出现字符串结束符 ‘\0’ 为止。
    参数:
    str:字符串首地址
    format:字符串格式,用法和printf()一样
    返回值:
    成功:实际格式化的字符个数
    失败: - 1

    	char 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);
    
  13. sscanf()
    #include <stdio.h>
    int sscanf(const char *str, const char *format, …);
    功能:从str指定的字符串读取数据,并根据参数format字符串来转换并格式化数据。
    参数:
    str:指定的字符串首地址
    format:字符串格式,用法和scanf()一样
    返回值:
    成功:参数数目,成功转换的值的个数
    失败: - 1

    	char 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);
    
  14. strchr()
    #include <string.h>
    char *strchr(const char *s, int c);
    功能:在字符串s中查找字母c出现的位置
    参数:
    s:字符串首地址
    c:匹配字母(字符)
    返回值:
    成功:返回第一次出现的c地址
    失败:NULL

       char src[] = "ddda123abcd";
       char *p = strchr(src, 'a');
       printf("p = %s\n", p);
    
  15. strstr()
    #include <string.h>
    char *strstr(const char *haystack, const char *needle);
    功能:在字符串haystack中查找字符串needle出现的位置
    参数:
    haystack:源字符串首地址
    needle:匹配字符串首地址
    返回值:
    成功:返回第一次出现的needle地址
    失败:NULL

       char src[] = "ddddabcd123abcd333abcd";
       char *p = strstr(src, "abcd");
       printf("p = %s\n", p);
    
  16. 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, "*");
    	}
  1. 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);
      
  • 14
    点赞
  • 43
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值