栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > C/C++/C#

C语言回顾(修饰词篇)

C/C++/C# 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

C语言回顾(修饰词篇)

修饰词 const

const修饰的量为一个常量即不能被修改的量。

1、const用于定义常变量时,要进行初始化
    const int a=10; //合法
    const int a;  //非法,未初始化,虽然不会出错,但是后面也不能对其赋值
2、数据类型对于const而言是透明的(也就是说确定数据类型的时候不必带上它)
    const int a=10;   等价于 int const a=10;
    const int *p1=&a; 等价于 int const *p1=&a; (两者都是修饰*p1)  但不等价于 int *const     p1=&a; (这个是修饰p1) 
3、const用于封锁直接修饰的内容,该内容变为只读,该变量不能作为左值(左值:放在赋值号‘=’的左边,使用变量的写权限)
    const int a=10;//const封锁a
    a=100; //a作为左值,使用a的写权限,非法
    int b=a; //使用a的读权限,合法

    const int *p1=&a; //const修饰*p1,将p1作为左值合法,将*p1作为左值非法
    p1=&b; //使用p1做左值,合法
    *p1=200;//使用*p1做左值,非法
    int * const p2=&a; //const修饰p2,将p2作为左值非法,将*p2作为左值合法
    p2=&b;//使用p2做左值,非法
    *p2=100;//使用*p2做左值,合法
volatile

volatile的本意是“易变的” 因为访问寄存器要比访问内存单元快的多,所以编译器一般都会作减少存取内存的优化,但有可能会读脏数据。当要求使用volatile声明变量值的时候,系统总是重新从它所在的内存读取数据,即使它前面的指令刚刚从该处读取过数据。

1、告诉编译器不优化

比如要往某一地址送两指令:
int *ip =...; //设备地址
*ip = 1; //第一个指令
*ip = 2; //第二个指令
以上程序compiler可能做优化而成:
int *ip = ...;
*ip = 2;
结果第一个指令丢失。如果用volatile, compiler就不允许做任何的优化,从而保证程序的原意:
volatile int *ip = ...;
*ip = 1;
*ip = 2;

2、用volatile定义的变量会在程序外被改变,每次都必须从内存中读取,而不能重复使用放在cache或寄存器中的备份。

应用场景:

1、中断服务程序中修改的供其它程序检测的变量需要加volatile。变量所在内存的值可能已经被改了,但是在其他程序中的调用可能一直读取的是寄存器中的值,从而导致程序运行错误。(内存是SRAM,寄存器是R1,R2等或者cache)

2、多任务环境下各任务间共享的标志应该加volatile

频繁地使用volatile很可能会增加代码尺寸和降低性能,因此要合理的使用volatile。

__attribute__

__attribute__用来设置函数的属性(Function Attribute )、变量的属性(Variable Attribute )和类型的属性(Type Attribute )

__attribute__ 语法格式为:__attribute__ ((attribute-list))

其位置约束:放于声明的尾部“;”之前。说白了,它就是相当于是个形容词,它存在于被形容的函数或变量或类型的后面,紧挨着,进行说明。

GNU 和ARM编译器都支持__attribute__

aligned (alignment)

指定对象的对齐格式(以字节为单位),属于类型的属性,多用来形容结构体类型,注意,是类型,不是变量。变量属性也有 aligned,不常用,变量属性的话就跟在变量后面。

struct S { 
short b[3]; 
} __attribute__ ((aligned (8)));


struct m
{
    char a;
    int b;
    short c;
}__attribute__((aligned(4))) mm;

packed

就是告诉编译器取消字节对齐(使用1字节对齐),按照实际占用字节数进行对齐,属于类型的属性,多用来形容结构体,联合体。

struct packed_struct
{
     char c;
     int  i;
     struct unpacked_struct s;
}__attribute__ ((__packed__));

used

__attribute__((used))可以是函数的属性也可以是变量的属性,修饰函数就跟在函数后面,修饰变量就跟在变量后面。其作用是告诉编译器,我定义的东西,就算没有调用,也不能被优化掉

unused

__attribute__((unused))可以是函数的属性也可以是变量的属性,修饰函数就跟在函数后面,修饰变量就跟在变量后面。表示该函数或变量可能不使用,这个属性可以避免编译器产生警告信息

weak

__attribute__((weak))可以是函数的属性也可以是变量的属性,修饰函数就跟在函数后面,修饰变量就跟在变量后面。其所形容的函数是个弱符号。

在c语言中,函数和初始化的全局变量是强符号,未初始化的全局变量是弱符号。强符号和弱符号的定义是连接器用来处理多重定义符号的,它的规则是:不允许多个强符号;如果一个强符号和一个弱符号,这选择强符号;如果多个弱符号,则任意选一个。

通俗的说:当代码中有两个相同的函数或者变量。其中一个以__attribute__((weak))修饰了,那么这个就是弱符号。这时候编译器在编译的时候即使这两个名字是一样的也不会报错。当程序中调用的时候会调用没有被修饰那个,修饰的那个将会被忽略掉。

使用场景:

int  __attribute__((weak))  func(......)
 
{
 
    return 0;
 
}
at

__attribute__((at))绝对定位,可以把变量或函数绝对定位到Flash中,或者定位到RAM。

int value __attribute__((at(0x20000000))) = 0x33;//将变量定位到RAM中
const char ziku[] __attribute__((at(0x0800F000)))   = {0x1, 0x2, 0x3}; //将只读的常量放在flash中
void func (void) __attribute__((at(0x08001000)));//将函数放在指定flash中
 
void func (void) {
    int i;
    for (i = 0; i < 100; i++){
    }
}

变量指定的地址只能位于RAM区;常量和代码只能位于Flash区。不然在链接阶段会出错

section

__attribute__((section(“section_name”)))函数或数据放入指定名为"section_name"对应的段中。

const int descriptor[3] __attribute__ ((section ("descr"))) = { 1,2,3 };
long long rw[10] __attribute__ ((section ("RW")));

注意:变量的段定义在RAM中,常量和函数的段定义在Flash中。变量和常量不能放在一个段中,会报错。自定义的变量段在内存中的分布是连续的,且根据名字进行排列。

宏定义的用法

宏定义 #define 在C程序编译的第一个步骤预处理阶段被编译,其作用就是将宏名替换为替换列表中得内容。

宏定义的一般写法
#define   标识符(也称为宏名)   替换列表
(替换列表可以是数,字符串字面量,标点符号,运算符,标识符,关键字,字符常量。注意:替换列表是可以为空的)
无参定义
//定义常量
#define N 100
//重定义数据类型
#define pin (int*)
#define u32 unsigned int
//定义一个循环
#define LOOP for(;;)

有参定义

#define 宏名(形参表) 替换列表

需要注意的是“边缘效应”

例如:
#define M(a,b) a*b
假设 a = 1, b = 2, M(a, b)所得结果为 2,这应该都没问题
但如果 a = 1+1,b = 2,M(a, b)所得结果不是4,应该是3
因为#define只是起到替换作用,所以最后的表达式应该替换为 1+1*2,所以结果为3

因此形参都最好加上括号:
#define M(a,b) ((a)*(b))
这样如果 a = 1+1,b = 2,M(a, b)所得结果不是4,因为替换后为
(( 1+1)*( 2 ))

多行定义

在进行宏定义的时候可以使用反斜杠接续符 ’ ’ ,来接续上一行,反斜杠作为接续符时,在本行其后面不能再有任何字符,空格都不行

#define MAX(X,Y) do { 
语句1; 
语句2; 
} while(0) 
条件编译

在大规模的开发过程中,特别是跨平台和系统的软件里,define最重要的功能是条件编译。

#ifndef __headerfileXXX__
#define __headerfileXXX__
  …
  …
 #endif
#ifdef XXX
…
#else
… 
#endif

取消宏定义

#define 的作用域为自 #define 那一行起到源程序结束。如果要终止其作用域可以使用 #undef 命令

#undef  标识符
#、##、@#

"#"用来把参数转换成字符串,是给参数加上双引号。
"##"则用来连接前后两个参数,把它们变成一个字符串,
"#@"是给参数加上单引号。

#define CAT(x,y) x##y       
#define TOCHAr(a) #@a       
#define TOSTRING(x) #x      
#define f(a,b) a##b 
#define d(a) #a 
#define s(a) d(a) 

void main( void ) 
{ 
    puts(d(f(a,b))); 
    puts(s(f(a,b))); 
} 

输出结果: 
f(a,b) 
ab
    

预定义宏
宏描述
__FILE__当前源文件的名称,字符串常量
__DATE__当前源文件编译日期用 “mm dd yyy”形式的字符串常量表示
__LINE__当前源义件中的行号,用十进制整数常量表示
__TIME__当前源文件的最新编译吋间,用“hh:mm:ss”形式的字符串常量表示
__FUNCTION__当前调用的函数名 字符串
__STDC__如果今前编泽器符合ISO标准,那么该宏的值为1,否则未定义
__STDC_VERSION__如果当前编译器符合C89,那么它被定义为199409L;如果符合C99,那么它被定义为199901L:在其他情况下,该宏为宋定义
其他的技巧
#define test ("1" "2" "3") //test的值为"123"
转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/1030949.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号