C与C++

来源:互联网 发布:英格拉姆2018赛季数据 编辑:程序博客网 时间:2024/05/21 16:54

C语言是C++的子集,C++C语言的超集。C++是在C语言的基础上,添加了面向对象和泛型编程的功能后,扩展而成的。

C语言是一种可移植的系统语言,起源于Unix操作系统的开发。C语言是一种灵活高效的高级程序设计语言,但是同时也具有一些面向机器的低级语言特征,所以有人称它是一种中级语言。

1)起源

虽然早在19574IBMJohn W. Bacus)就推出了第一个高级程序设计语言FORTRANFORmula TRANslation公式转换),1958GAMM/ACMJohn W. BackusPeter NaurC. A. R. Hoare)又推出了ALGOLALGOrithmic Language算法语言)。但它们都是为数学和科学计算设计的,不适合于系统编程。

1964John Kemeny Thomas Kurtz简化Fortran发明了BASICBeginners All-purpose Symbolic Instruction Code BASIC初学者通用指令码)语言,1970年瑞士计算机科学家Niklaus Wirth又在ALGOL68的基础上创造了Pascal语言。前者是为专入门的文科学生设计的,简单易学,至今仍然是业余爱好者的宠物;后者则是结构化程序设计语言的典范、结构优美,曾被广泛用于计算机教学;但是,它们的功能有限、灵活性不够、效率也不高,也都不适合于编写系统编程。

附注:1991年和2002年微软公司的Alan CooperPaul Vick分别开发出Visual BasicVisual Basic .NET1983Borland公司的Anders HejlsbergPhilippe Kahn 开发出Turbo Pascal1985Niklaus Wirth和苹果公司的Larry Tesler又创造了Object Pascal1995Borland公司的Anders Hejlsberg等人在其基础上开发出Delphi

1970年,AT&T贝尔实验室的Ken ThompsonDennis Ritchie等人在DEC公司的PDP-7小型机上开发出了Unix操作系统,最初的实现是用汇编语言写成的。为了使Unix操作系统具有可移植性,迫切需要一种高级语言工具。为此,(出生在美国纽约的计算机科学家)Dennis RitchieB语言为基础,参考了Algol68,于1972年设计出了C语言。1973年他们用C语言重写了Unix1975年又利用C语言将Unix移植到了PDP-11上。

http://p.blog.csdn.net/images/p_blog_csdn_net/touzani/303255/o_Ken_n_dennis.jpg

2)特点

C语言是一种可移植的系统语言,拥有充分的控制语句和数据结构功能,包含丰富的操作符,从而能够提供强大的表达能力,可以用于许多不同的应用领域。但是,C语言并不是面向科学家和计算机业余爱好者的,而是专门为程序员设计的。

为了进行高效的系统编程,C语言提供了强大的功能和极大的灵活性。与其它高级语言相比,C语言的语法简洁、表达自由、库函数丰富。如果将编程比作造房子,则FortranBasic等语言就像一些已经预先造好的大预制件,使用起来简单快捷,但是灵活性差、且功能有限,只能造某些固定模式的房屋;而C语言就像一块块的小砖,使用起来虽然繁琐,但是灵活性强、而且功能无限,能够造各式各样的建筑物,不过这就要求C语言程序员具有很高的专业水平。

因此,C语言假设使用者都是计算机专家,采取的是程序员负责制。它不进行完备的类型检查,对数组越界也没有限制。为了进行高效的系统编程,C语言还提供了指针和指针运算,程序员可以随意操作全部内存,任意修改任何内容。

表达的自由性和操作的任意性,也给C语言带来了很多编程问题和安全隐患。特别是C语言的++/--运算符和指针运算,更是倍受指责。

与其它高级语言相比,C语言提供了一些低级语言特征,更面向机器。所以,也有人称C语言是介于高级语言和低级语言之间的一种中级语言。

3K&R C

开始的很多年,C语言没有国际标准,只有一个事实标准K&R C。直到1989年和1990年,才分别推出了ANSI CISO CC89C90);1999ISO又推出了第2版(C99)。

不像AlgolCOBOLAda等语言,C语言并不是政府部门或国际组织的产物,而属于个人的作品。虽然由于C语言性能优越,使用得越来越广泛,但是在最初的十几年间,C语言唯一的“标准”是1978Brian KemighanDennis Ritchie编写的《C程序设计语言》(The C Programming Language)一书,通常称其为K&R C或经典C

该书的附录“C参考手册”(C Reference Manual)成为了C语言的实现指南,但是书中缺少对库函数标准的描述,一般以Unix实现的库函数所为事实标准。

说明:因为C语言的语法成分简单,很多基本功能(例如I/O语句)都是靠库函数来实现的。所以,C语言比其它高级语言更依赖于库函数。

4C89/C90标准

1983ANSIAmerican National Standards Institute 美国国家标准协会)设立了一个X3J11小组,着手进行C语言的标准化。并最终于1989年推出ANSI C (ANSI X3.159-1989)1990年它又成为国际标准ISO CISO/IEC 9899:1990 Programming languages – C,程序设计语言——C),原来叫做ANSI CISO C,现在通常称其为C89C90

标准的指导原则是:

l     相信程序员;

l     不妨碍程序员做需要完成的事情;

l     让语言保持短小简单;

l     只提供一种方法来执行一种操作;

l     使程序运行速度快,即使不能保证其可移植性。(不追求定义的抽象统一,更优先考虑运行效率)

这些其实也正是C语言的设计初衷,所以ANSI/ISOC标准,对原始的C语言修改并不多。标准CK&R C的主要改变是,增加了函数原型(prototype),强调对函数的输入参数进行严格的类型检查;并补充定义了C语言的标准函数库。

函数原型的类型检查是指:在编译时,对调用函数的实参和函数定义时的形参的类型是否一致,进行严格的检查。目的是减少程序纠错的难度(这类问题在运行时很难查出),同时保证运行的安全和稳定性(避免函数调用栈溢出)。(这也为实现C++函数的参数型多态性提供了条件。)

例如:

u       K&R C:(过时的)

power(); // 函数的前向声明

power(x, n) // 函数定义

int x, n;

{

……

}

int p = power(3) * power(3.9, 5.4); // 使用(编译可通过,运行时产生逻辑错误)

u       C89/C90:(推荐的)

int power(int x, int n);  // 函数声明1(函数原型)

int power(int, int);  // 函数声明2(函数原型)

int power(int x, int n)  // 函数定义

{

……

}

int p = power(3) * power(3.9, 5.4); // 使用(编译不能通过,错误:第1个调用

// 的参数太少、警告:第2个调用的参数自动转换为(3, 5)

 

l     C89/C90K&R C的其它改变有:

n         删除了关键字:entry(条目/入口)

n         增加了关键字:const(常型变量)、enum(枚举类型)、signed(有符号的,例如signed char)、void(空/无,可用于函数返回值和形参、通用指针类型)、volatile(易变变量,防止编译器错误的优化)

n         传递结构:允许将结构本身作为参数传递给函数(原来只允许传地址)

n         函数原型:增加了函数原型(便于编译器进行类型检查)

n         增加了预处理指令:#elifelse if)、#error(错误,强制编译停止)、#line(修改当前行号和源文件名)、#pragma(附注/编译指令,编译器定义的与实现有关的指令)

n         定义了固有宏:__LINE__(当前行号)、__FILE__(源文件名)、__DATE__(当前系统日期)、__TIME__(当前系统时间)、__STDC__(标准C版时为1

5C99标准

C90的修订工作开始于1994年,在ISO C++1998)标准推出之后,ISO又于19991216,推出了C语言标准的第2版:ISO/IEC 9899:1999 Programming languages – C(程序设计语言——C),一般称其为C99

1)修订目标

C99保持了C语言的本质特性,C继续是一种短小、清晰和高效的语言。C99并没有增加新的本质特性,但是为了满足新的需要,也进行很多小的修订。

l     C99主要的修订目标有三点:

n         支持国际化编程,引入了支持国际字符集Unicode的数据类型和库函数;

n         修正原有版本的明显缺点。如整数的移植方法,例如int8_tint16_tint32_tint64_t等类型;

n         针对科学和工程的需要,改进计算的实用性。例如添加了复数类型和新数学函数。

2)具体修改

l     C99C89/C90的具体修改有:

n         增加了C++//注释风格:原来C语言之支持多行注释:/*……*/C99现在也识别单行注释:// ……

n         增加了关键字:inline(内联函数)、restrict(限制)、_Bool(布尔类型)、_Complex(复数)、_Imaginary(虚数)

u       内联函数(inline):链接时不是进行函数调用而是嵌入函数体代码(可减少调用的时间开销,适用于要求运行速度快的小型函数)

u       restrict:只能用于指针,表明该指针是数据对象的唯一且初始的方式(不是通过指针的赋值运算另外来得的),便于编译器进行代码优化。

附注:微软公司VC扩展的restrict,是让编译器限制别名(如用typedef定义的)的使用,必须类型名完全一致(只是类型等价还不够)时,类型检验才能通过

u       _Bool:布尔类型,为整数类型,一般用1(或非0)表示真、用0表示假。如果包含了C99新增加的标准库头文件stdbool.h,则可以用bool来代替_Bool,并可使用truefalse,从而与标准C++兼容

u       复数和虚数类型:

l         如果包含了C99新增加的标准库头文件complex.h后,就可以用complex来代替_Complex、用imaginary来代替_Imaginary

l         有三种复数和虚数类型:float _Complexdouble _Complexlong double _Complexfloat _Imaginarydouble _Imaginarylong double _Imaginary

l         C99内部,是用二元数组来实现复数的,第一个数组元素为实部、第二个数组元素为虚部

l         可以用实数和(定义在complex.h中的)I来初始化一个复变量。例如:

#include <complex.h>

double complex z = 6.0 – 8.5 * I, w = 3.0, u = 4.0 * I;

 

C99的关键字(Keywords)(共37个)

auto

enum

restrict

unsigned

break

extern

return

void

case

float

short

volatile

char

for

signed

while

const

goto

sizeof

_Bool

continue

if

static

_Complex

default

inline

struct

_Imaginary

do

int

switch

 

double

long

typedef

 

else

register

union

 

其中:黑色的为K&R C原有的关键字,绿色的为C90添加的关键字,红色的为C99新增的关键字。(C90删除了K&R C的关键字entry

 

n         增加了数据类型:(unsigned) long long [int]64位整数)(对应的打印输出格式为%lld%llu

n         定义了可移植整数类型:因为同一整数类型,在不同字长的计算机系统中,可能位数不一样,这给移植带来了问题。因此,C99在新增加的头文件inttypes.h中定义了已有整数类型的一些别名,便于程序移植。例如:int8_tint16_tint32_tint64_tuint8_tuint16_tuint32_tuint64_tintptr_tuintptr_t。以及表示对应类型常量的方法,例如INT8_C(128)INT32_C(1234)

n         增加了预定义宏:C99新加了两个预定义宏:__STDC_HOSTED__(是本机环境时为1,否则为0)和__STDC__VERSION__=199901L时为C99,否则为C89/C90

n         提供了一个预定义标识符:__func__,为一个代表函数名的字符串(该函数中包含有此标识符),该标识符具有函数作用域。(宏具有文件作用域)

n         增加了浮点常量的十六进制格式:pP表示后跟二进制指数(的十进制值)。例如:0xa.1cp10 = (10 + 1/16 + 12/256) * 210 = 10352.0

n         增加了浮点数的十六进制打印格式符:%a%A(代替十进制的%e%E)、%La%LA(代替十进制的%Le%LE

n         可指定初始化的条目:在对数组和结构进行初始化时,原来必须从头到尾顺序指定初值,最多只能省掉对尾部部分赋初值(未被初始化的条目全被置为0)。C99可以用[i] = v的方法,在花括号中为指定条目i赋初值v。例如:

int a[6] = {[5] = 123}; // 最后一个数组元素的初值为123,其余的值全为0

int days[12] = {31, 28, [4] = 31, 30, 31, [1] = 29}; // 则初值序列为31 29 0 0

// 31 30 31 0 0 0 0 0

n         支持变长数组(VLA = Variable-Length array):原来的C语言(包括C++)要求数组定义中的大小必须是整数常量[表达式],而C99允许使用变量。例如:

#define SIZE 12

……

int m, n;

……

int a1[5], a2{SIZE}; // C89可以

float a3[n], a4[m][n]; // C89不允许,C99可以

注意:这里的变长,不是说数组的大小可以随意改变;而是指在定义时,数组的大小可以是变量。一旦定义完成,数组的大小就已固定,不允许再进行改变。

n         复合文字(compound literal混合直接量):可以用于定义无名数组、给函数传递数组和结构常量参数、给其他数组和结构赋初值等。例如:

int *pt = (int []) {53, 16, 88}; // 定义无名数组

int sum(int a[], int n); // 函数原型

int total = sum((int []) {4, 3, 5, 2}, 4); // 给函数传递数组常量参数

struct book { // 定义结构

       char tutle[41];

       char author[31];

       float price;

};

struct book bk; // 定义结构变量

……

bk = (struct book) {“C Programming”, “Dennis Ritchie”, 9.8}; // 给结构赋初值

n         允许在代码块的任何地方定义变量:原来C语言的块变量定义,必须位于块的开始处(在任何可执行代码之前)。C99允许在代码块的任何地方(包括控制语句部分)定义变量(~C++),例如:for(int i = 0; i < 10; i++) {……}

n         旧关键字的新位置:C99允许将类型限定词(如constvolatilerestrict)和存储限定词(如autoregisterstatic)放在函数原型和函数头形参的方括号内。例如:

void f(int *const a1, int *restrict a2, double static a3[20]); // 老风格

void f(int a1[const], int a2[restrict], double a3[static 20]); // 新风格

n         弹性数组成员(flexible array member):C99允许在结构的最后定义一个大小可伸缩的弹性数组成员,可以用于结构指针,根据允许情况来动态分配内存。但是操作就像它本来就有这么多数组元素似的,增加了编程的灵活性和源码的可读性。例如:

strtuct flex {

       int count;

       double average;

       double scores[]; // 弹性数组成员

}

struct flex *pf;

……

pf = malloc(sizeof(struct flex) + n * sizeof(double));

pf->count = n;

……

double sum = 0;

for (int i = 0; i < n; i++) {

       fstrm >> pf->scores[i];

       sum += pf->scores[i]

}

pf->average = sum / n;

n         编译指令:在C99中定义了如下三个标准编译指令:

#pragma STDC FP_CONTRACT on-off-switch // 允许或禁止浮点表达式压缩

#pragma STDC FENV_ACCESS on-off-switch // 通知是否访问浮点环境

#pragma STDC CX_LIMITED_RANGE on-off-switch // 通知复数是否范围有限

其中的on-off-switch取值为ONOFFDEFAULT之一。

n         C99还新提供了_Pragma预处理运算符,可以将字符串转换成常规的编译指令。例如:

_Pragma(“use_bool true false”)

等价于

#pragma use_bool true false

3新增标准库

l         C99的标准库和头文件

n         标准头文件:<assert.h><complex.h><ctype.h><errno.h><fenv.h><float.h><inttypes.h><iso646.h><limits.h><locale.h><math.h><setjmp.h><signal.h>

<stdarg.h><stdbool.h><stddef.h><stdint.h><stdio.h><stdlib.h><string.h><tgmath.h><time.h><wchar.h><wctype.h>

 

n         新增加的头文件:在上面的标准头文件中,C99新增加的为:<complex.h><fenv.h><inttypes.h><iso646.h><stdbool.h><wchar.h><wctype.h>。下面将逐个加以简单介绍

 

n         complex.h:定义了宏compleximaginaryI,定义了各种数学函数(与不同数学函数相比,增加的前导字符c表示复数,尾部字符fl分别表示floatlong double类型,没有尾部字符的为double型),例如:

double complex ccos(double complex z);

float complex ccosf(float complex z);

long double complex ccosl(long double complex z);

C99中的复数处理函数(省略了fl版)

原型

说明

double carg(double complex z);

返回z的相位角/辐角(弧度)

double cimag(double complex z);

返回z的虚部(实数形式)

double complex conj(double complex z);

返回z的共轭复数

double complex cproj(double complex z);

返回z在黎曼域上的投影

double creal(double complex z);

返回z的实部(实数形式)

注意:该库VC05不支持

 

n         fenv.h:提供对浮点数环境的访问和控制

u       开启/关闭对浮点环境的访问:#pragma STDC FENV_ACCESS ON / OFF

u       浮点环境类型:fenv_t(整个浮点环境)、fexcept_t(浮点状态标志的集合)

u       标准的浮点异常和控制宏:

 

表示

异常

FE_DIVBYZERO

抛出被0除异常

FE_INEXACT

抛出不精确值异常

FE_INVALID

抛出非法值异常

FE_OVERFLOW

抛出上溢异常

FE_UNDERFLOW

抛出下溢异常

FE_ALL_EXCEPT

抛出位异常或实现所支持的所有浮点异常

控制

FE_DOWNWARD

向下舍入

FE_TONEAREST

最近舍入

FE_TOWARDZERO

0舍入

FE_UPWARD

向上舍入

FE_DFL_ENV

默认环境(const fevn_t*类型)

u       库函数

原型

说明

void feclearexcept(int excepts);

清除异常excepts

void fegetexceptflag(fexcept_t *flagp, int excepts);

excepts异常的标志存储到flagp

void feraiseexcept(int excepts);

抛出excepts异常

void fesetexceptflag(const fexcept_t *flagp, int excepts);

将异常标志excepts设置为flagp状态

int fetestexcept(int excepts);

返回excepts指定查询状态的标志位

int fegetround(void);

返回当前的舍入方向

int fesetround(int round);

将舍入方向设为round,成功返回0

void fegetenv(fenv_t *envp);

将当前环境存入envp

int feholdexcept(fenv_t *envp);

将当前环境存入envp,清除浮点状态标志,成功返回0

void fesetenv(const fenv_t *envp);

建立envp表示的环境

void feupdateenv(const fenv_t *envp);

建立envp表示的环境,并抛出自动存储的浮点异常

注意:该库VC05也不支持

 

n         inttypes.hC语言中的同一整数类型,在不同字长的计算机系统中,可能位数不一样,这给移植带来了问题。因此,C99在新增加的头文件inttypes.h中定义了可移植的整数类型。

 

固定长度类型

类型名

printf说明符

scanf说明符

最小值

最大值

int8_t

PRId8

SCNd8

INT8_MIN

INT8_MAX

int16_t

PRId16

SCNd16

INT16_MIN

INT16_MAX

int32_t

PRId32

SCNd32

INT32_MIN

INT32_MAX

int64_t

PRId64

SCNd64

INT64_MIN

INT64_MAX

uint8_t

PRIu8

SCNu8

0

UINT8_MAX

uint16_t

PRIu16

SCNu16

0

UINT16_MAX

uint32_t

PRIu32

SCNu32

0

UINT32_MAX

uint64_t

PRIu64

SCNu64

0

UINT64_MAX

 

最小长度类型

类型名

printf说明符

scanf说明符

最小值

最大值

int_least8_t

PRILEASTd8

SCNLEASTd8

INT_LEAST8_MIN

INT_LEAST8_MAX

int_least16_t

PRILEASTd16

SCNLEASTd16

INT_LEAST16_MIN

INT_LEAST16_MAX

int_least32_t

PRILEASTd32

SCNLEASTd32

INT_LEAST32_MIN

INT_LEAST32_MAX

int_least64_t

PRILEASTd64

SCNLEASTd64

INT_LEAST64_MIN

INT_LEAST64_MAX

uint_least8_t

PRILEASTu8

SCNLEASTu8

0

UINT_LEAST8_MAX

uint_least16_t

PRILEASTu16

SCNLEASTu16

0

UINT_LEAST16_MAX

uint_least32_t

PRILEASTu32

SCNLEASTu32

0

UINT_LEAST32_MAX

uint_least64_t

PRILEASTu64

SCNLEASTu64

0

UINT_LEAST64_MAX

 

最快的最小长度类型

类型名

printf说明符

scanf说明符

最小值

最大值

int_fast8_t

PRIFASTd8

SCNFASTd8

INT_FAST8_MIN

INT_FAST8_MAX

int_fast16_t

PRIFASTd16

SCNFASTd16

INT_FAST16_MIN

INT_FAST16_MAX

int_fast32_t

PRIFASTd32

SCNFASTd32

INT_FAST32_MIN

INT_FAST32_MAX

int_fast64_t

PRIFASTd64

SCNFASTd64

INT_FAST64_MIN

INT_FAST64_MAX

uint_fast8_t

PRIFASTu8

SCNFASTu8

0

UINT_FAST8_MAX

uint_fast16_t

PRIFASTu16

SCNFASTu16

0

UINT_FAST16_MAX

uint_fast32_t

PRIFASTu32

SCNFASTu32

0

UINT_FAST32_MAX

uint_fast64_t

PRIFASTu64

SCNFASTu64

0

UINT_FAST64_MAX

 

最大长度类型

类型名

printf说明符

scanf说明符

最小值

最大值

intmax_t

PRIdMAX

SCNdMAX

INTMAX_MIN

INTMAX_MAX

uintmax_t

PRIuMAX

SCNuMAX

0

UINTMAX_MAX

 

可保存指针值的整数类型

类型名

printf说明符

scanf说明符

最小值

最大值

intptr_t

PRIdPTR

SCNdPTR

INTPTR_MIN

INTPTR_MAX

uintptr_t

PRIuPTR

SCNuPTR

0

UINTPTR_MAX

 

注意:该库VC05也不支持。但是微软在VC中定义了扩展的C++关键字:__int8__int16__int32__int64;在Windows API定义了INT32INT64UINT32UINT64等数据类型;在.NET框架也定义了Int16Int32Int64UInt16UInt32UInt64等结构类型。

 

n         iso646.h:定义了运算符的替代字符串

and

and_eq

bitand

bitor

compl

not

not_eq

or

or_eq

xor

xor_eq

运算符

&&

&=

&

|

~

=

||

|=

^

^=

注意:该CVC05支持

 

n         stdbool.h:定义了bool_Bool)、false0)和true1)等宏。

注意:该CVC05也不支持。但是C++标准支持,它们都是C++的关键字。

 

n         wchar.h:定义了扩展的多字节字符和宽字符工具:(参见wchar.doc,该CVC05支持)

u       数据类型:

l         wchar_t:可表示本地环境所支持的最大扩展字符集的一种整数类型,在VC05中定义为unsigned short2B

l         wint_t:可表示最大扩展字符集任何值以及其他值的一种整数类型,在VC05中定义为int4B

l         size_t:由sizeof返回的整数值类型,在VC05中定义为unsigned int4B)(在Win64中定义为unsigned __int64

l         mbstate_t:为一种非数组的对象类型,用于保存多字节字符与宽字符之间转换所需状态信息。在VC05中定义为int4B

l         struct tm:用来保存日历时间成分的结构类型,至少应该包含下列成员:

int tm_sec; // seconds after the minute — [0, 60]

int tm_min; // minutes after the hour — [0, 59]

int tm_hour; // hours since midnight — [0, 23]

int tm_mday; // day of the month — [1, 31]

int tm_mon; // months since January — [0, 11]

int tm_year; // years since 1900

int tm_wday; // days since Sunday — [0, 6]

int tm_yday; // days since January 1 — [0, 365]

int tm_isdst; // Daylight Saving Time flag

VC05中定义也为:

struct tm {

        int tm_sec;     /* seconds after the minute - [0,59] */

        int tm_min;     /* minutes after the hour - [0,59] */

        int tm_hour;    /* hours since midnight - [0,23] */

        int tm_mday;    /* day of the month - [1,31] */

        int tm_mon;     /* months since January - [0,11] */

        int tm_year;    /* years since 1900 */

        int tm_wday;    /* days since Sunday - [0,6] */

        int tm_yday;    /* days since January 1 - [0,365] */

        int tm_isdst;   /* daylight savings time flag */

        };

u       符号常量宏:

l         NULL(空指针VC05中定义为((void *)0)

l         WCHAR_MAXwchar_t的最大值VC中定义为0xFFFF[65535]

l         WCHAR_MINwchar_t的最小值VC05中定义为0

l         WEOF(宽字符文件的结尾,是EOF的宽字符表示,在VC05中定义为(wint_t)(0xFFFF)[65535]

u       宽字符I/O函数:

l         I/O函数:(字符和串格式符从%c%s改为%lc%ls

int fwprintf(FILE * stream, const wchar_t * format, ...);

int fwscanf(FILE * stream, const wchar_t * format, ...);

int swprintf(wchar_t * s, size_t n, const wchar_t * format, ...);

int swscanf(const wchar_t * s, const wchar_t * format, ...);

int vfwprintf(FILE * stream, const wchar_t * format, va_list arg);

int vfwscanf(FILE * stream, const wchar_t * format, va_list arg);

int vswprintf(wchar_t * s, size_t n, const wchar_t * format, va_list arg);

int vswscanf(const wchar_t * s, const wchar_t * format, va_list arg);

int vwprintf(const wchar_t * format, va_list arg);

int vwscanf(FILE * stream, const wchar_t * format, va_list arg);

int wprintf(const wchar_t * format, ...);

int wscanf(const wchar_t * format, ...);

wint_t fgetwc(FILE *stream);

wchar_t *fgetws(wchar_t * s, int n, FILE * stream);

wint_t fputwc(wchar_t c, FILE *stream);

int fputws(const wchar_t * s, FILE * stream);

int fwide(FILE *stream, int mode);

wint_t getwc(FILE *stream);

wint_t getwchar(void);

wint_t putwc(wchar_t c, FILE *stream);

wint_t putwchar(wchar_t c);

wint_t ungetwc(wint_t c, FILE *stream);

l         字符串工具:

double wcstod(const wchar_t * nptr, wchar_t ** endptr);

float wcstof(const wchar_t * nptr, wchar_t ** endptr);

long double wcstold(const wchar_t * nptr, wchar_t ** endptr);

long int wcstol(const wchar_t * nptr, wchar_t ** endptr, int base);

long long int wcstoll(const wchar_t * nptr, wchar_t ** endptr, int base);

unsigned long int wcstoul(const wchar_t * nptr, wchar_t ** endptr, int base);

unsigned long long int wcstoull(const wchar_t * nptr, wchar_t ** endptr, int base);

wchar_t *wcscpy(wchar_t * s1, const wchar_t * s2);

wchar_t *wcsncpy(wchar_t * s1, const wchar_t * s2, size_t n);

wchar_t *wcscat(wchar_t * s1, const wchar_t * s2);

wchar_t *wcsncat(wchar_t * s1, const wchar_t * s2, size_t n);

int wcscmp(const wchar_t *s1, const wchar_t *s2);

int wcscoll(const wchar_t *s1, const wchar_t *s2);

int wcsncmp(const wchar_t *s1, const wchar_t *s2, size_t n);

size_t wcsxfrm(wchar_t * s1, const wchar_t * s2, size_t n);

wchar_t *wcschr(const wchar_t *s, wchar_t c);

size_t wcscspn(const wchar_t *s1, const wchar_t *s2);

size_t wcslen(const wchar_t *s);

wchar_t *wcspbrk(const wchar_t *s1, const wchar_t *s2);

wchar_t *wcsrchr(const wchar_t *s, wchar_t c);

size_t wcsspn(const wchar_t *s1, const wchar_t *s2);

wchar_t *wcsstr(const wchar_t *s1, const wchar_t *s2);

wchar_t *wcstok(wchar_t * s1, const wchar_t * s2, wchar_t ** ptr);

wchar_t *wmemchr(const wchar_t *s, wchar_t c, size_t n);

int wmemcmp(wchar_t * s1, const wchar_t * s2, size_t n);

wchar_t *wmemcpy(wchar_t * s1, const wchar_t * s2, size_t n);

wchar_t *wmemmove(wchar_t *s1, const wchar_t *s2, size_t n);

wchar_t *wmemset(wchar_t *s, wchar_t c, size_t n);

size_t wcsftime(wchar_t *s, size_t maxsize, const wchar_t *format, const struct tm *timeptr);

size_t wcsfxtime(wchar_t *s, size_t maxsize, const wchar_t *format, const struct tmx *timeptr);

l         转换函数:

wint_t btowc(int c);

int wctob(wint_t c);

int mbsinit(const mbstate_t *ps);

size_t mbrlen(const char * s, size_t n, mbstate_t * ps);

size_t mbrtowc(wchar_t * pwc, const char * s, size_t n, mbstate_t * ps);

size_t wcrtomb(char * s, wchar_t wc, mbstate_t * ps);

size_t mbsrtowcs(wchar_t * dst, const char ** src, size_t len, mbstate_t * ps);

size_t wcsrtombs(char * dst, const wchar_t ** src, size_t len, mbstate_t * ps);

 

n         wctype.h:提供与ctype.h类似的宽字符函数。(也参见wchar.doc,该CVC05也支持)

u       类型:

l         wint_t:可保存扩展字符值的一种整数类型,VC05中定义为unsigned short2B

l         wctrans_t:表示特定字符映射的一种标量类型,VC05中定义为wchar_t2B

l         wctype_t:表示字符分类的一种标量类型,VC05中也定义为unsigned short2B

u       符号常量宏:

l         WEOF:宽字符输入的文件结尾,VC05中定义为(wint_t)(0xFFFF) [65535]

u       函数:

l         分类函数:

int iswalnum(wint_t wc);

int iswalpha(wint_t wc);

int iswcntrl(wint_t wc);

int iswdigit(wint_t wc);

int iswgraph(wint_t wc);

int iswlower(wint_t wc);

int iswprint(wint_t wc);

int iswpunct(wint_t wc);

int iswspace(wint_t wc);

int iswupper(wint_t wc);

int iswxdigit(wint_t wc);

l         可扩展分类函数:

int iswctype(wint_t wc, wctype_t desc);

wctype_t wctype(const char *property);

l         转换函数:

wint_t towlower(wint_t wc);

wint_t towupper(wint_t wc);

wint_t towctrans(wint_t wc, wctrans_t desc);

wctrans_t wctrans(const char *property);

4数学改进

除了上面以及讲过的新增关键字_Complex(复数)和_Imaginary(虚数)、floatlong double版的数学函数、以及复数运算的标准库外。C99对数学库也进行了许多修改,增加了两个数据类型,并添加了大量新的数学函数。

l         math.h:除了上面这些新增加的库和头文件外,为了支持IEC 60559:1989Binary floating-point arithmetic for microprocessor systems微处理器系统的二进制浮点运算)浮点运算标准,C99还对数学库进行了如下扩展:

n         float_tdouble_t类型:是C99新增加的分别进行floatdouble运算最快的的类型。(位数:double_tfloat_tfloat_tfloatdouble_tdouble

n         floatlong double版函数:原来的很多数学函数只有double版,C99新增加了float版和long double版的数学函数。做法是:改变输入参数和返回值,并在函数名的尾部,增加一个字符fl,来分别表示floatlong double类型(没有尾部字符的为原来的double型)。例如:

double cos (double x);

float cosf (float x);

long double cosl (long double x);

n         新符号常量:

C99新增加的数学符号常量

说明

HUGE_VALF

最大的float

HUGE_VALL

最大的long double

INFINITY

无穷大

NAN

非数

FP_INFINITE

浮点无穷大分类号

FP_NAN

非浮点数分类号

FP_NORMAL

正常浮点数分类号

FP_SUBNORMAL

精度降低的浮点数分类号

FP_ZERO

浮点数0的分类号

FP_FAST_FMA

速度≥double操作数的乘法和加法的函数fma

FP_FAST_FMAF

速度≥float操作数的乘法和加法的函数fmaf

FP_FAST_FMAL

速度≥long double操作数的乘法和加法的函数fmal

FP_ILOGB0

表示ilogb(0)函数返回值的整数常理表达式

FP_ILOGBNAN

表示ilogb(NAN)函数返回值的整数常理表达式

MATH_ERRNO

= 1

MATH_ERREXCEPT

= 2

MATH_ERRHANDLING

= MATH_ERRNOMATH_ERREXCEPT

MATH_ERRNO | MATH_ERREXCEPT

n         新函数:

C99新增加的数学函数

floatlong double版数学函数未写出)

原型

说明

int fpclassify(real-floating x);

返回x的浮点数分类值的宏

int isfinite(real-floating x);

x有穷时返回非0值的宏

int isinf(real-floating x);

x无穷时返回非0值的宏

int isnan(real-floating x);

x非数时返回非0值的宏

int isnormal(real-floating x);

x为正常数时返回非0值的宏

int signbit(real-floating x);

x为负数时返回非0值的宏

double exp2(double x);

2x

double expm1(double x);

ex-1

int ilogb(double x);

返回x(以FLT_RADIX为基数)的指数int

double log1p(double x);

log(1+x)

double log2(double x);

log2x

double logb(double x);

返回x(以FLT_RADIX为基数)的指数

double scalbn(double x, int n);

返回x*FLT_RADIXn

double scalbln(double x, long n);

返回x*FLT_RADIXn

double cbrt(double x);

double hypot(double x, double y);

double erf(double x);

返回x的误差函数

double erfc(double x);

返回x的补余误差函数

double lgamma(double x);

ln|Γ(x)|

double tgamma(double x);

Γ(x)

double nearbyint(double x);

(使用浮点环境指定的舍入方向)将x舍入到最近的整数(double值)

double rint(double x);

nearbyint,但是抛出inexact异常

long lrint(double x);

(使用浮点环境指定的舍入方向)将x舍入到最近的整数(long值)

long long llrint(double x);

(使用浮点环境指定的舍入方向)将x舍入到最近的整数(long long值)

double round(double x);

x舍入到最近的整数(四舍五入)(double值)

long lround(double x);

x舍入到最近的整数(四舍五入)(long值)

long long llround(double x);

x舍入到最近的整数(四舍五入)(long long值)

double trunc(double x);

double remainder(double x, double y);

返回x除以y的余数

double remquo(double x, double y, int *quo);

返回值同remainder,将x/y取整后放入quo

double copysign(double x, double y);

sign(x)*|y|

double nan(const char *tagp);

返回NaN类型的double表示

double nextafter(double x, double y);

y=x时返回xy>x时返回比x大的下一个double值、y<x时返回比x小的下一个double

double fdim(double x, double y);

|x-y|

double fmax(double x, double y);

max(x, y)

double fmin(double x, double y);

min(x, y)

double fma(double x, double y, double z);

(x*y)+z

int isgreater(real-floating x, real-floating y);

返回x>y的宏

int isgreaterequal(real-floating x, real-floating y);

返回x>=y的宏

int isless(real-floating x, real-floating y);

返回x<y的宏

int islessequal(real-floating x, real-floating y);

返回x<=y的宏

int islessgreater(real-floating x, real-floating y);

返回x<y || x>y的宏

int isunordered(real-floating x, real-floating y);

xy中至少一个为NaN时返回1,否则0

注意:该库VC05也不支持。但是VC05支持原数学函数的floatlong double版。

5扩展字符支持

除了上面讲过的wcahr_t类型定义和新增加的两个宽字符标准库(<wchar.h><wctype.h>)外,C99中还有其他对扩展字符支持方面。

l         其它扩展字符支持:因为C语言最初并不是为国际字符集设计的,所以除了上面的wchar.hwctype.h库外,还有如下一些扩展字符支持的方面:

n         三元字符(trigraph):考虑到有些键盘并不能提供C中所使用的全部符号,早在C89中就已经定义了一些三元字符序列,作为这些符号的可选表示:(为C语言的保留字,但不是关键字,VC05支持)

三元字符

??=

??(

??/

??)

??’

??<

??!

??>

??-

符号

#

[

/

]

^

{

|

}

~

三元字符在任何地方都是同样的含义,即使在字符串中也是如此。

n         二元字符(digraph):由于三元符号太笨拙,C99新增加了二元字符:(也为C语言的保留字,但不是关键字,不过VC05不支持)

二元字符

<:

:>

<%

%>

%:

符号

[

]

{

}

#

与三元字符不同,二元字符在字符串中没有特别意义(视作原来的两个字符)。

n         UCNUniversal-Character-Name通用字符名):C99允许使用ISO/IEC 10646Unicode)国际字符集中的字符(包括汉字)作为标识符的一部分,但是扩展字符中的数字字符也不能作为标识符的起始字符。可以使用/u/U开头的4个(16位)或8个(32位)十六进制数字(/uhhhh/UHHHH/uhhhhhhhh/UHHHHHHHH)来表示扩展字符。(VC05支持[作为C++标准的一部分])。例如:

       wchar_t buf[80];

       wchar_t ch1 = '', ch2 = L'';  // ch1为多字节字符,ch2为宽字符

       int 变量1 = 25, /u53D8/u91CF2 = 30; // 如果变量名后不加12

// 则报变量重名的错误

       变量1 *= /u53D8/u91CF2;

       swprintf(buf, 80, L"%lc = 0x%X = 0x%X,  %lc = 0x%X = 0x%X",

ch2, ch1, ch2, L'', '', L''); 

       pDC->TextOut(10, 10, buf);

       swprintf(buf, 80, L"变量1 = %d,  /u53D8/u91CF2 = %d",

                                   变量1, /u53D8/u91CF2);

       pDC->TextOut(10, 40, buf);

输出结果为:(“变量”的GB码为B1E4 C1BFUnicode码为53D8 91CF

变=0xB1E4=0x53D8,  量=0xC1BF=91CF

变量1=750, 变量2=30

n         前缀与修识符:宽字符常量和宽字符串常量,可以用L前缀来表示。在格式化I/O函数中,可以用%lc%ls修识符来显示宽字符及其串数据

6CC++的差别

虽然C++C的超集,但是它们之间还是存在细小的差别,特别是C99增加的新特性中,有许多当前的C++标准还不支持。下面列出一些CC++的差别:

 

l         函数原型

C++中是必须的,在C中(虽然被极力推荐,但仍然)是可选的。例如:

int myfun(); // C中被认为是前向声明

// C++中等价于函数原型:int myfun(void);

int main() {

       ……

       myfun(20,45); // C++会报错(函数不是两个输入参数)

       ……

}

int myfun(int a, int b) {……} // C++被认为是另一个函数

 

l         强制转换

C中,自动转换似乎可以无处不在。例如,可以不经过强制转换,直接将void *型的变量或表达式赋值给其他指针类型的变量。但是这样做在C++中确是不被允许的,而必须使用显式的强制转换。这是因为,太任意的自动类型转换,会造成很多很难查出的程序问题。所以,C++的新规定,是出于程序安全和故障排查的考虑而制定的。

 

l         char常量

Cchar常量被处理为int类型(4B),而在C++则被处理为char类型(1B)。例如:

char ch = ‘A’;

C中常量A被存储为一个int值,但变量ch则只占1个字节;而在C++中常量A和变量ch都只占1个字节。又例如:

int x = ‘ABCD’;

C中是可以的(变量x的值为1094861636),在C++中则是非法的(但在VC05中可以)。而

char c = ‘ABCD’;

CC++中都是可以的,不过会发出截断警告(变量c的值为D

 

l         const修识符

n         在缺省情况下,全局的常型变量,即在函数体外部声明的,例如:

const double PI = 3.14159;

C中具有外部链接(只能在一个文件中定义,在其它文件中,必须用extern进行引用声明后,才能被使用);在C++中则具有内部链接(可以定义在头文件中定义,使每个包含该头文件的代码文件,都有该变量的一个拷贝)。

n         可以在C中,通过将全局的常型变量声明为静态的,使其变成内部链接的:

static const double PI = 3.14159;

也可以在C++中,使用extern关键字使全局的常型变量变成外部的。

n         C++中常型变量可以用来声明数组的大小,但是在C中不行。例如:

const int SIZE = 100;

double a[SIZE]; // C++中等价于double a[100];

                      // C中则创建了一个变长数组,还需要给它分配空间

n         C++中可以使用一个已经初始化了的常型变量给另一个常型变量赋值,但在C中这样做是非法的。例如:

const double RATE = 0.5;

const double STEP = 24.0;

const double LEVEL = RATE * STEP; // C++中合法,在C中非法

 

l         结构和联合

n         声明了一个带有标记(tag)的结构或联合之后,在C++可以直接用此标记作为类型名,但是在C中则不行(必须前面带有struct关键字才可以)。例如:

struct point {

       int x;

       int y;

};

struct point p1; // CC++中都可以

point p2; // C++中可以,但在C中不行

n         嵌套的结构在C中可以直接引用,在C++中则必须采用定位符。例如:

struct box {

       struct point {int x;        int y;} ul;

       struct point lr;

};

struct box rect; // CC++中都可以

struct point dot1; // C中可以,但在C++中不行

box::point dot2; // C++中可以,但在C中不行

 

l         枚举

n         与结构类似,在C++允许声明枚举变量时省略enum关键字,但是C不行。例如:

enum season {spring, summer, autumn, winter};

season ssn = spring; // C中非法,在C++中合法

n         在枚举的使用中,C++C更严格。在C++中,不允许把整数值赋给枚举变量,也不能递增一个枚举变量。例如:

enum season {spring, summer, autumn, winter};

enum season ssn;

ssn = spring; // CC++中都可以

ssn = 2; // C中会发出警告,但在C++中是一个错误

ssn = (enum season)1; // CC++中都可以

ssn++; // C中可以,但在C++中是一个错误

 

l         指向void的指针

C++中不能把指向void的指针,不经显示转换,而直接赋給其它类型的指针。但是在C中则可以。例如:

int a[4] = {1, 2, 3, 4};

int *pi;

void *pv;

pv = a; // CC++中都可以

pi = pv; // C中可以,但在C++中非法

pi = (int *)pv; // CC++中都可以

 

l         布尔类型:C++中的布尔类型bool和值truefalse都是关键字。在C99_Bool类型是关键字,但是booltruefalse却是在stdbool.h中定义的typedef类型和符号常量宏。

 

l         可选的拼写:andornot_eq等在C++中都是关键字;但在C99中是在iso646.h头文件中定义的宏。

 

l         宽字符类型:wchar_tC++是关键字;但在C99中是在wchar.h等头文件中定义的typedef类型。

 

l         复数类型:在C99中可以通过关键字_Complex_Imaginary来使用内置的复数类型。但是C++中是通过头文件complex中的模版类来使用复数类型的。(在C中也可以通过complex.h来使用各种复函数)

 

l         内联函数:与全局常型变量类似,C99的内联函数默认是外部链接的,而C++的内联函数则默认是内部链接的。在C中还允许混合使用函数的内联和外联,但是在C++中是不允许的。

 

l         标准C++中目前所没有的C99特性:

n         指定初始化条目;

n         复合初始化条目;

n         受限指针;

n         变长数组;

n         弹性数组成员;

n         long longunsigned long long类型;(VC05支持)

n         可移植的整数类型;

n         通用字符名;(VC05支持)

n         附加的数学库函数;

n         通过fenv.h访问浮点环境;

n         若干预定义的标识符,如__func__

n         具有可变数目的参数宏。

7)参考文献

l     Stephen Prata(云巅工作室译). C Primer Plus(第五版)中文版. 人民邮电出版社,2005. 16/626/60元。

l     ISO/IEC 9899:1999 Programming languages – CC99.pdf

l     ISO/IEC 9899:TC22005.5.6Programming languages – CCommittee Draft)(ISO C CD 2005-5-6 N1124.pdf