C语言
C语言
一个C语言程序有且只有一个main函数,是程序运行的起点
每个C语言程序写完后,都是先编译(.c),再链接(.obj),最后运行(.exe)
标识符:
- 由数字、字母、下划线组成
- 开头不能是数字
- 严格区分大小写
C语言只有八(0)、十、十六(0x)进制,没有二进制
整型一般4B,字符型1B,双精度一般8B
算术表达式: + - * / %
- “/“:两边都是整型则结果为整型,若有一边是小数则结果也为小数
- “%”:取余运算,只能对整数进行操作
- “/”:取整运算
赋值表达式:
-
定义时,不可以连续赋值
例:int x=y=10;× int x,y;x=y=10;√
-
赋值的左边只能是一个变量
-
复合表达式:
例:int a=2;a*=2+3;(相当于a=a* (2+3))
-
自加表达式:
例:int a=5;++a;(a== 6)/a++;(a==5)
例:int i=7;j=i++; j=7/i=8
int i=7;j=++i; j=8/i=8
-
逗号表达式:
z=(2,3,4);z为4
z=2,3,4;整个逗号表达式值为4,但z为2
三种取整丢小数情况:
- int a=1.6
- b=(int)a;
- 1/2;3/2;
字符:
'a':字符1B
"ab":字符串2B (包含一个结束符号\0)
''1"× ‘12’×
字符是可以进行算术运算的 ,例:'0'-0=48
输入输出函数:要在最前面加上#include<stdio.h>
-
printf()
%d十进制 %o八进制 %#o带前导的八进制 %x十六进制 %#x带前导的八进制%%:输出一个百分号
%md:输出m位十进制整数,小于则左端补空,大于则按实际输出
例:
#include <stdio.h>
int main()
{
printf("%5d",1234567890); /*1234567890*/
return 0;
}
#include <stdio.h>
int main()
{
printf("%5d",12); /* 12*/
return 0;
}
%m.nf:m为最小宽度,n为小数点后位数,小数位数被截断时按四舍五入输出(小数点占一位)
例:
#include <stdio.h>
int main()
{
printf("%6.2f",34.56789); /* 34.57*/
return 0;
}
-
scanf(),
scanf("%d,%d",&x,&y,);第二个部分一定是地址
char a=getchar(); 从键盘输入一个字符给变量a(多个字符用getschar)
putchar('y'); 把字符y输出到屏幕中
scanf与gets区别:scanf("%s",a);不可以接收空格 ;gets(a);可以接收空格
关系表达式:
#include<stdio.h>
int main(){
int x=1;
int y=0;
int z=2;
int a;
a=x<y<z;
printf("%d",a); /*1-----------x<y<z为真*/
return 0;
}
逻辑表达式:(表达式的数值只能为1或0)
&&、||、!
优先级:!>&&>||
按位与或异或取反
&、|、^、~(对补码按位取反)
负数在计算机中以补码的形式存在
~:无论正负数,先对数值本身加1,再改变符号位
循环:
-
输入123,输出321逆序:
int i=123;while(i!=0)
函数:
-
当子函数在主函数之后时需要声明,当子函数在主函数之前时则不用
-
函数的参数可以是常量、变量、表达式、甚至是函数调用
-
只能是形参向实参的传递
-
实参与形参之间传数值,形参变化不会改变实参;实参与形参之间传地址,形参变化有可能会改变实参;
-
指令的划分以函数为单位,函数调用时,在堆栈区域申请一片栈帧(函数的局部变量、参数都分配在此);函数返回时,堆栈上的栈帧就要释放,栈帧的分配和释放遵循后进先出
-
函数定义中可以调用本函数——递归,一定要设置出口,否则会造成栈溢出;
递推:小问题——>大问题;递归:大问题——>小问题传值:
exch(int x,int y){ int t; t=x; //子函数没有返回值,ab值不会改变 x=y; y=t; } void main(){ int a,b; scanf("%d,%d",&a,&b); exch(a,b); printf("a=%4d,b=%4d\n",a,b); }
传址:
void fun(int *a,int *b){ int t; t=*a;*a=*b;*b=t; } main(){ int x=1,y=3; fun(&x,&y); printf("%d,%d",x,y); //3,1 }
指针:
指针的定义:
-
int x=3;int *p=&x (定义的同时初始化)
-
int x; int *p; x=3;p=&x; (定义之后初始化)
p当做地址来使用,可以用在scanf函数中,scanf("%d",p);
*p++ 地址变化
(*p)++ 数值变化
int *p,a[]={1,3,5,7};p=a; 把数组第一个元素的地址赋值给p
此时*p++本身数值为1,指向3的地址,(*p)++指针不移动,数值变为2
C语言中没有字符串变量,所以用数组和指针存放字符串
一级指针:*p,存放变量的地址
二级指针:**p,存放一级指针的地址
例:int x=7;int *p=&x,**q=p;
问:*p为多少?*q为多少? * * q为多少? **q=&x写法正确吗?
7 p 7 不正确,二级指针存放一级指针的地址不能存放变量的地址
移动指针:
char *s="yuanyu";while(*s){printf("%c",*s);} 变量的同时又赋值要加*
char ch[]="iamhandsome";
char *p=ch;
问:*(p+2)和*p+2的结果是多少?
m i+2=k
函数返回值是地址:
int *fun(int *a,int *b){
if(a>b) return a;
else return b;
}
main(){
int x=7,y=8,*max;
max=fun(&x,&y);
printf("%d",*max); //8
}
函数指针的用法:
int add(int x,int y){}
main(){
int (*f)();
f=add;
}
调用:
- add(2,3)
- f(2,3)
- (*f)(2,3)
三名主义:
-
数组名:表示第一个元素的地址,不可以自加,是地址常量名
-
函数名:表示该函数的入口地址
-
字符串常量名:表示第一个字符的地址
数组:
数组在定义时就要指定大小
当数组元素过多时需要定义为全局变量(函数之外),因为函数被分配的内存区域有限
存放类型一致,多个数组元素的地址是连续的
最大特点:一次存入,多次使用
一维数组a[10]:
- a表示数组名,是第一个元素的地址,即a[0]的地址,等价于&a
- a是地址常量,只要出现a++,或者a=a+2赋值都是错误的
- a是一维数组名,是一个列指针
二维数组a[3][3]:
- a表示数组名,是第一个元素的地址,即a[0][0]的地址
- a是地址常量,只要出现a++,或者a=a+2赋值都是错误的
- a是一维数组名,是一个行指针
- a[0]、a[1]、a[2]也是地址常量,不可以进行赋值,都是列指针,a[0]+1是跳一列
一维数组初始化:
int a[5]={1,2,3,4,5}; √
int a[5]={1,2,3, }; √
int a[]={1,2,3,4,5}; √
int a[5]={1,2,3,4,5,6}; ×数组越界
一维数组的定义:数组个数一定是常量
int a[5] √
int a[1+1] √
int a[1/2+4] √
int x=5,int a[x]; ×x是变量
define P 5 int a[P] √define后的P是符号常量(宏定义)
例:
#include <stdio.h>
int main()
{
int score[8]={75,68,89,72,62,83,85,92};
int sum=0;
int average=0;
int max=0;
int min=100;
int *p;
for(p=&(score[0]);p<=&(score[7]);p++){
sum+=*p;
if(*p>max){
max=*p;}
if(*p<min){
min=*p;}
}
printf("sum=%d\n",sum); //sum=626
printf("average=%f\n",sum/8.0); //average=78.250000
printf("max=%d,min=%d",max,min); //max=92,min=62
return 0;
}
二维数组初始化:
int a[2][3]={1,2,3,4,5,6}; √
int a[2][3]={1,2,3,4,5,}; √
int a[2][3]={{1,2,3} {4,5,6}}; √
int a[2][3]={{1,2,} {3,4,5}}; √
int a[2][3]={1,2,3,4,5,6,7}; ×数组越界
int a[2][]={1,2,3,4,5,6}; ×不可以缺省列个数
int a[][3]={1,2,3,4,5,6}; √
a[2]=*(a+2)
a[2][3]=*(a+2)[3]=*(*(a+2)+3)
常用函数:
-
strlen()字符串测长度函数
strlen与sizeof区别:
- strlen是一个库函数,sizeof是一个操作符
- sizeof参数可以是数据的类型也可以是变量,strlen只能以“\0”结尾的字符串做参数
- sizeof计算的是数据类型占内存的大小,strlen计算的是字符串的实际长度
-
strcat()字符串连接函数
-
strcmp()字符串比较函数
-
strcpy()字符串拷贝函数
-
malloc()动态分配内存函数(free释放)返回类型是void*
define:
- 预处理
- 宏定义
宏定义只是做替换,会出现优先级问题
int a,b,c,d,e;a=1;b=2;c=6;d=4;e=f(a+b)*f(c+d);
#define f(x) (x*x) e=(a+b*a+b)*(c+d*c+d)=5*34=170
#define f(x) x*x e=a+b*a+b*c+d*c+d=1+2+12+24+4=43
#define f(x) ((x)*(x) e=((a+b)*(a+b)*(c+d)*(c+d))=3*3*10*10=900
文件:
- 引入头文件:#include
<>中是C语言的库,而“ ”中的是自己定义的 - 引入另一个.c中的函数要记得调用前先定义
字符串:
字符串即字符型数组,正文以‘\0’结束,但字符串长度不包括‘\0’
scanf只能接收一个单词,遇见空格即结束,此时可以使用
- gets(str);获取带空格的字符串
- strlen(srt);获取字符串的长度
- strcpy(str,str1);给字符串str重新赋值,当str1为空时可清空str
- strcat(str,str1);将两个字符串连接起来
- char *c=strchr(str,'a');printf("%lld",c-str+1);查找str中第一次出现'a'的位置
- char *c1=strchr(str,"abc");printf("%lld",c1-str+1);查找str中第一次出现"abc"的位置
#include <stdio.h>
#include <string.h>
int main()
{
char str[100];
gets(str);
printf("%s\n",str);
printf("%d\n",strlen(str));
for (int i = 0; i <strlen(str) ; ++i) {
printf("%c ",str[i]);
}
}
//不可将strlen(str)赋值引用,否则将输出不了空格
赋值:
char ch[10]={"abcdfegh"}; √
char ch[10]="abcdfegh"; √
char ch[10]={'a','b','c','d','f','e','g','h'}; √
char *p="abcdefgh"; √
char *p; p="abcdefgh"; √
char ch[10]; ch="abcdefgh"; ×数组名不可以赋值
char *p={"abcdefgh"}; ×不能出现大括号,指针没有大括号的说法
把s指针的字符串赋值到t指针中:
- while((*t=*s)!=null)
- while(*t=*s)
- while(*t++=*s++);
typedef:取别名
- typedef int qq那么int x可以写成qq x
- typedef int *qq那么int *x可以写成qq x
变量
- 全局变量:extern
- 静态变量:static只初始化一次,在上一次变化的基础上继续变化
- auto变量:只能在函数内修饰局部变量,在内存栈上进行分配
- register变量:允许将局部变量的值存放在寄存器内
调用变量会使用距离最近的被赋值的值
#include <stdio.h>
int a=1;
int b=10;
int c;
void add(){
c=a+b;
printf("%d\n",a);
}
int main() {
a=20;
// int a=20; //此时重新定义了a, c=11,a=1
add(); //20
printf("%d",c); //30
return 0;
}
宏定义
必须独占一行,前面只能是空格
- #define PI 3.1415926
- #define CH *
- #define mj(a,b) a*b*b
条件编译
不符合条件则不进行编译
- #if:涉及到宏定义的条件判断必须用条件编译
- #ifdef:如果已经定义过则执行
- #ifndef:如果没有定义过则执行