概述
C语言基础学习笔记
第一部分
循环后面的笔记
计算机程序描写的是步骤:数数几位数的程序实现
int x=rand()%100;
思路:人数 从左往右 三位数逆序:反复/10 计数
int x;
int n=0;
scanf("%d",&x);
n++;
x/=10;
while(x>0){
n++;
x/=10;
}
printf("/dn",n);
n++
while (x>0){
x/=10; --循环体 死循环避免
n++;
}
先判断条件是否满足,满足执行循环体,
边界测试条件 0 1 10 …
printf("x=%d,n=%d",x,n); //调试输出
printf("heren"); // 提交前注释掉
---------------
do-while循环
数位数的算法
先做事情再判断条件
do{
<循环体语句>
}while(条件语句)
//数位数
#include <stdio.h>
int main()
{
int x;
int n=0;
scanf("%d",&x);
do{
x/=10;
n++;
}while(x>0);//while结尾要用;
printf("%d",n);
return 0;
}
for 循环
eg:阶乘 n!=1x2x3x4x…xn
两种写法: while循环和for循环
int n;
scanf("%d",&n);
int fact=1;
int i=1;
while(i<=n){
fact*=i;
i++;
}
printf("%d!=%dn",n,fact);
写法2:
int n;
scanf("%d",&n);
int fact=1;
int i=1;
for(i=1;i<n;i++){
fact*=i;
}
printf("%d!=%dn",n,fact);
初始条件 循环条件 循环每轮做的事情
int i=1;
int fact=1;
for(i=2;i<=n;i++)
{
fact*=1;
}
printf("")
循环次数
for(i=0; i<n; i++)
- 循环次数为n
- for==while循环
// for循环
for(初始动作;条件;每轮的动作){
...
}
for中的每一个表达式都是可以省略的
for (;条件;)==while(条件)
tips for loops:
·如果有固定次数 for
·如果必须执行一次 do-while
·others while
for ( int i=10; i> 1; i /=2 ) {
printf("%d ", i++);
}
// 输出结果:10 5 3 2
第二部分 数据类型及条件语句
bool类型
#include<stdbool.h> 头文件
逻辑运算
! 非
&& 与
|| 或
表达区间的表达式
x>4 &&x<6 (4,6)
c>='A'&& c<='Z' 判断字母是否是大写字母
!age<20 单目运算符高于双目运算符
!>&&>||
运算符的优先级
优先级 运算符 结合性
1 () 从左到右
2 !+ - ++ -- 从右到左(单目的+和-)
3 * / % 从左到右
4 + - 从左到右
5 < <= > >= 从左到右
短路
逻辑运算是自左向右进行的
对于&& 左边是false时就不做右边了
对于|| 左边是true就不做右边了
eg:
int a=-1;
if(a>0 && a++>1){
printf("OKn");
}
printf("%dn",a);
// result:-1
- conclude: 不要把赋值,包括复合赋值组合进表达式
条件运算符
- 优先级高于赋值运算符,但低于其他运算符
m<n?x:a+5
a++>=1 &&b-->2?a:b
//问号前面是条件 冒号前面是条件满足的值 冒号后面是条件不满足的值
嵌套条件表达式
条件运算符自右向左结合的
- 逗号运算 所有运算符中最低的
- 逗号连接两个表达式,并以其右边的表达式的值作为它的结果
- 逗号两边的表达式会先计算,逗号的组合关系是自左向右的,故左边的表达式会先计算,右边的表达式的值留下来作为逗号运算的结果
// for中使用逗号
for(i=0,j=10; i<10; i++,j--)
4.2级联和嵌套的判断
嵌套的if-else语句
eg:3个数中 a b c找最大的
// 分段函数计算
if
else if
else--对应最后一个if
int f
if(x<0){
f=-1;
} else if(x==0){
f=0;
}else{
f=2*x;
}
printf("%d",f); // 单一出口
4.3多路分支
switch-case
switch(type=控制表达式 只能是int类型的结果){
case 1=常量 可以是常数,也可以是常数计算的表达式2、1+1、:
p;
break;--由break分割 case1: case2: p;break;
case 2:
p;
break;
...
default:
p;
break;
}
分支-06.成绩转换 写题目
循环的例子
int x;
int ret=0;
x=128;
while(x>1){
x/=2;
ret++;
}
4.4 do-while
// 算平均数
// 直到读到-1 循环结束 正整数相加
do{
scanf("%d",&number);
if(number!=-1){
sum=sum+number;
count++;
}
}while(number!=-1) // 条件满足进行循环 number==-1 结束循环
// for循环是条件不满足结束循环 i<n i==n结束循环
// replace code:
scanf("%d",&number);
while(number!=-1){
sum+=number;
count++;
scanf("%d",&number);
}
// 猜数游戏--二分法
// 分析 使用循环 文字描述核心程序 重点是循环的条件 以及循环结束的条件
/*
number:存放随机数
count:计次数
a:用户输入数据
compare 输出“大”,“小”
再次输入a 直到猜中然后结束
rand()随机数函数
*/
#include <stdio.h>
#include<stdlib.h>
#include <time.h>
int main()
{
srand(time(0));
int number=rand()%100+1;
int count=0;
int a=0;
...
do{
p
s
count++;
if(...){
...
}else if(){
...
}
}while(a!=number); 循环结束为成立的条件
--适合使用do-while循环
}
// 整数的分解->数的逆序
// I位数 分解
/*
number%10 得到个位数
number/10 去掉个位数
number/10 %10 得到10位数
*/
第三部分 循环
5.1循环控制
判断素数代码如下:
#include <stdio.h>
int main()
{
int x;
scanf("%d",&x);
int i;
int isPrime=1; //x是素数
for(i=2;i<x;i++){
if(x%i==0){
isPrime=0;
}
}
}
// break:跳出循环
// continue:跳过循环
5.2嵌套循环
输出100以内的素数
凑硬币 三重循环
接力break exit=0;
goto语句 goto out 最内层到最外层
5.3 循环应用
求和 f(n)=1+1/2+1/3+1/4…代码如下:
#include <stdio.h>
int main()
{
int n;
int i;
double sum=0.0;
scanf("%d",&n);
for(i=1;i<=n;i++){
sum+=1.0/i;
}
printf("f(%d)=%fn",n,sum);
return 0;
}
求和:f(n)=1-1/2+1/3-1/4+1/5-…代码如下:
#include <stdio.h>
int main()
{
int n;
int i;
double sum=0.0;
int sign=1;
scanf("%d",&n);
for(i=1;i<=n;i++){
sum+=sign*1.0/i;
sign=-sign;
}
printf("f(%d)=%fn",n,sum);
return 0;
}
求最大公约数 --两种算法 一、枚举 二、辗转相除法
int a,b;
int min;
scanf("%d%d",&a,&b);
if(a<b){
min=a;
}else{
min=b;
}
int ret=0;
int i;
for(i=1;i<min;i++){
if(a%i==0){
if(b%i==0){
ret=i;
}
}
}
printf("%d和%d的最大公约数是%d.n",a,b,ret);
辗转相除法
1、如果b=0,计算结束,a就是最大公约数
2、否则,计算a/b的余数,让a=b,而b等于那个余数
3、return step 1
—good habit 先敲框架,再敲内容
整数分解
正序整数分解
- 输入一个非负整数,正序输出它的每一位数字
- 输入:13425
- 输出:1 3 4 2 5
review
先逆序,在分解 适用于末尾无0的数字
第四部分 数组及函数
6.1数组
1、定义数组
int number[100];
2、对数组中的元素赋值
while(x!=-1){
number[cnt]=x;
sum+=x;
cnt++;
scanf("%d",&x);
}
tips:使用数组中的元素 遍历数组,数据与列表的区别
定义数组
数组:
元素具有相同的数据类型
一旦创建,不能改变大小
int a[10] :a[0]...a[9]
数组的单元:下标从0开始
有效的下标范围
数组越界 指针出错
[0,数组下标-1]
统计数字出现次数
数组初始化
magic number
const int number=10;--数组的大小
int count[number];--定义数组
for(i=0;i<number;i++){
count[i]=0;
}--初始化数组
count[x]++ 数组参与运算 方式很多
for(i=0;i<number;i++){
printf("%d:%dn",i,count[i]);
}--遍历数组输出
6.2函数定义与使用
“代码复制”表示代码质量不良
函数定义
函数头 返回类型void sum--函数名(int begin,int end--参数表){
int i;--函数体
}
调用函数
函数名(参数值)
()起到了表示函数调用的重要作用
即使没有参数也需要()
·若有参数,需要给出正确的数量和顺序
·这些值会被按照顺序依次用来初始化函数中的参数
–函数返回值
6.3函数的参数和变量
函数原型
函数的先后关系
函数声明 位置 函数定义位置
-函数参数
调用函数:
·如果函数有参数,调用函数时必须传递给它数量、类型正确的值
·可以传递给函数的值是表达式的结果,include:
字面量、变量、函数的返回值、计算的结果
类型自动转换 C++/Java更严格
传值:每个函数有自己的变量空间,参数也独立 形式参数
void swap(int a,int b)//形参
swap(a,b)--a,b为实参
本地变量,局部变量
自动变量:生存期是自动的
变量的生存期和作用域
本地变量的规则:
1、本地变量是定义在块内的
可以定义在函数的块内
可以定义在语句的块内
可以定义在一对大括号内
2、程序进入块内前,变量不存在,程序离开这个块,变量消失
3、快外面定义的变量在里面依旧有效
4、块里面定义的和外面定义的重名无法掩盖外面的
5、不能在一个块内定义同名的变量
6、本地变量默认不会被初始化
7、参数在进入函数的时候被初始化了
—其他细节
1、没有参数时 void f(void)–明确表明无任何参数
void f()–不确定参数形式
逗号运算符?
函数不能嵌套
return (i);表达式 --建议写 retrun i;表达式
6.4二维数组
二维数组定义
int a[3][5]
通常理解为a是一个3行5列的矩阵
二维数组的遍历 一重for循环遍历行号 第二重循环遍历列号
1、二维数组的初始化
int a[][5]={
{0,1,2,3,4},
{2,3,4,5,6},
};
·列数是必须给出的,行数可以由编译器来数
·每行一个{},逗号分隔
// tic-tac-toe游戏 井字棋
// 读入矩阵
const int size=3;
int board[size][size];
int i,j;
int num0fX;
int num0fO;
int result=-1;//-1 平局 1 X win 0 O win
//读入矩阵
for(i=0;i<size;i++){
for(j=0;j<size;j++){
scanf("%d",&board[i][j]);//读入矩阵
}
}
//检查行
for(i=0;i<size&&result==-1;i++){
num0fO=num0fX=0;
for(j=0;j<size;j++){
if(board[i][j]==1){
num0fX++;
}else{
num0fO++;
}
}
if(num0fO==size){
result=0;
}else if(num0fX==size){
result=1;
}
}
//检查列
if(result==-1){
for(j=0;j<size&&result==-1;j++){
num0fO=num0fX=0;
for(i=0;i<size;i++){
if(board[i][j]==1){
num0fX++;
}else{
num0fO++;
}
}
if(num0fO==size){
result=0;
}else if(num0fX==size){
result=1;
}
}
}
第五部分 数组的运算及排序算法
7.1 数组运算
数组的集成初始化
数组初始化02
int num0[10] = {0};
数组的大小:
- sizeof给出的整个数组所占据的内容的大小,单位是字节
- sizeof(a)/sizeof(a[0])
- sizeof(a[0])给出的数组中单个元素的大小,相除得到了数组的单元个数
- 一旦修改代码中的初始数据,不需要修改遍历代码
—数组的赋值 不可以直接赋值 只能遍历数组(Python区别)
数组变量本身不能被赋值,必须采用遍历–the only one
for(i=0;i<length;i++){
b[i]=a[i];
}
eg:通常都是使用for循环,让循环变量i从0到<数组的长度,让循环体内最大的i正好是数组最大的有效下标
常见错误:
1、循环结束条件是<=数组长度
2、离开循环后,继续用i的值来做数组元素的下标
数组作为函数参数时,必须再用另一个参数来传入数组的大小
- 不能在[]中给出数组的大小
- 不能再利用sizeof来计算数组的元素个数
eg: isPrime
01、如果x是偶数,n/2
02、sqrt(x) 循环sart(x)
–unix系统中 man sqrt
03、判断是否能被已知的且<x的素数整除
int isPrime(int x);
ways01:
int isPrime(int x){
int ret=1;
int i;
if(x==1) ret=0;
for(i=2;i<x;i++){
if(x%i==0){
ret=0;
break;
}
}
return ret;
}--从2到x-1测试是否可以整除
程序效率
分析时间复杂度 :对于n要循环n-1遍,当n很大时就是n遍
ways02:
int isPrime(int x){
int ret=1;
int i;
if(x==1||
(x%2==0&&x!2))
ret=0;
for(i=3;i<x;i+=2){
if(x%i==0){
ret=0;
break;
}
}
return ret;
}--去掉偶数后,从3到x-1,每次加2
如果x是偶数,则非素数
循环(n-3)/2 +1遍—趋近于n/2
ways03:
int isPrime(int x){
int ret=1;
int i;
if(x==1||(x%2==0&&x!=2))
ret=0;
for(i=3;i<sqrt(x);i+=2){
if(x%i==0){
ret=0;
break;
}
}
return ret;
}--无需到x-1 到sqrt(x)即可
ways04:
判断是否能被已知的且<x的素数整除
int main(void){
const int number=100;
int prime[number]={2};
int count=1;
int i=3;
while(count<number){
if(isPrime(i,prime,count)){
prime[count++]=i;
}
i++;
}
for(i=0;i<number;i++){
printf("%d",prime[i]);
if((i+5)%5) printf("t");
else printf("n");
}
return 0;
}
int isPrime(int x,int knownPrimes[],int numberOfKnownPrimes){
int ret=1;
int i;
for(i=0;i<numberofKnownPrimes;i++){
if(x%knownPrimes[i]==0){
ret=0;
break;
}
}
return ret;
}
素数表
构造素数表
- 构造n以内的素数表
1、令x为2
2、将2x、3x、…倍数删除
3、令x为下一个没有被标记为非素数的数,重复2;直到所有的数都已经尝试完毕
—伪代码
构造n以内(不含)的素数表
1、开辟prime[n],初始化其所有元素五日1,prime[x]为1表示x是素数
2、令x=2
3、如果x是素数,则对于(i=2;xi<n;i++)令prime[ix]=0
4、令x++,如果x<n,重复3,or end;
7.2搜索
在一个数组中找到某个数的位置 基本方法:遍历
int search(int key,int a[],int len){
int ret=-1;//默认找不到
for(int i=0;i<len;i++){
if(key==a[i]){
//return i;
ret=i;//返回元素所在位置
break;
}
}
//return -1;//违反单一出口 虽然没有ret变量
return ret;
}
进阶eg:
char *name[]={'','',};
结构体 struct{
int amount;
char *name;
}coins[]={
,,,
}
sizeof(coins)/sizeof(coins[0])
线性搜索没有效率,
对于有序数组,
二分搜索法
left right mid
mid=left+right /2
left=mid+1
...
right=mid-1;
二分搜索的效率很高:log2N 以2为底的N的对数
N | log2N |
---|---|
10 | 3 |
100 | 7 |
1000 | 10 |
1000000 | 20 |
1000000000 | 30 |
7.3 排序初步
排序算法
无序数组-有序数组-二分法
选择排序 选出最大的数 和位置交换
一、找出最大的数 另i=0的数最大 遍历 找出max
二、和最大位置的数交换 len-1开始
遍历全部数组
第六部分 指针及字符串
8.1指针
sizeof(int) 4byte
&:获得变量的地址,操作数必须是变量
printf("%pn",&i);
printf("%x",&i);
x86和x64 int的结果不同
指针
scanf
// 指针 就是保存地址的变量
int i;
int* p=&i;
int* p,q;
//*p是一个int int *p,*q;
指针变量:变量的值是内存的地址;普通变量的值是实际的值;指针变量的值是具有实际值的变量的地址
作为参数的指针
void f(int *p) 在被调用的时候得到了某个变量的地址;
int i=0; f(&i); 在函数里面可以通过这个指针访问外面的这个i
&i是i的地址
f函数可以防访问外部变量i
访问那个地址上的变量*
* 是一个单目运算符,用来访问指针的值所表示的地址上的变量
int k=*p;
*p=k+1;
传入地址
传入函数的数组:实际上是指针,sizeof(a)==sizeof(int*),但是可以用数组的运算符[]进行运算。
数组变量是特殊的指针
int *const a[];
8.2字符类型
char是一种整数,也是一种特殊的类型,字符。
reason:
01:用单引号表示的字符字面量:‘a’,‘1’;
02:''也是一个字符
// printf和scanf 用%c来输入输出字符
c=1
d='1'
printf("c=%d",c); result:c=1
printf("d=%d",d); result:d=49
// problem:如何输入'1'这个字符给char c?
scanf("%c",&c);//输入字符型 1相当于49
scanf("%d",&i); c=i;//输入49 得到字符型'1'
// scanf不能处理char型变量 需要赋值给int型变量
混合输入
scanf("%d %c",&i,&c);
scanf("%d%c",&i,&c); 不能读到空格后面的数字 c=32 ' '
字符计算
c='A';
c++;
- 一个字符加一个数字得到ASCII码表中那个数之后的字符
- 两个字符的减,得到它们在表中的距离
/逃逸字符/
字符 | 意义 |
---|---|
b | 回退一格 |
b | 回退一格 |
t | 到下一个表格位 |
n | 换行 |
r | 回车 |
" | 双引号 |
’ | 单引号 |
反斜杠本身 |
8.3字符串
char word[]={'H','e','l','l','o','!'}; // 字符数组
// 字符串:
char word[]={'H','e','l','l','o','!','