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"



