第一章:C语言关键字(7)

2019-04-14 23:24

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 标准。

2、如果函数的参数可以是任意类型指针,那么应声明其参数为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明显不是一个“纯粹的,脱离低级趣味的”函数!

下面的代码执行正确:

例子1:memset 接受任意类型指针 int IntArray_a[100];

memset (IntArray_a, 0, 100*sizeof(int) ); //将IntArray_a 清0

例子2:memcpy 接受任意类型指针

int destIntArray_a[100], srcintarray_a[100]; //将srcintarray_a 拷贝给destIntArray_a memcpy (destIntArray_a, srcintarray_a, 100*sizeof(int) );

有趣的是,memcpy 和memset 函数返回的也是void *类型,标准库函数的编写者都不是一般人。

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

void 不能代表一个真实的变量。因为定义变量时必须分配内存空间,定义void 类型变量,编译器到底分配多大的内存呢。

下面代码都企图让void 代表一个真实的变量,因此都是错误的代码: void a; //错误

function(void a); //错误

void 体现了一种抽象,这个世界上的变量都是“有类型”的,譬如一个人不是男人就是女人(人妖不算)。

void 的出现只是为了一种抽象的需要,如果你正确地理解了面向对象中“抽象基类”的概念,也很容易理解void 数据类型。正如不能给抽象基类定义一个实例,我们也不能定义一个void(让我们类比的称void 为“抽象数据类型”)变量。

void 简单吧?到底是“色”还是“空”呢?

13. C语言return关键字

return 用来终止一个函数并返回其后面跟着的值。

return (Val);//此括号可以省略。但一般不省略,尤其在返回一个表达式的值时。return 可以返回些什么东西呢?看下面例子: char * Func(void) {

char str[30]; … return str; }

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

注意:return 语句不可返回指向“栈内存”的“指针”,因为该内存在函数体结束时被自动销毁。

留个问题: return ;

这个语句有问题吗?如果没有问题,那返回的是什么?

14. C语言const关键字—也许该被替换为readolny

const 是constant 的缩写,是恒定不变的意思,也翻译为常量、常数等。很不幸,正是因为这一点,很多人都认为被const 修饰的值是常量。这是不精确的,精确的说应该是只读的变量,其值在编译时不能被使用,因为编译器在编译时不知道其存储的内容。或许当初这个关键字应该被替换为readonly。那么这个关键字有什么用处和意义呢?

const 推出的初始目的,正是为了取代预编译指令,消除它的缺点,同时继承它的优点。我们看看它与define 宏的区别。(很多人误以为define 是关键字,在这里我提醒你再回到本章前面看看32 个关键字里是否有define)。

一、const 修饰的只读变量

定义const 只读变量,具有不可变性。例如: const intMax=100; intArray[Max];

这里请在Visual C++6.0 里分别创建.c 文件和.cpp 文件测试一下。你会发现在.c 文件中,编译器会提示出错,而在.cpp 文件中则顺利运行。为什么呢?我们知道定义一个数组必须指定其元素的个数。这也从侧面证实在C 语言中,const 修饰的Max 仍然是变量,只不过是只读属性罢了;而在C++里,扩展了const 的含义,这里就不讨论了。

注意:const 修饰的只读变量必须在定义的同时初始化,想想为什么?

留一个问题:case 语句后面是否可以是const 修饰的只读变量呢?请动手测试一下。

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

编译器通常不为普通const 只读变量分配存储空间,而是将它们保存在符号表中,这使得它成为一个编译期间的值,没有了存储与读内存的操作,使得它的效率也很高。例如: #define M 3 //宏常量

const int N=5; //此时并未将N 放入内存中 ......

int i=N; //此时为N 分配内存,以后不再分配! int I=M; //预编译期间进行宏替换,分配内存 int j=N; //没有内存分配

int J=M; //再进行宏替换,又一次分配内存!

const 定义的只读变量从汇编的角度来看,只是给出了对应的内存地址,而不是象#define

一样给出的是立即数,所以,const 定义的只读变量在程序运行过程中只有一份拷贝(因为它是全局的只读变量,存放在静态区),而#define 定义的宏常量在内存中有若干个拷贝。

#define 宏是在预编译阶段进行替换,而const 修饰的只读变量是在编译的时候确定其值。 #define 宏没有类型,而const 修饰的只读变量具有特定的类型。

三、修饰一般变量

一般常量是指简单类型的只读变量。这种只读变量在定义时,修饰符const 可以用在类型说明符前,也可以用在类型说明符后。例如: int const i=2; 或const int i=2;

四、修饰数组

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

五、修饰指针

const int *p; // p 可变,p 指向的对象不可变 int const *p; // p 可变,p 指向的对象不可变 int *const p; // p 不可变,p 指向的对象可变 const int *const p; //指针p 和p 指向的对象都不可变

在平时的授课中发现学生很难记住这几种情况。这里给出一个记忆和理解的方法:先忽略类型名(编译器解析的时候也是忽略类型名),我们看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 指向的对象都不可变

六、修饰函数的参数

const 修饰符也可以修饰函数的参数,当不希望这个参数值被函数体内意外改变时使用。例如:

void Fun(const int i);

告诉编译器i 在函数体中的不能改变,从而防止了使用者的一些无意的或错误的修改。

七、修饰函数的返回值

const 修饰符也可以修饰函数的返回值,返回值不可被改变。例如: const int Fun (void);

在另一连接文件中引用const 只读变量: extern const int i; //正确的声明

extern const int j=10; //错误!只读变量的值不能改变。

注意这里是声明不是定义,关于声明和定义的区别,请看本章开始处。

讲了这么多讲完了吗?远没有。在C++里,对const 做了进一步的扩展,还有很多知识未能讲完。有兴趣的话,不妨查找相关资料研究研究。

15. C语言volatile关键字—最易变的关键字

volatile 是易变的、不稳定的意思。很多人根本就没见过这个关键字,不知道它的存在。也有很多程序员知道它的存在,但从来没用过它。我对它有种“杨家有女初长成,养在深闺人未识” 的感觉。

volatile 关键字和const 一样是一种类型修饰符,用它修饰的变量表示可以被某些编译器未知的因素更改,比如操作系统、硬件或者其它线程等。遇到这个关键字声明的变量,编译器对访问该变量的代码就不再进行优化,从而可以提供对特殊地址的稳定访问。

先看看下面的例子: int i=10;

int j = i;//(1)语句 int k = i;//(2)语句

这时候编译器对代码进行优化,因为在(1)、(2)两条语句中,i 没有被用作左值。这时候编译器认为i 的值没有发生改变,所以在(1)语句时从内存中取出i 的值赋给j 之后,这个值并没有被丢掉,而是在(2)语句时继续用这个值给k 赋值。编译器不会生成出汇编代码重新从内存里取i 的值,这样提高了效率。但要注意:(1)、(2)语句之间i 没有被用作左值才行。


第一章:C语言关键字(7).doc 将本文的Word文档下载到电脑 下载失败或者文档不完整,请联系客服人员解决!

下一篇:四年级下册品社教学计划

相关阅读
本类排行
× 注册会员免费下载(下载后可以自由复制和排版)

马上注册会员

注:下载文档有可能“只有目录或者内容不全”等情况,请下载之前注意辨别,如果您已付费且无法下载或内容有问题,请联系我们协助你处理。
微信: QQ: