Dissection C Chapter 1_1

最近自己一直在忙于找工作,在面试的时候发现专业基础知识不是很牢固,很多细节问题只是知道但说不出个所以然,因此下定决心“重学”一些基础课程,以温故而知新!偶然发现一本觉得很不错的书《Dissection C》(不适合初学者),深入浅出地分析和讲解了C语言中一些需要注意的知识点,书很薄,经典的东西通常都是简练的,不说废话。很多计算机科班出身的人都会认为自己的C语言学的不错,但实际上那只是自己的感觉,要真正精通一门技术,非十年修炼难以。通过对本书的阅读和总结,希望自己可以从这本书中得到提升。(成功者总是站在巨人的肩膀上 :),感谢本书的作者! )

PS: 为了便于总结,相关的知识点放在一起进行归纳。

[1] C语言有多少个关键字?sizeof怎么用?它是函数吗?
[2] 什么是定义?什么是声明?它们有何区别?
[3] 关键字角色的逐个分析
      (1) auto
      (2) register
      (3) static
      (4) 基本数据类型
      (5) sizeof

[1] C语言有多少个关键字?sizeof怎么用?它是函数吗?
很多人不知道C语言有多少个关键字,大多数人会认为sizeof是函数,因为它后面跟着一对括号,当然在某种情况下也可以不跟。
基础不牢的人会惊奇地发现C语言标准定义的32个关键字,sizeof不是函数竟然是关键字。

                                        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

计算对象所占内存空间大小



[2] 什么是定义?什么是声明?它们有何区别?
int i;// 定义
extern int i;// 声明

定义:就是(编译器)创建一个对象,为这个对象分配一块内存并给它取上一个名字,这个名字就是我们经常所说的变量名或对象名。
注意:
(1) 这个名字一旦和这块内存匹配起来,它们就同生共死,终生不离不弃。
(2) 这块内存的位置也不能被改变。
(3) 一个变量或对象在一定的区域内(比如函数内,全局等)只能被定义一次,如果定义多次,编译器会提示你重复定义同一个变量或对象。

声明,有两重含义如下:
第一重含义:告诉编译器,这个名字已经匹配到一块内存上了,下面的代码用到的变量或对象是在别的地方定义的。声明可以出现多次。
第二重含义:告诉编译器,这个名字我先预定了,别的地方再也不能用它来作为变量名或对象名。这种声明最典型的例子就是函数参数的声明。

注意:
定义和声明最主要的区别是:定义创建了对象并为这个对象分配了内存,声明没有分配内存。

[3] 关键字“角色”的逐个分析
(1) auto
你就当它不存在吧,编译器在默认的缺省情况下,所有变量都是auto的。

(2) register
这个关键字请求编译器尽可能地将变量存在CPU内部寄存器中而不是通过内存寻址访问以提高效率。注意是:尽可能,不是绝对。因为,一个CPU的寄存器也就那么几个或几十个,你要是定义了很多register变量,编译器不可能全部把这些变量放入寄存器中。

皇帝身边的小太监——寄存器
小太监是皇帝的中转站。CPU看做皇帝,内存看做大臣,寄存器看做小太监。(这里不考虑CPU的高速缓存区)。
数据从内存里拿出来放到寄存器,然后CPU再从寄存器里读取数据来处理,处理后同样把数据通过寄存器存放到内存里,CPU不直接和内存打交道。
为啥要这么麻烦呢?速度!就是因为速度。寄存器其实就是一块一块小的存储空间,只不过其存取速度要比内存快得多。近水楼台先得月嘛,它离CPU很近,CPU一伸手就拿到数据了,比往那么大的一块内存里去寻找某个地址上的数据是不是快多了?那有人问既然它速度那么快,那我们的内存硬盘都改成寄存器得了呗,问题是,你真有钱!

使用register修饰符的注意点:
虽然寄存器的速度非常快,但是使用register修饰符也有些限制的:
(1) register变量必须是能被CPU寄存器所接受的类型。意味着register变量必须是一个单个的值,并且其长度应<=整形类型的长度。
(2) register变量可能不存放在内存中,所以不能用取地址运算符“&”来获取register变量的地址。

(3) static
这个关键字在C语言里主要有两个作用,C++对它进行了扩展。

第一个作用:修饰变量。
变量可以分为局部和全局变量,但它们都存在内存的静态区。
静态全局变量:
作用域仅限于变量被定义的文件中,其他文件即使用extern声明也没法使用它。准确地说,作用域是从定义之处开始,到文件结尾处结束,在定义之处前面的那些代码行也不能使用它,要想使用就需要在前面再加extern语句。如果想方便些,可以直接在文件顶端定义,就不在需要使用extern语句了。
静态局部变量:
在函数体里面定义的,就只能在这个函数里用了,同一个文档中的其他函数也用不了。由于被static修饰的变量总是存在内存的静态区,所以即使这个函数运行结束,这个静态变量的值还是不会被销毁,函数下次使用时仍能用到这个值。
例子:
#include <cstdio> static int j;// 静态全局变量 void fun1() { static int i=0;// 静态局部变量 ++i; } void fun2() { j=0; ++j; } int main() { for (int k=0; k<10; ++k) { fun1(); fun2(); } return 0; } /* output: i=10 j=1 */

 

第二个作用:修饰函数。
函数前加static使得函数成为静态函数。但此处“static”的含义不是指存储方式,而是指对函数的作用域仅局限于本文件(所以又称为内部函数)。
使用内部函数的好处是:不同人编写不同的函数时,不用担心自己定义的函数,是否会与其它文件中的函数同名。

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

(4) 基本数据类型
short, int, long, char, float, double

Dissection C Chapter 1_1_第1张图片

在32位的系统上,char是1B,short是2B,int是4B=long是4B=float是4B,double是8B。
注意:不同的平台会有所不同,具体值可以通过使用sizeof关键字测试一下。

变量的命名规则
【规则1-1】 命名应当直观且可以拼读,可望文知意,便于记忆和阅读。
标识符最好采用英文单词或其组合,不允许使用拼音。程序中的英文单词一般不要太复杂,用词应当准确。

【规则1-2】命名的长度应当符合“min-length && max-information”的原则。
C是一种简洁的语言,命名也应该是简洁的。

【规则1-3】当标识由多个词组成时,每个词的第一个字母大写,其余全部小写。
比如:int CurrentVal;

【规则1-4】尽量避免名字中出现数字编号,如Value1,Value2等,除非逻辑上的确需要编号。比如驱动开发时为管道命名,非编号名字反而不好。
初学者总是喜欢用带编号的变量名或函数名,这样子看上去很简单方便,但其实是一颗颗定时炸弹。

【规则1-5】在对多个文件之间共同使用的全局变量或函数要加范围限定符(建议使用模块名(缩写)作为范围限定符)。
比如:GUI_,等。

【规则1-6】标识符名分为两部分:规范标识符前缀(后缀)+含义标识。非全局变量可以不使用范围限定符前缀。

【规则1-7】作用域前缀命名规则。


【规则1-8】数据类型前缀命名规则。
比如:int iVariable; void * vpVariable;

【规则1-9】含义标识命名规则,变量命名使用名词性词组,函数命名使用动词性词组。
比如:
变量名 DataGotFromSD// 从SD中取得的数据
函数名 GetDataFromSD// 取数据

【规则1-10】程序中不得出现仅靠大小写区分的相似的标识符。
比如:int x, X;// 变量x与X容易混淆

【规则1-11】一个函数名禁止被用于其它之处。
比如:函数名和变量名相同。

【规则1-12】所有宏定义、枚举常数、只读变量全用大写字母命名,用下划线分割单词。
比如:
const int MAX_LENGTH=100;// 这不是常量,而是一个只读变量
#define FILE_PATH "/usr/tmp"

【规则1-13】考虑到习惯性问题,局部变量中可采用通用的命名方式,仅限于n、i、j等作为循环变量使用。
一般来说,习惯上用n, m, i, j, k等表示int类型的变量;c, ch等表示字符类型变量;a等表示数组;p等表示指针。当然这仅仅是一般习惯。

【规则1-14】定义变量的同时,千万别忘了初始化。定义变量时,编译器并不一定清空了这块内存,它的值可能是无效的数据。

【规则1-15】不同类型数据之间的运算要注意精度扩展问题,一般低精度数据将向高精度数据扩展。

(5) sizeof
最冤枉的关键字,常年被人误认为函数。
sizeof是关键字不是函数,其实就算不知道它是否为32个关键字之一时,我们也可以借助编译器确定它的身份。
int i=0;
A) sizeof(int);        B) sizeof(i);        C) sizeof int;        D) sizeof i;
在32位系统下,毫无疑问,A和B都正确。通过VC6、VS2008或任意一编译器调试,我们发现D也正确。sizeof后面没有括号也可以,说明sizeof不是函数。
再想想C为什么编译器提示出错?我们可以在int前加unsigned、const等关键字但不能加sizeof关键字。
记住:sizeof在计算变量所占空间大小时,括号可以省略,而计算类型大小时不能省略。为了便于记忆,我们在使用sizeof关键字的时候,还是乖乖地写上括号,继续让sizeof装作一个“函数”吧。

#include <cstdio> void fun(int b[100]) { printf("sizeof(b)=%d/n",sizeof(b)); // 4 } int main() { int a[100]={0}; int *p=NULL; printf("sizeof(p)=%d/n",sizeof(p)); // 4 printf("sizeof(*p)=%d/n",sizeof(*p)); // 4 printf("sizeof(a)=%d/n",sizeof(a)); // 400 printf("sizeof(a[99])=%d/n",sizeof(a[99])); // 4 printf("sizeof(a[100])=%d/n",sizeof(a[100])); // 4 printf("sizeof(&a)=%d/n",sizeof(&a)); // 4 printf("sizeof(&a[0])=%d/n",sizeof(&a[0])); // 4 fun(a); return 0; }

 

2010-10-6   wcdj

你可能感兴趣的:(c,扩展,语言,float,编译器,fun)