C语言关键字学习总结
其实在写代码的时候,关键字还是用的比较多的,这里主要就平常中用到的常用关键字进行总结,便于更全面的理解其在代码中的意图。
static关键字(C语言)
static可以用来修饰局部变量、全局变量、函数
1、局部变量:
生命周期:原先存在栈中,生命周期语句执行完毕便结束了。现在存放到静态数据区,生命周期持续到整个程序执行结束。
作用域:并没有改变作用域,还是仅限于该语句块。并且只在初次运行的时候进行初始化,下次调用时它的值是上一次函数调用结束之后的值。每次调用后值会被保存。
例如:
#include<stdio.h>
void fun()
{
static int a=1; a++;
printf("%d\n",a);
}
int main(void)
{
fun();
fun();
return 0;
}
输出结果:
2
3
2、全局变量:
对于一个全部变量,既可以在本源文件中被访问到,也可以在同一个工程的其它源文件中被访问(只需用extern进行声明即可)。
如果加上static,限制该全局变量的作用域范围,由原来的整个工程可见变为本源文件可见。
3、函数:
与修饰全局变量大同小异,就是改变了函数的作用域。
修饰函数时还有一处不同,就是调用静态函数时,函数指针指向的地址始终是固定的,而普通函数每次返回的地址不一样(这条特性是一个工作老手告诉我的,还没来得及验证)
extern用在变量或者函数的声明前,用来说明“此变量/函数是在别处定义的,要在此处引用”。另外,用extern会加速程序的编译过程,这样能节省时间。
C++中static还有一些不同,这里就暂时先不讨论。
const关键字(C语言)
C语言中保留的一个关键字,它用来限定一个变量是只读的,即不可变的。
1、用const修饰一般变量
用const修饰的变量必须在声明时进行初始化(用来修饰函数的形参除外)。
const int n; 这种声明方式是错误的
const int n=5; 正确
void fun(const int n); 正确
const char a; 错误
char * const p; 错误
const char *p;正确(注意这种为什么是正确的),因为这里const是修饰p指向的变量,而不是指针变量p本身
一旦一个变量被const修饰后,在程序中除初始化外对这个变量进行的赋值都是错误的。
const int n=5;
n=3; 错误
2、const与指针搭配使用
两个基础概念:指针常量和常量指针
指针常量:即指针本身的值是不可改变的,而指针指向的变量的值是可以改变的;
常量指针:即指针指向的变量的值是不可改变的,而指针本身的值是可以改变的;
如果const在’*’左边,则表示指针指向的变量的值不可变;
如果const在’*’右边,则表示指针的值是不可变的;
#include<stdio.h>
int main(void)
{
const int a=3;
int* pa=&a;
*pa=4;
printf("%d\n",*pa);
printf("%d\n",a);
return 0;
}
编译结果:
e:\c++\test\test.c(6) : warning C4090: 'initializing' : different 'const' qualifiers
输出结果是:4
4
注意:
不能显式地通过赋值语句去改变a的值,但是不代表在程序中不能通过其它方法来修改这个值。
const用法(C++)
C语言中用const修饰的变量仍然是一个变量;而在C++中用const修饰过后,就变成常量了。
其他的,后面有空在总结吧。部分参考这里
register关键字用法
register:这个关键字请求编译器尽可能的将变量存在CPU 内部寄存器中而不是通过内存寻址访问以提高效率。注意是尽可能,不是绝对。
寄存器其实就是一块一块小的存储空间,只不过其存取速度要比内存快得多。
数据从内存里拿出来先放到寄存器,然后CPU 再从寄存器里读取数据来处理,处理完后同样把数据通过寄存器存放到内存里,CPU 不直接和内存打交道。
有个故事,叫皇帝身边的小太监,挺形象的:
大家都看过古装戏,那些皇帝们要阅读奏章的时候,大臣总是先将奏章交给皇帝旁边的小太监,小太监呢再交给皇帝同志处理。这个小太监只是个中转站,并无别的功能。
好,那我们再联想到我们的CPU。CPU 不就是我们的皇帝同志么?大臣就相当于我们的内存,数据从他这拿出来。那小太监就是我们的寄存器了(这里先不考虑CPU 的高速缓存区)。数据从内存里拿出来先放到寄存器,然后CPU 再从寄存器里读取数据来处理,处理完后同样把数据通过寄存器存放到内存里,CPU 不直接和内存打交道。这里要说明的一点是:小太监是主动的从大臣手里接过奏章,然后主动的交给皇帝同志,但寄存器没这么自觉,它从不主动干什么事。一个皇帝可能有好些小太监,那么一个CPU 也可以有很多寄存器,不同型号的CPU 拥有寄存器的数量不一样。
为啥要这么麻烦啊?速度!就是因为速度。寄存器其实就是一块一块小的存储空间,只不过其存取速度要比内存快得多。进水楼台先得月嘛,它离CPU 很近,CPU 一伸手就拿到数据了,比在那么大的一块内存里去寻找某个地址上的数据是不是快多了?那有人问既然它速度那么快,那我们的内存硬盘都改成寄存器得了呗。我要说的是:你真有钱!
一些限制:
(1)register变量必须是能被CPU所接受的类型。
这通常意味着register变量必须是一个单个的值,并且长度应该小于或者等于整型的长度。不过,有些机器的寄存器也能存放浮点数。
(2)因为register变量可能不存放在内存中,所以不能用“&”来获取register变量的地址。
(3)只有局部自动变量和形式参数可以作为寄存器变量,其它(如全局变量)不行。
在调用一个函数时占用一些寄存器以存放寄存器变量的值,函数调用结束后释放寄存器。此后,在调用另外一个函数时又可以利用这些寄存器来存放该函数的寄存器变量。
(4)局部静态变量不能定义为寄存器变量。不能写成:register static int a, b, c;
(5)由于寄存器的数量有限(不同的cpu寄存器数目不一),不能定义任意多个寄存器变量,而且某些寄存器只能接受特定类型的数据(如指针和浮点数),因此真正起作用的register修饰符的数目和类型都依赖于运行程序的机器,而任何多余的register修饰符都将被编译程序所忽略。
注意:
早期的C编译程序不会把变量保存在寄存器中,除非你命令它这样做,这时register修饰符是C语言的一种很有价值的补充。然而,随着编译程序设计技术的进步,在决定哪些变量应该被存到寄存器中时,现在的C编译环境能比程序员做出更好的决定。实际上,许多编译程序都会忽略register修饰符,因为尽管它完全合法,但它仅仅是暗示而不是命令。
auto关键字
用于声明变量的生存期为自动,所有的变量默认就是auto的。
关于C++ 11中auto的用法这里就先不讨论了,后面再总结
inline内联函数
调用函数时需要一定的时间和空间的开销。C++提供一种提高效率的方法,即在编译时将函数调用处用函数体替换,类似于C语言中的宏展开。这种在函数调用处直接嵌入函数体的函数称为内联函数(inline function),又称内嵌函数或内置函数。
注意:是在函数定义时增加 inline 关键字,而不是在函数声明时。
优点:
内联函数可以有效避免函数调用的开销,程序执行效率更高
缺点:
如果被声明为内联函数的函数体非常大,则编译器编译后程序的可执行码将会变得很大。当内联函数的函数体过大时,一般的编译器会放弃内联方式,而采用普通的方式调用函数。这样,内联函数就和普通函数执行效率一样了。
总结:通常在程序设计过程中,我们会将一些频繁被调用的短小函数声明为内联函数。
调用内联函数和调用正规函数是等价的,差别仅仅是更快
和宏定义的区别:
1、宏是由预处理器对宏进行替代,而内联函数是通过编译器控制来实现的。
2、内联函数是真正的函数,只是在需要用到的时候,内联函数像宏一样的展开,所以取消了函数的参数压栈,减少了调用的开销。你可以象调用函数一样来调用内联函数,而不必担心会产生于处理宏的一些问题。
栈: 在函数调用时,第一个进栈的是主函数中函数调用后的下一条指令(函数调用语句的下一条可执行语句)的地址,然后是函数的各个参数,在大多数的C编译器中,参数是由右往左入栈的,然后是函数中的局部变量。注意静态变量是不入栈的。
感兴趣进一步移步这里:函数调用栈的分析参考