《Expert C Programming 》学习笔记

以下代码均在  VS2005 里面编译,

一、P19

      将 char ** 类型 传给 const char **  

 

      1 foo(const char **p){ }

  2

  3 void main(int argc, char **argv)

  4 {

  5    foo(arvg);

  6 }

  如果编译这段代码,编译器会发出一条警告消息:

  line 5:warning argument is incompatible with prototype

  (第5行:警告:参数与原型不匹配)。

  疑问是:实参char *s与形参const char *p是相容的(标准库中所有的字符串处理函数都是这个样子的),为什么实参char **argv与形参const char **p不相容呢?

  在ANSI C标准中有这么一句话:每个实参都应该具有自己的类型,这样它的值就可以赋值给与它所对应的形参类型的对象(该对象的类型不能含有限定符)。

  这就是说参数传递过程类似于赋值。所以,除非一个类型为char **的值可以赋值给一个const char **类型的对象,否则肯定会产生一条诊断信息。要使以上赋值合法,必须满足下列条件之一:

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

  正是这个条件,使得函数调用中实参char *能够与形参const char*匹配。

  1 char *cp;

  2 const char *ccp;

  3 ccp = cp;         //right

  4 cp = ccp;         //产生编译警告

  要想彻底理解为何char ** 与 const char **是不相容的,我们得先回顾一下const float *:它并不是一个有限定符的类型——它的类型是“指向一个具有const限定符的float类型的指针”,也就是说const限定符是修饰指针所指向的类型,而不是指针本身。

  类似的,const char **也是一个没有限定符的指针类型。它的类型是“指向有const限定符的char类型的指针的指针”。

  由于char **和const char **都是没有限定符的指针类型,但它们所指向的类型不一样(前者指向char*,后者指向const char*),因此它们是两种不同的类型,是不相容的。因此,类型为char **的实参与类型为const char **的形参是不相容的。它违反了上文黑体字所列出的约束。

      上面的阐述,看得不太懂!!!

编译结果:

用c编译     0个warning,0个错误。(看来没有遵守 ANSI C)

用c++编译 error C2664:  不能将参数 1 从“char **”转换为“const char **”

 

二、

P30 最后一段讲到 

switch存在一些问题,其中之一。。。。。 

还有P31最后一段讲到 在C语言中,const 关键字并不真正表示常量,

本人写了个例子:

 

void fun()
{
	int x=1;
	const int cy =2;
	switch (x)
	{
		int temp=3;
		temp++; 
	case 1: 
		{
			int y=temp;
			y++;
			break;
		}
	case cy: 
		{
			break;
		}
	default:
		break;
	}


  

用c编译

error C2051: case 表达式不是常量

( 如果注释掉 case cy: 整段) 报一个警告 warning C4700: 使用了未初始化的局部变量“temp” ,

运行时,根本不执行第7行,直接执行到第11行

用c++编译

error C2360: “temp”的初始化操作由“case”标签跳过(即temp定义在那里是不合法的) 

但在C++中   case cy: 的用法 合法

 

三、P34

 

char *str[] =

{ "I",

"love" 

"you"

};

love后面忘记了逗号, 如果你修改 str[2] 等于修改了其他的变量

 

char *text="abc\

def";

char *text2="abc"

"def";

text 和 text2不相等,text 很可能多了些tab

 

 

四、P64

 

讲到C语言声明的优先级规则如下:

 

    A 声明从它的名字开始读取,然后按照优先级顺序依次读取;

    B 优先级从高到低依次是:

    B.1 声明中被括号括起来的那部分;

    B.2 后缀操作符:括号()表示这是一个函数,而方括号[]表示这是一个数组;

    B.3 前缀操作符:星号*标识“指向……的指针”;

    C 如果const和(或者)volatile关键字的后面紧跟类型说明符(如int,long等),那么它作用于类型说明符,在其他情况下,const和(或)volatile关键字作用于它左边紧邻的指针星号。

    并举例说明:char * const * (*next)();

    A      next                  ——next为声明的名字

    B.1 (*next)              ——next为一个指向……的指针

    B.2 (*next)()            ——next是一个函数指针

    B.3 *(*next)()          ——next是一个函数指针,这个函数返回一个指向……的指针

    C    char * const     ——指向字符类型的常量指针

    故 char * const *(*next)();的含义就是:   next是一个函数指针,这个函数返回一个指向字符类型的常量指针

    下面我们来自己分析一个声明:

    int(*foo())[];

    自己先推导一下,然后看看结果结果:foo为一个函数,这个函数返回一个指向整型数组的指针。对不对呢?下面我们来看具体的推导过程:

    A    foo                      ——foo为声明的名字

    B.2 foo()                  ——foo为一个函数

    B.3 (*foo())              ——foo为一个函数,这个函数返回一个指向……的指针

    B.2 (*foo())[]            ——foo为一个函数,这个函数返回一个指向数组的指针

    C    int (*foo())[]        ——foo为一个函数,这个函数返回一个指向整型数组的指针

 

 

五、P68 

讲到了  typedef 和 宏的区别

 

六、P83--P86 4.3节

讲到了数组和指针访问的区别

个人感觉,结合下面本人给的例子来看,清晰些

 

/*1.c*/
char Jan[10] = "Jan";
char Feb[10] = "Feb";

/*2.c*/
extern char  Jan[];
extern char *Feb;
int  main()
{
	char *p = Jan;
	char c_Jan=Jan[1];
	char c_Jan2=*(Jan+1);
	char c_p = p[1];
	char c_p2 = *(p+1);
	/*运行时出错 地址无法访问*/
	char c_Feb=Feb[1];
	return 0;
}
/*对应的汇编*/
	char *p = Jan;
0042D66E  mov         dword ptr [p],offset _Jan (493000h) 
	char c_Jan=Jan[1];
0042D675  mov         al,byte ptr [_Jan+1 (493001h)] 
0042D67A  mov         byte ptr [c_Jan],al 
	char c_Jan2=*(Jan+1);
0042D67D  mov         al,byte ptr [_Jan+1 (493001h)] 
0042D682  mov         byte ptr [c_Jan2],al 
	char c_p = p[1];
0042D685  mov         eax,dword ptr [p] 
0042D688  mov         cl,byte ptr [eax+1] 
0042D68B  mov         byte ptr [c_p],cl 
	char c_p2 = *(p+1);
0042D68E  mov         eax,dword ptr [p] 
0042D691  mov         cl,byte ptr [eax+1] 
0042D694  mov         byte ptr [c_p2],cl 
	// 运行时出错 地址无法访问
	char c_Feb=Feb[1];
0042D697  mov         eax,dword ptr [_Feb (49300Ch)] 
0042D69C  mov         cl,byte ptr [eax+1] 
0042D69F  mov         byte ptr [c_Feb],cl 
	return 0;
0042D6A2  xor         eax,eax 


 

 

七、P103

讲到  

SunOS 中跟 Interpositioning 有关的一个Bug

为了更好的理解,本人写了一个可执行的例子 

 

1.c

double  sqrt(double x, double y)
{
	return x+y;
}

void fun();

int  main()
{
	fun();
	return 0;
}


2.c

#include <math.h>
double fun(double x)
{
	double xx = sqrt(x);
	return xx;
}


运行后,发现fun 函数里的 sqrt跑到了  double  sqrt(double x, double y);这个函数里,所以我们要坚决避免这样的同名函数

但是在C++中 却不会出现这种现象,

 

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

P172 

讲到了 类型提升的问题  char ch = 'A';
 printf("%d %d/n", sizeof(ch), sizeof ('A'));

//  保存为.c     输出  1  4

//  保存为.cpp 输出  1  1  

表8-1 做出了解释

 

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

P176

讲到函数声明和定义的问题

1.c

#include <stdio.h>
void newdef(float d, char i)
{
 printf("newdef: float=%f, char=%x/n", d, i);
}

 

2.c


int  main()
{
 newdef(10.0, 3);
 return 0;
}
VS下编译 时 会出现 warning C4013: “newdef”未定义;假设外部返回 int

运行结果为 newdef: float=0.000000, char=0

 

如果将两个.c 改为.cpp   会直接报错

error C3861: “newdef”: 找不到标识符

 

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

 

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

P201 底部

什么时候数组和指针是相同的

 

规则1:表达式中的数组名(与声明不同)被编译器当作指向该数组第一个元素的指针
规则2:下标总是与指针的偏移量相同
规则3:在函数的声明中,数组名被编译器当作指向该数组第一个元素的指针

 

P212

图9-8 多维数组的存储

 

P225

数组和指针参数是如何被编译器修改的

"数组名被改写成一个指针参数"规则并不是递归定义的.数组的数组会被改写为"数组的指针",而不是"指针的指针"

                       实参                                  所匹配的形参
数组的数组   char c[8][10];                    char (*)[10];          数组指针
指针数组      char *c[15];                       char **c;               指针的指针
数组指针      char (*c)[64];                    char (*c)[64];         不改变
指针的指针   char **c;                           char **c;                不改变

 

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

P266--P268 提到了 C 、C++的区别

 

 

 

 

 

 

 

你可能感兴趣的:(《Expert C Programming 》学习笔记)