1.数组名作实參

    在C语言中,我们没有办法将一个数组作为函数參数传递,假设我们使用数组名作为參数。这个时候数组名立马会被转换为指向该数组的第一个元素的指针。

    关于这一点的理解能够向前深入一步,比方定义的数组为int a[3],那么a作为參数传递之后会变为int *类型;假设定义的数组为int a[3][4],那么a作为參数传递之后被变为int (*)[4]。假设定义的数组为int a[3][4][5]。那么a作为參数传递之后会变为int (*)[4][5];兴许的以此类推。为什么能够这样呢?由于C语言中的多维数组都是利用一维数组仿真出来的,即一维数组的每个元素都能够是别的类型的数据单元。即便这个数据单元又是还有一个数组,然而依据上面的观点,一维数组a在被作为參数传递的时候会自己主动退化为指向该一维数组的第一个单元的指针,所以假设第一个单元是一个一维数组,那么a就退化为一个一维数组指针,假设a的第一个单元是一个二维数组。那么a就退化为一个二维数组指针,所以上面的结论是不难得出的。



2.看以下的代码片段输出会是多少?

void print(int b[])

{

    printf("%d", sizeof(b));

}

int main(void)

{

    int a[4];

    print(a);

    return 0;

}

分析:要弄清楚这段代码片段的输出。还是要清楚函数调用时候数组的传递过程。上面第一点已经说过了。在传递參数的时候数组已经自己主动被退化为指向其第一个单元的指针,所以在函数传递的过程中相当于出现了这种一个赋值的过程,int b[] = a或者更清楚一些int b[] = &a[0],可是这种语句编译器会觉得是一个错误的语法!可是实际中我们常常可能会这样来使用却并没有报错,这是由于编译器在这里会将b强制做一次退化,退化为一个int *的指针类型。所以上面的程序片段输出内容显而易见,输出的就是一个int类型的指针变量的大小,也就是4(32位系统)。



3.main函数參数的两种形式

int main(int argc, char *argv[])

int main(int argc, char **argv)

须要注意的是,前一种写法强调的重点在于argv是一个指向某数组的起始元素的指针,该数组的元素为字符指针类型。



4.改动字符串常量

下面的这样的写法:

    char *p = "xyz";

    p[0] = 'A';

编译的器件可能不会产生问题,可是执行的时候非常可能会碰到类似于某地址不能为written这样的提示。K&RC中对这样的改动行为的说明是:试图改动字符串常量的行为是没有定义的。

尽管有些编译器同意这样的行为。可是这样的写法是不值得提倡的。



5.空指针

    除了一个重要的例外情况,在C语言中将一个整数转换为一个指针,最后得到的结果都取决于详细的C编译器实现。

这个特殊情况就是常数0。编译器保证由0转换而来的指针不等于不论什么有效的指针,出于代码文档化的考虑,常数0这个值经经常使用一个符号来取代:

#define NULL 0

须要记住的是当常数0被转换为指针使用时,这个指针绝对不能被解除引用(解除引用即是使用(*p)这类取该地址中内容的操作),换句话说。当我们将0赋值为一个指针变量时,绝对不能企图使用该指针所指向的内存中所存储的内容。



6.C语言中“不正确称边界”的优点

    在C语言中定义了一个数组int a[10]之后,数组的下标0~9为合法的下标,而下标10已经超出了数组的范围。这样做的优点是什么呢?

第一个优点,请看以下的一个样例:

for(i = 0; i < 10; i++)

    a[i] = *p++;

假设用户给出了begin(0)和end(10)的范围之后要求对这之间的单元进行操作,假设用户给定的begin和end是同样的话,上面这样的写法全然能够避免出现错误。同一时候要操作的单元个数能够通过end-begin简单的就算出来,这样做的前提就是用户给出的begin和end都是遵守C语言的“不正确称边界”用法。而假设不使用不正确称边界时候(这时候数组的下标为1~10合法)的诸如代码:

for(i = 1; i <= 10; i++)

    a[i] = *p++;

才干够完毕对数组的初始化或者遍历等操作,这样写之后。实际操作的单元个数为10-1+1=10个,这种计算过程假设程序猿在编程的时候忘了加上一个1那么非常easy造成程序的bug。同一时候假设将1和10换成begin和end变量的话,那么用户在调用这个函数的时候传递的begin和end值就算是同一个值,这段代码也会操作到数组中的a[begin]值,这个也会造成调用者使用的困难。

第二个优点是我们能够将&a[10]来作为一个推断条件。作为缓冲区或者数组操作完毕的一个标志,这在实际编程中也是相当方便的。尽管对a[10]的值进行操作是属于非法的行为,可是在ANSI中明白规定了&a[10]这样的操作是合法的。

7.--n >= 0和n-- > 0

    在大多数的C语言实现中,--n >= 0至少与等效的n-- > 0一样快,甚至在某些C实现中还要更快,第一个表达式--n >= 0的计算是首先从n中减去1,然后将结果与0比較。第二个表达式的计算则首先保存n,然后从n中减去1,最后比較保存值与0的大小。



8.求值顺序

    C语言中仅仅有四个运算符(&&、||、?:、,)存在规定的求值顺序,运算符&&和运算符||首先对左側的操作数求值,仅仅有在须要的时候才会对右側的操作数求值。运算符?:有三个操作数。在a?b:c中,操作数a首先被求值,依据a的值在求操作数b或者c的值(b和c仅仅有一个表达式会被计算)。

而逗号运算符。首先对左側的操作数求值,然后该值被“丢弃”,在对右側操作数求值,整个表达式的值是最右側表达式的值。

逗号运算符举例:a = (1, 2, 3);

a最后被赋值为3。

注意:分隔函数參数的逗号并不是逗号运算符。比如:f(x, y)中的求值顺序是没有定义的,而在函数g((x,y))中却是确定的先x后y的顺序,在后一个样例中,函数g仅仅有一个參数。这个參数的值就是括号里逗号运算符的值。

注意:在C语言中其它全部运算符对其操作数求值的顺序是没有定义的。特别地,赋值运算符并不保证不论什么求值顺序。假设在一个表达式中出现对同一变量的多次使用中出现了++或者--等操作后果有时是不可估计的。比如:

y[i] = x[i++];



9.逻辑运算的结果

    逻辑运算符的结果是一个逻辑值,即真(1)或假(0)。而逻辑推断的时候通常约定将0视作假。非0视作真。所以!10表达式的值为假(0),由于10非0在进行非运算的时候被视作真,真的非即为假。

10.溢出

    C语言中存在两类整数算术运算,有符号数与无符号数运算。

无符号数运算中没有溢出的说法,然而有符号数操作就可能会发生溢出的情况,当一个运算的结果发生“溢出”时。作出不论什么如果都是不安全的。当碰到可能溢出的情况应该採取的方法是将两个操作数a和b都强制转换为无符号整数:

if((unsigned)a + (unsigned)b > INT_MAX)

    complain();

此处的INT_MAX是一个已定义常量,代表可能的最大整数值。

ANSI C标准在<limits.h>中定义了INT_MAX;假设在其他的C语言实现上,读者可能须要自己又一次定义这个值。

《C陷阱与缺陷》整理二的更多相关文章

  1. 读书笔记--C陷阱与缺陷(二)

    第二章 1. 理解函数声明 书中分析了复杂的类型声明方式,也说明了使用typedef声明会更好理解,推荐大家使用typedef进行函数声明. 书中类型分析一层一层挖掘,让读者可以理解多层嵌套的类型含义 ...

  2. C陷阱和缺陷学习笔记

    这段时间把<C陷阱和缺陷>看了,没时间自己写总结.就转一下别人的学习笔记吧http://bbs.chinaunix.net/thread-749888-1-1.html Chapter 1 ...

  3. 阅读《C陷阱与缺陷》的知识增量

    版权声明:本文为Focustc原创文章.转载请注明作者及出处. https://blog.csdn.net/caozhankui/article/details/35925939 看完<C陷阱与 ...

  4. 读书笔记--C陷阱与缺陷(一)

    要参与C语言项目,于是作者只好重拾C语言(之前都是C++,还是C++方便). 看到大家都推荐看看  C陷阱与缺陷(C traps and pitfalls),于是好奇的开始了这本书的读书之旅. 决定将 ...

  5. 单元测试系列之十:Sonar 常用代码规则整理(二)

    摘要:帮助公司部署了一套sonar平台,经过一段时间运行,发现有一些问题出现频率很高,因此有必要将这些问题进行整理总结和分析,避免再次出现类似问题. 作者原创技术文章,转载请注明出处 ======== ...

  6. 读书笔记--C陷阱与缺陷(七)

    第七章 1.null指针并不指向任何对象,所以只用于赋值和比较运算,其他使用目的都是非法的. 误用null指针的后果是未定义的,根据编译器各异. 有的编译器对内存位置0只读,有的可读写. 书中给出了一 ...

  7. [C陷阱和缺陷] 第1章 词法“陷阱”

    有感自己的C语言在有些地方存在误区,所以重新仔细把"C陷阱和缺陷"翻出来看看,并写下这篇博客,用于读书总结以及日后方便自身复习. 第1章 词法"陷阱" 1.1 ...

  8. C语言学习书籍推荐《C陷阱与缺陷》下载

    下载地址:点我 凯尼格 (作者), 高巍 (译者) <C和C++经典著作:C陷阱与缺陷>适合有一定经验的C程序员阅读学习,即便你是C编程高手,<C和C++经典著作:C陷阱与缺陷> ...

  9. C陷阱与缺陷(二)

    第二章 语法陷阱 2.1 理解函数声明 (*(void(*)())0)();任何C变量的声明都由两部分组成:类型以及一组类似表达式的声明符.一旦我们知道了如何声明一个给定类型的变量,那么该类型的类型转 ...

随机推荐

  1. Xamarin.Android开发实践(四)

    原文:Xamarin.Android开发实践(四) Xamarin.Android下获取与解析JSON 一.新建项目 1.新建一个Android项目,并命名为为NetJsonList 2.右击引用,选 ...

  2. JRebel 6 破解版及使用方法

    最近更新到jrebel6.2.1了,我自己做了个技术分享的微信公众号(茶爸爸),有心的朋友可以来这里一起学习 云盘下载链接: http://pan.baidu.com/s/1bnGzMUF 配置: - ...

  3. 【转】2014区域赛小结(牡丹江&&鞍山)by kuangbin

    Posted on 2014年10月20日 by kuangbin 最后的两场区域赛结束了! ICPC生涯的最后两场区域赛,选择了前两个赛区——牡丹江和鞍山,主要是时间比较靠前,而且我向来对东北赛区有 ...

  4. DZNEmptyDataSet框架简介

    给大家推荐一个设置页面加载失败时显示加载失败等的框架. 下载地址:DZNEmptyDataSet https://github.com/dzenbot/DZNEmptyDataSet 上效果首先在你的 ...

  5. 【HTML相关】iframe+javascript实现一个表单提交后多个处理文件按序处理

    最近在弄一个网页的问题,总结如下. [问题描述] 页面中包括以下几个部分:1)表单form,供用户输入图片文件:2)iframe1,显示a.php文件的内容,a.php接收客户端图片并保存,后台程序处 ...

  6. javascript 学习随笔6

    改变html内容 document.getElementById("p1").innerHTML="New text!"; var element=docume ...

  7. ECSHOP常用函数

    lib_time.php gmtime() #获得当前格林威治时间的时间戳 /$0 server_timezone() #获得服务器的时区 /$0 local_mktime($hour = NULL ...

  8. Week7(10月24日)

    Part I:提问  =========================== 1.数据验证属性的练习. 按要求写出教室和课程的模型类. (1)教室类主键不自动增值,手工输入. (2)教室名字不超过10 ...

  9. iOS开发之计算动态cell的高度并缓存

    项目中有个类似微博那样的动态cell,文字和图片的多少都不是确定的 刚开始使用autolayout,结果很多问题,最后我发现了一个框架 FDTemplateLayoutCell 写的很好,自动布局ce ...

  10. 学习MongoDB 二:MongoDB加入、删除、改动

    一.简单介绍 MongoDB是一个高性能.开源.无模式的文档型数据库,是当前NoSQL数据库产品中最热门的一种.数据被分组存储在数据集中,被称为一个集合(Collenction)和对于存储在Mongo ...