嵌入式开发--C语言之语法概述(1.2)

1.GCC介绍和使用

1.1介绍

今天我们就进入到嵌入式c语言高级部分的学习了,我们先要明确一个基本目标。众所周知,c语言作为我们嵌入式开发中的一个核心工具,那么对c语言的掌握程度越高 ,对于后面我们遇到的一些问题,你会更加容易理解。我们作为一个嵌入式工程师来说,在以后遇到问题,我们解决问题的能力就会非常的强大。所以对c语言的学习呢,我们更多的是希望大家通过语法去了解到c语言为什么会这么设计。以及呢,它在设计中还蕴含了一些什么样的设计思想。

对于要想学好一门程序设计语言,我们实际上可以认为它是分两个部分来走的。第一个部分就是我们所说的程序设计思想的学习。其实就是大部分书上大量篇幅中讲到的if的介绍、while的介绍,for的介绍啊,然后如何用if、for、while,然后用一些我们的关键字,对于某些数据进行什么排序啊查找啊等等的实现,那这些呢,我们认为它只能叫做程序设计思想,但是我们说你如果要想真正掌握一门语言的话啊,你不仅需要有基本的程序设计思想,你还需要了解这个语言工具的一些特性。

我们更多的是希望大家去理解c语言在嵌入式开发中,它的一些工具特性,c语言为什么作为我们最早最老的一种一门语言,到现在还在使用啊?其原因就在于它操作底层的性能,或者叫操作底层的便易性 ,应该是其他语言没法比拟的,所以说它一直有他存在的道理,这个道理自然而然也就是c语言最大的特性,就是它操作底层,操作什么内存类型资源的这种方式,应该说这种操作方式它是独一无二的啊,准确来说,即便不是独一无二的,但是至少是最合理、最合适的一门语言。

最典型的一个例子就是指针啊,很多同学可能在学校中所学的什么冒泡法啊,什么插入排序啊,等等这些这些算法啊,更多的我们认为是一种程序设计的思想,即就是该如何去写一个简单程序,但是一旦让你使用指针,利用指针去操作的话,你会发现你就没有办法快速的写出合适的程序。那原因就是对于这个工具的特性,你并没有了解。那么程序设计思想呢,应该说这个是学不来的,这个需要练的,就是你不要不断的通过一些例题和一些你程序的一些积累才能练出来。但是工具特性的学习呢 ,因为它的特点非常明显,所以我们通过一定的培训或者一定的辅导过后,你应该能够很快的掌握,所以说呢,我们希望大家去学习c语言了,更多的还是先学习这样一种特性。

可以自己回答三个问题。我们讲很多 ,比如关键字 int char 甚至来说我们的指针等等,后面的课程中你可以去总结一下。当我们想用它了,哎,int是什么时候用的?应该怎么用呢?当然我们说对于c语言来说啊,这个工具怎么用,应该是非常的简单的,因为它的基本语法这个肯定是要先掌握的,然后什么时候用?大家可以总结一下。又在什么场景下出现,它为什么会出现这个东西啊,那么他肯定有它的意义,比如说我写int而不写char,他肯定有区别。那为什么我一定要写int而不写char,或者他俩能不能交换呢。对不对?哎,大家都带着这样的疑问去学习的话呢,可能对c语言的很多理解会更加深入啊,其实最后一个问题就是为什么要这样设计。这个内容就相对来讲就比较复杂了,可能不是我们这一门课程能解决的。可能需要大家后期学到arm开发,一些我们驱动程序的一些开发过后,你才能去理解到c语言在这样设计中有它的一个怎样的想法在里面。

知其然知其所以然啊,这是我们的基本定位和想法,那么这是一种思路和方法,希望大家呢尽量的按照这个思路慢慢的去学啊,我们还是希望偏向于嵌入式的一些实际工程开发,然后给大家进行相应的举例,看举例来学习, 比如说像我们c语言中啊,大家可能了解过的,比如说那个运算符的优先级,这样的知识点呢,我们可能就不想给大家介绍了,因为我们都知道,什么加减乘除位运算啊,我们的逻辑运算式在写成一个表达式的时候呢,一般它有一个优先级的一个一个执行过程,对不对,哎,但是呢,我们说实际上我们确实没有必要记忆优先级的高低。因为在我们实际开发中有这么一个万能的钥匙,就是圆括号,那么利用这样一个圆括号呢,可以帮我们人为地把一些什么优先级给它定义出来,并且这样的话,还不会产生歧义,别人也能更好的看懂你的代码。

1.2gcc概述

今天我们就进入到嵌入式c语言高级编程中的第一个内容。首先我们先看看这个阶段我们需要掌握的重点及难点。那么首先重点我们希望大家能够掌握在c语言中是如何将逻辑语言c变成机器指令的过程。因为这个概念的掌握对于我们后期学习arm开发有帮助。作为一个嵌入式开发人员的话,直接关系到我们后面在学习linux驱动,学arm体系结构的学习效率。

来看看第一个我们需要给大家介绍的概念----gcc。我们为什么要谈gcc或者为什么有的gcc这个东西?实际上计算机是一个非常傻瓜化的东西,因为它只认识高低电平。可是如果我们让它来处理人的逻辑,让大家更早的去接触高低电平这样一种非常枯燥的资源,我们实在不能接受。所以那些之前的大牛们就把它设计成了一个叫做高级语言的一个东西。所以说呢,其实我们所谓的计算机编程,其实就是一个翻译的过程。把我们人能理解的一套语言(c语言)翻译成机器能识别的语言(高低电平)。那gcc是什么呢?就是翻译官,用来翻译的。它的具体解释如下:

GUN:开源社区的标杆。

gcc----起源: GUN c compile (只支持c)

--------发展: GUN compiler collection (后来可以翻译更多的语言)

gcc -o output 1.c

-o必须和output一起,输入文件必须有后缀。区别不同后缀名。

gcc -v -o output 1.c
加-v看见使用到哪些“翻译官”

1.3C语言编译过程介绍

预处理 (替换)
cpp -o a.i 001.c
【gcc -E】

编译 (关键字是编译器处理的)
/usr/lib/gcc/i486-linux-gnu/4.4.3/cc1 -o a.s 001.c
【gcc -S】

汇编
as -o a.o a.s
【gcc -c】

链接
/usr/lib/gcc/i486-linux-gnu/4.4.3/collect2 -o build a.o+…
【gcc -o】
gcc -o build 001.c

【问题】
defineinclude是关键字吗?
不是关键字,因为关键字是编译器处理的,而这两个是预处理进行替换的。

1.4C语言编译常见错误举例

预处理错误
#include “name”
#include < name >
not find
gcc -I(大写)跟查找头文件的目录

编译错误
语法错误 ; { }等忘记写了。

链接错误
1.原材料不够
undefined reference to ‘fun’
2.寻找标签是否实现了,链接时是否加入一起链接
3.原材料多了,只保留一个标签实现就好
multiple definition of `fun’

1.5C语言预处理介绍

#include 包含头文件

#define 宏 替换,不进行语法检查,编译过程中才会进行语法检查
#define 宏名 宏体 请对宏体加括号,安全
#define ABC 5+3
printf(“the %d\n”,ABC5); 5+35
#define ABC (5+3)
#define ABC(x) (5+(x)) 宏函数。安全起见,参数也加括号

#ifdef #else #endif

预定义宏 (系统定义的宏)
FUNCTION : 函数名
LINE : 行号
FILE : 文件名

1.6条件预处理的应用

一套代码,维护多个版本
调试版本
发行版本
#ifdef ABC
#endif
gcc -D :
gcc -DABC1 === #define ABC1

1.7宏展开下的#、##使用

#字符串化
##连接符号
#define ABC(x) #x 即字符串x
printf(ABC(abc\n)); 就会打印abc

#define MYDAY(x) day##x
int day1=10;
printf(“the day is %d\n”,MYDAY(1)); 打印10

2.C语言常用关键字及运算符操作

									+-char
									+-int,long,short
					------数据类-----+-unsigned,signed
					-				+-float,double
					- 			  	+-void
					-
					-				+-struct
					-				+-union
			-------自定义数据类型----+-enum
			-						+-typedef
			-
			-
			-		       +-if,else
  关键字----------逻辑结构--+-switch,case,default
	        -			   +-do,while,for
	        -		       +-continue,break,goto
	        -
	        -
	        -					+-auto
	        -					+-register
	        ------类型修饰符-----+-static
	        -					+-const
	        -					+-extern	
	        - 					+-volatile
	        -
	        -
	        -------杂项-----+-return
					        +-sizeof

2.1C语言常用关键字及运算符课程介绍

【重点】
掌握C语言的常用关键字及其应用场景,使用技巧
掌握位运算的典型操作 (硬件的操作单位是bit,但是实际定义的最小单位是char,8位)
掌握常用的逻辑操作
【难点】
when to do? how to do? why to do?

2.2关键字概念及sizeof、return

所谓的关键字是其实就是我们所谓的c语言编译器。他预先已经给我们定义了一定意义的一个字符串。它实际上对我们的一些字符串的一些特殊意义进行了一次预先展开。
32个关键字。就是字符串,被编译器赋予了一定的意义。

sizeof是关键字,在编译的时候就使用了。
int a;
printf(“the a is %d\n”,sizeof(a)); -------->>实际上返回值用%lu打印,才是标准的。
sizeof : 编译器给我们查看内存空间容量的一个工具

2.3数据类型关键字介绍及char类型

那么首先我们先看一下,为什么会提出数据类型这个概念呢?那么说到这个问题,我们先看第一个问题,大家觉得c语言的操作对象是什么? 或者说c语言最终的操作目标是什么呢?答案是:我们在这个地方给一个非常宽泛的概念---->>资源。怎么理解这个资源呢?众所周知,所谓的计算机系统,无外乎由cpu作为我们的核心处理器,外围有一堆资源,然后我们通过所谓的地址总线和数据总线和它们进行访问,最后把得到的数据内容交由cpu进行处理。那么c语言呢,作为我们cpu执行指令的一个下达者,实际上就是要考虑在这些资源中,如何去找到和访问它。那么c语言的资源里头包含什么呢,最典型的其实就是我们大家非常熟悉的内存。这里的内存指的是所有内存类型的资源,不仅仅是内存条的资源。即lcd,led,显卡,一些i2c设备等等都算。

也就说c语言操作最终的对象就是内存,那么我们如何去描述这些内存?既然最终的目的是操作它。那么我们是不是要让c语言编译器去描述它或者说让我们的程序员能够描述它,然后这样的话呢,我们人和人之间就利用这样的一个概念进行交流,然后c语言编译器再把我们人和人交流的这个逻辑结构转换成对应的硬件结构。那么也就产生了第二个问题,c语言如何描述这些资源的属性呢?那么我们说那么这些资源 ,首先它的属性非常复杂 ,比如说我们的led、lcd它可能又都有不同的类型,但是不管怎么说,我们首先先考虑第一个问题就是资源中属性的大小问题,所以c语言在设计的时候,就考虑一个问题。我要想描述内存的资源,那么自然而然我就需要考虑这个资源的大小怎么去定义? 好,那么由我们第一组关键字就是数据类型关键字,它来帮我们来解决大小限制的问题。所以说我们可以把数据类型关键字当成是我们限制内存大小的一组特殊意义的字符串。

这里有个一点,那就是面试官问你数据类型的大小,这个问题的答案是:由编译器决定的,没有固定的值。

那么, 有了以上的想法过后,我们先看第一个关键词,这个关键字的产生并不是平白无故的,char类型应该是我们整个数据类型产生的第一个类型。因为从技术角度来讲,我们最终的目的是操作硬件芯片,那么硬件芯片可操作的最小单位是多少呢? 那这个单位我们称之为比特(bit),它实际上只有一个高和低这两种状态, 也就是硬件所说的高低电平,那么在软件中我们把它称之为位,那么1位就代表高电平或者低电平,如果说c语言有这样一个关键字bit那岂不是就可以直接通过软件控制硬件了吗?但是实际上在软件开发上,在计算机发展的历史过程中,我们并没有这样做,因为如果这样做的话,我们软件开发的成本也就是开发的量会非常的庞大,所以我们的软件采用了一个集合方式,即我们软件操作最小单位是一组bit–>> 8个bit等于一个(字节)Byte。所以说我们在软件概念中经常会听到bit和Byte的概念(即大B和小b的关系问题)。无论怎样理解,我们必须明白的概念是------>>bit代表硬件规范,Byte代表软件规范。

例如下面定义两个数组:
char buf[xxx]----一般硬件相关,可以用这个。
int buf[xxx]

2.4数据类型之int、long、short.

大小:
根据编译器来决定
编译器最优的处理大小:
系统一个周期,所能接受的最大处理单位,int
32bit 4B int
16bit 2B int
int a; char a;

整型常量
8bit的最大值是256,如果你定义一个char a=300,a++得到的结果并不是301。
char a = 300; 300l 300L
2B 65535
int a = 66535;
进制表示(十进制是给人看的。8进制和16进制是二进制的衍生版,是给机器看的)
10
十进制 八进制 十六进制 二进制
3bit 8进制
111 0x7
1000 0x8 int a = 010; //8
12 001 010
4bit 16进制 int a = 0x10 //16

以上是我们嵌入式开发中应该必备的一个基本概念。了解之后,我们接着把后面出来long和short看一下,其实它们就是在补充int和char带来的一个短板。那么short,我们更多的时候,并不是说我们会定义short a或int a,如果我们在没有什么特殊要求的情况下,我们一般都写int不会写short,除非说我们这个空间恰好有一个非常严格的限制,就是它对于长度必须满足16个bit,不能超过,那这个时候,我们可以用short来作为我们32位中的16比特来处理,而long类型,它是一个我们c语言可扩的一个类型,它可以帮我们把c语言不断的去累加,比如说我们随着计算机的位数增加,它可以long long这样一些类型,来表示64等等,当然这些不管怎么变,最终都要看你的编译器是否支持,所以说对于这个部分,这三个类型我们都可以称之为是整型类型的一个,整型类型的组合它主要是考察大家在我们内存中如何分配的问题。所以这几个关键字大家一定要对它的大小有个清晰的认识,以及我们在使用的场景上,在以后我们在随着代码深入和我们以后的硬件体系的建立那么你们会慢慢看到,为什么用char而不用int,或者用char和int的一些好处。

2.5数据类型之符号数、浮点类型

我们再看两个关键字,这两个关键字呢,也可以称之为数据类型的一个限制标志,那么它们就是unsigned和signed。我们称之为无符号数和有符号数。
那么这两个关键字呢,在我们平时定义的时候呢,好像并没有经常去书写,比如我们一般写int a,char a,这种写法,实际上是我们程序员的一个偷懒的行为。系统编译器实际上会把int a展开,它的解释代码会帮我们解释这样的数据是signed的,也就说如果我们没有进行任何显式的声明数据为signed,一般情况下我们定义的int、char、long、short编译器自动定义成有符号数。
而如果是无符号数,那么必须在前面用unsigned定义,这是它的一个简单语法使用。那么这两个关键字到底有什么意义呢?首先我们先说它们的最大区别其实就是这段内存的最高字节,我们究竟把它当成符号位还是当成数据位。那么有符号数,我们更多的时候是把它利用在一种数字的概念,比如说我们是把它作为加减乘除使用的这样一个标识或者这样一个物理意义的时候,那么我们就不加思索地把这样的空间当成有符号的数字来使用。而无符号数,更多的时候是指的是数据,什么是数据呢?就比如说我们摄像头采集或者是显卡产生的数据,那这样的数据我们都使用无符号数。
在我们在实际工程开发的过程中呢,有些人可能没有进行区别,直接拿过来用, 好像代码没有什么太大问题,但是一旦我们出现了像我们嵌入式中经常会用到的位运算,涉及到的是一个右移操作。那么就会出现一些问题,我们来举个例子,我们定义一个char a =-1;让a>>1不断的右移,最终会不会变成0?如果是unsigned char会不会变成0?

好,所以说呢,在我们的嵌入式开发中,我们无符号数的利用,更多的时候是面向于数据的采集和数据的申请,所以这点大家一定要清晰。比如说如果我们定义的这段内存空间它的意义更多的在于数据那么这个时候我们建议大家尽量的用unsigned去定义而不是偷懒直接写成int a,因为这样的话会导致我们后期大型程序维护的时候可能会出现一些稀奇古怪的问题啊,这样偷懒对于代码的可读性也不是太好,因为别人一看到unsigned立刻想到,哦,原来是你代表的是数据的意义。如果只是定义一个普通的char a那么我们就 认为你是一种带有数学意义的一个数字啊或有加正负号的数字的概念。

最后我们再看这个float和double,
我们在c语言中提供的两种浮点运算,所谓的浮点运算呢,我们认为它其实就是小数,前面呢我们给大家介绍的这几个关键字啊其实都代表是整数。那么什么是浮点数、什么是整数呢?浮点数和整数的重点不同在于它们在内存中的表达形式不一样,那你看比如说我们说整数0x10,那么我们都知道它在内存中表示的二进制是 0001 0000,就是十进制来的16,平时看到的内容和我们在内存中所表达的内容实际上是一致的。 但是如果现在有个数字1.0,那么是不是我们在内存中描述成是这样的就是0001 0000?这样描述的话那么这个岂不是就是16,这样肯定是不对的。我们说1.0这个浮点数要想在内存中描述,并不是我们想象的这么简单,至于为什么,暂时不需要理解,在这个地方我们现在只需要记住。浮点数 在内存中的表示形式跟我们整形是完全不一致的。那么要想把一段内存当成浮点数来使用,那么我们必须按照特殊的float或double的形式让我们编译器或让我们的cpu进行这样内存的一个处理。
那么这个地方到底1.0怎样表示呢,我们不要求大家去记或者是了解啊,因为不同的计算机组成原理中它对于浮点数的具体表示方法是不一致的。
double a=1.0;
float b =1.0f;
1.0在我们内存中是八个字节,所以说浮点数是非常耗内存空间的啊,所以说在某些场合下如果我们能不用小数呢就尽量不要用小数。那么当然用了小数过后,它有些的算法可能会更加精确。但是这个时候我觉得double已经太大了啊,没有必要搞得这么精度这么深啊,这个时候呢,我们还有一个方法就是1.0后面加上一个小f,那么这样的话呢就告诉我们编码器1.0不再使用八个字节而使用float就是四个字节。关于浮点数呢,我们不要求大家能够掌握得多么深,因为它其实就是一些算法中可能会用到,那么在我们实际开发中呢,可能涉及到的不会太多。

最后一个数据类型,是void,这是一个称之为不限制内存大小的一个关键字。也就说void a那么这个实际上是没有什么意义的,因为这个a并没有告诉你它所管辖的空间的大小范围。所以这种写法呢 更多的时候是一种占位标志,它更多的意义是告诉我们申请一种变量名,那具体这个变量是什么。需要进行强制类型转换等等操做。void是声明标志而不是使用标志,比如你定义void a,再a++;这样写法是错误的。

2.6自定义数据类型struct、union

前面的讲解钟我们只会发现其实int等数据类型只是我们描述外界事物的一个最小单位。我们如果说想让c语言去描述更复杂的数据类型。光用基本数据类型肯定满足不了我们的要求。C语言又不可能全部把我们所需要的数据类型全部定义完,所以说给我们提供了一个新的概念叫自定义数据类型。那么自定义数据类型,我们重点放在前两个也就struct和union。那么这两个关键字应该是使用最频繁的。typedef我们不能称之为自定义数据类型我们可以称之为别名。这种别称更多的时候就有点像给别人起个外号而已啊,但这个外号呢肯定不是为了去取笑别人啊 ,而是一个让我们程序员或者说我们人和人之间交流的时候更有相应的交流意义而已,啊好这是我们关于自定义数据类型的一个定义,所以我们可以这样认为虽然编c语言编译器他的默认定义的数据类型的内存分配不符合实际资源的需求。所以我们通过基本元素的集合来定义我们的 新的数据结构。

定义:
struct myabc{
unsigned int a;
unsigned int b;
unsigned int c;
unsigned int d;
};

使用:
struct myabc mybuf;

比如我们操作一个原理图中的关于寄存器设置的表,那么这个寄存器描述组,这样一个资源组我们该如何定义呢?我们的想法当然是把整个表定义成一个对象,那么很显然我们就可以使用struct来定义,如上操作,需要注意的是因为是数据,而不是数学含义,所以用unsigned类型。

那共用体呢,相对而言跟结构体呢它有一个本质的区别是什么呢,结构体是一个元素和一个元素之间递增的关系,就是说你定义完这个元素过后下一个元素一定是在这个元素的结尾往后增,也就是说他每一个变量的起始地址都是上一个结束地址。而共用体就是共用大家的起始地址然后申请的内存空间。所以这样的话呢 那么有什么意义呢,那么这个意义一般来说它并不代表我们去描述什么呢。去描述我们真实的物理 情况 ,因为我真实的物理情况是每一个方格应该有他自己的一套空间去维护它,因为一般的情况啊那么共同体更多的是一种技巧性的代码。 共同体呢我们不要求大家去灵活运运用啊,但是呢能够通过共用体这种思想慢慢去读啊或者说去理解在内核中或者是我们应用编程中他的一些技巧性的代码啊能够理解到就可以啊,所以这是我们说共同体的概念。

定义:
union myabc{
unsigned char a;
unsigned int b;
};

使用:
union myabc mybuf;

2.7自定义数据类型enum

这个enum关键词是一个地位很低的关键词.
不是强制符号,而是人与人之间交流用的集合的表示方法。

是缩写enumerate 一一列举
被命名的整型常数的集合
#define MON 0
#define TUE 1
#define WED 2
enum abc{ MOD = 0,TUE,WED }
enum 枚举名称{ 常量列表 };
enum week{
Monday = 0 ,Tuesday =1 ,Wednesday = 2,
Thursday,Friday,
Saturday,Sunday
};

2.8自定义数据类型typedef

含义:数据类型的别名

int a =170;
int b = 3600;
以上写法,对于a和b的理解不太清楚。

len_t a = 170;
time_t b = 3600;
以上写法,能猜出a和b的用法

int a; a是一个int类型的变量
typedef int a_t; a_t是一个int类型的外号

2.9逻辑结构关键字

熟悉语法就可,其他没什么好说的。

swtich(整形数字)
float a;
switch(a){
case 1.0:
break;
case 2.0:
}

goto在函数内部使用,应该问题不大。其他场景下,若非必然情况,尽量少用。

2.10类型修饰符(一)_register

类型修饰符其实就是对资源存放位置的一个限定

auto int a; 就是普通的可读可写区域的int a
register int a;
限制变量定义在寄存器上的修饰符
定义一些快速访问的变量
但是并不绝对在寄存器上,编译器会尽量的安排CPU的寄存器去存放这个a,如果寄存器不足时,a还是放在存储器中。cpu的寄存器越多,cpu越强。
&这个符号对register不起作用(取地址)

内存(存储器) 寄存器
0x100 R0,R2

2.11类型修饰符(二)_static_const

static应用场景:
修饰3种数据:
1)、函数内部的变量
int fun()
{
int a; ===> static int a;
}
2)、函数外部的变量
int a; ====> static int a;
int fun()
{
}
3)、函数的修饰符
int fun(); ===> static int fun();

extern外部申明,这里暂时不做说明,放到工程中看。

const应该说c语言的一个软肋,也是c语言的一个中看不中用的关键字,有点像register。原本初衷是好的,希望定义一个不能更改的常量。但是最终是一个只读的变量。C语言考虑并没有特别深,就是他还站在内存思想来考虑的。我们还是可以通过某些方法----->>指针来改变它。那么其实我们在说到这里也再提一个新的概念叫内存泄漏。内存泄漏是非常恐怖的事情,甚至来说可能会得到一些意想不到的结果,这个意想不到应该是成为黑客的必经之路。

2.12类型修饰符(三)_volatile

它的操作对象是编译器,而不是内存。
告知编译器编译方法的关键字,不优化编译
修饰变量的值的修改,不仅仅可以通过软件,也可以通过其他方式(硬件外部的用户)

c语言代码:
int a = 100;
while( a==100 );
mylcd();

汇编的伪代码:
[a] : a的地址
f1: LDR R0,[a]
f2: CMP R0,#100
f3: JMPeq f1 ----> JMPEQ f2(打开优化了)
f4: mylcd();

但是a是由外部改变的(比如外部键盘输入等等),这个时候肯定是不能优化的。

3.C语言运算符操作

			------算术操作运算符--(+、-)
			-				   --(*、/、%)
			- 			  	
			-
			-				--(||、&&)
			-				--(>、>=、<、<=)
			-------逻辑运算----(!)
			-				--(?:)
			-
			-
			-		       --(<<、>>)
  运算符----------位运算------(&、|)
	        -			   --(^、~)
	        -		       
	        -
	        -
	        -					
	        -					
	        ------赋值运算---------(=)
	        -					--(+=、-=、&=、...)
	        -					
	        - 					
	        -                      --(())
	        -                      --([])
	        -------内存访问符号-------({})
					               --(->、.)
                                   --(&、*)

3.1常用运算符(一)_mod

语法应该都知道。熟练运用括号就行。
乘和除在大部分CPU中是不支持的。

int a = b*10; CPU可能多个周期,甚至要利用软件的模拟方法去实现乘法
int a = b+10; CPU 一个周期可以处理

%:
0 % 3 = 0 1%3 = 1 2%3=2 3%3=0 4%3=1 … …
n % m = res [0 - m-1]
使用场景:
取一个范围的数:
eg. 给一个任意的数字,得到一个1到100以内的数字?
(m% 100)+1 ===> res;
得到M进制的一个个位数
循环数据结构的下标

3.2常用运算符(二)_逻辑运算符

非0为真,0为假
返回结果就是1 0
int a = -1;
if(a)

A || B ===不等价 B || A (A为真的时候B还执行吗?同理如下)
A && B ===不等价 B && A

3.3位运算符(一)移位运算符

左移 : 乘法 2 二进制下的移位
m<<1; m
2
m<<n; m2^n(2的n次方)
4: 0 0 1 0 0
8: 0 1 0 0 0
int a = b
32; ===> b<<5
[数据、数字]
-1 *2 =-2:
8bit
1 0 0 0 0 0 0 1 1 0 0 0 0 0 1 0
1 1 1 1 1 1 1 0 符号位取反 1 1 1 1 1 1 0 1
1 1 1 1 1 1 1 1 最后位加1,得到内存中的状态 ==== -1 1 1 1 1 1 1 1 0=-2

左移对数据或者数字不影响。

右移:除以2
m >> n m/2^n
符号变量有关
int a; a>>n
unsigned int a a>>n
死循环:
int a = xxx;
while(a){
a = a>>1;
}
printf("-------------\n");

3.4位运算符(二)与或运算符

& 、 |
A & 0 ----> 0
&作用1: 屏蔽
int a = 0x1234;
a & 0xff00; 屏蔽低8bit,取出高8bit
A & 1 —> A
&作用2:取出
&:在硬件中是清零器 clr

| :
A | 0 === A
保留
A | 1 === 1
设置为高电平的方法,设置set
设置一个资源的bit5(即第6位)为高电平,其他位不变
int a;
a =a| 1 0 0 0 0 0
即a=a| (0x1<<5) ========>>a|(0x1<<n)

题目:清除第五位,其它位置不变。
int a;
a = a & 0 1 1 1 1 1 31 即是a & 31 但是31是int型,是32bit,前面的位数也要考虑到
~(0x1<<5)
a = a & ~(0x1<<5) ==========a = a & (~(0x1<<n)

我们想资源456bit设置为101?

3.5位运算符(三)取反异或运算符

^ :相同位0,不同为一
1 ^ 1 = 0 0 ^ 0 = 0
1 ^ 0 = 1
主要用于算法(加密算法等等) AES SHA1

交换两个数:
int fun()
{
int a = 20;
int b = 30;
int c; c = a;
xxxx-----> a = b; b = c;

}
a = a ^ b;
b = a ^ b;
a = a ^ b;

~取反要逐位取反,看系统是多少位。
0xf0 ~ 0xffff ff0f

3.6常用运算符(三)_内存访问符

  • 15
    点赞
  • 146
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值