目录
数据类型
基本类型 | 整型 | 有符号基本型 | int | |||
有符号短整型 | short | |||||
有符号长整型 | long | |||||
无符号基本型 | unsigned | |||||
无符号短整型 | unsigned short | |||||
无符号长整型 | unsigned long | |||||
浮点型 | 单精度浮点型 | float | ||||
双精度浮点型 | double | |||||
字符型 | 有符号字符型 | char | ||||
无符号字符型 | unsigned char | |||||
无值类型 | void | |||||
指针类型 | ||||||
构造类型 | 包含数组,结构体,联合体 |
一些基本的运算符和优先级
ps:
1.赋值运算符是右结合性
2.自增自减运算符的运算对象只能是变量,而不能是常量和表达式
(1)关于自增自减需要注意的,自增自减是后缀运算符时,例:(i++)+j,先使用运算符的对象运算整个表达式的值,后进行自增自减,自增自减是前缀运算符时,反之。
表达式
判断表达式类别应根据表达式最后运算的运算符的类别来判定,这里会考虑优先级问题,也就是,如果某一运算符在整个表达式的运算过程中是最后运算的运算符,表达式类别就是该运算符从属类型。
算数表达式 :例:(k=i++)/ 3*a
逗号表达式:例:x=a=3,6*a
赋值表达式 :例:x=(a=3,b*3)
类型转换表达式: 例:b=(int)x%a
关系表达式: 即用关系运算符将运算对象连接起来的符合C语言规则的表达式称为关系表达式(概念理解即可)
例:a>b 3+7!=10
逻辑表达式:即用逻辑运算符将运算对象连接起来的符合C语言规则的表达式称为关系表达式
例:a>b&&a<c
条件表达式:实现双分支选择结构的表达式
即用条件运算符将运算对象连接起来的符合C语言规则的表达式称为关系表达式先判断第一目逻辑量,如果逻辑量为真,选取第二目结果作为整个表达式的值,否则选取第三目结果作为整个表达式的值
语句
表达式语句:(这个很容易理解)在表达式后加一个分号“;”构成表达式语句。不再赘述
流程控制语句:(分支和循环都有涉及)
空语句:只有一个分号的语句
复合语句:(很常见,不赘述)以花括号括起来的功能相对独立的语句段
函数调用语句 :(在函数中会详细解释)例:scanf("%d",&s); printf("好好学习"); cos(x); sin(x); 这些都是常见的函数调用语句。
循环
循环结构概念
程序段在程序中需要被重复执行若干次会用循环结构实现;
程序设计:
1.构造循环体: 需要重复执行的部分,利用C语言规则归纳出一组相同的程序段(也就是大括号里的部分)
2.寻找控制循环变量
3.找出循环控制变量的三个要素
(1)循环控制变量的初值(i=0)
(2)循环的条件(i<30)
(3)使循环趋于结束的部分(i++)
ps:三要素缺一不可
实现循环的语句
while语句
ps:逻辑量可以是任意表达式,反映的是循环的条件
while(逻辑量)
循环体语句
while 语句在执行时,首先判断逻辑量,如果为1,则首先执行循环体语句,然后继续判断逻辑量;
如果为0,则结束循环,执行循环的后续语句。
do~while 语句
do
循环体语句
while (逻辑量)
do~while 语句在执行时,首先执行循环体语句,然后判断逻辑量,如果为1,则继续执行循环体语句,如果为0,则结束循环,执行循环的后续语句。
for语句
for (表达式1;表达式2;表达式3)
循环体语句
ps:(1)for语句中的各表达式和逻辑量都可以省略,但间隔符号分号不能省略
(2)当循环变量已经赋予初值时,for语句可以省略表达式1;
(3)for 语句中可以省略表达式2,应该在循环体中增加使循环趋于结束的操作
(4)for 语句中如果省略了逻辑量,则循环条件为真,那么应该在循环体中加循环结束的语句,否则就是死循环;
(5)for循环如果省略了表达式1和表达式2,此时的for循环相当于while循环
循环嵌套
这里主要靠逻辑思维和算法
举一些常见的例子
/***数列前20阶乘的求和***/
/***数列前20阶乘的求和***/
#include <stdio.h>
void main ()
{
int i;
float sum=0,j=1;
for(i=1;i<=20;i++)
{
j=j*i;
//printf("%.0f ",j);
sum=sum+j;
//printf("%.0f\n",sum);
}
printf("%.0f\n",sum);
}
/****水仙花数****/
#include<stdio.h>
void main()
{
int i,j,sum,a,b,c;
printf ("水仙花数有:");
for (i=100;i<=999;i++)
{
a=i%10;
c=i/100;
b=i/10-c*10;
sum=a*a*a+b*b*b+c*c*c;
if(sum==i)
{
printf("%d ",i);
}
}
}
/****sinx的计算****/
#include <stdio.h>
#include <math.h>
void main ()
{
int i=1,s=1;
float x,xn,fac,sinx=0;
printf ("please input x:");
scanf ("%f",&x);
fac=1;
xn=x;
do
{
sinx+=s*xn/fac;
xn=xn*x*x;
fac=fac*(i+1)*(i+2);
i+=2;
s=-s;
}while (fabs(xn/fac)>1e-6);
printf("sinx=%f",sinx);
}
分支和跳转
if语句
ps:if括号中是一个逻辑量,除了常用的关系表达式和逻辑表达式以外也可以是一个任意形式的数据(表达式,常量,变量,函数调用等)。
if(逻辑量)
语句1
[else
语句2
]
switch语句
ps:switch可以实现多分支结构
switch(表达式) //表达式的值可以是任意类型,系统会自己转换为整型和字符型
{
case:常量1:语句组1 //case之后只能是常量和常量表达式
case:常量2:语句组2
.
.
case:常量n:语句组n
[default:语句组n+1]
}
关于分支的几个典型例题
/****一元二次方程组的解****/
#include<stdio.h>
#include<math.h>
void main()
{
float a,b,c;
double x,t;
scanf("%f%f%f",&a,&b,&c);
if(b*b-4*a*c==0)
{
x=t=(-b)/2*a;
printf("此方程只有一个根");
printf("x=t=%f",x);
}
if(b*b-4*a*c<0)
{
printf("此方程无解");
}
if(b*b-4*a*c>0)
{
x=(-b+sqrt(b*b-4*a*c))/2*a;
t=(-b-sqrt(b*b-4*a*c))/2*a;
printf("此方程有俩个根");
printf("x=%f t=%f",x,t);
}
}
/*****成绩等级****/
函数
函数的定义与使用
函数的定义:函数是一块代码,接受零个或多个参数,做一件事情并返回零个或多个值(可以类比数学中的函数)
函数的定义形式
类型说明符 函数名(带类型说明的形式参数表列)//函数头
//函数体{
数据描述部分 //定义函数中用到的变量
算法实现部分 //实现函数的功能
}
ps:
(1)在定义函数时,确定函数的原型,根据函数的功能,确定函数有无形参,有几个形参及形参的类型,确定函数是否需要向主调函数返回一个值,根据返回值的类型确定函数的类型,如果没有返回值,则函数的定义应为void。
(2)一个函数的定义不能包括在另一个函数内部
(3)函数名后面的括号不可省略
(4)有参函数定义时,形参表列是带类型说明的形参表列,每个形参都必须独立声明类型。
(5)函数值的类型由函数原型中的函数类型决定,return 语句中,“表达式”的值类型应与函数类型一致,如果俩者不一致,则以函数类型为准,自动把“”表达式的值转换为函数返回值的类型
例:
double area (double x,double y,double z)//函数说明部分,x,y,z为形式参数
{
double ss,t;
t=(x+y+z)/2;
ss=sqrt(t*(t-x)*(t-y)*(t-z));
return ss;
}
函数的声明
1.库函数的声明:例常见的#include <stdio.h>
2.自定义函数的声明
类型说明符 被调函数名 (形参的类型表列);
(也就是函数头加了个分号)
3.函数调用
函数名(实际参数表列)//实参必须是常量,变量或者表达式等具有的确定值的量
ps:函数调用时,实参和形参数量要一致,类型要匹配,并且按顺序一一对应。
函数调用的方式
函数的嵌套调用
在被调用函数的函数体中允许调用其他函数,并且函数调用可以根据需要多层嵌套
例:求三个数其中最大值和最小值的差
#include <stdio.h>
int dif (int,int,int);
int max (int,int,int);
int min (int,int,int);
void main()
{
int a,b,c,d;
scanf("%d%d%d",a,b,c);
d=dif(a,b,c);
printf ("MAX-MIN=%d",d);
}
int dif (int x ,int y,int z)
{
int m,n;
m=max(x,y);
n=min(x,y);
return=m-n;
}
int max(int x ,int y,int z)
{
r=x>y?x:y;
r=r>z?r:z;
return r;
}
int min (int x,int y,int z)
{
r=x<y?x:y;
r=r<z?r:z;
return r;
}
函数的变量
1.局部变量:在函数或者复合语句中内部定义的变量叫局部变量
对于静态局部变量和动态局部变量的几点说明:
①静态局部变量在分配存储空间时赋初值,仅赋初值一次;局部动态变量赋初值是在函数调用时进行,函数每调用- -次都要重新赋初值。
②静态局部变量未赋初值,自动赋值为0;动态局部变量不赋初值则其值是随机值。
③静态局部变量具有全局生存期,函数调用结束后仍然存在;动态局部变量具有局部生存期,函数调用结束后不存在。
④静态局部变量和动态局部变量都具有局部的可见性,起作用的范围都是本函数。
⑤静态局部变量具有可继承性;动态局部变量无可继承性。例6-10中第2次调用func( )函数时用到的m、i是第1次调用func( )函数后的结果。
2.全局变量:定义在函数之外的变量也叫外部变量,全局变量不属于某个函数,属于源程序文件
( 1)全局变量都是静态存储方式,存储在静态存储区,具有全局生存期。全局变量未初始化时自动初始化为0。全局变量不能定义为auto和register存储类别。
(2)程序中全局变量和局部变量可以同名。但在局部变量的作用范围内,全局变量被屏蔽,起作用的是局部同名变量。
一些典型函数
数组
数组的定义及初始化
一维数组的定义形式:类型说明符 数组表达式 [整型常量的表达式]//元素数量必须是整数
例:int a[10];
10个单元,每个 单元为一个整型的变量,可以出现在赋值的左边或者右边,例:a[1]=a[2]+6;
定义及初始化数组:例:
const int number=10; //定义数组的大小
int x;
int count [number]; //定义数组
int 1
for (i=0;i<=number ;i++) //初始化数组
{
count [i]=0;
}
scanf ("%d",&x);
while (x!=-1){
if (x>=0 &&x>=9){
count[x]++; //数组参与运算
}
scanf ("%d",&x);
}
for (i=0,i<number,&x) //遍历数组输出
{
printf ("%d:%d\n",i,count[i]);
}
数组元素初始化注意几点:
1. {初值表列}称为初值集合。初值之间使用逗号分隔,与数组的每一个元素具 备左对应关系。如:
int a[5]={1,2,3,4,5};
在定义数组a的同时,将1初始化给a[0].,2初始化给a[1], 3 初始化给a[2], 4初始化给a[3], 5初始化给a[4]。
2.初值集合中的初值可以是任意确定的值,如果类型不一致会进行 类型的自动转换。如程序段:
int a=1; int b[3]={a,a+3.5,5.8};
在定义数组b的同时.将a的值1初始化给b[0],a+3.5的值4.5自动转换为整型4初始化给b[1],5.8自动转换为整型5初始化给b[2]。
3.初值集合中的初值表列,可以是前面部分元素的初值。当给数组部分元素赋初值时,其余元素自动初始化为0。例如:
int a[10]-1.21;
则表示将a数组中a[0]和a[1]初始化为1和2,其余元素初始化为0。
4.当对全部元素赋初值,数组长度可以省略,但[ ]不能省略。程序执行时按照初值集合中初值的个数确定数组的长度。例如:
int a[1=(1,2);
则表示将a数组中的a(0]和a([]初始化为1和2,同时确定了数组的长度为2。
5.数组元素的初始化只能在数组定义时进行。例如:int a[5]; a[5]={1,2.3,4,5}; 这样的赋值方式是错误的。
6.通常都是使⽤for循环,让循环变量i从0到<数组的⻓度,这样循环体内最⼤的i正好是数组最⼤的有效下标。
二维数组
a[0][0] | a[0][1] | a[0][2] | a[0][3] | a[0][4] |
a[1][0] | a[1][1] | a[1][2] | a[1][3] | a[1][4] |
a[2][0] | a[2][1] | a[2][2] | a[2][3] | a[2][4] |
二维数组的遍历
for (i=1;i<3;i++){
for(j=0;j<5;j++){
a[i][j]=i*j;
}
}
int a[][5]={
{0,1.2,3,.4}
{2,3,4,5,6}
}
指针
指针
指针就是保存地址的变量,存放那个变量的地址就表示指向哪个变量的指针
形式: 类型说明符 *指针变量名
//“类型说明符”是指指针变量所指向数据的类型
*为指针变量的定义符
指针初始化:
(1)将变量的地址初始化赋给指针
例:int a;
int *p=&a;
但是int *p=&a,a;是错误的,把一个变量赋给指针变量时,注意该变量必须在此之前已经定义,
(2)可以用初始化了的指针变量给另一个指针变量做初始值
int x;
int *p=&x;
int *q=p;
(3)可以将一个指针变量初始化为一个零指针。
例如:
int *p=NULL;
为明确表示指针变量不指向任何变量,C语言中,约定用0值表示这种情况,称指针值为0的指针变量为零指针。通常,将0标记为符号常量NULL (在stdio.h文件中给出NULL的宏义:#define NULL 0
如果指针变量没有初始化,程序中需要使用指针变量进行间接访问时,则要通过赋值方式建立指向关系,例如:
int a;
int *p
p=&a;:
注意被赋值的指针变量前不能再加“*” 说明符,如写为“*p=&a," 是错误的。
变量的值是内存的地址
普通变量的值是实际的值
指针变量的值是具有实际值的变量的地址
指针的运用
1.
/***交换俩个变量的值***/
void main()
{
int t= *pa;
*pa=*pb;
*pb=t;
}
-1或0 (在文件操作会看到大量的例子)
但是当任何数值都是有效的可能结果时,就得分开返
指针运算
int a[I0];
int *p= a;
*(p+1)-> a[|]
如果指针不是指向一片连续分配的空间,如数组,则这种运算没有意义
给指针加、减一一个整数(+, +=,-=)●递增递减(++/-)
两个指针相减
但是指向不同类型的指针是不能直接互相赋值的
这是为了避免用错指针
指针的类型转换
●void*表示不知道指向什么东西的指针●计算时与char*相同(但不相通)
●指针也可以转换类型
●int *p = &i; void*q = (void*)p;
指针的用处
●需要传入较大的数据时用作参数
●传入数组后对数组做操作
●函数返回不止一个结果
●需要用函数来修改不止一个变量
●动态申请的内存
结构体和联合体
结构体类型及结构体变量
结构体中的数据项称为成员。结构体成员可以是基本类型成员或构造类型成员。结构体数据类型具有很强的数据描述能力,但C语言中并不提供现成的结构体类型,用户在使用结构体类型之前,必须先定义。
结构体类型的定义
结构体类型定义的目的是确定该类型中有哪些成员,以及每个成员分别属于什么类型数据。定义了结构体类型,才能用该类型去说明结构体变量、结构体数组、结构体指针等。
结构体类型定义的- -般形式为:
struct 结构体类型名
类型说明符成员名1;
类型说明符成员名2;
类型说明符成员名n;
其中,struct是定义结构体类型的关键字,不能省略。结构体类型名遵循标识符命名规则,用来标识所定义的结构体类型。花括弧{}内是该结构体所包含的各个成员,类型说明符用来说明每个成员的类型,它们必须是有效的数据类型,可以是int. float、 double. char 以及指针、数组、联合体等类型。结构体成员名同样遵循标识符命名规则,可以与程序中其他变量名相同,互不干扰。
例如,定义一个“学生信息”的结构体类型:
struct student
int num
char name [10];
char sex;
char addr[50];
定义了一个结构体类型名为student的结构体类型,包含有6个成员。第1个成员为num (学号),整型成员;第2个成员为name (姓名), 字符数组;第3个成员为sex (性别),字符成员;第4个成员为age (年龄),整型成员;第5个成员为addr (家庭住址),字符数组;第6个成员为score (成绩),整型成员。定义以后该结构体类型的类型说明符为struct student。利用结构体,把与学生相关的若千个属性组合成- -个组合项, 很好地反映了学生的整体信息。
又如,有关日期的结构体数据类型可定义为:
struct date
{
int year;
int month;
int day;
}
定义了一个名为date的结构体类型来表示8期的构成情况,它包含有3个成员,分别是year(年)、month(月)和day(日),且都是整型。由于date结构体类型的3个成员数据类型相同,所以也可以用数组来表示,但采用结构体描述可以增加程序的可读性,使程序更清晰。
结构体类型的作用域和生存期和变量的作用域和生存期相似。结构体类型定义在函数外,则为全局结构体类型,从定义开始到文件结束都有效;结构体类型定义在函数内,则为局部结构体类型,仅在本函数有效。
在结构体类型的定义中,详细列出了一个结构体的组成情况,给出了结构体的各成员名及其类型,但这仅仅是描述了一种数据的组织形式,系统不可能给类型分配存储空间。结构体类型说明符(如struct student)是-一个类型名, 而不是变量名。就像C语言中其他的类型名(如: int、float、 char 等) -一样,用户可以使用定义好的结构体类型说明符定义变量。所不同的是整型、实型、字符型等基本的数据类型是C语言已经定义好的,可以直接使用,而结构体数据类型要求用户根据实际问题的需要自行构造。程序中要想使用结构体变量,必须先构造该结构体类型,然后再定义该类型的变量。
联合体是c语言中提供的另外-种构造数据类型, 又称为共用体。在一些特殊的应用中, 有时希望在不同时刻把几种不同类型的数据存放在同一-段存储单元中。 例如有变量x,能根据问题的需求或存放整型数据,或存放浮点型数据,或存放字符型数据,联合体就可以满足这种要求。从形式上看,联合体与结构体相似,都由不同数据类型的数据成员项组成,但两者有着本质上的区别,它们使用内存的方式截然不同。结构体变量中的各个成员占有独立的内存空间,它们同时存在于结构体的整体空间中,结构体变量所占内存字节数是各个成员所占内存字节数之和。而联合体变量的各个不同类型的成员共用同一段内存空间, 联合体变量所占内存字节数是占字节数最多的成员所占的字节数。10.5.1联合体类型的定义
联合体类型的定义方式与结构体很类似,定义的一-般形式为:
union联合体类型名
类型说明符成员名1;
类型说明符成员名2;
类型说明符成员名 n; .
其中,union 是定义联合体类型的关键字。联合体类型名遵循标识符命名规则,用来标识所
定义的联合体类型。花括弧{内是联合体中的各个成员,成员名也是一个合法的标识符, 用来标识联合体成员,类型说明符指定每个成员的类型,必须是有效的数据类型。
例如:
union udata{
It i;
char c;
float f;
定义了一个名为union udata的联合体类型,它由3个成员i、c和f组成,每个成员有各自的数据类型,分别为int、char 和float,即整型、字符型和单精度型。
10.5.2联合体变量的定义
联合体变量的定义和结构体变量的定义方式类似,也有3种形式,以union udata类型为例,说明如下:
(1)先定义联合体类型,再定义联合体变量
union udata
int i;
char c;
float f;
union udata u, *p; .
(2)定义联合体类型的同时定义联合体变量
union udata
int i;
chasci.
(3)直接定义联合体变量
int 1;
float f;
以上3种方式都是定义了联合体变量和指向联合体数据的指针变量。定义为union udata类型的变量u,可以存放char或int或float类型的数据。
union udata类型的联合体变量u,包含有3个成员i、
c和f。执行时系统给联合体变量u分配了4个字节
的存储空间,而不是分别给成员i、c. f分配两个字。
宏定义
无参宏定义
无参数宏定义的格式为:
#define 标识符 替换列表
替换列表可以是数值常量、字符常量、字符串常量等,故可以把宏定义理解为使用标识符表示一常量,或称符号常量。
说明:
1) # 可以不在行首,但只允许它前面有空格符。例如:
#define PI 3.1416 //正确,该行#前允许有空格
int a;#define N 5 //错误,该行#前不允许有空格外的其他字符
2) 标识符和替换列表之间不能加赋值号 =,替换列表后不能加分号
#define N =5 //虽语法正确,但预处理器会把N替换成=5
int a[N]; //错误,因为宏替换之后为 int a[=5];
宏定义不是语句,是预处理指令,故结尾不加分号。如果不小心添加了分号,虽然有时该宏定义没问题,但在宏替换时,可能导致 C 语法错误,或得不到预期结果。例如:
#define N 5; //虽语法正确,但会把N替换成5;
int a[N]; //语法错误,宏替换后,为int a[5;];错误
3) 由于宏定义仅是做简单的文本替换,故替换列表中如有表达式,必须把该表达式用括号括起来,否则可能会出现逻辑上的“错误”。例如:
#define N 3+2
int r=N*N;
宏替换后为:
int r=3+2*3+2; //r=11
如果采用如下形式的宏定义:
#define N (3+2)
int r=N*N;
则宏替换后,为:
int r=(3+2)*(3+2); //r=25
4) 当替换列表一行写不下时,可以使用反斜线\作为续行符延续到下一行。例如:
#define USA "The United \
States of \
America"
该宏定义中替换列表为字符串常量,如果该串较长,或为了使替换列表的结构更清晰,可使用续行符 \ 把该串分若干行来写,除最后一行外,每行行尾都必须加续行符 \。
如果调用 printf 函数,以串的形式输出该符号常量,即:
printf("%s\n",USA);
则输出结果为:The United States of America
注意:续行符后直接按回车键换行,不能含有包括空格在内的任何字符,否则是错误的宏定义形式。
带参宏定义
带参数的宏定义格式为:
#define 标识符(参数1,参数2,...,参数n) 替换列表
例如,求两个参数中最大值的带参宏定义为:
#define MAX(a,b) ((a)>(b)?(a) : (b))
当有如下语句时:
int c=MAX(5,3);
预处理器会将带参数的宏替换成如下形式:
int c=((5)>(3)?(5) : (3));
故计算结果c=5。
删除宏定义的格式为:
#undef 标识符
说明:
1) 标识符与参数表的左括号之间不能有空格,否则预处理器会把该宏理解为普通的无参宏定义,故以下是错误的带参宏定义形式。
#define MAX (a,b) ( (a) > (b) ? (a) : (b) ) //错误的带参宏定义格式
2) 宏替换列表中每个参数及整个替换列表,都必须用一对小括号 () 括起来,否则可能会出现歧义。