《C和指针》读书笔记(7)

声明:该读书笔记摘抄自《C和指针》——Kenneth A.Reek (著)    徐波(译)。为了克服自己走马观花,提高阅读和学习效率,决定将自己在读书过程中遇到的一些知识点加以摘抄和总结备忘,在此感谢原书作者和翻译。

一、数组名

1、一个误区:数组名并不表示整个数组,它大多时候只是一个指针常量,指向数组的第一个元素,它的类型取决于数组元素类型。这一点解释了为什么C语言的数组不能整体拷贝,只能循环拷贝赋值。如果你将数组名赋值给另一个数组名,例如,int  a[10] ;  intb[10];  a = b; ,就已经错的一塌糊涂:

    1)数组名是一个常量指针,它不可以被赋值;

    2)就是上面提到的数组不能整体拷贝,因为你得到的仅仅是一个指向数组第一个元素的指针常量而已。

注:个人认为这两点其实本质上是一个意思。即,假如数组名不是一个指针常量而是表示整个数组,那么它可以整体赋值。

2、数组名大多时候是一个常量指针,但是有且只有两种情况,数组名不用指针常量来表示:

    1)作为sizeof的表达式的时候,数组名表示整个数组,sizeof会返回整个数组占用的字节数;

    2)当数组名作为操作符&的操作数时,会产生一个指向该数组的指针。

二、数组的下标引用

1、当用下标来访问数组时,下标值可以为任意整数值,可以为负,但是程序员自己必须保证访问的有效性,即不能跳出数组的前边界;下标值也可以大于数组实际长度,这通常用来实现不对称边界,但是要避免对超出数组有效长度的地址进行访问(只要地址,不要值)。

2、C语言并不会对数组下标有效性进行安全检查,C语言标准也并没有规定编译器必须对数组下标进行有效性检查,对数组下标的有效性检查会大大增加程序运行时间和空间的开销。也就是说保证数组访问不越界的工作应该交由程序员来做。

3、a[2] 和2[a] 都是合法的,并且具有相同的效果,这缘于C语言实现下标的方式,因为在C语言中对下标的访问都可以转换为对等的间接访问表达式。a[2] 、2[a]都会被编译器转换为*(2 + a),这两种下标书写形式对编译器来说并无差异。当然,如果你不是在参加混乱代码大赛,在代码可读性上没有人会喜欢2[a]这种书写方式。

三、指针与下标

1、加入下标和指针都可以实现某种需要,下标一定不会比指针更有效率,并且有时候指针比下标更有效率。

2、指针比下标更有效率的场合:当在数组中循环变量一次以固定步长移动时,固定数字与固定数字的相乘是在编译期间完成的,不会占用运行时间,更有效率一些。

3、一个高效率的数组的拷贝, 将数组y的元素赋给数组x

#define      SIZE             10

int    x[size], y[size] ;

void array_to_array(void)

{

register   int     *p1, *p2;

for (p1=x, p2=y; p1<&x[SIZE]; ) {

*p1++ =  *p2++;

}

}

四、数组和指针

1、数组和指针都具有指针值,都可以进行间接访问和下标引用操作,但是需要注意的是他们并不等价。声明一个数组时,编译器首先根据数组元素个数为该数组分配内存,然后再创建数组名,它是一个指针常量,指向数组的第一个元素。声明一个指针变量时,编译器只为该变量分配4个字节的内存。

2、当一个数组名作为参数传递给一个函数时,该数组名退化为指针,此时,在函数内部对其sizeof时,得到的结果是4(32位机器),

3、编译器允许数组不完整初始化,但只允许顺序省略后面的初始值,被省略的初始值全部为0;例如,int   a[10] = {1,2};   a[0] = 1, a[1] = 2, a[2]~a[9]全部为0。另外,对于全局数组,默认初始值都是0, 但是如果对于一个局部数组,并且所有元素都没有初始化时,其所有元素初始值都是不确定的。

4、在数组声明时,允许编译器自动计算数组长度,例如,int   a[ ] = {1, 2, 3, 4, 5};

5、字符数组的初始化,

  方法1、char    a[ ] = {'h', 'e', 'l', 'l', o'', '\0'};        只适用于较短的字符数组初始化

  方法2、char    a[ ] = "hello";   注意这种初始化方法与字符串常量的区别:当它用于初始化一个字符数组时,它就是一个字符数组的初始化列表,其他的任何情况都是字符串常量。

五、多维数组

1、int     a[3][4]; 可以将a看作一个具有三个元素的一维数组,只不过每个元素又是一个具有4个元素的一位数组;

同样,int     a[2][3][4];将a看作一个具有两个元素的一维数组,其中每个元素又是一个具有三个元素的一维数组,而这三个元素的每一个又是一个具有4个元素的一维数组。可见,多维数组可以看作是很多一维数组的嵌套集合。

2、多维数组在内存中的存储顺序

在C语言中,多维数组元素的存储顺序按照最右边的下标率先变化,这称为行主序。

3、指向数组的指针

例如:int  a[3][4], *(p)[4] = a; 此时p指向该二维数组的第一行,p+1指向第二行,每一行是一个具有4个元素的数组,此时p就是一个指向数组的指针。

特别注意:若p初始化为int  *p = a; 则是错的,因为p是一个指向整型的指针,而二维数组名a是一个指向二维数组第一行的一维数组的指针常量,也就是说a是一个指向数组的指针,相当于二级指针,用一个二级指针初始化一个一级指针,显然不合适。此时应该使用行指针变量,即int   (*p)[4]才正确,要注意,除了第一维声明为指针p外,其他各维必须明确用下标指定。 若将行指针变量定义为int   (*p)[ ]; 此时p仍然是一个指向一个一维数组的行指针变量,但是此时一维数组的长度却没有指定,于是当p指针参与运算时,它指向的数组将会被视为空数组,即数组长度为0,计算指针p的步长时将与0相乘,这种情况可能不是你想要的,应该避免。

4、多维数组作为函数参数

例如有一个二维数组:  int  a[3][4];    

a作为参数传递给函数:      func(a);

      函数func的声明形式为: void func(int  p[ ][4])   或者void func(int   (*p)[4])

  注:要区别void  func(int   **p) ,这种声明形式是错误的,p是一个真正的二级指针,即p被声明为一个指向整型的指针的指针。

5、多维数组的初始化

原则:按照最右边的下表率来变化。

6、多维数组的长度自动计算

注意,在多维数组中只有第一维的长度才能自动计算,其余各维必须显示指定。

七、指针数组

下表优先级高于间接访问符优先级


             以上为第八章“数组和指针”摘抄总结,未完待续。。。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值