C专家编程学习笔记(1)

用了那么久的C语言,看了这本书才发现还有好多东西不是很清楚,C专家编程(Expert C Programming)这本书不错,受益匪浅.
以下就是从中摘录的知识点,以加深印象.

第1章:C:穿越时空的迷雾
1.两个操作数都是指向有限定符或无限定符的相容类型的指针,左边指针所指向的类型必须具有右边指针所指向类型的全部限定符.
char *cp;
const char *ccp;
ccp = cp;
ccp是指向const限定符的char指针,cp是无限定符的char指针,因此可以赋值cp给ccp,反之则不行.

2.类型升级
当执行算术运算时,操作数的类型如果不同,就会发生转换.数据类型一般朝着浮点精度更高,长度更长的方向转换.整形数如果转换为signed不会丢失信息,就转换为signed,否则转换为unsigned.
int arary[]={12,34,12,17,204,99,16};
#define TOTAL_ELEMENTS (sizeof(array)/sizeof(array[0]))

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变成很大的正整数,表示式值为假

第二章:这不是Bug,而是语言特性
1.break跳出的是最近的那层循环语句或switch语句
switch(line){
/*...*/
case THING2:
    if(x == STUFF) {
         do_first_stuff();
         if(y == OTHER_STUFF)
              break;
         do_later_stuff();
    }/*代码意图是跳转到这里......*/
    initialize_modes_pointer();
    break;
    default:
           processing();
}/*...事实上跳到了这里*/
   use_modes_pointer();/*致使modes_pointer未初始化*/

2.p= N * sizeof * q
该表达式等于p = N * sizeof(*q),这里是一个乘号.当sizeof的操作数是个类型名时,两边必须加上刮号(这常常使人误以为它是个函数),但操作数如果是变量则不必加刮号.
apple = sizeof(int) * p;
是(sizeof(int)) * (p)还是 sizeof((int)*p)
经测试如果p是个整数,结果为(sizeof(int)) * (p),如果p是个指针,那编译器会报错(error C2297: '*' : illegal, right operand has type 'void *')

3.算术运算高于位移运算符
msb<<4 + lsb
误以为的结果: (msb<<4) + lsb
实际结果:       msb<<(4 + lsb)

4.结合性
所有的赋值符(包括复合赋值符)都具有右结合性,就是说表达式中最右边的操作最先执行,然后从右到左依次执行.
如:
int a, b = 1, c = 2;
a = b = c;
先c赋值给b,然后b赋值给a,最终a的值是2

第三章:分析C语言的声明
1.
char (*j)[20];   /*j是一个指向数组的指针,数组内有20个char元素.*/
j = (char (*)[20]) malloc(20);
如果把星号两边看上去多余的括号拿掉,代码会变成非法的.
设计指针和const的声明可能会出现几种不同的顺序:
const int * grape;
int const * grape;
in * const grape_jelly;
在最后一种情况下指针是只读的,而在另外两种情况下,指针所指向的对象是只读的.当然对象和指针都可以是只读的,下面两种声明方法都能做到这一点:
const int * const grape_jelly;
int const * const grape_jelly;

2.
函数的返回值不能是一个函数,所以像foo()()这样是非法的.
函数的返回值不能是一个数组,所以像foo()[]这样是非法的.
数组里不能有函数,所以像foo[]()这样是非法的.
但下面这样则是合法的:
函数的返回值允许是一个函数指针,如: int(*fun())();/*... fun()是一个函数,返回值是一个指针,这个指针一个函数指针,该函数返回值是int ...*/
函数的返回值允许是一个指向数组的指针,如: int(*foo())[]; /*... foo()是一个函数,返回值是一个指针,这个指针指向一个int型的数组 ...*/ 
数组里面允许有函数指针,如 int(*foo[])() /*... foo[]是一个数组,这个数组元素是指针,该指针指向返回值为int的函数 ...*/
数组里面允许有其他数组,所以你经常能看到 int foo[][]

3.理解C语言声明的优先级法则
A.声明从它的名字开始读取,然后按照优先级顺序依次读取
B.优先级从高到低依次是:
  B.1 声明中被括号括起来的部分
  B.2 后缀操作符:
        括号()表示这是一个函数,而方括号[]表示这是一个数组
  B.3 前缀操作符:
        星号*表示"指向...的指针"
C.如果const和(或)volatile关键字的后面紧跟类型说明符(如int,long等)那么它作用于类型说明符.在其他情况下,const和(或)volatile关键字作用于它左边紧邻的指针星号.
例: char * const *(*next)();
next是一个指针,它指向一个函数,该函数返回另一个指针,该指针指向以一个类型为char的常量指针

例: char *(* c[10])(int **p)
c是一个数组[0..9],它的元素类型是函数指针,其指向的函数返回值是一个指向char的指针,该函数的唯一参数是指向指针的指针

4.typedef和宏定义的区别
(1)可以用其他类型说明符对宏类型名进行扩展,但对typedef所定义的类型名确不能这样做.
#define peach int
unsigned peach i; /* 没问题 */

typedef  int banana;
unsigned banana i; /* 错误!非法 */
(2)在连续几个变量的声明中,用typedef定义的类型能够保证声明中所有的变量均为同一种类型,而用#define定义的类型则无法保证.
#define int_ptr int *
int_ptr chalk, cheese;
经过宏扩展,第二行变为:
int * chalk, cheese;
这使得chalk和chesse成为不同的类型
typedef char * char_ptr;
char Bentley, Rolls_Royce;
Bentley和Rolls_Royce的类型依然相同

5.typedef struct
typedef struct fruit{int weight, price_per_lb;}fruit;  /* 语句1 */
struct veg{int weight,price_per_lb;}veg;  /* 语句2 */
语句1声明了结构标签"fruit"和由typedef声明的结构类型"fruit",其实际效果如下:
struct fruit mandarin; /* 使用结构标签"fruit" */
          fruit mandarin; /* 使用类型标签"fruit" */
语句2声明了结构标签"veg"和变量veg.只有结构标签能够在以后的声明使用,如struct veg potato;如果试图使用veg potato这样的声明将是一个错误.

你可能感兴趣的:(编程,c,struct,processing,语言,fun)