读书笔记|[C和指针] ch08. 数组

第八章:数组 GitHub 链接:ch08. 数组
本章总结及注意点 读书笔记|[C和指针] ch08. 数组
文章图片

部分课后习题解答 8.7 问题

  1. 问题是个好问题,但是,ints 数组下标 0 对应的是 10,1 对应的是 20…大意了啊。参考答案:
    读书笔记|[C和指针] ch08. 数组
    文章图片

  2. 运算符优先级不同。第二个等价于 array[j] + i
  3. 不能。因为指针指向数组左边界的前一个位置了,数组越界,赋值是非法的。
  4. 不想写这个转换了,不过回文字符串的判断还是蛮重要的。参考答案:
    读书笔记|[C和指针] ch08. 数组
    文章图片

  5. 绝大多数都是下标…参考答案:
    读书笔记|[C和指针] ch08. 数组
    文章图片

  6. 书上讲解的很清楚了,关于汇编不做实验了。
  7. 和机器性能及编译器都有关。关于及时可以使用相关库函数。
  8. 警告!!!重点坑点!!!
    第一个赋值中。答案给的解释看不懂,甚至一度认为这样的写法是正确的。有一说一,答案给的解释是真不行!这个问题应该归结到 extern 数组与 extern 指针中去。如果在 main 文件中使用 extern char *data, 编译不会出错,先让 char data[100] = {10}; ,然后打印 data 的值是 10, 所以猜测 main 文件中 data 的值是 test 文件 data 指针指向地址内容,继续验证让 char data[100] = {0, 0, 0, 0,1}; ,打印的值是 0,再让 char data[100] = {0, 0, 0,1}; ,打印的值是 16777216(就是小端模式下的 0x1000000),所以引用方式是错误,编译器把数组的内容解释为指针值(地址),只取数组的前4个字节(32位模式)。应该保持类型匹配,使用 extern char data[],编译器就会把 data 值设置为数组首地址。第二个赋值中。 编译器认为 b 是个数组名,故将 12 加到 b 的存储地址,然后间接访问获取值。然而 b 只是一个指针变量,它的存储位置和数组的起始位置没有任何关系,从内存中提取到后面的 3 个整形位置实际上是从任意变量中取得的,并非从数组的首地址开始向后偏移 3 个整形大小取到数组的第三个元素。所以都是未知,随机的。很不错的一个问题! 参考答案:
    读书笔记|[C和指针] ch08. 数组
    文章图片

    这个问题在 《C专家编程》中也提及到了。
    重点参考博文:
    • C语言 extern 数组
    • extern用于数组和指针
  9. int coin_values[] = { 1, 5, 10, 25, 50, 100 };
  10. &array[1][2] 是真滴恶心,看着都已经越界了。但数组的连续存储特性,其就等价于 &array[2][0]。涨知识了!参考答案:
    读书笔记|[C和指针] ch08. 数组
    文章图片

  11. 关于 array[1][0] + 1 手误算错了。参考答案:
    读书笔记|[C和指针] ch08. 数组
    文章图片

  12. 不会…参考答案:当执行“按照元素在内存中出现的顺序对元素进行访问”的操作时…内存中就是按照行主序来进行存储的。
    读书笔记|[C和指针] ch08. 数组
    文章图片

  13. 注意 *( *( *array + 1 ) + 2 ) array[0][1][2] 还有 **( *array + 1 ) array[0][1][0]。参考答案:
    读书笔记|[C和指针] ch08. 数组
    文章图片

  14. 显然,i 声明为整形指针变量的时候不会报错。
  15. 第二个有意义啊。双指针算法中经常都是这样,&& 有运算符截断的特性,所以前者判断越界后后者就不再进行检查,就避免了越界错误。写在后面…啥也不是。
  16. array1 其作为函数参数,实际上是一个指针变量。指向作为实际参数传递的数组,该指针变量的值可以被修改,同时数组作为参数并没有给其分配内存空间。而 array 是指针常量,因此其值不能更改,且在这个函数中给其分配了 10 个整数的空间。相关代码见 demo02.c,这个写的很抽象,个人理解用 hh…
  17. 第一个参数是一个标量,写不写 const 意义都不是很大。对这份拷贝的修改并不会影响原来的参数,const 关键字的作用并不是防止原来的参数被修改。第二个参数实际上是一个指向整形的指针,传递给函数的是指针的拷贝,对其进行修改并不会影响到指针参数本身,但是可以通过该指针执行间接访问来修改指向的值,const 关键字在此用于防止这种修改。这种 const 关键字的应用是非常广泛的。
  18. void function( int array[][2][5] );
    void function( int (*array)[2][5] );
  19. 在其末尾附加一个空串即可。
    for( kwp = keyword_table; **kwp != '\0'; kwp++ )
8.8 编程练习
  1. 虽然花括号确实需要锻炼,不过这也太硬核了吧…我寻思直接针对下标进行赋值它不香吗?要求静态初始化,而不使用赋值语句,意味着数组必须在静态内存中,还要记得下标从 0 开始,且数据范围大多在 0~255 之间,故采用 unsigned char 类型即可。 demo03.c
  2. 就是个数组值的一一对应的关系。完全可以 if-else 嵌套搞定,但是针对数据量大的时候显然是不合适的。循环是一种非常优雅的解决方案!见 demo04.c,注意初始化数组时,给首项挖个空留着。
  3. 判断 a[i][i] == 1 即可。见 demo05.c。然而答案给出了一个两重循环的写法…但我觉得针对当下问题而言是没必要的。
  4. 思路和上面一样,不重复写了,终究还是知道了矩阵大小。
  5. 模拟矩阵乘法可还行。见 demo06.c。矩阵相关运算是一个很好练习二维、多维数组的题目!矩阵快速幂、矩阵斐波拉切数列等等。
  6. 看不懂题,在这写作文呢?
  7. 同上。
  8. 经典的 dfs 问题啊。我很喜欢!
    请参考我的博文:[dfs] n 皇后(模板+经典)
随笔
无疑是重点中的重点,数组和指针的结合,C 语言接近汇编的效率,在本章终于领略到了!
  1. 数组名的值是一个指针常量,指向内存中数组的起始位置。程序经过编译、链接,链接完成后内存中数组的位置就是固定的,当程序运行时不可移动数组,即不可再修改数组名这个常量值。
  2. 数组名做 sizeof 操作符、左 & 的操作数时不以指针常量来表示。其中,sizeof 返回整个数组的长度,而不是指向数组的指针长度。取址符将产生一个指向数组的指针,而不是指向某个常量值的指针。
  3. 除优先级外,下标引用和间接访问完全相同。负值作为数组下标,从指针间接访问方面很好理解。2[array] = *(2 + (array))
  4. 8.1.4 节,层层优化一个简单的数组拷贝程序,让 C 语言的代码真正可以和汇编代码媲美!使用指针,优化掉计数器,使用寄存器…NP!
  5. 数组和指针。声明定长数组,编译器会为数组元素保留内存空间,然后创建数组,数组名为常量指针,指向这段空间的起始地址。而声明一个指针变量,编译器只为指针变量保存空间,不为其指向的任何值分配空间,且指针变量未被初始化指向任何的内存空间。P150 精华。
  6. 所有的参数都是通过传值方式传递的,数组的传值调用行为是传递给函数的是参数的一份拷贝,即指向数组起始位置的指针的拷贝,函数可以自由地操作其形参指针而不用担心修改对应的作为实参的指针。const 在函数参数中的灵活运用。
  7. 数组形参只是传递的指向数组第一个元素的指针,函数并不为数组参数分配空间。存储与静态内存的数组只初始化一次,默认初始化值为 0。局部变量由于在堆栈上,内存并不相同,在缺省情况下是未初始化的,若在声明中给出了初始值,其实是以一条隐式的赋值语句来初始化。
  8. 字符数组与字符串常量的区别。
  9. 多维数组和指针的联系。一维数组名是 指向元素类型的指针,多维数组以低维数组做元素,即 a[3][10],其数组名是一个指向一个包含 10 个整形元素的数组的指针。
  10. 下标引用的优先级高于间接访问。数组指针和指针数组的区别。指针指向数组,数组内元素是指针。
  11. 多维指针第一维元素大小不重要,编译器可以推导。但是绝不能全部省略第二维及以后各维的长度,若省略则编译器无法对各下标进行求值,第一维在下标求值中也用不到,所以可以省略。一个常见错误就是将二维数组和二级指针混为一谈,相互的错误传参。
  12. 指针数组。int *a[10],下标引用优先级高于间接访问,故在此先执行下标引用,故 a 是某类型的数组,取得数组元素后随即执行间接访问,那么这个元素就是个指针,最终的结果是一个整型值。故 a 中的元素类型就是指向整形的指针。这点经常在字符串指针数组中常用。
疑问
  1. 指针数组本章讲的非常少。另外,指针数组和数组指针的概念其并没有做系统性对比。
  2. 关于数组名是否分配空间问题,是不会。数组名 和 &数组名,都是同一个值,均为该数组的首地址。
  3. 关于 问题 8 那个 extern 天坑问题,将数组名声明为指针,该指针存储的是数组首元素的值,而不是数组的首地址。
  4. 关于矩阵乘法,这是个很重要的一点。并且代码采用了行主序+指针的方式实现,很是简单,且添加 register 优化是一个极大提升效率的做法!这个得参考答案,我并没有实现。
  5. 【读书笔记|[C和指针] ch08. 数组】针对编程 6、7 实在不想看这么多的文字…头大。

    推荐阅读