C语言(关键字)

枚举类型

  • 枚举是一种特殊的构造类型,它本身和int 是等价的,但它的值是有限个int 常量的合集,这些常量称为枚举符。
enum test{MON, TUE, WED, THU, FRI, SAT, SUN};
enum test a = MON; //定义枚举变量a,它的值是枚举常量MON。
  • 用枚举定义的变量,其值只能取枚举列表里面的值,如果不在该列表中,则会报错或者警告
  • 枚举符的值可以相同,但是这样做容易引起歧义,所以在设计枚举类型的时候要注意避免。
  • 在枚举符的作用域内有与之同名的其他标识符时,该枚举符将被覆盖。

const关键字

  • 如果希望程序中的某个变量具有只可读,不可写的属性,那么可将变量定义成只读变量。
  • const 变量只能在定义时赋初值或是作为函数的形参时由实参赋初值。
  • 使用const 修饰的变量,只是让变量具有可读不可写的属性——只能通过变量名读取变量,而不可以通过变量名去写入变量。如果我们不通过变量名,而是通过地址的形式,const 变量仍可以修改
const int i = 1;
int *p = &i;
*p = 10; //通过指针p 修改只读变量i 的值
printf(“%d\n”, i); //i 的值变成 10

const 修饰指针

const 修饰指针的意义非常重大,需要重点掌握。const 修饰指针可以有以下几种情况:

const int * p; // p 的值可变,但不可以使用p 去修改p 指向的对象
int const * p; //同上
int * const p; //不可以通过p 去修改p 的值,但可以通过p 去修改p 指向的对象
const int * const p; //p 和p 指向的对象都不可以通过p 去修改

字符串常量中的const

char *p1 = “hello”;
const char *p2 = “hello”;

不管加不加const,”hello”这个字符串的内容都改不了,但是,加上const 有一个好处,那就是在编译期间就可以检查出错误。


const 与define

const 与define 在某种程度上都可以用于定义“常量”,但是const 与define 还是有本质区别的,从以下几点分析:

  1. 编译器处理方式不同
    • define 宏是在预处理阶段进行文本替换
    • const 只读变量是在编译运行阶段使用
  2. 类型和安全检查
    • define 宏没有具体的类型,仅仅在预编译时进行替换,不做类型检查
    • const 只读变量有具体的类型,在编译阶段会执行类型检查。
  3. 存储方式不同
    • define 宏不占用存储空间
    • const 只读变量会在内存中分配空间
      const 推出的初始目的,正是为了取代预编译指令,消除它的缺点,同时继承它的优点。

volatile 关键字

  • 概念:
    volatile 是易变的、不稳定的意思。通过volatile 关键字可以定义易变变量。
  • 编译器特点:
    当一个数据需要频繁使用时,从内存把数据载入CPU 或是从CPU 将数据写回内存将消耗大量的时间。因此,可以把数据保存在寄存器里,因为寄存器的存取速度与CPU 的计算速度相同,CPU 计算的有多快,寄存器就可以以多快的速度进行存取。
  • 优化:
    编译器根据以上特点,在编译程序时,会对具有上述特点的一些变量(比如常见的循环控制变量)进行优化,将它们保存在寄存器中。准确说就是,每次要用到这个变量时,都必须从内存中直接读取这个变量的值,而不是使用在它在寄存器中的备份。
volatile int i = 0; //每次用于i 时都要从内存去取值
for(i = 0; i < 10000; i++)
{
////
}
volatile int i = 0;
i = 1; //这些代码不会被优化
i = 2; //这些代码不会被优化
i = 3; //这些代码不会被优化
i = 4;
  • 应用场景:
    1. 中断服务程序中修改的供其他程序检测的变量需要加volatile
      (一个中断服务子程序中会访问到的非自动变量)
    2. 多任务环境下各任务间共享的变量应该加volatile
      (多线程应用中被几个任务共享的变量)
    3. 并行设备的硬件寄存器通常也要加volatile 说明,因为每次对它的读写都可能有不同的意义。(如状态寄存器、控制寄存器)

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


register关键字

  • register 关键字与volatile 关键字作用刚好相反。使用register 修饰后,编译器会尽可能将这个变量存储在CPU 内存的寄存器中,而不是通过内存寻址去访问。
  • 注意,CPU 的寄存器是数量有限的,一般也就那么几个或是几十个,如果用户定义了很多的register 变量,那么并不是所有的register 变量都会真的存储在寄存器中。
  • 使用register 修饰的变量必须是CPU 寄存器所能接受的类型,这意味着register 变量必须是一个值,且长度小于或等于整型数据的长度。因为register 变量可能不存放在内存中,所以不能对register 变量使用“&”进行取地址操作。

typedef与#define的区别

  1. 处理阶段不一样,#define 在预处理阶段处理,而typedef 则在编译阶段处理。
  2. 功能不一样,typedef 只可以为类型重命名,而#define 的工作原理是文本替换,除了可以用来定义类型,还可以用于定义常量,编译开关等。
  3. 作用域不一样,typedef 有作用域,而#define 的作用域是整个程序。
  4. 对指针的操作不一样:
typedef char* String1;
#define String2 char*

String1 p1, p2; //p1 和p2 都是char*类型
String2 p1, p2; //这个宏会展开成char *p1, p2; p1 是char*类型,p2 的类型则是char

全局变量的extern

在一个源文件中定义的全局变量也可以被另一个源文件使用,但是和函数不一样,全局变量的引用需要强制使用extern 关键字进行声明,否则会出现变量未定义情况。
1.c

int global = 1;
void hello(void){
printf(“hello, world\n”);
}

2.c

extern int global; //使用extern 对全局变量进行声明
int main(){
hello();
printf(“global is %d\n”, global);
}

注意一点,使用extern 声明全局变量时,不可以对全局变量进行赋值,如下:

extern int global = 2; //不可以赋值,因为此处是对extern 进行声明作用

static静态变量

  • static 可以修饰普通的局部变量,表示这个变量是一个静态变量,它和全局变量一样长驻于内存的数据段,但是作用域只限于定义处的那个函数。
  • 当static 修饰全局变量时,这个全局变量在本文件的使用性质并没有改变,但是,不可以用extern 将该文件引用到别的文件中去使用。
  • 当static 修饰一个函数时,这个函数将变成一个内部接口,它只能在该文件中被其他的函数调用,而不可以被其他文件所调用,即使用extern 进行声明也不行。

总结:
修饰局部变量:让局部变量的生存周期变为全局的。
修饰全局变量:让全局变量只能在本文件中使用
修饰函数:让函数只能在本文件中使用。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值