C语言

本文详细介绍了C语言中的const关键字的使用,包括const修饰变量、指针和结构体成员,强调了const在代码可读性和优化上的作用。接着讨论了循环语句,如for、while的使用细节和嵌套循环。同时,文章讲解了#define预处理指令和typedef的区别,以及枚举和三元运算符的应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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

表达式12: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

https://blog.csdn.net/fxltsbl007/article/details/56302518?spm=1001.2014.3001.5501

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语言的数据类型如下图:

https://i-blog.csdnimg.cn/blog_migrate/dabd028c787610debea2ff26f3d60ddb.png#pic_center

定义方法

 

方法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. valuefloat型的,s1.value就相当于float类型的变量名一样,按照float类型来使用;
例如;printf(“%s\n%s\n%f”,s1.title,s1.author,s1.value);//访问结构体变量元素
注意scanf(“%d”,&s1.value); 这语句存在两个运算符,&和结构成员运算符点,按照道理我们应该将(s1value括起来,因为他们是整体,表示s1value部分)但是我们不括起来也是一样的,因为点的优先级要高于&

2,如果其成员本身又是一种结构体类型,那么可以通过若干个成员运算符,一级一级的找到最低一级成员再对其进行操作;
结构体变量名.成员.子成员………最低一级子成员;

 

3结构体成员变量的访问除了可以借助符号".",还可以用"->"访问

struct声明的位置

关于其struct声明的位置,也就是这段代码要放到哪里。同样这也是具有作用域的。
这种声明如果放在任何函数的外面,那么则可选标记可以在本文件中,该声明的后面的所有函数都可以使用。如果这种声明在某个函数的内部,则它的标记只能咋内部使用,并且在其声明之后;

 

https://i-blog.csdnimg.cn/blog_migrate/cc7422f09e175856afd30437d97846b8.jpeg

定义结构体变量

之前我们结构体类型的定义(结构体的声明)只是告诉编译器该如何表示数据,但是它没有让计算机为其分配空间。我们要使用结构体,那么就需要创建变量,也就是结构体变量;
创建一个结构体变量;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;

//注意这里不再是定义声明结构体类型,而是直接创建结构体变量了,这个编译器会分配内存的;

//这样的确可以省略标识符也就是结构体名,但是只能使用一次;因为这是;声明结构体的过程和定义结构体变量的过程和在了一起;并且个成员变量没有初始化的;

//如果你想多次使用一个结构体模块,这样子是行不通的;

其二

https://i-blog.csdnimg.cn/blog_migrate/97f5e5a37e551d945b13449ee411f08c.jpeg

总结:

总结一下关于结构体变量的定义;

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落、

结构体内存对齐

 

 

 

来源:

https://blog.csdn.net/zhengnianli/article/details/87568902?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522160644500319724848176372%2522%252C%2522scm%2522%253A%252220140713.130102334..%2522%257D&request_id=160644500319724848176372&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~top_click~default-4-87568902.pc_search_result_no_baidu_js&utm_term=%E7%BB%93%E6%9E%84%E4%BD%93&spm=1018.2118.3001.4449

 

 

https://blog.csdn.net/zw1996/article/details/53844585?utm_medium=distribute.pc_relevant_t0.none-task-blog-BlogCommendFromBaidu-1.control&depth_1-utm_source=distribute.pc_relevant_t0.none-task-blog-BlogCommendFromBaidu-1.control

 

4,循环语句

  1. for( i=1; i<=100; i++ ) sum=sum+i;

先给i赋初值1,判断i是否小于等于100,若是则执行语句,之后值增加1。再重新判断,直到条件为假,即i>100时,结束循环。相当于:

  1. i=1;
  2. while(i<=100){
  3. sum=sum+i;
  4. i++;
  5. }

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(循环条件)”,则不做其它处理时便成为死循环。例如:

  1. for( i=1; ; i++ )  sum=sum+i;

相当于:

  1. i=1;
  2. while(1){
  3.     sum=sum+i;
  4.     i++;
  5. }

4) 省略了“表达式3(循环变量增量)”,则不对循环控制变量进行操作,这时可在语句体中加入修改循环控制变量的语句。例如:

  

  1. for( i=1; i<=100 ; ){
  2.     sum=sum+i;
  3.     i++;
  4. }

5) 省略了“表达式1(循环变量赋初值)”和“表达式3(循环变量增量)”。例如:

  1. for( ; i<=100 ; ){
  2.     sum=sum+i;
  3.     i++;
  4. }

相当于:

  

  1. while(i<=100){
  2.     sum=sum+i;
  3.     i++;
  4. }

6) 3个表达式都可以省略。例如:
    for( ; ; )  语句
相当于:
    while(1)  语句
7) 表达式1可以是设置循环变量的初值的赋值表达式,也可以是其他表达式。例如:

  1. for( sum=0; i<=100; i++ )  sum=sum+i;

8) 表达式1和表达式3可以是一个简单表达式也可以是逗号表达式。

  1. for( sum=0,i=1; i<=100; i++ )  sum=sum+i;

或:

  1. for( i=0,j=100; i<=100; i++,j-- )  k=i+j;

9) 表达式2一般是关系表达式或逻辑表达式,但也可是数值表达式或字符表达式,只要其值非零,就执行循环体。例如:

  1. for( i=0; (c=getchar())!=\n’; i+=c );

又如:

  1. for( ; (c=getchar())!=\n’ ; )
  2.     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
  •  
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值