以下代码均在 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++的区别