云题海 - 专业文章范例文档资料分享平台

当前位置:首页 > C语言课件

C语言课件

  • 62 次阅读
  • 3 次下载
  • 2025/5/2 8:29:51

8.9 存储类说明符

auto说明符auto表明一个变量具有自动存储期。该说明符只能用在具有代码块作用域的变量声明中,因其已经拥有自动存储期,因此它主要用来明确指出意图,使程序更易读。

register说明符register 也只能用于具有代码块作用域的变量。它将一个变量归入寄存器存储类,这相当于请求将该变量存储在一个寄存器内以更快地存取。它的使用也使你不能获得变量的地址。

static说明符static在用于具有代码块作用域变量的声明时,使该变量具有静态存储期,从而得以在程序运行期间始终存在并保留其值,而变量仍具有代码块作用域和空链接;而在用于具有文件作用域的变量声明时,表明该变量具有内部链接。

extern说明符extern表明你在声明一个已经在别处定义了的变量。如果包含extern的声明具有文件作用域,所指向的变量必然具有外部链接;如果包含extern的声明具有代码块作用域,所指向的变量可能具有外部链接也可能具有内部链接,这取决于该变量的定义声明。

typedef关键字typedef与内存存储无关,由于语法原因被归入此类。特别地,不可以在一个声明中使用一个以上存储类说明符,这意味着不能将其它任一存储类说明符作typedef的一部分。

8.10 存储类和函数

函数也具有存储类。函数可以是外部的(默认)或者静态的。外部函数可被其他文件中的函数调用,而静态函数只可以在定义它的文件中使用。 8.11 随机数函数

可以对标准的ANSI C函数srand( )和rand( ).使用同样的技术。使用这些函数时,要包含stdlib.h头文件。

Srand();作为产生种子的函数;rand()作为产生随机数用,其种子来源于srand。 srand( ( unsigned int )time( 0 ) ); int roll = rand( ) % sides + 1;

9 预处理命令

编译程序之前,先由预处理器检查程序(因此称为预处理器)。根据程序中使用的预处理器指令,预处理器用符号缩略语所代表的内容替换程序中的缩略语。预处理器可以根据你的请求包含其它文件,还可以选择让编译器处理那些代码。 9.1 #define 语句

与所有预处理指令一样,预处理指令#define用#符号作为行的开头。ANSI标准允许#符号前有空格或制表符,而且还允许在#和指令的其余部分之间有空格。但是旧版本的C一般要求指令在最左边一列开始,并且#和指令的其余部分之间不能有空格。指令可出现在源文件的任何地方,其定义的作用域从定义出现的位置开始直到文件的结尾。

每个#define行(即逻辑行)由三部分组成。第一部分为指令#define本身。第二部分为所选择的缩略语,被称为宏。像本例中的这些宏用来代表值,它们被称为类对象宏(C还有类函数宏)。宏的名字中不允许有空格,而且必须遵守C标识符的命名规则。第三部分(行的其余部分)称为替换列表或主体。预处理器在程序中发现了宏的实例后,总会用实体代替该宏(有一种例外,以后进行说明)从宏变成最终的替换文本的过程称为宏展开。#define行中的注释在预处理器处理之前会被一个空格所替代。

例外情况是双引号中的宏。因此下面的语句: printf( \将输出 TWO: OW 9.1.1 程序的可扩展性

假设现在需要将数组dataValues的大小从1000增加到2000,这就需要修改所有涉及到和数组 dataValues大小值1000相关的语句。一个更好地处理数组边界且使得程序更易于扩展的方法是定义一个符号来表示数组上界。因此,如果使用#define语句定义一个合适的名称,例如MAXIMUM:

#define MAXIMUM 1000

那么后面就可以使用如下MAXIMUM的代码行来定义数组 dataValues 9.1.3 高级预定义类型

C程序中一旦遇到预定义名称,则#define语句中宏名称右边的部分将纯粹按字面替代此预定义名称。 9.1.3.1 参数和宏

通过使用参数,可以创建外形和作用都与函数相似的类函数宏。宏的参数也用圆括号括起来,因此,带参数的宏看上去与函数非常相似。类函数宏的定义中,用圆括号括起一个或多个参数,随后这些参数出现在替换部分。

IS_LEAP_YEAR的定义也可以带参数,如:

#define IS_LEAP_YEAR( y ) y%4 == 0 && y0 != 0 || y@0 == 0

和函数不一样的是, 参数y的类型无需定义, 因为这里仅仅是进行纯粹的文字替换而非调用函数。

注意,在#define语句中,预定义名称和参数列表的左括号间是不允许有空格的。

在定义宏的时候有一个需要小心避免的陷阱: #define SQUARE( x ) x*x … y = SQUARE( v + 1 );

此语句并不会如你所期望的那样将(v+1)2赋给y,因为预处理器只是对宏定义中的参数作字面的替换,上面的表达式会被替换为

y = v + 1 * v + 1;

要解决这个问题,只要在宏SQUARE的定义中加入圆括号: #define SQUARE(x) ( ( x ) * ( x ) ) 于是

y = SQUARE( v + 1 ); y = ( ( v + 1 ) * ( v + 1 ) );

9.2 #include 语句

预处理器可以使你不必每次都在新的程序里将那些宏再输入一次,只要使用#include语句就可以把另一个文件中的宏定义统统包括到新程序中来。这些文件通常以 .h 结尾且被称为头文件或包含文件。

头文件两边的双引号告诉预处理器,先在源文件所在目录查找此头文件,找不到则自动到其他的系统目录去找。

如果像下面这样,使用符号 < 和 > #include 则预处理器会到系统包含文件所在的特定目录去查找此头文件,再次强调:这些目录是系统相关的。

9.3.1 #ifdef、#endif、#else、和 #ifndef 语句

当创建一个较大的程序的时候,可能有很多的地方是依赖于特定的计算机硬件或软件环境的,那么这时候就可以有很多宏定义来代表这些随不同计算机系统而改变的地方。

通过使用预处理器的条件编译特性,可以有效减少程序在不同平台间迁移时所需改变的部分,也可以将为不同机器所定义的值集中到一起处理。

#ifdef UNIX # define DATADIR \ #else # define DATADIR \ #endif

如果#ifdef行所指定的符号被定义过了——通过#define语句或编译程序时的命令行——那么随后的行都将被执行或处理直到遇到#else、#elif或 #endif,否则它们被忽略。

10.2.2 定义和引用

两类基本的指针运算符:取地址运算符&和间接引用运算符*。 一些特别情形(设有 int x = 0, y, *p = &x; ): y=*p++; ? y=*( p++ ); ? y=*p; p++; y=*++p; ? y=*( ++p ); ? p++; y=*p; y=( *p )++; ? y=*p; ( *p )++; y=++( *p ); ? (*p)++; y=*p;

10.2.3 指针与函数

a) 如果指针变量总是指向k,则可以声明一个常量指针

int * const p = &k;

p = &m; /* 非法 */ *p = 20; k=20; /* 合法 */

b) 如果指针p所指向的值不允许通过指针变量p改变,则可以声明为 const int *p = &k;

*p = 20; /* 非法 */ k = 20; p = &m; /* 合法 */

c) 如果指针变量和所指向的对象的值都不允许通过指针来改变, 则可以声明为 const int * const p = &k;

*p = 20; /* 非法 */ p = &m; /* 非法 */ k = 20; /* 合法 */ 10.3.1 指向数组的指针

C编译器将不带下标的数组名形式上当作一个指向数组的指针。另一个等效的方法是通过对数组的首元素取地址运算符使指针指向values的起始地址 10.3.2 通过指针引用数组元素

10.3.4 指针与多维数组

为了方便讨论,我们采用比较小的数组。假定有如下声明: int zippo[4][2];

数组名zippo同时也是数组首元素的地址。此时,zippo的首元素本身又是一个含有两个int元素的数组。因此zippo也就是一个含有两个int元素的数组的地址。

在C语言中,二维数组是以行序优先排列的。也就是说,在数组zippo中,每一行数组元素可以被看作一个单一的元素zippo[ i ],其中的i是行号。

由于zippo是数组首元素的地址,所以zippo的值和&zippo[ 0 ]的值是相同的。而zippo[ 0 ]本身又是一个包含两个整数的数组,所以zippo[ 0 ]的值和

&zippo[ 0 ][ 0 ]的值也是一样的。也就是zippo = &zippo[ 0 ] = &zippo[ 0 ][ 0 ]

zippo[ 0 ] ? *zippo

zippo[ i ] ? *( zippo + i )

zippo[ i ][ j ] ? *( *( zippo + i ) + j )

虽然zp只是一个指针而非数组名,但形如zp[2][1]这样的标识也是允许的。更一般地,不论是数组名或是指针,都可以通过使用指针或数组的标记法来表示其元素:

zippo[ i ][ j ] ? *( *( zippo + i ) + j ) zp[ i ][ j ] ? *( *( zp + i ) + j )

但有一点要记住,指针zp可以改变它的值 zp = zp + 1;

但zippo却不行,因为zp是一个纯粹的指针变量而zippo是一个其地址为常量的数组。 10.4.2 常量字符串与指针

如果textPtr被声明为一个字符指针,即 char *textPtr;

则语句 textPtr = \将一个指向常量字符串\的指针赋给textPtr。请注意小心区别字符指针和字符数组。上面这样的赋值方法对于字符数组来说是非法的。

char text[ 80 ]; text = \非法 */ text ={ \非法 */ text[80] = \非法 */ text[80] ={ \非法 */ 唯一合法的时候 char text[ 80 ] = \10.4.3 关于NULL

NULL是一个字符或指针值。

作为字符的时候,NULL通常写作'\\0'。它是C程序中的字符串结束符,会被自动地附加在每个字符串的末尾。一般你无需为此末尾的'\\0'操心,因为它总是会被自动加上,但有时在终止字符串搜索时会非常有用。

但是当NULL作为指针值的时候,表示指针不指向任何对象。返回值为指针的函数操作失败的时候,通常会返回NULL。而返回值总是可以被检测的。

然而,我们通常将NULL当作一个特殊的数字,那就是零。

零( 0 )是一个int数值。基于标准转换规则,0可以被当作任何类型——如int、float、char或指针——的常量。0应归于哪一种类型由其所在的位置决定,它总是习惯上(但非必然)根据其相应类型按位以全0方式填充。任何数据对象都不会被分配到地址为0的地方。

另一方面,我们知道零被用来表示逻辑假值,而任何非零值都表示真,这就可以帮助我们简化前面讨论过的表达式。 10.5 指向函数的指针

指向函数的指针也是指针,即指向函数入口地址的变量。必须牢记一点:程序运行时也要在主内存中获取一部分空间。不管是编译后的可执行代码还是使用过的变量都放在主内存中。因此,和字符一样,程序代码中的函数就像一片字符区域一样,也能用一个地址来表征。关键是你或编译器/处理器如何来解释指向这块内存区域的指针。 10.6 返回指针值的函数

实际上,这个概念我们已经提过几次了,函数也可以返回一个指向某对象的指针或空指针。声明返回指针值的函数的方法为:

int *func( int a, int b );

搜索更多关于: C语言课件 的文档
  • 收藏
  • 违规举报
  • 版权认领
下载文档10.00 元 加入VIP免费下载
推荐下载
本文作者:...

共分享92篇相关文档

文档简介:

8.9 存储类说明符 auto说明符auto表明一个变量具有自动存储期。该说明符只能用在具有代码块作用域的变量声明中,因其已经拥有自动存储期,因此它主要用来明确指出意图,使程序更易读。 register说明符register 也只能用于具有代码块作用域的变量。它将一个变量归入寄存器存储类,这相当于请求将该变量存储在一个寄存器内以更快地存取。它的使用也使你不能获得变量的地址。 static说明符static在用于具有代码块作用域变量的声明时,使该变量具有静态存储期,从而得以在程序运行期间始终存在并保留其值,而变量仍具有代码块作用域和空链接;而在用于具有文件作用域的变量声明时,表明该变量具有内部链接。 extern说明符extern表明你在声明一个已经在别处定义了的变量。如果包含extern的声明具有文件作用域,所指向的变量必然具有

× 游客快捷下载通道(下载后可以自由复制和排版)
单篇付费下载
限时特价:10 元/份 原价:20元
VIP包月下载
特价:29 元/月 原价:99元
低至 0.3 元/份 每月下载150
全站内容免费自由复制
VIP包月下载
特价:29 元/月 原价:99元
低至 0.3 元/份 每月下载150
全站内容免费自由复制
注:下载文档有可能“只有目录或者内容不全”等情况,请下载之前注意辨别,如果您已付费且无法下载或内容有问题,请联系我们协助你处理。
微信:fanwen365 QQ:370150219
Copyright © 云题海 All Rights Reserved. 苏ICP备16052595号-3 网站地图 客服QQ:370150219 邮箱:370150219@qq.com