《C和指针》随记

/* 前阵子花两周时间又把《C和指针》过了一遍,陆陆续续做了些笔记,现总结如下。。。*/

第一章 快速上手

     1. EOF是一个int型数值。

     2. 去除某段代码,将其注释掉不是一个好选择,更高效、安全的办法是使用 #if    #endf

#if 0
    statements
#endif

 

    3. 在C语言中,数组参数是以引用形式进行传递的,也就是传址调用,而标量和常量则是按值传递的。在函数中对标量的任何修改都会在函数返回时丢失;而当被调用函数修改数组参数的其中一个元素时,调用函数所传递的数组就会被实际地修改。

    4. 字符串常量“hello”在内存中占据6个字节,按顺序分别是h、e、l、l、o和NUL。

    5. 由于scanf函数的实现原理,所有标量参数的前面必须加“&”。不过,数组参数前面不用加“&”,但是,数组参数中如果出现了下标引用,也就是说实际参数是数组的某个特定元素,那么它的前面也必须加“&”符号。   

    6. 执行顺序是 s1>s2>s4>s3。

for(s1;s2;s3){
    s4;
}

第二章 基本概念

     1. 注意养成良好的编程习惯。

第三章 数据

    1. 当一个字符串常量出现在一个表达式当中时,表达式所使用的值就是这些字符所存储的地址,而不是这些字符本身。因此,可以把字符串常量赋值给一个“指向字符的指针”,后者指向这些字符所存储的地址。

    2. 如果内层代码块有一个标识符的名字与外层代码块的一个标识符同名,内层的那个标识符就将隐藏外层的标识符——外层的那个标识符无法再内层代码块中通过名字访问。

    3. 属于external链接属性的标识符不论声明多少次、位于几个源文件,都表示同一个实体。

    4. 关键字external和static用于在声明中修改标识符的链接属性。如果某个声明在正常情况下具有external链接属性,在它前面加上static关键字可以使它的链接属性变为internal。

    5. 在定义类型的新名字时,使用typedef而不是#define。

第四章  语句

   1. break和continue语句,任何一条如果出现于嵌套的循环内部,它只对最内层的循环起作用,。无法使用break或者continue语句影响外层循环的执行。

   2. 注意:在switch语句中,当没有break时,执行流意外的从一个case顺延到下一个case。

   3. 为了代码的健壮性,在switch语句中应当使用default语句。

第五章 操作符和表达式

   1. 算数左移和逻辑左移是相同的,即都在右边补0;它们只有在右移时不同,且只有当操作数是负数时才不同。

   2. 不要混用整形和布尔型值。

第六章 指针

   1. 不能简单地通过检查一个值的位来判断它的类型。为了判断值的类型(以及它的值),必须观察程序中这个值的使用方式。

   2. NULL指针不指向任何东西。要使一个指针变量为NULL,可以给它赋一个零值。

   3. 从定义上看,NULL指针并未指向任何东西,因此对一个NULL指针进行解引用操作是非法的。在对指针进行解引用操作前,首先必须确保它并非NULL指针。

   4. 指针变量可以作为左值,并不是因为他们是指针,而是因为他们是变量。

   5. * 操作符具有从右向左的结合性。 

   6. 如果将一个字符指针加1,运算结果产生的指针将指向内存中的下一个字符。

   7. 指针的加法运算没太大意思。而对于指针减法,只有当两个指针都指向同一个数组中的元素时,才允许一个指针减去另一个指针。同一个数组中两个减法的值是两个指针在内存中的距离(以数组元素的长度为单位,而不是以字节为单位),因为减法运算的结果将除以数组元素类型的长度。

   8. 任何指针之间都可以进行比较,测试它们之间相等或不相等。

   9. 声明一个指针变量并不会自动分配任何内存。在对指针进行间接访问前,指针必须进行初始化,或者使它指向现有的内存,或者给它分配动态内存。

第七章 函数

   1. 在涉及到使用原型函数时,最好使用头文件。

   2. 当程序调用一个无法见到原型的函数时,编译器认为该函数返回一个整型值。对于那些并不返回整形值的函数,这种认定可能会引起错误。所以,所有的函数都应该具有原型,尤其是安歇返回值不是整形的函数。

   3. 当一个数组作为参数传递给函数时,数组名的值实际上是一个指针,传递给函数的就是这个指针的一份拷贝。下标引用实际上是间接访问的另一种形式。

   4. 使用前缀自增或者自减可以避免越出数组边界。

   5. 通过对static关键字的合理使用可以限制对模块的访问(多用于黑盒设计)。

   6. C通过运行时堆栈支持递归函数的实现。

第八章 数组

   1. 数组名是一个指针常量,也就是数组第一个元素的地址。它的类型取决于数组元素的类型:如果它们是int类型,那么数组名的类型就是“指向int的常量指针”;如果它们是其他类型,那么数组名的类型就是“指向其他类型的常量指针”。

   2. 数组名不作为指针常量表示的情况有两种——就是当数组名作为sizeof操作符或者单目操作符&的操作数时。sizeof返回整个数组的长度(字节数)。对数组名取地址所产生的是一个指向数组的指针,而不是一个指向某个指针常量的指针。

   3. 关于数组的指针与下标,通常来说,下标绝不会比指针更有效率,但指针有时会比下标更有效率。(P144有一个简要的对二者的说明)。不过,这两个技巧要合理使用,不要为了效率上的细微差别而牺牲了可读性。

   4. 关于寄存器指针变量:我们可以对指针使用寄存器变量,这样就不必复制指针值。但是,它们必须被声明为局部变量。

       寄存器存在于CPU内部,运算速度非常快,因为内存中的数据必须载入寄存器才能计算。如果直接定义一个变量为寄存器变量,则少了载入等过程。对于频繁使用的变量可以把它放在寄存器中提速。

   5. 声明一个数组时,编译器将根据声明所指定的元素数量为数组保留内存空间,然后再创建数组名,它的值是一个常量,指向这段空间的起始位置。声明一个指针变量时,编译器只为指针保留内存空间,它并不为任何整型值分配内存空间。

   6. 多维数组的存储顺序是根据最右边的下标率先变化的原则确定的。同时,在多维数组中,只有第一维才能根据初始化列表缺省地提供,剩余的几个维必须显式地写出。

   7. 下标引用优先级高于间接访问、

第九章 字符串、字符和字节

   1. NUL字节是字符串的终止符,但它本身并不是字符串的一部分,所以字符串的长度并不包括NUL字节。

   2. 表达式中不要混用有符号数和无符号数。

   3. 避免讲一个长字符串复制到短数组中从而导致溢出。

第十章 结构和联合

   1. 声明结构是有两种比较好的方式,分别是结构标签和typedef。

   2. 结构不能包含类型也是这个结构的成员,但它的成员可以是一个指向这个结构的指针。如果你觉得一个结构内部包含一个指向该结构本身的指针有些奇怪,请记住它事实上所指向的是同一种类型的不同结构。更加高级的数据结构,如链表和树,都是用这种技巧实现的。每个结构指向链表的下一个元素或者树的下一个分支。

   3. 在确定结构中某个元素位置时要考虑边界对齐。sizeof返回值包含了结构中因边界对齐而浪费的内存空间。

   4. 相比将整个结构当做参数传入函数,把指向结构的指针传入函数显然是更高效的办法,因为指针比整个结构要小得多。传递指针要付出的代价主要是我们必须在函数中使用间接访问来访问结构成员。结构越大,把指向它的指针传递给函数的效率就越高。

   5. 一个联合的所有成员都存储于同一个内存位置。通过访问不同类型的联合成员,内存中相同的位组合可以被解释为不同的东西。

第十一章 动态内存分配

   1. malloc的参数就是需要分配的内存字节数。如果内存池中的可用内存可以满足这个需求,malloc函数就返回一个指向被分配的内存块起始位置的指针。

   2. 动态内存分配最常见的错误就是忘记检查所请求的内存是否成功分配。第二大错误是操作内存时超出了分配内存的边界。

   3. 释放一块儿内存的一部分是不允许的。动态分布的内存必须整块儿一起释放。但是,realloc函数可以缩小一块动态分布的内存,有效地释放它尾部的部分内存。

   4. 分配内存并使用完之后要注意释放内存,不然会引起内存泄漏。

   5. 如果一个指针不是从早先的malloc、calloc、realloc函数返回的,它是不能作为参数传递给free函数的。

第十二章 使用结构和指针

   1. 单链表、双链表的插入和删除操作。

第十三章  高级指针话题

   1. 函数调用操作符 () 优先级高于间接访问操作符。

   2. 函数只能返回标量值,不能返回数组。

   3. 字符串和数组无法作为参数传递给数组,但指向它们的指针却可以。

   4. 当一个字符串常量出现于表达式当中时,它的值是一个指针常量,是指向字符串第一个字符的指针。和数组名一样,你既可以使用指针表达式也可以用下标来使用字符串常量。

      所以表达式 “xyz”+1  的结果是一个指针,指向字符串中的第二个字符:y。

   5. 如果某个执行环节实现了命令行参数,这些参数是通过两个形参传递给main函数的。这两个形参分别是argc和argv。argc是一个整数,用于表示参数的数量。argv是一个指针,它是指向一个序列的字符型指针的指针。该序列中的每个指针指向一个命令行参数,其中第一个参数是程序的名字。该序列以一个NULL指针作为结束标志。程序可以通过对argv使用间接访问操作来访问命令行参数。

第十四章 预处理器

   1. 在用#define指令插入大段代码时,如果这部分代码出现在程序的几个地方,那么更好的方法显然是把它实现为一个函数。

   2. 宏与函数:

宏非常频繁的用于执行简单的计算,比如

#define MAX(a,b)   ((a)>(b)? (a):(b))

在类似这种小型计算中,若使用函数进行调用和返回占用的计算资源可能更多,所以这种情况下使用宏比使用函数在程序的规模和速度方面都更胜一筹。但是,更为重要的是,函数的参数必须声明为一种特定的类型,所以它只能在类型合适的表达式上使用。反之,宏可以用于整型、长整型、单浮点型、双浮点型以及其他任何可以用>操作符比较值大小的类型。换句话说,宏与类型无关

   3. 当头文件被包含时,位于头文件内的所有内容都要被编译。这个事实意味着每个头文件只应该包含一组函数或数据的声明。和把一个程序需要的所有声明都放入一个巨大的头文件相比,使用几个头文件,每个头文件包含用于某个特定函数或模块的声明的做法更好一些。

   4. 不要在一个宏定义的末尾加上分号,使其成为一条完整的语句。

   5.整个宏定义的两边要加上括号,另外宏定义中每个参数也要加上括号。

 

 

/*结束*/

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值