读书笔记===C专家编程[1]

读书笔记,仅作为博主关心的、不理解的 相关点的解释,整理为笔记,方便之后的再次查看。
第一章:C: 穿越时空的迷雾

C简史:

a>Multics工程,目的是创建一个操作系统,,,,失败==经验
b>UNIX系统的产生:一个叫Ken Thompson的研究员+同事 Dennis Ritchie 移植某些软件到PDP-7硬件平台;期间Ken Thompson 为PDP-7 编写了操作系统(汇编实现),最后 另一个大爷Brian Kernighan 在1970年给它取名为UNIX,并总结了Multics工程的经验教训。
c>C语言的产生:开发UNIX过程中,Ken Thompson+ Dennis Ritchie二位尝试+创造了 BCPL> B> new B> C语言(早期的)==1965.7月–1972.3月期间
d>可移植的标准I/O库(open、read、write等io函数):最早由Mike Lesk于1972年左右 编写;
e>C语言最早流通版本(K&R C):1978年,经典著作 《The C Programming Language》出版,作者Ken Thompson+ Dennis Ritchie;二位因此而闻名
e2>C语言编译器:1978年,steve Johnson编写了pcc可移植的C编译器
f> ANSI C:1983年,ANSI组织成立C语言工作小组,开始了C语言的标准化工作;
1989年12月,C语言标准草案被ANSI组织接纳;之后ISO C标准出现;最终1990年ANSI C接纳ISO C;于是 世人说的 标准C语言=ISO C,,,,但ANSI C参与的工作更多,业内名声也就更大,最终定格于 ANSI C标准;

提及的技术点:

虽然约定采用“.h”作为头文件的扩展名,但在头文件和包含实现代码的 对象库之间在命名上却没有相应的约定,这多少令人不快。????===不理解,


1978年,steve Johnson编写了pcc可移植的C编译器==有机会尝试了解下。


可移植的代码定义:–p13

  • 只是用ASNI C语言标准 已确定的 C语言特性
  • 不突破由编译器实现的限制
  • 不产生任何依赖于编译器定义的 或未确定或未定义等特性的 输出

不可移植的代码:p12

a>由编译器确定的特性:当整型数向右移位时,要不要扩展他的符号位???

int a= (~0);//0xFF FF FF FF
a>>=1;//此时 最高位 bit31位,应不应该动???

b>未确定的==ANSI C标准中,未明确规定该怎样做
参数求值顺序?

int i = 3;
printf("%d,%d,%d\n", i, ++i, ++i );

测试结果:5,5,5
感兴趣的可以再试下:

int i = 3;
printf("%d,%d,%d\n", i, i++, i++ );

测试结果:
5,4,3
已知的是:
1、参数传递顺序:从右到左 入栈
2、i++,为先用后加;++i为加完再用。

坏代码:
c>未定义的:在某些不正确情况下的做法,ANSI C并未规定应该如何处理
eg:当一个有符号整数溢出时,该采取什么行动???

p14:

/*并不严格遵守标准:其输出结果是由编译器定义的*/
#include <limits.h>
#include <stdio.h>
int main()
{
	(void)printf("biggest int is %d",INT_MAX);
	return 0;
}

INT_MAX宏为标准C库定义,为int类型的最大值;
对于16位编译器,int占16位(2字节),int的最大值为32767.
对于32位编译器,…


预处理:p17

a>替换源文本中的三联符。三连符是C语言为了适应某些输入设备上没有的字符而设定的,比如??=表示"#"。
b>预处理规则定义得更加严格,有一条新规则就是相邻的字符串字面值会被自动连接在一起;eg:“ab”“cde” 等价于 “abcde”

整型升级与 寻常算数转换:p22

ANSI C标准:
第6.2.1.1节 字符和整型(整形升级)
char,short int或者int型位段(bit-field),包括它们的有符号或无符号变型,以及枚举类型,可以使用在需要int或unsigned int的表达式中。
如果int可以完整地表示源类型的所有值,那么该类型的值就转换为int,否则转换为unsigned int。此为整型升级。

第6.2.1.5节 寻常算数转换
许多操作数类型为算术类型的 双目运算符 会引发转换,并以类似的方式产生结果类型。

  • 浮点相关:

    如果其中一个操作数类型是long double,那么另一个操作数也被转换为long double。其次如果一个操作数的类型是double,那么另一个操作数也被转换为double,再次,如果其中一个操作数的类型是float,那么另一个操作数也被转换为float。

  • 整型相关:
    否则两个操作数进行整形升级,执行下面的规则:
    如果其中一个操作数的类型是unsigned long int,那么另一个操作数也被转换成unsigned long int。其次,如果其中一个操作数类型是long int,而另一个操作数的类型是unsigned int,如果long int能够完整表示unsigned int的所有值,那么unsigned int 类型操作数操作数被转换为long int,如果long int 不能完整表示unsigned int的所有值,那么两个操作数都被转换为unsigned long int。再次,如果一个操作数的类型是long int,那么另一个操作数被转换为long int。再再次,如果一个操作数的类型是是unsigned int,那么另一个操作数被转换为unsigned int。如果所有以上情况都不属于,那么两个操作数都为int。
    采用通俗语言大意如下:
    当执行算术运算时,操作数的类型如果不同,就会发生转换。数据类型一般朝着浮点精度更高、长度更长的方向转换,整型数如果转换为signed不会丢失信息,就转换为signed,否则转换为unsigned。

一个栗子:p24

int array[] = { 23, 34, 12, 17, 204, 99, 16 };
#define TOTAL_ELEMENTS (sizeof(array)/sizeof(array[0]))
int main()
{
   int d = -1, x;
   /*...*/
   if (d <= TOTAL_ELEMENTS - 2)
   {
       x = array[d+1];
   }
   /*...*/
}

TOTAL_ELEMENTS所定义的值是unsigned int 类型(因为sizeof()的返回类型是无符号数)。
if语句在signed int和unsigned int 之间测试相等性,所以d被升级为unsigned int类型,
-1 转换成unsigned int 的结果将是一个非常巨大的正整数。致使表达式的值为假。
修复这个bug:对TOTAL_ELEMENTS 强制类型转换。

if (d < (int)TOTAL_ELEMENTS - 2)

对无符号类型的建议

  1. 尽量不要再你的代码中使用无符号类型,以免正价不必要的复杂性。尤其是,不要因为无符号数不存在负值(如年龄、国债)而用它来表示数量。
  2. 尽量使用像int那样的有符号类型,这样在涉及升级混合类型的复杂细节时,不必担心边界情况(如-1被翻译为非常大的正数)
  3. 只有在使用位段和二进制掩码时,才可以用无符号数。应该在表达式中使用强制类型转换,使操作数均为有符号数或者无符号数,这样就不必由编译器来选择结果的类型。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值