<你必须知道的495个C语言问题>学习笔记 (一)

前言:这本书是我在看完 <C Primer Plus >后打算找本书练习一下,结果发现,这书里面很多问题,真的很坑. 我至今没遇到,甚至不会有那种想法. 好吧,不说其它,把笔记弄上来,一些让我无名郁闷的就留给后人补充吧. 我这文章建议可以一览而过


基本问题(很多问题在<C prime plus>有提及)

1 声明和初始化

   1.1  根据需要选择整数的类型,不要把期望给予编译器.一般 int;数值大 :long ;注重空间:short; 避免符号扩展:unsigned 

   1.2   C99 定义了long long来保证长度至少有64位

   1.3  关于定义和声明 全局变量和函数  :可以有多处"声明",但只能只有一个"定义".最好:在某个相关的.c文件里面定义,然后在头文件.h中进行外部声明,

 在需要使用的时候,只要包含该.h即可

   1.4  extern  :作为一种格式上的提示,表明函数上的定义可能在另一个源文件.  extern int F() 和 int F() 本质上没有什么区别

    1.6   C里面的struct 如何定义一个包含指向自己的指针

 

struct node {
         char*item ;
         structnode *next;
};
typedef struct node *NODEPTR

   1.7怎样建立和理解非常复杂的声明?

关于从内到外” 解释和理解 C 语言声明

        方法1:直接暴力法 ,用typedef 逐步完成声明;

//用 typedef 逐步完成声明:
typedef char *pc;/* 字符指针 */
typedef pc fpc();/* 返回字符指针的函数 */
typedef fpc *pfpc;/* 上面函数的指针 */
typedef pfpcfpfpc(); /* 返回函数指针的函数 */
typedef fpfpc*pfpfpc; /* 上面函数的指针 */
pfpfpc a[N]; /* 上面指针的数组 */

 2:用cdecl程序,它可以把英文转为C或者把C转为英文

        举例: 定义一个包含 N 个指向返回指向字符的指针的函数的指针的数组

         

char*(*(*a[])())()

cdecl> declare a as array ofpointer to function returningpointer tofunction returning pointer to char
通过类型转换, cdecl 也可以用于解释复杂的声明, 指出参数应该进入哪一对括号 (如同在上述的复杂函数定义中)

   


 

   1.8函数调用前必须先声明

    3 表达式

      3.1 为什么这样的代码: a[i] = i++; 不能工作? i++ 产生副作用,但又在其他地方被引用,a[i]里面的i 不能确定为是新值还是旧值.C标准将其视为无定义

      3.2 i++ *i++的问题 跟上面一样,结果取决于编译器,包含多个不确定的副作用的代码的行为总是被认为未定义

      3.3 i = i++ ;(同上)

      3.5运算符优先级和括弧只能赋予表达是计算部分的顺序

        例如: f() + g() *h(),乘法运算在加法之前, 但这并不能说明这三个函数哪个会被首先调用。如果你需要确保子表达式的计算顺序,你可能需要使用明确的临时变量和独立的语句。

      3.6  while((c = getchar())

!= EOF &&c != ’\n’)

      3.7 序列点:序列点是一个时间点(在整个表达式全部计算完毕之后或在 ||、 &&、 ? : 或逗号运算符处, 或在函数调用之前), 此刻尘埃落定, 所有的副作用都已确保结束 在上一个            和下一个序列点之间, 一个对象所保存的值至多只能被表达式的计算修改一次。而且前一个值只能用于决定将要保存的值,第二句确保在修改之前才访问变量的表达式为合法

      3.8 " a[i] = i++; 我们不知道 a[] 的哪一个分量会被改写,但 i的确会增加 1"这个说法是错的(无定义状态是针对整个式子的)

      3.12 ? : 操作符, 跟多数操作符一样, 生成一个值, 而不能被赋值

    4 指针

      4.1 操作指针P本身和操作指针指向的内存的区别

        对指针本身赋值,使之指向别处 : P= malloc(10)

        使用* : *P = 'H'

      4.2"++"或者"--"操作符的优先级比"*"高,所以*P++相当于*(P++) ;自增 p 并返回 p 自增之前所指向的值

      4.4 在 C 中, 参数是通过值传递的。被调函数仅仅修改了传入的指针副本。你需要传入

指针的地址 (函数变成接受指针的指针), 或者让函数返回指针。

      4.6 int f(int *)引入一个常数

        先定义一个临时变量,然后把它的地址传给函数

        使用“复合常量: f((int[]){5});


 5空指针

    5.1空指针 表示 “未分配” 或者 “尚未指向任何地方”的指针

          取地址操作符 & 永远也不能得到空指针

          与任何对象或函数的指针值都不相等

        对 malloc() 的成功调用也不会返回空指针, 如果失败, malloc() 的确返回空指针

          空指针可以确保不指向任何对象或函数; 而未初始化指针则可能指向任何地方

          每种指针类型都有一个空指针, 而不同类型的空指针的内部表示可能不尽相同,编译器必须时刻明确需要那种空指针, 以便在需要的时候加以区分

5.2获得空指针的做法

    指针上下文中的常数 0 会在编译时转换为空指针

    在初始化、赋值或比较的时候, 如果一边是指针类型的值或表达式, 编译器可以确定另一边的常数 0 为空指针并生成正确的空指针值

              if(p) 等价于 if(p != 0)。

而这是一个比较上下文, 因此编译器可以看出 0 实际上是一个空指针常数

 5.5  NULL 在机器的定义定义为0

          在指针上下文中 NULL 和 0 是完全等价的, 而未加修饰

的 0 也完全可以接受。任何使用 NULL (跟 0 相对) 的地方都应该看作一种温和的提示, 是在使用指针; 程序员 (和编译器都) 不能依靠它来区别指针 0 和整数 0

5.12遵循的规则

    1. 当你在源码中需要空指针常数时, 用“0”或 “NULL”。

2. 如果在函数调用中 “0” 或 “NULL”用作参数, 把它转换成被调函数需要的指针类型

6 数组和指针

     6.3  C语言中“指针和数组等价:意思是操作等价,定义完全不是同一回事

     6.4作为函数形参的数组和指针申明可以互换,这是一种便利做法,传入的永远是复制的值,而不是实际值

     6.5指针是可变的左值,但数组不是.所以*P++是合法的,但a++就不行

    6.6 数组和指针的区别

       数组自动分配空间, 但是不能重分配或改变大小。指针必须明确赋值以指向分配的空间 (可能使用 malloc), 但是可以随意重新赋值 (即, 指向不同的对象), 同时除了表示一个内存块的基址之外, 还有许多其它的用途。由于数组和指针所谓的等价性(参见问题 6.3), 数组和指针经常看起来可以互换, 而事实上指向 malloc 分配的内存块的指针通常被看作一个真正的数组(也可以用 [ ] 引用)。

    6.9 数组引用会退化为指针,但arr 和&arr 还是有区别的,区别在于类型.

    8 字符和字符串

8.1 strcat()用于连接字符串;比较两个字符串, 一般使用库函数 strcmp():

8.2  C中的字符串用字符的数组表示,而C语言从来不会把数组作为一个整体操作 (赋值, 比较等)

8.3  定义完数组后若不初始化,之后是不能整体赋值的,但可以用可以使用strcpy()代替:

8.4  数字以及它们对应的字符集的相互转化:相互转换时,加上或减去常数’0’,

8.5  C语言中的字符常数是int型,因此sizeof(’a’) 是 sizeof(int),这是另一个与 C++ 不同的地方

9 布尔表达式和变量

      C语言里面没有布尔类型,(用int 更快,用char 节省空间)

        C 语言中的确任何非零值都都被看作真, 但这仅限于“输入”, 也就是说, 仅限于需要布尔值的地方,所以,把TURE定义为1是可以的,

不用担心某个内置的函数或关系操作符 “返回” 不是 1而是2345...

      预处理宏 TRUE 和 FALSE (当然还有 NULL) 只是用于增加代码可读性, 而不是因为其值可能改变

14 浮点运算

      精度引起的问题(二进制存储,转为十进制的损失)

        例如 float 变量赋值为 3.1,printf输出的值可能为 3.0999999

        下溢、误差的累积和其它非常规性是常遇到的麻烦

      double下两个数的比较要注意!

        if (a == b) /* 错! */

        if (fabs(a - b) <= epsilon *fabs(a))

       epsilon 被赋为一个选定的值来控制 “接近度”。你也要确定a不会为0.利用一个精确的阈值。这个阈值和作比较的浮点数值大小有关

      14.6 怎么取整数

        对于整数 (int)(x + 0.5) ;将负数考虑进去,我们可以这样: int)(x < 0 ? x- 0.5 : x + 0.5)

      14.7 幂运算

        可以用pow(),但是不建议.提供幂运算的处理器很少; 一般用乘法更有效

          (ps:乘除尽量考虑2,这样可以用移位来更高效解决问题)

      14.8 PI 的定义不再标准,要使用的话,可以自定义或者用4*atan(1.0) 或 acos(-1.0) 计算出来

    10 C预处理器

      10.4  习惯放在 .h文件里的 :

        1宏定义 (预处理 #defines) 2 结构、联合和枚举声明;3 typedef 声明;4  外部函数声明;5 全局变量声明

      10.6   #include <> 和 #include "" 有什么区别

         <> 语法通常用于标准或系统提供的头文件, 而 "" 通常用于程序自己的头文件。

    11 ANSI/ISO 标准C

      11.24   不能对void*指针进行运算,因为编译器不知道所指对象的大小,这样是没法移动指针的

      11.26    ANSI/ISO 标准声称malloc(0) 可能返回一个空指针或者指向 0 字节的指针; 其行为由实现定义。

      11.35   面对未定义行为的时候,包括范围内的实现定义行为和未确定行为,

编译器可以做任何实现,其中也包括你所有期望的结果。但是依靠这个实现却不明智

    12 标准输入输出库

      12.2   在键盘输入EOF  : ctrl +D 或者ctrl+Z

      12.6   printf 的格式串中输出一个 ’%' (使用'%%')

      12.15   scanf格式串中\n不表示等待换行符,而是读取并放弃所有的空白字符(C Primer 多次提到)



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值