STC32位8051单片机原理及应用 课件 第八章 C语言程序设计5_第1页
STC32位8051单片机原理及应用 课件 第八章 C语言程序设计5_第2页
STC32位8051单片机原理及应用 课件 第八章 C语言程序设计5_第3页
STC32位8051单片机原理及应用 课件 第八章 C语言程序设计5_第4页
STC32位8051单片机原理及应用 课件 第八章 C语言程序设计5_第5页
已阅读5页,还剩37页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

第八章C语言程序设计何宾2023.08属性关键字__attribute__允许在声明时指定特殊的属性该关键字后面是双括号内的属性规范属性

--alias(“target”)使用该属性为变量和函数指定多个别名如果在当前翻译单元中定义了变量或函数,则别名引用将替换为对变量或函数的引用,别名将与原来的名字一起发出如果在当前翻译单元中没有定义变量或函数,则别名引用将替换为对实际变量或函数的引用如果将变量或函数定义为static,则用别名替换变量或函数名字如果别名被声明为外部,则该变量或函数声明为外部属性

--alias(“target”)变量alias属性的C语言描述inta=100;//声明并初始化全局整型变量aintb=200;//声明并初始化全局整型变量bexternintx__attribute__((alias("a")));//给整型变量a起别名为xexterninty__attribute__((alias("b")));//给整型变量b起别名为yvoidmain(void)//定义main主函数{ volatileintz;//声明整型变量z z=x+y;//x+y→zwhile(1);//无限循环,设置断点}属性

--alias(“target”)函数alias属性的C语言描述charcomp(chara,charb)__attribute__((alias("add")));charadd(chara,charb){ return(a+b);}voidmain(void){ volatilecharx; x=comp(10,20); while(1);}属性

--aligned(n)aligned(n)是一个变量和函数属性,用于指定变量、结构体字段或函数的最小对齐方式n以字节为单位指定对齐方式aligned属性优先于命令ORDER关键字_at_覆盖对齐属性属性

--aligned(n)aligned属性的C语言描述structnum{//定义结构体numchara__attribute__((aligned(2)));//定义字符变量a,2字节对齐intb__attribute__((aligned(4)));//定义字符变量b,4字节对齐longintc__attribute__((aligned(8)));//定义字符变量c,8字节对齐chard;//定义字符变量d};属性

--aligned(n)/*定义函数assign,入口参数为指向structnum的指针value,该函数为8字节对齐*/voidassign(structnum*value)__attribute__((aligned(8))){ value->a=15;//初始化结构变量中的元素a value->b=1000;//初始化结构变量中的元素b value->c=20000000;//初始化结构变量中的元素c value->d=30;//初始化结构变量中的元素d}属性

--aligned(n)voidmain(void)//定义main主函数{structnum*v;//定义指向structnum的指针变量*v assign(v);//调用函数assign while(1);//无限循环,设置断点}属性

--noreturnnoreturn是一个函数属性,用于通知编译器该函数永远不会返回然后编译器可以优化并生成更好的代码更重要的是,它有助于避免未初始化变量的虚假警告不要假设在调用noreturn函数之前,调用函数保存的寄存器已恢复noreturn函数的返回类型应该为void该属性在C251版本5.51中实现预处理器C251编译器中内置的预处理器处理源文件中的命令C251编译器支持所有美国国家标准学会(AmericanNationalStandardsInstitute,ANSI)标准C命令预处理是编译器执行的第一个操作预处理器的目的是在编译之前替换或插入额外的文本到源文件预处理器大多数预处理器函数需要通过在源代码中包含预处理器命令来激活然而,预处理器总是用单个空格代替每个C注释删除行延续符(由行末尾的反斜杠(“\”)表示),并连接分开的行以进行编译用预定义的文本代替预定义的宏名字预处理器

--头文件头文件或包含文件由预处理器包含和处理#include命令告诉C预处理器包含编译器输入流中指定的文件内容,然后继续处理原始文件的其余文件通常,头文件包含变量和函数声明以及宏定义但是,它们不局限于这些。头文件可以包含任何有效的C程序片段。然而,这种做法容易出错,非常混乱,不建议使用头文件通常包含其他头文件的#include命令因此,某些头文件可能会被包含多次。如果这些头文件定义了结构、typedef、程序代码或变量,那么编译器可能会生成错误或警告预处理器

--头文件为了避免更频繁地包含头文件,请在预处理文件中有条件包含头文件的内容例如:#ifndefFILENAME_INCLUDED#defineFILENAME_INCLUDED/*HeaderFileContent*/#endif当定义了FILENAME_INCLUDED时,表示已经包含头文件FILENAME根据C标准(ISO/IEC9899:1990),对于系统头文件,宏名字以“__”开头,对于用户头文件不应以”_”开头。宏名字应包含头文件的名字和一些附加文本预处理器

--头文件包含头文件的C语言描述头文件header.h#ifndefSYMBOL#defineSYMBOL

inta=10,b=20;

#endif预处理器

--头文件包含头文件main.c的C语言描述#include"header.h"externinta,b;voidmain(){ volatileintc=a+b;}预处理器

--宏宏允许程序开发人员为源代码块指定短名字在源文件中使用宏名字时,预处理器会将其替换为宏定义中指定的源代码块宏定义包括宏的名字、宏体,以及可以包含宏参数简单宏不要求参数,是定义的最简单宏复杂宏接受一个或多个参数,可以像函数一样使用宏运算符列表列出了宏定义中可能使用的特殊宏运算符预定义的宏列出编译器在编译时定义的宏宏

--简单宏简单的宏只是代码片段的缩写通常将其称为清单常数因为它定义了常数值的名字。在使用宏之前,必须使用#define命令定义宏,比如:#defineLEN128它定义了名字为LEN的宏当在程序中(或在预处理命令中)使用LEN时,它将使用文本128进行替换,比如charbuffer[N];被预处理器扩展为charbuffer[128];并且随后被编译器编译宏

--复杂宏复杂宏接受参数,并使用这些参数的值生成代码片段接受参数的宏看上去很像函数。但是,参数类型与C函数不同它们仅在展开时由传递给宏的文本替换与简单宏一样,必须使用#define命令定义带参数的宏,然后才能使用它们。参数列表用括号括起来,必须紧跟在宏名字的后面宏名字和左括号之间不能有空格例如:#defineMAX(x,y)((x)>(y)?(x):(y))定义了一个名字为MAX的宏,它接受两个参数x和y宏

--复杂宏当程序中(或预处理器指令)使用MAX时,将其替换为文本((x)>(y)?(x):(y))如果x和y是数值常量,则预处理器可以确定宏的结果并替换较大的值一个类似下面的C语言inta=MAX(15,20);由预处理器扩展为:inta=20;一个类似下面的C语言inta=MAX(myvar,20);由预处理器扩展为:inta=((myvar)>(20)?(myvar):(20));宏

--复杂宏传递给宏的参数个数必须与宏定义中指定的参数个数匹配通常的做法是用括号将宏定义中使用的参数括起来。这样做是为了使复合表达式在传递给宏时不会产生不必要的副作用比如:#defineMAX(x,y)((x)>(y)?(x):(y))将下面的C语句inta=MAX(x-5,10);扩展为inta=((x-5)>(10)?(x-5):(10));宏

--复杂宏在没有附加括号的情况下,比如#defineMAX(x,y)x>y?x:y将下面的C语句inta=MAX(x-5,10);扩展为inta=x-5>(10)?x-5:10;具有潜在不同的含义宏

--复杂宏多次使用参数的宏可能会给程序带来不希望的副作用,例如:

#defineMAX(x,y)((x)>(y)?(x):(y))maxval=MAX(a+b,func(c));扩展为maxval=((a+b)>(func(c))?(a+b):(func(c)));函数func在程序中似乎只能被调用一次,但由于宏的定义,它实际上被调用了两次。每个调用可能返回不同的值,MAX的结果可能不正确宏

--复杂宏宏可以用null或空参数列表定义。比如:#defineMYMACRO()(func();)要调用这样的宏,必须指定宏名字以及空参数列表。例如:MYMACRO()要将空参数传递给宏,必须在该参数的位置至少包含一个空格字符。宏

--复杂宏支持与C99标准相对应的可变参数宏如果宏定义的最后一个参数是...,则宏体中标识符__VA_ARGS__的任何出现都将替换为与省略号...匹配的所有参数,包括它们之间的逗号。至少有一个参数必须与省略号匹配例如:#defineCHECK(v,...)if(!(v))printf(__VA_ARGS__)CHECK(a==b,"a:%dnotequalb:%d\n",a,b);扩展为:if(!(a==b))printf("a:%dnotequalb:%d\n",a,b);宏

--宏操作符在#define或#if和#elif预处理器命令中可以使用三个预处理器操作符字符串化操作符(#)标记粘贴操作符(##)定义的操作符宏操作符

--字符串化操作符(#)在宏定义中使用字符化串或数字符号操作符(‘#’)时,会将宏参数转换为字符串常数该操作符只能在具有指定参数或参数列表的宏中使用当字符串化操作符紧跟在宏参数之一的名字之前时,传递给宏的参数被括在引号中,并被看作是字符串文字。例如:#definestringizer(x)printf(#x"\n")stringizer(text)上面的代码从预处理器得到以下实际输出:printf(“text\n”)宏操作符

--字符串化操作符(#)展开显示该参数是按字面意思转换的,就像它是一个字符串一样。当预处理器字符串化x参数时,生成的行是:printf(“text”“\n”)在编译时,将由空格分隔的字符串链接起来。因此,这两个字符串被组合为“text\n”如果作为参数传递的字符串包含通常应进行文字化或转义的字符(例如“和\),则会自动添加所需的\字符宏操作符

--标记粘贴操作符(##)宏定义中的标记粘贴操作符(##)组合了两个参数它将宏定义中两个单独的标记连接到单个标记中如果宏定义中使用的宏参数的名字前面或后面仅跟着标记粘贴操作符,则宏参数和标记粘贴操作符将替换为传递的参数值与不是宏参数名字的标记粘贴操作符相邻的文本不受影响。例如:#definepaster(n)printf("token"#n"=%d",token##n)paster(9);上面的代码导致预处理器的下面实际输出:printf(“token9=%d”,token9);该结果显示标记##n到标记9的并置。该例子中同时使用了字符化和标记粘贴操作符宏操作符

--定义的操作符在常量表达式中使用预处理器定义的操作符来确定是否定义了标识符(通过#define预处理器命令)如果定义了指定的标识符,则该值为真(非零)。如果未定义符号,则值为假(零)。定义的操作符指定如下:defined(identifier)或definedidentifierdefined操作符只能用在#if或#elif命令中预处理器

--预定义的宏C251编译器提供了以下预定义的常数常数功能__C251__编译器版本号(比如,401对应于版本4.01)__DATE__以ANSI格式开始编译的日期(monthddyyyy)__DATE2__以短格式开始编译的日期(mm/dd/yy)__FILE__正在编译的文件名字__LINE__正在编译的文件中的行号__MODEL__选择的存储器模型。0:SMALL;2:LARGE;3:TINY;4:XTINY;5:XSMALL__TIME__编译开始的时间__STDC__定义为1表示完全符合ANSIC标准__FLOAT64__定义为0表示使用32位浮点数。如果通过调用或#pragma给出FLOAT64命令,则定义为1。FLOAT64将浮点运算改为双精度(64位)__MODSRC__如果通过调用或#pragma指定STC32G系列单片机的二进制模式,则定义为0;如果指定了STC32G系列单片机的源模式,则定义为1宏调用的例子宏的C语言描述#include"stdio.h"//包含头文件stdio.h#include"reg251s.h"//包含头文件reg251s.h#definemax(a,b)(a>=b?a:b)//定义带有参数的宏max#definemin(a,b)(a<=b?a:b)//定义带有参数的宏min/****定义带有字符串化运算符的宏stringizer******/#definestringizer(x)printf(#x"\n")/*******定义带有标记粘贴运算符的宏paster*******/#definepaster(n)printf("a"#n"=%d\n",a##n)/**********定义可变参数宏CHECK***********/#defineCHECK(v,...)if(!(v))printf(__VA_ARGS__)预处理器

--宏调用的例子voidmain()//定义main主函数{ volatilecharx=100,y=-2;//定义并初始化字符型变量x和y volatilechara1=10;//定义并初始化字符型变量a1 SCON=0x52;//配置串口寄存器SCONTMOD=0x20;//配置串口寄存器TMODTCON=0x69;//配置串口寄存器TCONTH1=0xF3;//配置串口寄存器TH1/*****调用printf函数,打印调用宏max(x,y)的值******/printf("maximumvalueis=%d\n",max(x,y));

预处理器

--宏调用的例子/********调用printf函数,打印调用宏min(x,y)的值*********/printf("minimumvalueis=%d\n",min(x,y));stringizer(text);//调用宏stringizer paster(1);//调用宏paster CHECK(x==y,"x:%dnotequaly:%d\n",x,y);//调用宏CHECK printf("%s\n",__DATE__);//调用预定义的宏__DATE__ printf("C251Versionis%d\n",__C251__);//调用预定义的宏__C251__while(1);//无限循环,设置断点}预处理器

--取消定义或重定义程序开发人员使用#define命令定义宏,使用#undef命令未定义或取消定义的宏例如:#defineVALUE123intv1=VALUE;#undefVALUEintv2=VALUE;扩展为:intv1=123;intv2=VALUE;预处理器

--使用宏的注意事项使用宏时,需要注意下面的一些规则:宏定义不保存到目标文件中它们仅在单个源文件的持续时间内处于活动状态,从定义它们时开始,到它们未定义时(使用#undef)、重新定义或找到源文件结尾时结束程序开发人员希望在多个源文件中使用的宏定义可以在包含文件中定义,该文件包含在需要宏的每个源文件中当调用带有参数的宏时,宏处理器将参数替换到宏体中,然后再次处理结果以进行其他宏调用这使得从宏体和宏参数拼凑宏调用称为可能,但是令人困惑预处理器

--使用宏的注意事项大多数有经验的C程序员在宏体中使用宏参数时,都会将它们括在括号中该技术可防止对用作参数的复合表达式进行不期望的分组,并有助于避免操作符优先级规则覆盖宏的预期含义虽然宏可能包含对其他宏的引用,但对其自身的引用不会展开自引用宏是ANSI标准C的一个特殊功能,因为自引用不会被解释为宏调用,该特殊规则也适用于间接自引用宏(或通过另一个宏引用自身的宏)预处理器

--预处理器命令预处理器命令必须是一行中指定的第一个非空白文本。所有命令都以磅或数字符号字符(‘#’)作为前缀例如:#pragmaPRI

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论