1,基础
关系运算符
<,>,<=,>=,==,!=
1,双目运算符,从左向右结合,值是逻辑值真假
2,关系运算符低于算术运算符,都高于赋值运算符。
3,<,>,<=,>=优先级相同。==,!=优先级相同且低于其他关系运算符
c>a+b c>(a+b) |
a>b==c (a>b)==c |
a==b<c a==(b<c) |
a=b>c a=(b>c_ |
0==1>2 0==(1>2)值为1 |
逻辑运算符
&&,||,!(逻辑非)
1, 值是逻辑值真假
2,高——低 ! && ||
3,&&,||低于关系运算符。!高于关系和算术运算符
4,与表达式,从左向右运算,不符合就结束
a=1,b=2,c=3,d=4,m=n=1,执行(m=a>b)&&(n=c>d)后,m,n的值
a>b为假,m=0——(m=a>b)=0,因为与表达式,不需要计算后面表达式——
则m=0,n=1,表达式=0
5,或表达式,a||b||c,只要a为真,整个表达式为真,不用判定b和c从。A为假,判断b。b为假,判断c
a>b&&x>y (a>b)&&(x>y) |
a==b||x==y (a==b)||(x==y) |
!a||a>b (!a)||(a>b) |
瑞年
1,能被4整除,但不能被100整除
2,能被4整除,又能被400整除
((x%4==0)&&(x%100!=0))||(x%400==0)
条件1 条件2(400整除也可4整除)
条件运算符
Max=a>b?a:b
表达式1?2:3
如果a>b,则Max=a,否则=b
1,条件运算符高于赋值运算符,低于算术和关系运算符
Max=a>b?a:b Max=(a>b?a:b)
2,自右向左结合
a>b?a:c>d?c:d (a>b)?a: ((c>d)?c:d)
3,表达式2,3不仅是数值表达式,也可是赋值表达式或者函数表达式
a>b?(a=100):(b=100)
a>b?printf(“%d\n”,a): printf(“%d\n”,b)
2,Const
Const只是一个修饰符,不管怎么样a仍然是一个int型的变量
const int a;
int const a;
const int *a;
int * const a;
int const * a const;
本质:const在谁后面谁就不可修改,const在最前面则将其后移一位即可,二者等效
前两个的作用是一样,a是一个常整型数。第三个意味着a是一个指向常整型数的指针(也就是,指向的整型数是不可修改的,但指针可以,此最常见于函数的参数,当你只引用传进来指针所指向的值时应该加上const修饰符,程序中修改编译就不通过,可以减少程序的bug)。
第四个意思a是一个指向整型数的常指针(也就是说,指针指向的整型数是可以修改的,但指针是不可修改的)。最后一个意味着a是一个指向常整型数的常指针(也就是说,指针指向的整型数是不可修改的,同时指针也是不可修改的)。
如果应试者能正确回答这些问题,那么他就给我留下了一个好印象。顺带提一句,也许你可能会问,即使不用关键字 ,也还是能很容易写出功能正确的程序,那么我为什么还要如此看重关键字const呢?我也如下的几下理由:
1) 关键字const的作用是为给读你代码的人传达非常有用的信息,实际上,声明一个参数为常量是为了告诉了用户这个参数的应用目的。如果你曾花很多时间清理其 它人留下的垃圾,你就会很快学会感谢这点多余的信息。(当然,懂得用const的程序员很少会留下的垃圾让别人来清理的。)
2) 通过给优化器一些附加的信息,使用关键字const也许能产生更紧凑的代码。
3) 合理地使用关键字const可以使编译器很自然地保护那些不希望被改变的参数,防止其被无意的代码修改。简而言之,这样可以减少bug的出现。
const关键字至少有下列n个作用:
(1)欲阻止一个变量被改变,可以使用const关键字。在定义该const变量时,通常需要对它进行初始化,因为以后就没有机会再去改变它了;
(2)对指针来说,可以指定指针本身为const,也可以指定指针所指的数据为const,或二者同时指定为const;
(3)在一个函数声明中,const可以修饰形参,表明它是一个输入参数,在函数内部不能改变其值;
(4)对于类的成员函数,若指定其为const类型,则表明其是一个常函数,不能修改类的成员变量;
(5)对于类的成员函数,有时候必须指定其返回值为const类型,以使得其返回值不为“左值”。
3,结构体
定义
由一系列具有相同类型或不同类型的数据项构成的数据集合,这些数据项称为结构体的成员。通俗讲就像是打包封装,把一些有共同特征(比如同属于某一类事物的属性,往往是某种业务相关属性的聚合)的变量封装在内部,再通过一定方法访问修改内部变量。
结构体是C语言中的一种构造类型。C语言的数据类型如下图:
定义方法
方法1
1,首先使用关键字struct,他表示接下来是一个结构体。
2,后面是一个可选的标志(student),结构体名,可任意定义的。它是用来引用该结构体的快速标记。因此我们以后就会可以这样创建数据对象,可以使用这个模板去定义变量stu1,stu2,stu3。定义的时候不要忘了struct。
3,struct student stu1;//把stu1设为一个可以使用student结构体的结构体变量,则stu1这个变量就包含了其student结构体中的所有元素
方法2
相对于方法一,此处省略了结构体名。虽然更简洁了,但是因为没有了名字,后面就不能用该结构定义新的变量,结构体变量数目至少大于1
方法3
此处使用typedef为结构体模板struct student定义一个别名student, typedef给结构体创建一个别名。
补充
1,
2,结构体指针作函数入口参数,提高程序的可扩展性。在我们单片机程序开发过程中,经常会遇到要初始化一个外设比如串口,它的初始化状态是由几个属性来决定的,比如串口号,波特率,极性,以及模式。对于这种情况,在我们没有学习结构体的时候,我们一般的方法是:
-void USART_Init(u8 usartx,u32 u32 BaudRate,u8 parity,u8 mode);
但是如果我们这个函数的入口参数是随着开发不断的增多,就要不断的修改函数的定义,这时使用到结构体就能解决这个问题**,将串口有关的参数组合到一个结构体里面,在函数定义时将入口参数改为此结构体类型的形参,这样就可以在不改变入口参数的情况下,只需要改变结构体的成员变量,就可以达到上面改变入口参数的目的。**这样的好处是不用修改任何函数定义就可以达到增加变量的目的。
typedef为C语言的关键字,作用是为一种数据类型定义一个新名字。这里的数据类型包括内部数据类型(int,char等)和自定义的数据类型(struct等)。在编程中使用typedef目的一般有两个,一个是给变量一个易记且意义明确的新名字,另一个是简化一些比较复杂的类型声明
初始化结构
我们使用在一对花括号中括起来的初始化列表进行初始化,各初始化项用逗号分隔。
访问结构体成员
结构体成员的访问需要借助结构体成员运算符——点(.)。
点其结合性是自左至右的,它在所有的运算符中优先级是最高的
如:
或者利用方法一的申明
Strcut student stu1={“bcl”,520,2};
注意
1,若s1是个结构体,且s1. value是float型的,则s1.value就相当于float类型的变量名一样,按照float类型来使用;
例如;printf(“%s\n%s\n%f”,s1.title,s1.author,s1.value);//访问结构体变量元素
注意scanf(“%d”,&s1.value); 这语句存在两个运算符,&和结构成员运算符点,按照道理我们应该将(s1,value括起来,因为他们是整体,表示s1的value部分)但是我们不括起来也是一样的,因为点的优先级要高于&。
2,如果其成员本身又是一种结构体类型,那么可以通过若干个成员运算符,一级一级的找到最低一级成员再对其进行操作;
结构体变量名.成员.子成员………最低一级子成员;
3,结构体成员变量的访问除了可以借助符号".",还可以用"->"访问
struct声明的位置
关于其struct声明的位置,也就是这段代码要放到哪里。同样这也是具有作用域的。
这种声明如果放在任何函数的外面,那么则可选标记可以在本文件中,该声明的后面的所有函数都可以使用。如果这种声明在某个函数的内部,则它的标记只能咋内部使用,并且在其声明之后;
定义结构体变量
之前我们结构体类型的定义(结构体的声明)只是告诉编译器该如何表示数据,但是它没有让计算机为其分配空间。我们要使用结构体,那么就需要创建变量,也就是结构体变量;
创建一个结构体变量;struct book library;
看到这条指令,编译器才会创建一个结构体变量library,此时编译器才会按照book模板为该变量分配内存空间,并且这里存储空间都是以这个变量结合在一起的,这也是后面访问结构体变量成员的时候,我们就要用到结构体变量名来访问。
分析;
struct book的作用;在结构体声明中,struct book所起到的作用就像int,,,,等基础数据类型名作用一样。
struct book s1,s2,*ss;
定义两个struct book结构体类型的结构体变量,还定义了一个指向该结构体的指针,其ss指针可以指向s1,s2,或者任何其他的book结构体变量。
其实;
struct book library;
等效于;
struct book{
char …
….
…..
}librar;
这两种是等效的,只是第一种可以减少代码的编写量;
补充:
现在还是回到刚才提及的那个问题,可选标志符什么时候可以省略;
其一;
struct
{
char title[MAXTITL];
char author[MAXAUTL];
float value;
}library;
//注意这里不再是定义声明结构体类型,而是直接创建结构体变量了,这个编译器会分配内存的;
//这样的确可以省略标识符也就是结构体名,但是只能使用一次;因为这是;声明结构体的过程和定义结构体变量的过程和在了一起;并且个成员变量没有初始化的;
//如果你想多次使用一个结构体模块,这样子是行不通的;
其二
总结:
总结一下关于结构体变量的定义;
1;先定义结构体类型后再定义结构体变量;
格式为;struct 结构体名 变量名列表;
struct book s1,s2,*ss;//注意这种之前要先定义结构体类型后再定义变量;
2;在定义结构体类型的同时定义结构体变量;
格式为;
struct 结构体名
{
成员列表;
}变量名列表;//这里结构体名是可以省的,但尽量别省;
struct book
{
char title[MAXTITL];//一个字符串表示的titile 题目 ;
char author[MAXAUTL];//一个字符串表示的author作者 ;
float value;//一个浮点型表示的value价格;
}s1,s2;
3,直接定义结构体类型变量,就是第二种中省略结构体名的情况;
这种方式不能指明结构体类型名而是直接定义结构体变量,并且在值定义一次结构体变量时适用,无结构体名的结构体类型是无法重复使用的,也就是说,后面程序不能再定义此类型变量了,除非再写一次重复的struct落、
结构体内存对齐
来源:
4,循环语句
- for( i=1; i<=100; i++ ) sum=sum+i;
先给i赋初值1,判断i是否小于等于100,若是则执行语句,之后值增加1。再重新判断,直到条件为假,即i>100时,结束循环。相当于:
- i=1;
- while(i<=100){
- sum=sum+i;
- i++;
- }
If语句
for(循环变量赋初值; 循环条件; 循环变量增量) 语句
for(表达式1; 表达式2; 表达式3) 语句
它的执行过程如下:
1)先求解表达式1。
2)求解表达式2,若其值为真(非0),则执行for语句中指定的内嵌语句,然后执行下面第3)步;若其值为假(0),则结束循环,转到第5)步。
3)求解表达式3。
4)转回上面第2)步继续执行。
5)循环结束,执行for语句下面的一个语句。
While语句
表达式1;
while(表达式2){
语句
表达式3;
}
使用for语句应该注意:
1) for循环中的“表达式1(循环变量赋初值)”、“表达式2(循环条件)”和“表达式3(循环变量增量)”都是选择项,即可以缺省,但分号(;)不能缺省。
2) 省略了“表达式1(循环变量赋初值)”,表示不对循环控制变量赋初值。
3) 省略了“表达式2(循环条件)”,则不做其它处理时便成为死循环。例如:
- for( i=1; ; i++ ) sum=sum+i;
相当于:
- i=1;
- while(1){
- sum=sum+i;
- i++;
- }
4) 省略了“表达式3(循环变量增量)”,则不对循环控制变量进行操作,这时可在语句体中加入修改循环控制变量的语句。例如:
- for( i=1; i<=100 ; ){
- sum=sum+i;
- i++;
- }
5) 省略了“表达式1(循环变量赋初值)”和“表达式3(循环变量增量)”。例如:
- for( ; i<=100 ; ){
- sum=sum+i;
- i++;
- }
相当于:
- while(i<=100){
- sum=sum+i;
- i++;
- }
6) 3个表达式都可以省略。例如:
for( ; ; ) 语句
相当于:
while(1) 语句
7) 表达式1可以是设置循环变量的初值的赋值表达式,也可以是其他表达式。例如:
- for( sum=0; i<=100; i++ ) sum=sum+i;
8) 表达式1和表达式3可以是一个简单表达式也可以是逗号表达式。
- for( sum=0,i=1; i<=100; i++ ) sum=sum+i;
或:
- for( i=0,j=100; i<=100; i++,j-- ) k=i+j;
9) 表达式2一般是关系表达式或逻辑表达式,但也可是数值表达式或字符表达式,只要其值非零,就执行循环体。例如:
- for( i=0; (c=getchar())!=’\n’; i+=c );
又如:
- for( ; (c=getchar())!=’\n’ ; )
- printf("%c",c);
3,循环的嵌套
5、#define
#define是预处理指令,通常用它来定义常量(包括无参量与带参量),以及用来实现那些“表面似和善、背后一长串”的宏,在编译时不进行任何检查,只进行简单的替换。不作正确性检查,不关含义是否正确照样带入,只有在编译已被展开的源程序时才会发现可能的错误并报错
宏定义的一般形式为:
#define 宏名 字符串
这里所说的字符串是一般意义上的字符序列,不要和C语言中的字符串等同,它不需要双引号。
#define PI 3.1415926 //语句后面一定不能带“;”号,因为它也会被置换
程序中的:area=PI*r*r 会替换为3.1415926*r*r
如果你把#define语句中的数字9 写成字母g 预处理也照样带入。
#define INT int
#define TRUE 1
#define Add(a,b) ((a)+(b));
#define Loop_10 for (int i=0; i<10; i++)
6、typedef
typedef是在C语言中用来为复杂的声明定义简单的别名,它在自己的作用域内给一个已经存在的类型(一个标识符及关键字)一个别名。它本身是一种存储类的关键字,与auto、extern、mutable、static、register等关键字不能出现在同一个表达式中。
typedef取别名的一般形式为:
typedef 旧名字 新名字
它是语言编译过程的一部分,但它并不实际分配内存空间,实例像:
typedef int INT;
typedef int ARRAY[10];
typedef (int*) pINT;
typedef可以增强程序的可读性,以及标识符的灵活性,但它也有“非直观性”等缺点。
7、#define与typedef的区别
(1)#define之后不带分号,typedef之后带分号。
(2)#define可以使用其他类型说明符对宏类型名进行扩展,而 typedef 不能这样做。如
INT1可以使用类型说明符unsigned进行扩展,而INT2不能使用unsigned进行扩展。
(3)在连续定义几个变量的时候,typedef 能够保证定义的所有变量均为同一类型,而 #define 则无法保证。
宏定义只是简单的字符串代换(原地扩展),而typedef则不是原地扩展,它的新名字具有一定的封装性,以致于新命名的标识符具有更易定义变
量的功能。
如:
PINT1定义的p1与p2类型不同,即p1为指向整形的指针变量,p2为整形变量;PINT2定义的p1与p2类型相同,即都是指向 int 类型的指针。
下面看一个关于typedef的重要的问题!!请看代码:
这段代码编译会报一个错误(error: increment of read-only variable 'p2'),错误在哪呢?
答案与解析:
第五行代码p2++出错了。这个问题提醒我们:typedef和#define不同,它不是简单的文本替换。上述代码中const pStr p2并不等于const char * p2。const pStr p2和pStr const p2本质上没有区别,都是对变量进行只读限制,只不过此处变量p2的数据类型是我们自己定义的而不是系统固有类型而已。因此,const pStr p2的含义是:限定数据类型为char *的变量p2为只读,即为char* const p2,表明p2是一个指向char类型的常指针,所以p2是不可修改的,因此p2++错误。
下面看一看关于const声明的一些例子及其含义:
①a是常数,不可改变;
②b是常数,不可改变
③c是一个指向整型常数的指针,指针可以变,指针指向的内容不可以变;
④d是一个指向整型变量的常指针,指针不可以变,指针指向的内容可以变;
⑤e是一个指向整型常数的常指针,指针与指针指向的内容都不可变
8,枚举
很多时候,有一些数据的取值有限的,这时候可以把这些可能的结果列出来,并赋予新的名字,这样有助于提高代码的可读性。例如,一个星期有7天,一年有12个月,期末考试科目有6个科目等情况,完全可以一一列举出来。
枚举成员的值是根据前一个成员的值递增1。若上述定义中第一个成员MON不赋值的话,那么其默认为0。
枚举类型变量的定义方法
9,三元运算符
格式
- (关系表达式) ? 表达式1 : 表达式2