1.输入输出语句
1.1格式化输入输出
(1)printf()
函数原型:#include <stdio.h>
int printf(const char *format,...)
返回值:成功返回输出的字符,失败返回-1
格式符:
%d ---->十进制的输出
%f ---->浮点数的输出
%s ---->字符串的输出
%c ---->字符的输出
%e ---->浮点的指数的输出
%x ---->16进制的输出
%o ---->8进制的输出
%p ---->地址的输出
修饰符:
m ---->输出的域宽,不够的用空隔齐,默认右对齐(域宽<数据本身的长度时,以数据的长度为准)
.n ---->输出的小数位数,5舍6入
- ---->在域内左对齐
0 ---->域宽不够时,用0来补齐
# ---->十六或八进制的前导 0X,0
+ ---->有符号的正数之前显示+
l ---->在d或f之前 制定精度为long或double
(2)scanf()
函数原型:
#include <stdio.h>
int scanf(const char *format,...)
格式控制串 地址表
返回值:成功返回成功输入的个数,int类型,如没有获取到,返回0,错误结束返回-1
格式控制串与printf的类似。
scanf("%4d%2d%2d",&y,&m,&n); (域宽可以限定输入的界限)
输入:20220711
输出:
y: 2022 m: 07 n: 11
非法字符输入,,,,,会使变量赋值失败
输入:25,32,12
scanf("%d%d%d",&x,&y&z);
x:25 y=? z=?
输入了非法字符,导致非法字符之后的数据输入失败。
垃圾字符的处理:
1.加空格 scanf("%c %c",&a,&b); (字符读取中间加一个空格,会处理所有的垃圾字符,多个空格或多个回车)
2.%*nc 抑制赋 scanf("%c%*nc%c") 跳过这n次赋值,跳过n个垃圾字符
3.getchar(); 两次scanf之间加一个getchar() 用getchar吃垃圾字符,一次只能吃一个
1.2.字符的输入输出
(1)字符的输入:
#include<stdio.h>
int getchar(void); 成功返回读取到的字符
失败返回EOF(-1)
(2)字符的输出:
int putchar(char c); 成功返回输出的字符
失败返回EOF(-1)
1.3 字符串的输入输出:
1.gets():可能越界
#include <stdio.h>
char *gets(char *s); 成功返回首地址,失败返回NULL
s;用来保存输入的数据的内存块
输入的字符串自动补上‘/0’,输入的字符串不以空格结束,scanf的%s以空格结束,以回车结束
fgets(数组名,最大长度,流(stdin键盘输入));
arr[strlen(arr)-1]='\0' 会把回车也输入,需要把最后的回车置零
2.puts():
#include <stdio.h>
int puts(const char *s);(const 定义的元素不能修改)
成功返回非负数,失败返回EOF(-1)
puts,自带换行功能,puts("");
2.控制语句
2.1顺序结构
自上而下,顺序执行
2.2选择结构
1.if-else
if(表达式) 0为假,非0为真
{
语句块1;(表达式逻辑值为真)
}
else
{
语句块2;(表达式逻辑值为假)
}
2.阶梯结构(在elseif里进行第二次if)
if(表达式1)
{
语句块1:
}
else if(表达式2)
{
语句块2;
}
else if(表达式3)
{
语句块3;
}
......
else
{
语句块n;(以上表达式都不成立)
}
3.嵌套结构(在语句块中进行第二次if)
if(表达式1)
{
if(表达式2)
{
if(表达式3)
{
。。。。。。
}
else
{
语句块3;
}
}
else
{
语句块2;
}
}
else
{
语句块1;
}4.switch (表示式是整数类型不考虑浮点数)
switch(表达式)
{
case 1:语句块1;break;
case 2:语句块2;break;(break跳出switch)
。
。
default:语句块n;
}
当多个case所执行的语句块一样时,可以共用。case相邻,只用一次break;三目运算符:
(表达式1)?表达式2:表达式3
表达式1的值为真则执行表达式2,为假则执行表达式3
eg:max=(a>b)?a:b
等价于 if(a>b)
max=a;
else
max=b;
1.3循环结构
(表达式比循环体多执行一次),表达式不成立,循环体结束
循环一般有三个组成,初值,边界(结束点),自加或自减(方向)1.goto跳转
eg:
...
loop:
...
goto loop;(goto 会去到loop所标记的地方,继续向下运行)
2.while(表达式)(表达式成立执行)
{
循环体;
}
先判断表达式,再执行循环体3.do{
循环体
}while(表达式);
先执行一次,再来判断表达式是否成立。
4.for循环
for(表达式1;表达式2;表达式3)
{
循环体;
}
表达式1 一般给初值,表达式2 一般判断(做边界),表达式3,一般自加或自减
for(;;)相当于while(1),初值与自增可以在循环体中写
辅助控制语句:
break:跳出当前循环(一层)
continue:解说本轮循环,继续下一次循环(一次)
return:结束当前模块(结束函数),函数的返回值
exit:结束一个进程
3.数组
3.1数组的定义
具有一片连续的存储空间,并且是相同数据类型的多个变量的集合
3.2数组的一般形式
<存储类型> <数据类型> <数组名> [ 表达式];
存储类型:一般默认为自动存储 auto
数据类型:看要存储的数据是什么类型,就选择什么类型的数据类型;
数组名:合法的标识符
表达式:数组的维数(元素个数,为常量表达式)
eg:int arr[5]; ----> 表示一个可以存储5个整形数据的数组
首地址和普通的指针不一样
首地址、地址常量
arr+1,表示第二个元素的地址
arr++(×)arr++---->arr=arr+1,数组名不能自加,赋值,原因:数组名为首地址、数组名为地址常量,常量不能自加
3.3数组的引用
数组只能逐个访问; 数组名+下标
数组名[下表];(下标从0开始)、
eg: int arr[5]---->arr[0] ,arr[1] ,arr[2] ,arr[3] ,arr[4]
3.4数组的初始化
1.数组的完全初始化(值以逗号隔开)
int arr[5]={1,2,3,4,5};
2.数组的部分初始化(从左到右赋值,没赋值的部分自动补0)
int arr[5]={1,2,3};
eg: int arr[5]={0};数组的所有元素都赋值为0
3.不给下表的初始化:(数组大小由赋值决定,当我们给了n个初值name这个数组最多就只能存n个数据)
int arr[]={1,2,3,4,5}
3.5数组的大小
数组所占字节数=单个元素所占字节数*元素个数
sizeof(数组名) = sizeof(数据类型) * N
数组元素个数=sizeof(数组名)/sieof(数组类型)
eg: n=sizeof(arr)/sizeof(int);值用%ld来显示
3.6数组的常见错误
eg: int[5]
int[5]=100;//error
c语言不做越界检查,在使用数组的时候一定不要越界(0~4)
eg: int size=5;
int arr[size]={0};//error
数组的表达式不能为变量,size的值可能改变,但数组生成后长度不能改变
3.7常见的两种排序方法
冒泡排序:
顾名思义,水泡会自己向上浮,两个数据比较,小的一个向上移动,每次比较两个,一轮循环比较n-i-1次,进行n次循环。
比较n次,每次排列n-i-1次,(i表示第几次比较)
也就是两层循环,第一层从0~n-1,第二层控制0~n-i-1
eg:10个数字,第一次排列0~9,比较9次
第二次排列0~8,比较8次
......
第九次排列0~1,比较1次
比较内容根据从大到小排列,还是从小到大排序来决定,
eg:
int arr[n],i,j;
for(i=0;i<n;i++)
{
for(j=0;j<i-n-1;j++)
{
if(arr[j]>arr[j+1])//交换
{
a^=b;
b^=a;
a^=b;
}
}
}
选择排序:
找最值的下标,一次循环中,找到最值,然后放到集合首位,进行n-1次循环,每次都在最值之外的集合中找到新的最值,再把他移至此次集合的首位。
循环n-1次,最后一次不用比较
k指向i//假设a[i]为最值
每次循环是i与之后的所有数据比较
如果k下标对应的元素不是最值,则把k指向最值的下表
每次循环完都要比较k的值是否变化,如变化,则把新的最值换至i所在位置
eg:
int arr[n],i,j,k;
for(i=0;i<n-1;i++)
{
k=i;//首先假设第一个值是最大值
for(j=i;j<n;j++)//循环比较谁大的
{
if(arr[j]>arr[k])//如果arr[j]更大,那么k就记录i的值,也就是记录最值的下标
{
k=j;
}
}
if(k!=i)//说明最大值换过,交换
{
arr[i]^=arr[k];
arr[k]^=arr[i];
arr[i]^=arr[k];
}
}
4.字符一维数组
1.一般形式:(char占一个字节)
存储类型是char的一维数组
eg: char arr[5]
char a[6];
str="hello";//error 首地址是常量,不能改变
2.字符数组的初始化
1)逐个字符常量赋值(用下标来赋值)
①完全初始化 char str[5]={'h','e','l','l','o'}(可以,但是输出麻烦)
应该在最后加一个'/0',数组计算长度是需要+1 🤦♀️🤦♀️🤦♀️🤦♀️
char str[6]={'h','e','l','l','o','\0'}
printf("%s",首地址);
输出:从首地址到'\0'结束,
(一般会把字符数组当做字符串处理,完全初始化需要加上'\0')
②部分初始化 char str[5]={'h','e'}
(没有赋值的地方,用'\0'赋值)
char str[5]={65,66,67,68};
字符赋值可以是字符('A'),也可以是ascii值(65)
char str[5]={0};(全部赋值为'\0')
③不给下表的初始化
char str[]={'h','e','\0'}
末尾需要手动补上'\0'便于输出
2)字符串常量赋值(注意大小)
char str[6]="hello";
char str[6]={"hello"};
(“”输入的是字符串,自动在末尾补一个'\0')
5.字符串函数
eg:从终端输入一串字符,统计字符的个数
scanf %s 遇空格结束,要读取空格用gets
输出以 '\0' 结束
1.strlen():求字符串的有效长度,遇到'\0'结束
原型: #include <string.h>
size_t strlen(const char *s);
s:指向用来保存字符串内存块(字符数组)
返回值:字符数组的有效长度ld(不包含'\0')
sizeof()看的是总的大小
strlen()是到'\0'的大小
eg:
char str1[32]="hello";
char str1[]="how";
实现把str2里面的内容拷贝到str1中
2.strcpy():字符串的拷贝
原型: #include <strings.h>
char *strcpy(char *dest,const char *src) 将src指向的串拷贝到dest指向的空间
返回值:返回dest的地址(目标串的首地址)
注意:dest指向的空间需要足够大
在拷贝的时候,连同src指向的串的'\0'一起拷贝,作为新串的'\0'3.strcat():字符串的连接
#include <strings.h>
char *strcpy(char *dest,const char *src)
将sec指向的串连接到dest指向的串后哦面
返回值:返回dest的地址(目标串的首地址)
注意:dest指向的空间需要足够大
在拷贝的时候,连同src指向的串的'\0'一起拷贝,作为新串的'\0'(目的是为了dest这个数组后面有'\0'结尾)
最后也要 str[i]='\0';
如果初始化时已把字符数组全置为0,可以省略
4.strcmp():字符串的比较
#include <strings.h>
int strmp(const char *s1,const char *s2);
比较s1和s2,从左往右比较ASCII值,遇到不同或'\0'结束
返回值:
s1>s2 返回差值 正整数
s1<s2 返回-差值 负整数
s1==s2 返回0
6.二维数组
1.二维数组的一般形式
<存储类型><数据类型><二维数组名>[常量表达式1][常量表达式2],表达式定义的是行列数
eg:
int arr[2][3]--->表示一个两行三列的数组2.二维数组的访问:
数组名[行][列];
下标从0开始3.内存是一维的,二维数组按行序号优先存储
二维数组由多个一维数组组成
eg:
arr[2][3]
由两个一维数组arr[0],arr[1]组成,取元素也是数组名+下标
arr[0]是数组名,常量,表示地址
4.二维数组可以看成由多个元素组成(数量的组成)5.二维数组的初始化:
🐱🚀🐱🚀
没有赋值的地方补0
第一维长度在分行初始化时可以省略
初始化的内容定义了函数({}中有几个大括号就是几行)
不给第一维下标初始化时,几行能存下,就有几行
1)分行初始化:
eg:
int arr[2][3]={{1,2,3},{4,5,6}};
int arr[2][3]={{1,2},{4}};
int arr[][3]={{1,2,3},{4,5,6}};
int arr[2][]={{1,2},{4}};//error 无法确定列数
2)按元素排列顺序初始化:
从首位开始依次赋值
eg:
int arr[2][3]={1,2,3,4,5,6};
int arr[2][3]={1,2,3,4};
int arr[2][3]={0};
int arr[][3]={1,2,3};
//两行能存下就默认两行
int arr[][3]={1,2};
//变成一维数组 不建议使用
6.二维数组的大小
二维数组所占字节数=单行所占字节数*行数
sizeof(arr)=sizeof(arr[0])*m(行数)
单行所占字节数=单个元素所占字节数*列数
sizeof(arr[0])=sizeof(arr[0][0](数据类型))*n(列数)
行数=sizeof(arr)/sizeof(arr[0])
列数=sizeof(arr[0])/sizeof(arr[0][0](数据类型))
eg:打印杨辉三角1
1 1
1 2 1
1 3 3 1
1 4 6 4 1
规律:
a[i][j]=a[i-1][j-1]+a[i-1][j];
分两类数据处理:
j==0的,i==j的
和规律内的
解题思路千千种,总有更合适的算法补充:
转义字符
八进制形式的转义字符最多后跟三个数字,也即\ddd,最大取值是\177-->(127(十进制));
十六进制形式的转义字符最多后跟两个数字,也即\xdd,最大取值是\x7f-->(127(十进制))。
\017 八进制数,一个字符
\082 八进制数,3个字符(八进制数0~7),不会出现8
常用的四种交换方式:
1.借助第三个变量
c=a;
a=b;
b=a;
2.两个数之间进行加减运算,以实现两个数的交换
a=a+b;
b=a-b;
a=a-b;
3.通过异或来进行交换(数字才可行)
a^=b
b^=a
a^=b
4.通过位运算来实现交换(数据有多余的高位空间,不适用于大数)
a=a<<8;(int类型)
a=a+b;
b=a>>8;
a=a&0xff;