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

第8章:再论数组
1.什么时候数组和指针是相同的
C语言标注对此作了如下说明:
规则1: 表达式中的数组名(与声明不同)被编译器当作一个指向该数组第一个元素的指针.
int a[10], *p, i=2;
可以通过以下任何一种方法访问a[i];
p = a; p[i];
p = a; *(p+a);
p = a + i; *p
在表达式中,指针和数组是可以互换的,因为它们在编译器里的最终形式都是指针,并且都可以进行取下标操作.

规则2: 下标总是与指针的偏移量相同.

规则3: 在函数参数的声明中,数组名被编译器当作指向该数组第一个元素的指针.
在函数形参定义这个特殊情况下,编译器必须把数组形式改写成指向数组第一个元素的指针形式.编译器只向函数传递数组的地址,而不是整个数组的拷贝.因此在my_function()的调用上,无论实参是数组还是真的指针都是合法的.
my_function(int *turnip) { ... }
my_function(int  turnip[]) { ... }
my_function(int  turnip[200]) { ... }

2.Page 207页的编程挑战
按照要求,我写了以下代码:

打印出来的结果为:

含义分别为:
ga数组的首地址(ga第一个元素的地址) 0x422d8c
ga数组第一个元素的地址0x422d8c
ga数组第二个元素的地址0x422d8d
指向ga的指针的地址0x12ff30
ga第一个元素的地址0x422d8c
ga数组第二个元素的地址0x422d8d
指向ga的指针的地址0x12ff30
ga第一个元素的地址0x422d8c
ga数组第二个元素的地址0x422d8d
指向ga的指针加1的值(ga数组第二个元素的地址):0x422d8d

3.有一种操作只能在指针里进行而无法在数组中进行,那就是修改它的值.数组名是不可修改的左值,它的值是不能改变的.

4.数组和指针可交换性的总结
(1) 用a[i]这样的形式对数组进行访问总是被编译器"改写"或解释为像*(a+1)这样的指针访问.
(2) 指针始终是指针.它决不可以改写成数组.你可以用下标形式访问指针,一般都是指针作为函数参数时,而且你知道实际传递给函数的是一个数组.
(3) 在特定的上下文中,也就是它作为函数的参数(也只有这种情况),一个数组的声明可以看作是一个指针.作为函数参数的数组(就是在一个函数调用中)始终会被编译器修改成为指向数组第一个元素的指针.
(4) 因此,当把一个数组定义为函数的参数时,可以选择选择把它定义为数组,也可以定义指针.不管选择哪种方法,在函数内部事实上获得的都是一个指针.
(5) 在其他所有情况中,定义和声明必须匹配.如果定义了一个数组,在其他文件对它进行声明时也必须把它声明为数组,指针也是如此.

5.只有字符串常量才可以初始化指针数组.指针数组不能由非字符串的类型直接初始化.
char *vegetables[] = {"carror", "celery", "corn"} /* 没问题 */
int *weights[] = {{1,2,3,4,5},{6,7},{8,9,10}}; /* 无法成功编译 */

第10章 再论指针
1.pea[i][j]将被编译器解析为: *(*(pea + i) + j)

2.数组和指针参数是如何被编译器修改的
"数组名被改写成一个指针参数"规则并不是递归定义的.数组的数组会被改写为"数组的指针",而不是"指针的指针".

实参

所匹配的形式参数

数组的数组

char c[8][10];

char (*)[10];

数组指针

指针数组

char *c[15];

char **c

指针的指针

数组指针(行指针)

char (*c)[64];

char (*c)[64];

不改变

指针的指针

char **c

char **c

不改变


3.无法直接从函数返回一个数组,但是可以让函数返回一个指向任何数据结构的指针.但不能返回一个指向函数的局部变量的指针.

4.当需要在动态表中增长一个项目时,可以进行如下操作:
(1) 对表进行检查,看看它是否真的已满
(2) 如果确实已满,使用realloc()函数扩展表的长度.并进行检查,确保realloc()操作成功进行.
(3) 在表中增加所需要的项目.
用C代码表示,大致如下:


第11章 你懂得C,所以C++不在话下
1.C++对C语言的改进
(1) 在C语言中,初始化一个字符数组的方式很容易产生错误,就是数组很可能没有足够的空间存放结尾的NULL字符.C++对此作了一些改进,像char b[3]="Bob"这样的表达式被认为是一个错误,但它在C语言中却是合法的.
(2) 类型转换既可以写成像float(i)这样看上去更顺眼的形式,也可以写成像(float)i这样稍显怪异的C语言风格的形式.
(3) C++允许一个常量整数来定义数组的大小
const int size = 128;
char a[size];
这在C++中是允许的,但在C语言中却是错误的.
(4)声明可以穿插于语句之间.在C语言中,一个语句块中所有的声明都必须放在所有语句的前面.C++去掉了这个专横的限制.

2.在C++中存在,但在C语言中不存在的限制有:
(1) 在C++中,用户代码不能调用main()函数,但在C语言中却是允许的(不过这种情况极为罕见).
(2) 完整的函数原型声明在C++中是必须的,但在C语言中却没这么严格.
(3) 在C++中,由typedef定义的名字不能与已有的结构标签冲突,但在C语言中却是允许的(它们分属不同的名字空间).
(4) 当void*指针赋值给另一个类型的指针时,C++规定必须进行强制类型转换,但在C语言中确无必要.

3.在C++和C语言中含义不一样的特性:
(1) C++至少增加了十几个关键字.这些关键字在C语言中可以作为标识符使用,但如果这样做了,用C++编译器编译这些代码就会产生错误信息.
(2) 在C++中,声明可以出现在语句可以出现的任何地方.在C语言中的代码块中,所有的声明必须出现在所有语句的前面.
(3) 在C++中,一个内层作用域的结构名将会隐藏外层空间中相同的对象名.在C语言中则非如此.
(4) 在C++中,字符常量的类型是char,但在C语言中,它们的类型是int.也就是说,在C++中,sizeof('a')的结果是1,而在C语言中,它的值要大一些.
(5) 由于C++增加了新的//注释符,有时会在两种语言中产生微妙而怪异的差别.

附录A 程序员工作面试的秘密
1.关于增值语句
mango[i++] += y;
被当作:
mango[i] = mango[i] + y; i++;
而不是:
mango[i++] = mango[i++] + y;

++x表示取x的地址,增加它的内容,然后把值放在寄存器中;x++则表示取x的地址,把它的值装入寄存器中,然后增加内存中的x的值.

frotz[--j + i++] += --y;
相当于:
--y;
--j;
frotz[j + i] = frotz[j+i] + y;
i++;

2.函数库调用和系统调用区别何在
函数库调用:
在所有的ANSI C编译器版本中,C函数库是相同的
它调用函数库中的一个程序
与用户程序相联系
在用户地址空间执行
它的运行时间属于"用户"时间
属于过程调用,开销较小
在C函数库libc中有大约300个程序
记录于UNIX OS手册的第三节
典型的C函数库调用:system,fprintf,malloc

系统调用:
各个操作系统的调用是不同
它调用系统内核的服务
是操作系统的一个入口点
在内核地址空间执行
它的运行时间属于"系统"时间
需要在切换到内核上下文环境后然后切换回来,开销较大
在UNIX中有大约90个系统调用(MS-DOS中少一些)
记录于UNIX OS手册的第二节
典型的系统调用:chdir,fork,write,brk

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值