zoukankan      html  css  js  c++  java
  • 《C陷阱和缺陷》读书笔记-其二:语义陷阱

    第三章 语义陷阱

    1、指针和数组

    (1)对于数组:

    C语言中只有一维数组,且数组的大小必须在编译器间就作为一个常数确定下来,数组的元素可以是任何类型的对象;
    
    对数组的操作只有两个,确定数组的大小,以及获得指向该数组下标为0的元素的指针。
    
    

    (2)如果一个指针指向数组中的一个元素,对指针加一就可以获取指向数组中下一个元素的指针,对指针减一可以获取指向该数组中前一个元素的指针,加减n类似。针对指向数组元素的指针的加减,实际上是移动对应元素的指向。

    (3)数组名通常可以认为是数组中下标为0的元素的指针,例外情况是在sizeof(a)中,其结果是整个数组的大小,而不是单个元素的大小,因此,常用以下表达式求取数组长度sizeof(a) / sizeof(a[0])

    (4)数组名a可以表示数组下标为0的元素的指针,则数组下标为0的元素的值可以用*a来表示;又因为数组指针可以加减,则a + 1表示下标为1的元素的指针,a + n表示下标为n的元素的指针(n小于数组长度);进一步,数组下标为1的元素的值为*(a + 1),数组下标为n的元素的值为*(a + n),通常简写为a[n]。

    (5)对于二维数组,如int calendar[12][31];,calendar是一个有着12个数组类型元素的数组,每个数组类型元素又是一个有着31个整型元素的数组;可以理解为,每31个整数构成的一个数组,合并成一个大元素,12个这样的元素组合成一个新的数组,即二维数组:

    calendar[0]: 30个整数组成一个大元素
    calendar[1]: 30个整数组成一个大元素
    calendar[2]: 30个整数组成一个大元素
    calendar[3]: 30个整数组成一个大元素
    calendar[4]: 30个整数组成一个大元素
    calendar[5]: 30个整数组成一个大元素
    calendar[6]: 30个整数组成一个大元素
    calendar[7]: 30个整数组成一个大元素
    calendar[8]: 30个整数组成一个大元素
    calendar[9]: 30个整数组成一个大元素
    calendar[10]: 30个整数组成一个大元素
    calendar[11]: 30个整数组成一个大元素
    

    个人对二维数组的理解,可以扩展到多维:
    calendar[i]里面存放的是一个地址,该地址指向第i个大元素的首地址;
    这个12个地址组成一个一维数组,而calendar是这个一维数组的首地址,因此,calendar表示指向第0个大元素的地址,由于指针的可加性,*(calendar + i)表示指向第i个大元素的地址,简写为calendar[i];
    有了大元素的首地址,如果需要获取大元素中某个元素的值,可以用
    (*(calendar + i) + j)表示,其中i表示第i个大元素,j表示对应大元素中的第几个元素,简写为calendar[i][j]。

    (6)数组指针的应用:
    如上,calendar表示存放第一个大元素地址的地址,即指向大小为31的int数组的指针;
    定义如下变量:int (*monthp)[31];
    monthp是一个指针,该指针指向的是一个大小为31的int数组,与calendar意义相同,因此可以这样写:
    monthp = calendar; 或者 monthp = calendar[i];

    (7) 字符串

    字符串常量代表了一块包括字符串中所有字符以及一个空字符('')的内存区域的地址,所以字符串常量本质上是一个地址。

    字符串以空字符''作为结束标志,但是库函数strlen返回参数中字符串所包括的字符数目时并不计算这个空字符串;因此需要分配空间时,需要特别注意为末尾空字符分配一个字符空间。

    (8)数组作为入参
    将数组名作为参数传递时,C语言会自动将作为参数的数组声明转化为相应的指针声明,即指向该数组第一个元素的指针;
    int strlen(char s[])会被退化成int strlen(char *s)

    2、不对称边界

    (1)数组元素下标从0开始;

    (2)不对称边界通用原则:
    其一:首先考虑最简单情况下的特例,然后将得到的结果往外推;
    其二:仔细计算边界,绝对不能掉以轻心。

    (3)数组中实际不存在的“溢界”元素的地址位于数组所占内存之后,这个地址可以用于进行赋值和比较,但是不能引用。

    3、整数溢出

    如果算术运算符的一个操作数时有符号整数,另一个是无符号整数,那么有符号整数会被转换成无符号整数,不会发生“溢出”;如果两个操作数都是有符号整数,“溢出”就有可能发生;

    当发生整数溢出时,做出任何假设都是不安全的。

    4、其他

    (1)指针与指针所执行的数据有区别:指针是指向一个区域的起始地址,而对应的数据是该区域的内容,其内容长短指针无法确定,但是其内容的数据类型必须与指针的类型相匹配;且通过指针的加减访问的范围也仅局限于该区域,否则会发生不可预期的错误,即指针越界。

    (2)将常数0强制转换成指针,不等于任何有效的指针,即我们常用的空指针(#define NULL 0);
    此外,将其他整数转换为一个指针,得到的结果依赖于编译器的实现。

    (3)求值顺序:&& 和 || 首先对左侧操作数求值,只有在需要时才对右侧进行求值;三目运算符?: 根据判断条件,再确定求取哪一个表达式的值;逗号表达式,先对左侧操作数求值,然后该值丢弃,继续对右侧求值;

    赋值运算符不保证任何求值顺序。

    (4)建议给main函数添加返回值,0代表程序执行成功,非0表示程序执行失败。

  • 相关阅读:
    MongoDB repair on Ubuntu
    java后台图形相关代码,weblogic报错
    weblogic配置达梦数据源
    详解JavaScript中的this
    web app指南之构建html5离线应用
    android中的跨进程通信的实现(一)——远程调用过程和aidl
    android应用开发全程实录出版
    android窗口管理框架解析
    BizTalk调用SAP系统RFC含多个参数以及DateTime类型参数
    plsql连接64位oracle在windows 764下连接设置方法
  • 原文地址:https://www.cnblogs.com/HZL2017/p/14916662.html
Copyright © 2011-2022 走看看