基本概念和语法点
绪论
- C语言发展历程:机器语言-汇编语言-高级语言
- 程序错误分3种。1、语法错误通过编译器发现;2、逻辑错误通过调试查找;3、运行异常错误:对程序运行环境的非正常情况考虑不周
- C程序必须经过编译、链接后生成可执行文件(.exe)才能运行
- /……/用于行注释,/* …… */用于对程序块的注释
- C语音系统以文件为单位编译源程序,程序由函数组成
数据基本类型
- 整型默认long、designed,四字节,取值 -231~231-1,无符号 0~232-1,短整型short int 两字节,-215~215-1
- 实型默认double,即3.14f为float,3.14为double,float占4字节/小数点后6位/%f,double占8字节/小数点后12位/%lf。常量后加l或L表示长整型,加u或U表示无符号,加f或F表示float
- 10-3:1e-3,e或E前面必须有数字,后面必须是整数
- 八进制数:数字前加0,0111是73,-0111是-73;十六进制数:数字前加0x,0x111是273
- \t:8个空格;\r:代替该行最开始相等数目的字符("1239226\r561"输出为5619226);\b:光标左移并覆写(“12345\b\b\b\b678”,光标在5右边,移动4次到1的右边,故输出为16785)
- '\012’转义表示ASCII码为10即换行
- 区分:'a’为字符常量,占1字节,"a"为字符串,占2字节
- 实现四舍五入:
float a=1.23456;//保留两位
int i;
i=a*100+0.5;
a=i/100;//保留几位就用10的几次方
逻辑运算短路性质
当使用逻辑与运算符(&&)连接多个条件时,如果其中一个条件为假,则整个表达式的结果必定为假。因此,如果在计算过程中遇到一个假条件,后面的条件将不会被计算,直接返回假作为整个表达式的结果。
逻辑或运算符(||)同理。当使用逻辑或运算符连接多个条件时,如果其中一个条件为真,则整个表达式的结果必定为真。
三目运算符
格式:条件表达式 ? 表达式1 : 表达式2。例:
int score = 80;
String level = (score >= 90) ? "优秀" : ((score >= 80) ? "良好" : "及格");
在上述代码中,根据score的分数不同,返回不同的等级。如果分数大于等于90,则返回"优秀";如果分数大于等于80,则返回"良好";否则返回"及格"。
运算符的优先级和结合性
优先级从高到低:
() 、[]、->、.
!、++、-- (右结合)
*、 / 、%
+、-
<、>、<=、>=
==、!=
&&
||
?:
=、+=、-=(右结合)
,
宏定义和宏替换
注意:替换时不要人为加括号,如定义define N 1+2
(注意没有分号)则程序中N*2=5
函数的输入与输出
- 输出%:“%%”;输出\:“\”;输出":" \ " "
- 格式字符:%5d表示至少输出5字符,空格填左边,%-5d空格填右边;%m.nf输出float至少占m列,小数点也算一列,其中小数n位,四舍五入
- scanf无法输入空格
基本结构与基本语句
- 程序=算法+数据结构
- 基本结构:顺序、分支、循环
- 5种C基本语句:表达式语句、控制语句、函数调用语句、复合语句、空语句
- 在switch语句中,每个case后面都需要添加break语句,如果忘记添加break语句,程序将会继续执行下一个case中的代码;switch中的default可省
变量
1 作用域
1、局部变量的作用范围仅限于定义它的函数内部,而全局变量定义在函数外部,从定义处起作用
2、局部变量的生命周期只在函数执行期间存在,而全局变量的生命周期在整个程序执行期间都存在。
2 生存期
1、动态变量auto(默认类型)的内存管理由程序员手动控制,需要在适当的时候分配和释放内存空间,再次调用时重新分配内存,只在本函数起作用;
2、静态变量static的内存管理由编译器自动完成,无需程序员干预,且生存周期延长,调用结束不释放内存,下一次调用继续用。//函数调用形参不能是静态变量
- 一个编译后的C程序右4个内存分区
程序代码区
静态数据区:存放全局变量和静态变量
//全局变量不能用表达式定义,如int a=1,b=2,c=a+b;
是错误的
堆区
栈区:存放局部变量
数组
-
字符数组初始化:char s[]=“Asoul”;(夹带私货hh)也可以用get(s)和put(s)整体输入输出
-
注意:char s[5]={‘A’,‘s’,‘o’,‘u’,‘l’};此种形式不是字符串,无字符串结束标志,故只是普通的一维字符数组,不能由put(s)输出,只能用%c逐个输出
-
int a[2][3]={1,2,3,4,5,6},(*p)[3]=NULL;p=a;
程序中给行指针变量p赋值后,p指向a[0],即p的值为&a[0],即行地址,而不是a[0][0];输出a[i][j]可以用p[i][j]、*(p[i]+j)、*(*(p+i)+j)、(*(p+i))[j]
代替,若用列指针,则用*(p+i*3+j)
,其中3代表每一行元素的个数 -
区分 int (*p)[4] 和 int *p[4]
int (*p)[4]
是一个指针。p 可以指向一个包含4个整数的数组,通过 p可访问和操作该数组中的元素。例如,(p+1)我们可以访问数组中的第二个元素。
int *p[4]
是一个包含4个指针的数组,每个指针可以指向一个整数变量。通过 p[i],我们可以访问和操作第i个指针指向的整数。例如,(p[1])我们可以访问第二个指针指向的整数。 -
易错
1、int *p,m=1,n;
则用scanf("%d",&n);*p=n;
对p赋值是错误的,因为p没有赋初值;而p=&n;*p=n;
正确,p=&n对p赋予初值
2、char a[30],*p=a;p="asoul";
不能将字符串保存到数组a中,因为将p指向字符串后,p就不再指向数组a了,正确的保存方式:strcpy(p,"asoul");
3、char a[]="hello",*p="hello";
则sizeof(a)为6,sizeof(p)=2
结构体
- 结构体数据类型的定义格式:
struct student//student为结构体类型名
{
int num;
char name[20];
float score;
};
- 初始化
a.struct student stu1={11,"shuo",99.9};
b.
struct student//结构体类型名student可省
{
int num;
char name[20];
float score;
}stu1={11,"Shuo",99.9};
- 使用方法
#include <stdio.h>
#include <string.h>
struct student
{
int num;
char name[20];
struct information{int Class;float score;}in;
};
int main()
{
struct student stu1;
stu1.num=1211;
strcpy(stu1.name,"Gabe");
stu1.in.score=99.9;
printf("%d %s %.1f\n",stu1.num,stu1.name,stu1.in.score);
return 0;
}
- 指向结构体的指针
格式:struct student *p;
结构体指针变量引用成员的形式有3中:
若有声明:struct student stu1,*p=&stu1;
则stu1.num=101/(*p).num=101/p->num=101;
都可以将stu1的num赋值为101 - 指向结构体数组的指针变量
- 若有声明:
struct student stu[3],*p=stu;
则stu[0].num=101;(*p).num=101;p->num=101;
都可以将stu[0]的num赋值为101
int main()
{
struct student stu[5],*p=stu;
strcpy((*(p+2)).name,"Shuo");
printf("%s",(p+2)->name);
return 0;
}
注意:->优先级高于++,故以下一段程序输出102:
int main()
{
struct student stu[5],*p=stu;
p->num=101;
printf("%d",++p->num);
return 0;
}
枚举类型
enum Weekday {
Monday=1,
Tuesday,
Wednesday,
Thursday,
Friday,
Saturday,
Sunday
}workday,weekend;
指定Monday=1,后面的枚举元素的值依次加一,若没有指定,则从0开始
若weekend=Saturday;printf("%d",weekend);
则输出6
文件
- 文件操作基本步骤:打开-使用-关闭
- 文件指针变量的声明:格式
FILE *文件指针变量名
说明:FILE实际上是结构体类型名,必须大写,包括在#include <stdio.h>z中 - 文件常用函数
(1)打开文件,格式:文件指针名=fopen("文件名","使用文件方式");
(2)关闭文件,格式:fclose(文件指针名);
(3)fprintf(文件指针名,"格式控制字符串",输出表列);
例如fprintf(fp,"%d,%6.2f",i,t);
将i和t按%d,%6.2f的格式输出到fp指向的文件中
(4)fscanf(文件指针名,"格式控制字符串",输入表列);
例如fscanf(fp,"%d,%6.2f",&i,&t);
将文件中的%d赋值给i,将%6.2f赋值给t
(5)
c=fgetc(fp);
//从fp指针指向的文件中读取一个字符赋给变量c,
fputc(c,fp);
//把变量中的字符写到fp指针指向的文件
(6)
fputs("Hello",fp);
fgets(str,10,fp);
//从fp指针指向的文件中读9个字符到数组str中,因为要包括最后的’\0’
若用fscanf:fscanf(fp,"%s",str);
读入字符串时,遇换行符或文件尾(EOF,代表值为-1)结束,并在最后加’\0’
以下程序读懂即可:
#include <stdio.h>
int main()
{
char s1[80],s2[80];
int a,b;
FILE *fp;
if((fp=fopen("test","w"))==NULL)
puts("can't open file");
fscanf(stdin,"%s%d",s1,&a);//从键盘输入
fprintf(fp,"%s %d",s1,a);//写入文件test
fclose(fp);
if((fp=fopen("test","r"))==NULL)
puts("can't open the file");
fscanf(fp,"%s%d",s2,&b);//从文件test中读取
fprintf(stdout,"%s %d",s2,b);//输出到屏幕
fclose(fp);
return 0;
}
程序设计
1、反序数、数的拆分
(1)反序数
函数功能:输入一个数,将其逆序输出
#include<stdio.h>
int main()
{
int n,inverse_n=0;
scanf("%d",&n);
while(n!=0)
{
inverse_n=inverse_n*10+n%10;
n=n/10;
}
printf("%d",inverse_n);
return 0;
}
(2)数的拆分
a.水仙花数:一个三位数,其各位数字的立方和等于本身。
函数功能:输出所有的水仙花数。
#include<stdio.h>
int judge(int n);
int main()
{
int i;
for(i=100;i<1000;i++)
if(judge(i))printf("%d ",i);
return 0;
}
int judge(int n)
{
int t,k=n,sum=0;
while(k!=0)
{
t=k%10;
sum+=t*t*t;
k=k/10;
}
if(sum==n)return 1;
return 0;
}
b.同构数:出现在其平方数的右边,如5出现在25的右边,25出现在625的右边,故5和25为同构数。
函数功能:找出2~99中所有的同构数。
#include<stdio.h>
int main()
{
int i;
for(i=2;i<10;i++)
{
if(i*i%10==i)printf("%d ",i);
}
for(i=11;i<100;i++)
{
if(i*i%100==i)printf("%d ",i);
}
return 0;
}
总结:n%10得到n的最后一位,n/10的m次方得到的数是原来的n去掉后面的m位。
2、素数的判定
a.素数
除了1和本身外没有其他因数的数。
函数功能:找出2~99内所有的素数。/判断一个数是否为素数类似
#include<stdio.h>
int prime_number(int n);
int main()
{
int i;
for(i=2;i<100;i++)
if(prime_number(i))printf("%d ",i);
return 0;
}
int prime_number(int n)
{
int i;
for(i=2;i<n;i++)
if(n%i==0)return 0;
return 1;
}
b.超级素数
一个素数,依次去掉最低位,若得到的数均为素数,且各数字不为0,则称为超级素数
函数功能:查找100~9999中所有的超级素数
#include<stdio.h>
int prime_number(int n);
int super_prime_number(int n);
int main()
{
int i;
for(i=100;i<10000;i++)
if(super_prime_number(i))printf("%d ",i);
return 0;
}
int prime_number(int n)
{
int i;
if(n<2) return 0;//1不是素数
for(i=2;i<n;i++)
if(n%i==0)return 0;
return 1;
}
int super_prime_number(int n)
{
while(n!=0)
{
if(prime_number(n)==0)return 0;
n=n/10;//去掉最低位
}
return 1;
}
(不同定义版本的超级素数)若依次去掉最高位得到的数为素数:
int super_prime_number(int n)
{
int i;
if(prime_number(n%10)==0)return 0;//先判断最后一位
while(n>10)
{
if(prime_number(n)==0)return 0;
i=1;
while(n/i!=0)
{
i*=10;
}
n=n%(i/10);//去掉最高位
if(n<i/100)return 0;//除去含0的数
}
return 1;
}
3、斐波那契数列
Fib(n)=Fib(n-1)+Fib(n-2),Fib(0)=Fib(1)=1;
函数功能:求Fibonacci数列前20项并输出。
#include<stdio.h>
int Fib(int n);
int main()
{
int i;
for(i=0;i<20;i++)
printf("%d ",Fib(i));
return 0;
}
int Fib(int n)
{
if(n==0||n==1) return 1;
else return Fib(n-1)+Fib(n-2);
}
4、最大公约数和最小公倍数
a.最大公约数
#include<stdio.h>
int max_common_divisor(int a,int b);
int main()
{
int a,b,c;
scanf("%d %d",&a,&b);
c=max_common_divisor(a,b);
printf("%d",c);
return 0;
}
int max_common_divisor(int a,int b)
{
int n;
while(a%b!=0)
{
n=a%b;
a=b;
b=n;
}//辗转相除法
return n;
}
b.最小公倍数
最小公倍数等于上述程序中的a*b/c,只需将printf行改为printf("%d",a*b/c);
即可。
5、有条件的求和、求积
a.输入30个学生的单科成绩,输出高于平均分的成绩
b.去掉最高分和最低分算平均分
此链接程序设计第1题为a,第5题为b,点击目录可直接跳转
c.编写函数计算sinx的值,精度10^(-6)。
#include<stdio.h>
#include<math.h>
double sin(double x);
int main()
{
double x;//此处x为弧度制
scanf("%lf",&x);
printf("sin%lf=%lf",x,sin(x));
return 0;
}
double sin(double x)
{
int i=1,t=1;
double item=x,sum=0;
for(i=0;fabs(item)>=1e-6;i++)
{
sum+=item;
t=-t;
item=t*item*x*x/(2*i+2)/(2*i+3);
}
return sum;
}
6、一维数组最值问题、排序、查找
最值问题:
函数功能:数组中最大值和最小值交换位置。
#include<stdio.h>
int main()
{
int a[10]={1,9,6,2,0,4,8,7,3,5},i,temp;
int max=0,min=0;
for(i=0;i<10;i++)
{
if(a[i]>a[max])max=i;
if(a[i]<a[min])min=i;
}//此函数也可实现找到最大值和最小值的下标的功能
temp=a[min];
a[min]=a[max];
a[max]=temp;
for(i=0;i<10;i++) printf("%d ",a[i]);
return 0;
}
排序:冒泡排序、选择排序、插入排序
此链接程序设计第6题:三种排序方法
查找:顺序查找、二分查找
顺序查找:if(a[i]==n),输出i即可
二分查找:以下程序先将数据排序再进行二分查找:
#include <stdio.h>
int main()
{
int score[10],i,j,temp;
int low=0,high=9,mid,t,flag=0;//t为需要查找的成绩
printf("please input 10 students' score:\n");
for(i=0;i<10;i++)
scanf("%d",&score[i]);
for(i=0;i<9;i++)//冒泡排序需要循环n*(n-1)/2次
for(j=0;j<9-i;j++)
if(score[j]>score[j+1])
{
temp=score[j];
score[j]=score[j+1];
score[j+1]=temp;
}
for(i=0;i<10;i++)
{
printf("%-4d",score[i]);
}
printf("\n");
printf("input the score you want:");//二分法查找元素
scanf("%d",&t);
while(low<=high)
{
mid=(low+high)/2;
if(t==score[mid])
{
flag=1;
break;
}
if(t>score[mid]) low=mid+1;
else high=mid-1;
}
if(flag==1)
printf("%d在第%d的位置上",t,mid+1);
else printf("not exist");
return 0;
}
杨辉三角
#include <stdio.h>
int main()
{
int i,j,k,pa[100]={1};
scanf("%d",&k);
printf("%5d\n",pa[0]);
for(i=1;i<k;i++)
{
pa[i]=1;
for(j=i-1;j>0;j--)
pa[j]=pa[j]+pa[j-1];
for(j=0;j<=i;j++)
printf("%5d",pa[j]);
printf("\n");
}
return 0;
}
7、二维数组的生成、打印和矩阵转置
a.函数功能:输入一个3*4的矩阵,输出该矩阵和转置矩阵。
#include<stdio.h>
int main()
{
int a[3][4];
int i,j;
for(i=0;i<3;i++)
{
for(j=0;j<4;j++)
scanf("%d",&a[i][j]);
}//输入矩阵
for(i=0;i<3;i++)
{
for(j=0;j<4;j++)
printf("%d ",a[i][j]);
printf("\n");
}//输出矩阵
for(i=0;i<4;i++)
{
for(j=0;j<3;j++)//b[i][j]=a[j][i];
printf("%d ",a[j][i]);
printf("\n");
}
return 0;
}
b.求矩阵中的最大值以及其在数组中的位置的所有下标。
#include<stdio.h>
int main()
{
int a[3][3]={1,9,9,6,1,2,1,1};
int row=0,colume=0,i,j;
for(i=0;i<3;i++)
{
for(j=0;j<3;j++)
if(a[i][j]>a[row][colume])
{
row=i;
colume=j;
}
}//找到第一个最大值的行与列
for(i=0;i<3;i++)
{
for(j=0;j<3;j++)
if(a[i][j]==a[row][colume])printf("row:%d,colume:%d\n",i+1,j+1);
}
return 0;
}
c(选看).输出一个n阶的螺旋矩阵(顺时针,规定n<10)
程序设计思路:将螺旋矩阵的一周分为上、下、左、右四个部分,用4个循环分别赋值。
#include<stdio.h>
void f(int a[10][10],int n,int j);
int main()
{
int i,j,n;
int a[10][10];
scanf("%d",&n);//阶数
i=(n+1)/2;//从外到里一共i层
for(j=0;j<i;j++)
f(a,n,j);//用于给第j层赋值的函数
for(i=0;i<n;i++)
{
for(j=0;j<n;j++)
printf("%-5d ",a[i][j]);
printf("\n");
}
return 0;
}
void f(int a[10][10],int n,int j)
{
int i,k,m;
m=4*j*n-4*j*j+1;//第j层最左上角元素
for(k=j;k<n-j;k++)
a[j][k]=m+k-j;//上层
for(i=j+1;i<n-j;i++)
a[i][n-1-j]=a[i-1][n-1-j]+1;//右边
for(k=n-2-j;k>=j;k--)
a[n-1-j][k]=a[n-1-j][k+1]+1;//下层
for(i=n-2-j;i>j;i--)
a[i][j]=a[i+1][j]+1;//左边
}
8、字符串相关
(1)用数组或指针处理字符串
a.判断字符串是否为回文字符串
利用数组判断:此链接程序设计第12题
利用指针判断:此链接程序设计第2题
b.统计字符串中单词的个数
程序设计思路:
1、用一维字符数组存放字符串;
2、遇到空格表示单词结束,非空格单词没拼完。
#include<stdio.h>
#include<string.h>
int main()
{
char str[100];
int i,n,word=1;
gets(str);
n=strlen(str);
for(i=0;i<n;i++)
if(str[i]==' ')word+=1;
printf("%d",word);
return 0;
}
c.人名按照首字母排序
此链接程序设计第9题
(2)字符大小写转换
a.函数功能:将字符串中的大写字母转变为对应小写字母并输出
大小写字母的对应关系:‘a’-‘A’=32。
#include<stdio.h>
#include<string.h>
int main()
{
char str[100];
int i;
gets(str);
for(i=0;i<100;i++)
if(str[i]>='A'&&str[i]<='Z') str[i]=str[i]+32;
puts(str);
return 0;
}
b.函数功能:将字符串中前三个小写字母改为大写字母并输出。
#include<stdio.h>
#include<string.h>
int main()
{
char str1[20]={"xUjUNsHUO"},str2[20],*p=str2;
int i;
for(i=0;i<20;i++)
if(str1[i]>='a'&&str1[i]<='z') *(p++)=str1[i]-32;
for(i=0;i<3;i++)
printf("%c",str2[i]);
return 0;
}
(3)字符串连接
函数功能:将两个字符串按字典顺序排序,合并为一个新的字符串并删除该字符串中相邻位重复出现的字符。
#include<stdio.h>
#include<string.h>
void sort(char a[]);//排序
void merge(char *a,char *b,char *c);//将a、b整合并复制到c中
int main()
{
char a[50]="xujunshuo",b[50]="xiongmao",str[50]={0},*c=str;
/*要对str初始化,否则会输出很多个烫字大家可以试一试hhh*/
sort(a);
sort(b);
merge(a,b,c);
puts(str);
return 0;
}
void sort(char a[])
{
int i,j,n;
char t;
n=strlen(a);
for(i=0;i<n-1;i++)
for(j=i+1;j<n;j++)
if(a[i]>a[j]){t=a[i];a[i]=a[j];a[j]=t;}
}
void merge(char *a,char *b,char *c)
{
int i,n;
strcat(a,b);//将字符串b接在a后面,返回a的首地址
n=strlen(a);
for(i=0;i<n;i++)
if(a[i]!=a[i+1]) *c++=a[i];
}
(4)统计子串的个数和位置
a.函数功能:正向和反向搜索子串
#include <stdio.h>
#include <string.h>
void forwardSearch(char *str, char *sub_str);//正向查找函数
void reverseSearch(char *str, char *sub_str);//反向查找
int main()
{
char str[200]={"There is no dreams in the waves,only monsters,and the monsters are my only friend"};
char sub_str[100];
printf("请输入要搜索的子串:");
scanf("%s",sub_str);
forwardSearch(str,sub_str);
reverseSearch(str,sub_str);
return 0;
}
void forwardSearch(char *str,char *sub_str)
{
int len1=strlen(str),len2=strlen(sub_str);
int i,j;
for(i=0;i<=len1;i++)
{
for (j=0;j<len2;j++)
if (str[i+j]!=sub_str[j])break;
if (j==len2)
printf("正向搜索:子串在字符串中出现在位置 %d\n",i);
}
}
void reverseSearch(char *str,char *sub_str)
{
int len1=strlen(str),len2=strlen(sub_str);
int i,j;
for (i=len1-len2;i>=0;i--)
{
for (j=0;j<len2;j++)
if (str[i+j]!=sub_str[j]) break;
if (j==len2)
printf("反向搜索:子串在字符串中出现在位置 %d\n",i);
}
}
b.函数功能:查找字符串中出现某单词的个数和位置并替换该单词
#include <stdio.h>
#include <string.h>
int find_replace(char a[],char b[],char c[]);
int main()
{
char str[100]={"this is a test program and a test data."};
char substr1[10]="test",substr2[10]="actual";
int n;
n=find_replace(str,substr1,substr2);
printf("%d\n",n);
puts(str);
return 0;
}
int find_replace(char a[],char b[],char c[])
{
int i,j,k,count=0;
char temp[100];
for(i=0;a[i]!='\0';i++)
{
j=i;k=0;
while(a[j]==b[k]&&b[k]!='\0'){j++;k++;}//找相同字符串
if(b[k]=='\0')//a遇到空格时b输入完整则计数并交换
{
count++;
strcpy(temp,&a[j]);//所替换单词后的字符串先不动
strcpy(&a[i],c);//替换
i=i+strlen(c);//为下一次循环准备
strcat(a,temp);//将不动的部分接在后面
}
}
return count;
}