《C专家编程》读书笔记之 C:穿越时空的迷雾

 ***C的发展历程***

c语言的产生原于一个失败的工程Multics

BCPL -> B -> New B -> 早期的C

注释:
BCPL(Basic Combined Programming Language) 基本组合编程语言
B 在BCPL的基础上进行了简化,但由于硬件系统的内存限制,它只允许放置解释器,而不是编译器。它同时保持了BCPL语言无类型的特点,仅有的操作数就是机器的字。
New B 能同时解决多种数据类型,采用了编译模式而不是解释模式,并引入了类型系统。


早期的C 有以下一些语言特性:

(1)数组下标从0而不是1开始;

(2)基本数据类型直接与底层硬件相对应(这一点带来了极高的效率和移植性);

(3)auto关键字显然是摆设

(4)数组名可看作指针;

(5)float自动扩展为double

(6)不允许嵌套函数。

(7)register关键字

C编译器未实现的功能在运行时进行处理,既可出现在应用程序代码中,也可出现在运行时函数库中。

 

***ANSI C***

ANSI C标准的几个术语:
(1)不可移植的代码(unportable code):
         (a)由编译器定义的:由编译器设计者决定采取何种行动,可能并不相同,但都是正确的。
                   例如:当整型数向右移位时,要不要扩展符号位
         (b)未确定的:在某些正确情况下的做法,标准并未明确规定应该怎样做。
                   例如:计算参数的顺序
(2)坏代码(bad code):
        (a)未定义的:在某些不正确情况下的做法,但标准并未规定应该怎么做,即你可采取任何行动。
                  例如:当一个有符号整数溢出时该采取什么行动
        (b)约束条件:必须遵守的限制或要求,若不遵守,则程序行为将变成未定义的。
                  例如:%操作符的操作数必须属于整型(属于约束条件),若不符合,必引发一条错误信息。
                  声明一个已有的malloc函数(不属于约束条件),则可能出现任何情况。

(3)可移植代码(portable code):

        (a)只使用已经确定的特性

        (b)不突破任何由编译器实现的限制

        (c)不产生任何依赖由编译器定义的或未确定的或未定义的特性的输出


K&R C与ANSI C的一个重大区别:原型
原型的目的是当我们对函数作前向声明时,在形参类型中增加一些信息。这样,编译器就能够在编译时对函数调用中的实参和函数声明中的形参之间进行一致性检查。

 

                             使用原型之前                    使用原型之后
函数声明:         
char *  strcpy();                    char *  strcpy( char *  dst,  char *  src)
函数定义:         
char *  strcpy(dst, src)              char *  strcpy( char *  dst,  char *  src)
                  
char *  dst,  char *  src;             { ... }
                         
{ ... }

 

***指针赋值的规范***

规范:两个操作数都是指向有限定符或无限定符的相容类型的指针,左边指针所指向的类型必须具有右边指针所指向类型的全部限定符。

下面是相关示例:

  1. foo(const char**p){}

  2. int main(int argc,char* argv[])
  3. {
  4.       foo(argv);
  5. }
其中foo(argv)会编译警告:argument is incompatible with prototype

(1)合法情况:

char *  cp;
const   char *  ccp;
ccp 
=  cp;

    左操作数是一个指向有const限定符的char的指针,右操作数是一个指向无限定符的char的指针,char类型和char类型是相容的,左操作数所指向的类型具有右操作数所指向的类型的限定符(无),再加上自身的限定符(const),因而此赋值是合法的。

(2)非法情况:
char **  cp;
const   char **  ccp;
ccp 
=  cp;

    cpp是无限定符的指向const char*的指针,cp是无限定符的指向char*的指针,cp指向的和 ccp指向的都是无限定符的指针,且二者指向的指针是相容的(原因参见情况1),但由于相容性是不能传递的,const char*和char*不是相容的,因而此赋值是非法的。

 

***对无符号类型的建议***

K&R c采取的是无符号保留(unsigned preserving)原则,就是一个无符号类型和一个int或者更小的整型混合使用时,结果是无符号类型,它有时会使负数丢失符号位。

ANSI c标准则采用值保留(value preserving)原则,结果类型有可能是符号数,也可能是无符号数,如下图代码:


  1. #include <stdio.h>
  2. int main(int argc, char* argv[])
  3. {
  4.     if(-1<(unsigned char)1)
  5.     {
  6.         printf("-1<(unsigned char)1");//unsigned char转化为int型
  7.     }
  8.     else
  9.     {
  10.         printf("-1>(unsigned char)1");
  11.     }
  12. }
其结果在不同编译器也不同,但现在在dev c++和visual studio 2008中结果都是 -1<(unsigned char)1,符合ANSI标准,改为unsigned int之后结果就不一样了,结果会把-1转换为unsigned int


   尽量不要在代码中使用无符号类型,只有在使用位段和二进制掩码时,才可以用无符号数,但也要注意在表达式中使操作数均为无符号数或有符号数,这样就不必由编译器来选择结果的类型。

下面是一个错误示例:

  1. #include <stdio.h>


  2. double array[] = { 23,34,12,17,204,99,16 };

  3. //sizeof返回无符号类型数
  1. #define TOTAL_ELEMENTS (sizeof(array)/sizeof(array[0]))

  2. int main()
  3. {
  4.     int d = -1;
  5.     double x;
  6.     if(d <= TOTAL_ELEMENTS - 2)
  7.     {
  8.         x = array[d+1];
  9.         printf("%f",x);
  10.     }
  11.     else
  12.     {
  13.         printf("unsigned");
  14.     }
  15.     return 0;
  16. }
结果是打印出unsigned,这里d转化为了unsigned int型


TOTAL_ELEMENTS的类型是unsigned int(因为sizeof()的返回类型是无符号数),if语句在int和unsigned int之间测试相等性,所以d被升级为无符号数,-1转化为unsigned int的结果是一个非常巨大的正整数,致使表达式的结果为假(与预期不符)。要修正这个问题,只需对TOTAL_ELEMENTS进行强制类型转换既可:

if (d  <=  ( int )TOTAL_ELEMENTS  -   2 )

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值