神刀安全网

你所不能错过的 C 语言复习要点(一)关键字(上)

正如最近所看的 《 C 语言深度解剖》一书的前言所说,从这篇文章所开启的“你所不能错过的 C 语言复习要点”系列只是引领大家进入 C 语言的大门,谈不上“熟悉”和“精通”。

普通人用 C 语言在 3 年之下,一般来说,还没掌握 C 语言;
5 年之下,一般来说,还没熟悉 C 语言;
10 年之下,谈不上精通。

切回到有关“C语言 关键字(上)”的这一复习篇,你所不能错过的,是以下几个要点,其中绿色标示“数据类型”和“修饰”由本篇“关键字(一)”所涉及:

你所不能错过的 C 语言复习要点(一)关键字(上)

要点回顾(上)

1. extern — 最会带帽子的关键字

  • 定义创建了对象并为这个对象分配了内存,声明没有分配内存
  • 相同的定义只能出现一次,相同的声明可以出现多次
int i;    // 变量的定义 extern int i;    // 变量的声明  void fun(int i, char c);    // 函数的声明 void fun(int i, char c) {}    // 函数的定义

2. auto — 最宽恒大量的关键字

  • 编译器在默认的缺省情况下,所有变量都是用 auto 修饰的。

3. register — 最快的关键字

  • 这个关键字请求编译器尽可能的将变量存在 CPU 内部寄存器中而不是通过内存寻址访问以高效率。注意是尽可能,不是绝对。

3.1 寄存器

  • 数据从内存里拿出来先放到寄存器,然后 CPU 再从寄存器里读取数据来处理,处理完后同样把数据通过寄存器存放到内存里,CPU 不直接和内存打交道。

为什么寄存器比内存快?

你所不能错过的 C 语言复习要点(一)关键字(上)
CPU Registers 即为寄存器

3.2 使用 register 修饰符的注意点

  • register 变量必须是能被 CPU 寄存器所接受的类型
  • register 变量必须是一个单个的值,并且其长度应小于或等于整型的长度
  • register 变量可能不存放在内存中,所以不能用取址运算符“&”来获取 register 变量的地址

4. static — 最名不符实的关键字

关键字 static 有着不寻常的历史。起初,在 C 中引入关键字 static 是为了表示退出一个块后仍然存在的局部变量。随后,static 在 C 中有了第二种含义:用来表示不能被其它文件访问的全局变量和函数。为了避免引入新的关键字,所以仍使用 static 关键字来表示这第二种含义。

4.1 修饰变量

  • 变量又分为局部和全局变量,但它们都存在内存的静态区
  • 静态全局变量,作用域仅限于变量被定义的文件中
  • 静态局部变量,在函数体里面定义且只能在这个函数里用
  • 静态变量的值不会被销毁,函数下次使用时仍然能用到这个值

4.2 修饰函数

  • 函数前加 static 使得函数成为静态函数,指对函数的作用域仅局限于本文件(所以又称内部函数)

5. const 关键字

  • const 是 constant 的缩写,并不是英文直译的用来修饰“常量”,更精确的说是修饰“只读变量”,其值在编译时不能被使用,因为编译器在编译时不知道其存储的内容。
  • const 推出的初始目的,正是为了取代预编译指令,消除它的缺点,同时继承它的优点。

5.1 const 修饰的只读变量

  • 定义 const 只读变量,具有不可变性。该变量具有只读属性。

5.2 节省空间,避免不必要的内存分配,同时 高效率

  • 编译器通常不为普通 const 只读变量分配存储空间,而是将它们保存在符号表中,这使得它成为一个编译期间的值,没有了存储与读内存的操作,使得它的效率也很高。
  • const 定义的只读变量从汇编的角度来看,只是给出了对应的内存地址。

5.3 修饰一般变量

  • 只读变量在定义时,修饰符 const 可以用在类型说明符前,也可以用在类型说明符后。例如:
int const i=2;    // 正确 const int j=2;    // 正确

5.4 修饰数组

  • 定义或说明一个只读数组可采用如下格式:
int const a[5]={1, 2, 3, 4, 5};    // 正确 const int b[5]={1, 2, 3, 4, 5};    // 正确

5.5 修饰指针

  • 先忽略类型名(编译器解析的时候也是忽略类型名),我们看 const 离哪个近。“近水楼台先得月”,离谁近就修饰谁。
const int *p;    // p 可变,p 指向的对象不可变 int const *p;    // p 可变,p 指向的对象不可变 int *const p;    // p 不可变, p 指向的对象可变 const int *const p;    // 指针 p 和 p 指向的对象都不可变

5.6 修饰函数的参数

  • 当不希望这个参数值被函数体内意外改变时,const 修饰符也可以修饰函数的参数,从而防止了使用者的一些无意的或错误的修改。
void Fun(const int i);

5.7 修饰函数的返回值

  • const 修饰符也可以修饰函数的返回值,返回值不可被改变。例如:
const int Fun (void);
  • 在另一连接文件中引用 const 只读变量
extern const int i;    // 正确的声明 extern const int j=10;    // 错误!只读变量的值不能改变。

6. signed、unsigned 关键字

  • 既然计算机底层只认识 0 和 1 的二进制数字,那么正数和负数是怎么存储的呢?signed 和 unsigned 修饰的变量各有什么特点?
  • 答案很简单。在 32 位计算机的内部,一个 int 型的整数由 4 字节 32 位 0/1 数字组成,32 个数字中最高位(第一个数字)表示符号位并约定最高位如果是 1 则表示负数,如果是 0 则表示正数,这样一来,一个 32 位有符号的 int 型变量便能表示 -2147483648~2147483647 之间的数字,而无符号的 int 型变量便能表示 0~4294967295 之间的数字。
  • 其中有符号的 int 型变量由 signed 修饰,一般可以省去;无符号的 int 型变量由 unsigned 修饰,不能省去。
signed int i; unsigned int love; signed int u;
  • 然而用普通 01 码(原码)表示的数字中 +0 和 -0 各有一种表示方法,且原码无法,具体的计算机中负数其实是以补码的形式存储的。补码由反码 +1 而来,而反码由原码的每一位 0 变成 1,1 变成 0而来。同时反码和补码的出现使得计算机为辨别”符号位”而设计的电路不再复杂。具体的有关机器语言中原码、反码和补码的知识都在我的博客园里写过,期待之后整理到简书中来。

「C语言」原码反码补码与位运算

7. 数据类型

  • short、int、long、char、float、double 这六个关键字代表 C 语言里的六种基本数据类型。
你所不能错过的 C 语言复习要点(一)关键字(上)

7.1 数据类型与“模子”

  • 在每一个拥有数据类型的变量的声明时,都会分配给该变量特定的内存大小空间。放出基本数据类型在 32 位操作系统下特定的内存空间大小:
你所不能错过的 C 语言复习要点(一)关键字(上)

  • 具体有关各个数据类型的特点和区别及其混合运算与类型转换等可以参考我在博客园写的这篇文章,毕竟本文的重心在“关键字”上。

「C语言」数据类型及混合运算与类型转换

7.2 变量的命名规则

  • 命名和缓存失效是计算机科学面临的最难的两件事。其中符合规范的变量命名可以直观地看出代码的逻辑性,具体命名规则也同样加入自己的博客园文章地址,期待之后整理到简书中来。

「C语言」常量和变量的表示、应用和变量命名规则

8 void 关键字

  • “色即是空,空即是色”,void 也有它的妙用 —— void 的字面意思是“空类型”,void 则为“空类型指针”,void 可以指向任何类型的数据。
  • void 真正发挥的作用在于:
    1 -> 对函数返回的限定;
    2 -> 对函数参数的限定。
  • 任何类型的指针都可以直接赋值给 void ,无需进行强制类型转换。
    void *p1; int *p2; p1 = p2;

8.1 void 修饰函数返回值和参数

  • 如果函数没有返回值,那么应声明为 void 类型
    在给函数不加返回值说明时默认为 int 型,所以对于没有返回值的函数推荐用 void 声明。另外,加上 void 类型声明后,也可以发挥代码的“自注释”作用。所谓的代码的“自注释”即代码能自己注释自己。
  • 如果函数无参数,那么应声明其参数为 void
    在 C 语言中,可以给无参数的函数传送任意类型的参数,但是在 C++编译器中编译同样的代码则会出错。所以,无论在 C 还是 C++中,若函数不接受任何参数,一定要指明参数为 void。

8.2 void 指针

  • 千万小心又小心使用 void 指针类型。
    按照 ANSI(American National Standards Institute)标准,不能对 void 指针进行算法操作,即下列操作都是不合法的:
void * pvoid;pvoid++;    // ANSI:错误 pvoid += 1;    // ANSI:错误

但是大名鼎鼎的 GNU(GNU’s Not Unix 的递归缩写)则不这么认定,它指定 void 的算法操作与 char 一致。因此下列语句在 GNU 编译器中皆正确:

pvoid++;    // GNU:正确 pvoid += 1;    // GNU:正确

因此,为了兼容两大标准, 高程序的可移植性,我们可以这样编写实现同样功能的代码:

void * pvoid;(char *)pvoid++;    // ANSI:正确;GNU:正确(char *) pvoid += 1;    // ANSI:错误;GNU:正确
  • 如果函数的参数可以是任意类型指针,那么应声明其参数为 void *。
    典型的如内存操作函数 memcpy 和 memset 的函数原型分别为:
void * memcpy(void *dest, const void *src, size_t len); void * memset ( void * buffer, int c, size_t num );

8.3 void 不能代表一个真实的变量

  • void 不能代表一个真实的变量。
void a;    // 错误 function(void a);    // 错误

本篇测试

1 在声明 int i=0; 后以下哪个选项会报错呢?
A),sizeof(int);  B),sizeof(i);  C),sizeof int;  D),sizeof i;
2: sizeof(int)*p 表示什么意思?
3: 以下程序的输出结果是什么?
int main() {     char a[1000];     int i;     for(i=0; i<1000; i++) {         a[i] = -1-i;     }     printf("%d",strlen(a));return 0; }
4: -0 和+0 在内存里面分别怎么存储?
5:以下变量定义并初始化后 i+j 的值为多少?为什么?
int i = -20; unsigned j = 10;
6:以下代码有什么问题?
unsigned i; for (i=9;i>=0;i--) {     printf("%u/n",i); }
7:define 是关键字吗?
8:case 语句后面是否可以是 const 修饰的只读变量呢?
9:区分一以下几行代码(答案已给出)
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 指向的对象都不可变

附录: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 计算对象所占内存空间大小

写在最后

这篇文章,包括这整个”你所不能错过的 C 语言复习要点”系列灵感和素材均来源自《C 语言深度解剖》这本书,感谢作者对 C 语言的独特见解。该电子书的电子版已经长传至我的 github 仓库中,有兴趣的可以点入下载之~

Basisc-Programming-Ebook — Github

你所不能错过的 C 语言复习要点(一)关键字(上)

转载本站任何文章请注明:转载至神刀安全网,谢谢神刀安全网 » 你所不能错过的 C 语言复习要点(一)关键字(上)

分享到:更多 ()

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址