《C专家编程》Finux_you读书笔记(1)

p22-27<ANSI C的Bug>:

你认为下面程序会打印出什么?为什么?

 1 #include <stdio.h>
 2 #include <conio.h>
 3 int a[] = {1,3,4,5,6,7,8};
 4 /*(sizeof(a[0]))而不是sizeof(int)*/
 5 #define MAX_ELEMENT ((sizeof(a)) / (sizeof(a[0])))
 6 int main(void)
 7 {
 8     int d = -1;
 9     if(d <= MAX_ELEMENT - 2)
10     {
11         printf("There is no bug.\n");
12     }
13     else
14     {
15         printf("OH! FUCK! BUG!\n");
16     }
17     getch();
18     return 0;
19 }

是的,打印出:OH! FUCK! BUG! 这是C语言规则的一个小小的Bug。

(1)如果某个操作符的各个操作数属于不同的类型,那么除非其中一个操作数转换为另一个操作数的类型,否则操作就无法进行。下面的层次体系称为寻常算数转换(usual arithmetic conversion)

long double

double

float

unsigned long int

long int

unsigned int

int                                                           ——摘自《C和指针》p80

上述规则可以分析为:当执行算术运算时,操作数的类型如果不同,就会发生转换。数据类型一般朝着浮点精度更高、长度更长的方向转换。整形数如果转换为signed不会丢失信息,就转换为signed,否则转换为unsigned。

sizeof运算符的结果是无符号数,所以MAX_ELEMENT是无符号数。d就被升级为unsigned int,结果将是一个非常大的正数。

解决办法:将MAX_ELEMENTS强制转换为int型。:

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

(2)值得补充的一点:C的整形算术运算总是至少以整形类型的精度进行的。所以表达式中的char型和short型会在使用之前被转换为整型,这种转换称为为整型提升(integral Promotion):

char a, b, c;

a = b + c;

b和c被提升为普通整型,然后相加,结果将被截断存储于a中。注意溢出的情况!

(3)对无符号类型的建议:

尽量不要使用无符号类型,以免增加不必要的复杂性。尤其是,不要仅仅因为无符号数不存在负值而用它表示数量。

尽量使用int那样的有符号类型。这样在设计升级混合类型的复杂细节时,不必担心边界情况(如-1被翻译为非常大的正数)。

只有在使用位段和二进制掩码时,才可以用无符号数。在表达式中使用强制类型转换,是操作数均为有符号数或者无符号数,这样就不必由编译器来选择结果的类型

(4)#define MAX_ELEMENT ((sizeof(a)) / (sizeof(a[0])))中的sizeof(a[0])可以在不改写宏的情况下改变数组a元素类型。

(5)%的操作数必须属于整型,否则会引发编译错误。


2011-02-24 17:23:3

p27<这不是bug,而是语言特性>:

Bug是迄今为止地球上最庞大最成功的实体类型,有近百万已知的品种。在这个方面,它比其他任何已知的生物种类的总和还要多,而且至少多出4倍。 ——Snope,《Encyclopedia of Animal Life》

在本书的第二章“这不是bug,而是语言特性”中讲解了很多C语言中不好的特性,包括:多做之过、少做之过和误做之过。值得仔细研读。


p30<局部变量>:

int main()

{

char temp[1] = {'a'};

if(a[0])

{char temp[1] = {'b'};}

return 0;

}

上面代码中的两个temp互不可见。if下的temp只在大括号中有用。


p33<break中断了什么>:

break中断的是最进的那层循环语句或switch语句,而不是从最进的大括号跳出去。


p34<字符串合并>:

ANSI C引入的一个特性:相邻的字符串常量将会自动合并为一个字符串。如下面代码:

char *a[] = {

"ab"

"out",

"idear",

}

a[0]就成了“about”;因为"ab"后面遗漏了一个逗号。注意最后一个逗号,可以通过编译。


p47<编译器日期破坏>:

Sun的Pascal编译器中有一段程序打印日期,却时常出现乱码,最后查出错误在如下代码:

cahr * localized_time(char *filename)

{

char buffer[120];

......

return buffer;

}

原因你是知道的。解决的办法有如下几种

(1)返回一个指向字符串常量的指针:

char *fuc()

{

reutrn "Only works for simple strings";

}

但是需要改写字符串的内容时会有麻烦。

(2)使用全局声明的数组:

char  buffer[120];

char *fuc();

{

buffer[i] = ".......";

......

reutrn buffer;

}

缺点就是全局变量的可见性。

(3)使用静态数组:

char *fuc()

{

static char buffer[120];

......

return buffer;

}

这就可以防止任何人修改这个数组,只有拥有指向该数组的指针的函数(通过参数传递给它)才能修改这个静态数组。当然,和全局变量一样,大型缓冲区如果限制不用是非常浪费空间的。

(4)显示分配一些内存,保存返回的值:

char *fuc()

{

char *buffer = malloc(120);

......

return buffer;

}

缺点是程序员必须承担内存管理责任。根据程序的复杂度,可能很容易也可能很复杂。容易产生内存泄露。

(5)也许最好的办法是要求调用者分配内存来保存函数的返回值。为了提高安全性,调用者应该同时制定缓冲区大小(就像fgets)()):

void fuc(char *result, int size)

{

strncpy(result, "That'd be in the data segment, Bob", size);

}

buffer = malloc(size);

fuc(buffer, size);

......

free(buffer);

如果程序员能在同一代码块中同时进行malloc和free操作,内存管理最为轻松。


2011-02-25 16:33:42

p72<cdecl.c>:

编程要对解决问题的步骤十分清楚,然后再对其进行抽象,使之适合编程语言表达。这样才能写出好程序,比如下面是分析C语言声明的程序,很难想象如果不会分析声明怎么编出来:

  1 /*摘自《C专家编程》(Expert C Programming)*/
  2 #include<stdio.h>
  3 #include<string.h>
  4 #include<ctype.h>
  5 #include<stdlib.h>
  6 #define MAXTOKENS 100
  7 #define MAXTOKENLEN 64
  8 enum type_tag
  9 {
 10     IDENTIFIER,QUALIFIER,TYPE
 11 };
 12 struct token
 13 {
 14     char type;
 15     char string[MAXTOKENLEN];
 16 };
 17 
 18 struct token stack[MAXTOKENS];
 19 struct token this;
 20 #define pop stack[top--]
 21 #define push(s) stack[++top] = s;
 22 int top=-1;
 23 enum type_tag classify_string(void)
 24 /*推断标识符的类型*/
 25 {
 26     char *= this.string;
 27     if(!strcmp(s,"const"))
 28     {
 29         strcpy(s,"read-only");
 30         return QUALIFIER;
 31     }
 32     if(!strcmp(s,"volatile")) return QUALIFIER;
 33     if(!strcmp(s,"void")) return TYPE;
 34     if(!strcmp(s,"char")) return TYPE;
 35     if(!strcmp(s,"signed")) return TYPE;
 36     if(!strcmp(s,"unsigned")) return TYPE;
 37     if(!strcmp(s,"short")) return TYPE;
 38     if(!strcmp(s,"int")) return TYPE;
 39     if(!strcmp(s,"long")) return TYPE;
 40     if(!strcmp(s,"float")) return TYPE;
 41     if(!strcmp(s,"double")) return TYPE;
 42     if(!strcmp(s,"struct")) return TYPE;
 43     if(!strcmp(s,"union")) return TYPE;
 44     if(!strcmp(s,"enum")) return TYPE;
 45     return IDENTIFIER;
 46 }
 47 void gettoken(void/* 读取下一个标记到"this" */
 48 {
 49     char *= this.string;
 50     /* 略过空白字符 */
 51     while((*= getchar()) == ' ')
 52         ;
 53     if(isalnum(*p))
 54     {
 55         /* 读入的标识符以A-Z,0-9开头 */
 56         while(isalnum(*++= getchar()))
 57             ;
 58         ungetc(*p,stdin);
 59         *= '\0';
 60         this.type = classify_string();
 61         return;
 62     }
 63     if(*== '*')
 64     {
 65         strcpy(this.string,"pointer to")
 66             ;
 67         this.type = '*';
 68         return;
 69     }
 70     this.string[1= '\0';
 71     this.type = *p;
 72     return;
 73 }
 74 /* 理解所有分析过程的代码段 */
 75 void read_to_first_identifier()
 76 {
 77     gettoken();
 78     while(this.type != IDENTIFIER)
 79     {
 80         push(this);
 81         gettoken();
 82     }
 83     printf("%s is ",this.string);
 84     gettoken();
 85 }
 86 void deal_with_arrays()
 87 {
 88     while(this.type == '[')
 89     {
 90         printf("array");
 91         gettoken(); /*数字或']'*/
 92         if(isdigit(this.string[0]))
 93         {
 94             printf("0..%d",atoi(this.string)-1);
 95             gettoken();
 96         }
 97         gettoken();
 98         printf("of ");
 99     }
100 }
101 void deal_with_function_args()
102 {
103     while(this.type != ')')
104     {
105         gettoken();
106     }
107     gettoken();
108     printf("function returning");
109 }
110 void deal_with_pointers()
111 {
112     while(stack[top].type == '*')
113     {
114         printf("%s ",pop.string);
115     }
116 }
117 void deal_with_declarator()
118 {
119     /* 处理标识符之后可能存在的数组/函数*/
120     switch(this.type)
121     {
122     case '[':deal_with_arrays(); break;
123     case '(':deal_with_function_args();
124     }
125     deal_with_pointers();
126     /* 处理在读入到标识符之前压入到堆栈中的符号 */
127     while(top>=0)
128     {
129         if(stack[top].type == '(')
130         {
131             pop;
132             gettoken();
133             deal_with_declarator();
134         }
135         else
136            printf("%s",pop.string);
137     }
138 }
139 int main()
140 {
141     /* 将标记压入堆栈中,直到遇见标识符*/
142     read_to_first_identifier();
143     deal_with_declarator();
144     printf("\n");
145     getch();
146     return 0 ;
147 }


2011-02-28 20:21:41

posted @ 2011-02-24 17:23  Finux_you  Views(259)  Comments(0Edit  收藏  举报