C语言 —— 关键字(C语言标准定义的32个关键字:auto、register、static、sizeof、signed、unsigned 、break 、 continue 、void)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/weixin_42167759/article/details/83786298

C语言标准定义的32个关键字:(列出每个关键字的意义)

关键字 意 义
auto 声明自动变量,缺省时编译器一般默认为 auto
int 声明整型变量
double 声明双精度变量
long 声明长整型变量
char 声明字符型变量
float 声明浮点型变量
short 声明短整型变量
signed 声明有符号类型变量
unsigned 声明无符号类型变量
struct 声明结构体变量
union 声明联合数据类型
enum 声明枚举类型
static 声明静态变量
switch 用于开关语句
case 开关语句分支
default 开关语句中的“其他”分支
break 跳出当前循环
register 声明寄存器变量
const 声明只读变量
volatile 说明变量在程序执行中可被隐含地改变
typedef 用以给数据类型取别名(当然还有其他作用
extern 声明变量是在其他文件正声明(也可以看做是引用变量)
return 子程序返回语句(可以带参数,也可不带参数)
void 声明函数无返回值或无参数,声明空类型指针
continue 结束当前循环,开始下一轮循环
do 循环语句的循环体
while 循环语句的循环条件
if 条件语句
else 条件语句否定分支(与 if 连用)
for 一种循环语句(可意会不可言传)
goto 无条件跳转语句
sizeof 计算对象所占内存空间大小


1.1,最宽恒大量的关键字----auto
auto:它很宽恒大量的,你就当它不存在吧。编译器在默认的缺省情况下,所有变量
都是 auto 的。


1.2,最快的关键字---- register
register:这个关键字请求编译器尽可能的将变量存在 CPU 内部寄存器中而不是通过内
存寻址访问以提高效率。注意是尽可能,不是绝对。你想想,一个 CPU 的寄存器也就那么
几个或几十个,你要是定义了很多很多 register 变量,它累死也可能不能全部把这些变量放
入寄存器吧,轮也可能轮不到你。
使用 register 修饰符的注意点:虽然寄存器的速度非常快,但是使用 register 修饰符也有些限制的: register 变量必须是
能被 CPU 寄存器所接受的类型。意味着 register 变量必须是一个单个的值,并且其长度应小
于或等于整型的长度。 而且 register 变量可能不存放在内存中, 所以不能用取址运算符“ &”
来获取 register 变量的地址。


1.3,最名不符实的关键字----static
不要误以为关键字 static 很安静,其实它一点也不安静。这个关键字在 C 语言里主要有
两个作用, C++对它进行了扩展
1.3.1,修饰变量
第一个作用:修饰变量。变量又分为局部和全局变量,但它们都存在内存的静态区。
静态全局变量,作用域仅限于变量被定义的文件中,其他文件即使用 extern 声明也没法
使用他。准确地说作用域是从定义之处开始,到文件结尾处结束,在定义之处前面的那些
代码行也不能使用它。想要使用就得在前面再加 extern ***。恶心吧?要想不恶心,很简单,
直接在文件顶端定义不就得了。
静态局部变量,在函数体里面定义的,就只能在这个函数里用了,同一个文档中的其他
函数也用不了。由于被 static 修饰的变量总是存在内存的静态区,所以即使这个函数运行结
束,这个静态变量的值还是不会被销毁,函数下次使用时仍然能用到这个值。
static int j;
void fun1( void)
{
static inti = 0;
i ++;
}
void fun2( void)
{
j = 0;
j++;
}
intmain()
{
for(k=0; k<10; k++)
{
fun1();
fun2();
}
return 0;
}
i 和 j 的值分别是什么,为什么?(i会从1累加到10,就)
1.3.2,修饰函数
第二个作用:修饰函数。函数前加 static 使得函数成为静态函数。但此处“static”的含义
不是指存储方式,而是指对函数的作用域仅局限于本文件(所以又称内部函数)。使用内部函
数的好处是:不同的人编写不同的函数时,不用担心自己定义的函数,是否会与其它文件
中的函数同名。
关键字 static 有着不寻常的历史。起初,在 C 中引入关键字 static 是为了表示退出一个
块后仍然存在的局部变量。随后, static 在 C 中有了第二种含义:用来表示不能被其它文件
访问的全局变量和函数。为了避免引入新的关键字,所以仍使用 static 关键字来表示这第二
种含义。
当然, C++里对 static 赋予了第三个作用,这里先不讨论,有兴趣的可以找相关资料研
究。


1.4,基本数据类型----short、int、 long、char、float、double
C 语言包含的数据类型如下图所示:
 

基本数据类型

 一般来说习惯上用 n,m,i,j,k 等表示 int 类型的变量; c, ch 等表示字符类型变量; a 等表
示数组; p 等表示指针。当然这仅仅是一般习惯,除了 i,j,k 等可以用来表示循环变量外,别
的字符变量名尽量不要使用。


1.5,最冤枉的关键字----sizeof
sizeof 是关键字不是函数,其实就算不知道它是否为 32 个关键字之一时,我们也可以
借助编译器确定它的身份。看下面的例子:
int i=0;
A),sizeof(int); B), sizeof(i); C), sizeof int; D), sizeof i;
毫无疑问, 32 位系统下 A), B)的值为 4。那 C)的呢? D)的呢?
在 32 位系统下,通过 Visual C++6.0 或任意一编译器调试,我们发现 D)的结果也为 4。
咦? sizeof 后面的括号呢?没有括号居然也行,那想想,函数名后面没有括号行吗?由此轻
易得出 sizeof 绝非函数。
好,再看 C)。编译器怎么怎么提示出错呢?不是说 sizeof 是个关键字,其后面的括号
可以没有么?那你想想 sizeof int 表示什么啊? int 前面加一个关键字?类型扩展?明显不
正确,我们可以在 int 前加 unsigned, const 等关键字但不能加 sizeof。好,记住: sizeof 在
计算变量所占空间大小时,括号可以省略,而计算类型(模子)大小时不能省略。一般情况下,
咱也别偷这个懒,乖乖的写上括号,继续装作一个“函数”,做一个“披着函数皮的关键字”。
做我的关键字,让人家认为是函数去吧。


1.6,signed、unsigned 关键字
我们知道计算机底层只认识 0、 1.任何数据到了底层都会变计算转换成 0、 1.那负数怎么
存储呢?肯定这个“ -”号是无法存入内存的,怎么办?很好办,做个标记。把基本数据类
型的最高位腾出来,用来存符号,同时约定如下:最高位如果是 1,表明这个数是负数,其
值为除最高位以外的剩余位的值添上这个“ -”号;如果最高位是 0,表明这个数是正数,
其值为除最高位以外的剩余位的值。


1.7 break 与 continue 的区别
break 关键字很重要,表示终止本层循环。现在这个例子只有一层循环,当代码执行到
break 时,循环便终止。
如果把 break 换成 continue 会是什么样子呢? continue 表示终止本次(本轮) 循环。当
代码执行到 continue 时,本轮循环终止,进入下一轮循环。
while( 1)也有写成 while(true) 或者 while(1==1) 或者 while((bool) 1)等形式的,效果一
样。
do-while 循环:先执行 do 后面的代码,然后再判断 while 后面括号里的值,如果为真,
循环开始;否则,循环不开始。其用法与 while 循环没有区别,但相对较少用。
for 循环: for 循环可以很容易的控制循环次数,多用于事先知道循环次数的情况下。


1.8,void 关键字
void 有什么好讲的呢?如果你认为没有,那就没有;但如果你认为有,那就真的有。有点像“色即是空,空即是色”。
void 真正发挥的作用在于:
(1) 对函数返回的限定;  (2) 对函数参数的限定

众所周知, 如果指针 p1 和 p2 的类型相同, 那么我们可以直接在 p1 和 p2 间互相赋值;
如果 p1 和 p2 指向不同的数据类型,则必须使用强制类型转换运算符把赋值运算符右边的
指针类型转换为左边指针的类型。
例如:
float *p1;
int *p2;
p1 = p2;
其中 p1 = p2 语句会编译出错,提示“'=' : cannot convertfrom 'int *' to 'float *'”,必须改为:
p1 = (float *)p2;
而 void *则不同,任何类型的指针都可以直接赋值给它,无需进行强制类型转换:
void *p1;
int *p2;
p1 = p2;
但这并不意味着, void *也可以无需强制类型转换地赋给其它类型的指针。因为“空类型”可
以包容“有类型”,而“有类型”则不能包容“空类型”。比如,我们可以说“男人和女人都是人”,
但不能说“人是男人”或者“人是女人”。下面的语句编译出错:
void *p1;
int *p2;
p2 = p1;
提示“'=' : cannot convert from 'void *' to 'int *'”。
void 修饰函数返回值和参数
【规则 1-33】如果函数没有返回值,那么应声明为 void 类型
在 C 语言中,凡不加返回值类型限定的函数,就会被编译器作为返回整型值处理。但
是许多程序员却误以为其为 void 类型。例如:
add ( int a, int b )
{
return a + b;
}
int main(int argc, char* argv[]) //甚至很多人以为 main 函数无返回值
//或是为 void 型的
{
printf ( "2 + 3 = %d", add ( 2, 3) );
}
程序运行的结果为输出: 2 + 3 = 5
这说明不加返回值说明的函数的确为 int 函数。
因此,为了避免混乱,我们在编写 C 程序时,对于任何函数都必须一个不漏地指定其
类型。如果函数没有返回值,一定要声明为 void 类型。这既是程序良好可读性的需要,也
是编程规范性的要求。另外,加上 void 类型声明后,也可以发挥代码的“自注释”作用。所
谓的代码的“自注释”即代码能自己注释自己。
【规则 1-34】如果函数无参数,那么应声明其参数为 void
在 C++语言中声明一个这样的函数:
int function(void)
{
return 1;
}
则进行下面的调用是不合法的: function(2);
因为在 C++中,函数参数为 void 的意思是这个函数不接受任何参数。
但是在 Turbo C 2.0 中编译:
#include "stdio.h"
fun()
{
return 1;
}
main()
{
printf("%d",fun(2));
getchar();
更多免费资源:www.fishc.com
}
编译正确且输出 1,这说明,在 C 语言中,可以给无参数的函数传送任意类型的参数,
但是在 C++编译器中编译同样的代码则会出错。在 C++中,不能向无参数的函数传送任何
参数,出错提示“'fun' : function does not take 1 parameters”。
所以,无论在 C 还是 C++中,若函数不接受任何参数,一定要指明参数为 void
void 指针
【规则 1-35】千万小心又小心使用 void 指针类型。
按照 ANSI(American National Standards Institute)标准,不能对 void 指针进行算法操作,
即下列操作都是不合法的:
void * pvoid;
pvoid++; //ANSI:错误
pvoid += 1; //ANSI:错误
ANSI 标准之所以这样认定,是因为它坚持:进行算法操作的指针必须是确定知道其指
向数据类型大小的。也就是说必须知道内存目的地址的确切值。
例如:
int *pint;
pint++; //ANSI:正确
但是大名鼎鼎的 GNU(GNU's Not Unix 的递归缩写)则不这么认定,它指定 void *的算法
操作与 char *一致。因此下列语句在 GNU 编译器中皆正确:
pvoid++; //GNU:正确
pvoid += 1; //GNU:正确
在实际的程序设计中,为符合 ANSI 标准,并提高程序的可移植性,我们可以这样编写
实现同样功能的代码:
void * pvoid;
(char *)pvoid++; //ANSI:正确; GNU:正确
(char *)pvoid += 1; //ANSI:错误; GNU:正确
GNU 和 ANSI 还有一些区别,总体而言, GNU 较 ANSI 更“开放”,提供了对更多语法
的支持。但是我们在真实设计时,还是应该尽可能地符合 ANSI 标准。
【规则 1-36】如果函数的参数可以是任意类型指针,那么应声明其参数为 void *。
典型的如内存操作函数 memcpy 和 memset 的函数原型分别为:
void * memcpy(void *dest, const void *src, size_t len);
void * memset ( void * buffer, int c, size_t num );
这样,任何类型的指针都可以传入 memcpy 和 memset 中,这也真实地体现了内存操作
函数的意义, 因为它操作的对象仅仅是一片内存, 而不论这片内存是什么类型。如果 memcpy
和 memset 的参数类型不是 void *,而是 char *,那才叫真的奇怪了!这样的 memcpy 和 memset
明显不是一个“纯粹的,脱离低级趣味的”函数!
下面的代码执行正确:
例子: memset 接受任意类型指针
int IntArray_a[100];
memset (IntArray_a, 0, 100*sizeof(int) ); //将 IntArray_a 清 0
例子: memcpy 接受任意类型指针
int destIntArray_a[100], srcintarray_a[100];
//将 srcintarray_a 拷贝给 destIntArray_a
memcpy (destIntArray_a, srcintarray_a, 100*sizeof(int) );
有趣的是, memcpy 和 memset 函数返回的也是 void *类型,标准库函数的编写者都不是一
般人。


1.9,void 关键字

return 用来终止一个函数并返回其后面跟着的值。
return (Val); //此括号可以省略。但一般不省略,尤其在返回一个表达式的值时。
return 可以返回些什么东西呢?看下面例子:
char * Func(void)
{
char str[30];

return str;
}
str 属于局部变量,位于栈内存中,在 Func 结束的时候被释放,所以返回 str 将导致错误。


2.0,const 关键字

2.0.1,修饰一般变量
一般常量是指简单类型的只读变量。这种只读变量在定义时,修饰符 const 可以用在类
型说明符前,也可以用在类型说明符后。例如:
int const i=2; 或 const int i=2;
2.0.2,修饰数组
定义或说明一个只读数组可采用如下格式:
int const a[5]={1, 2, 3, 4, 5};或
const int a[5]={1, 2, 3, 4, 5};
2.0.3,修饰指针
先忽略类型名(编译器解析的时候也是忽略类型名),我们看 const 离哪个近。“近水楼
台先得月”,离谁近就修饰谁。
const int *p; //const 修饰*p,p 是指针, *p 是指针指向的对象,不可变
int const *p; //const 修饰*p,p 是指针, *p 是指针指向的对象,不可变
int *const p; //const 修饰 p, p 不可变, p 指向的对象可变
const int *const p; //前一个 const 修饰*p,后一个 const 修饰 p,指针 p 和 p 指向的对象
都不可变
2.0.4修饰函数的参数
const 修饰符也可以修饰函数的参数,当不希望这个参数值被函数体内意外改变时使
用。例如:
void Fun(const int i);
告诉编译器 i 在函数体中的不能改变,从而防止了使用者的一些无意的或错误的修改。
2.0.5修饰函数的返回值
const 修饰符也可以修饰函数的返回值,返回值不可被改变。例如:
const int Fun (void);
在另一连接文件中引用 const 只读变量:
extern const int i; //正确的声明
extern const int j=10; //错误!只读变量的值不能改变。

参考书籍《C语言深度剖析》

猜你喜欢

转载自blog.csdn.net/weixin_42167759/article/details/83786298