C缺陷与陷阱点滴(二)

 

*将程序分解成符号的过程,称为词法分析,每一个符号应该包含尽可能多的字符(贪心法)

*‘ ’空格符ASCII码值32,除了字符串和字符常量,符号的中间不能嵌有空白,老版C中允许用=+代替+=

*用单引号引起的一个字符实际上代表一个整数,整数值对应于该字符在编译器采用的字符集中的序列值,用双引号引起的字符串,代表的却是一个指向无名数组的起始字符的指针,该数组被双引号之间的字符以及一个额外的二进制值为0的字符'/0'初始化

* 'yes' 在vc6.0中最后得到的整数值即最后一个字符的整数值,在BORLAND C++中为y的整数值

* float (*h)()则h是一个函数指针,h所指向函数的返回值为浮点类型

*任何一个逻辑运算符的优先级低于任何一个关系运算符,移动运算符优先级比算术运算符低,比关系运算符高,按位异或运算符(^)的优先级介于按位与运算符与按位或运算符之间

*当一个声明的结尾紧跟一个函数的定义时,如果声明结尾的分号被省略,编译器可能会把声明的类型视作函数的返回类型

*f()是一个函数调用语句,而f;计算函数f的地址,却不调用该函数

*C中只有一维数组,而且数组的大小必须在编译期间就作为一个常数确定下来

*int a[3];  &a 是一个指向数组的指针,int *p = &a 在ANSI C中非法

*任何指针都是想象某种类型的变量

*给一个指针加上一个整数,与给该指针的二进制表示加上同样的整数,两者的含义截然不同

* *(a + 1)是数组a中下表为1的元素的引用,即a[i],由于a + i 与i+a的含义一样,因此a[i]与i[a]也具有同样的含义

* i = calendar[4][7]  

   i = *(calendar[4] + 7)

   i = *(*(calendar + 4) + 7)

   int calendar[12][13];   int *p;   int i;

   p = calender非法,因calendar是一个二维数组,再次上下文中使用calendar名称会将其转换为一个指向数组的指针,而p是一个指向整型变量的指针

  int (*ap)[31];声明了*ap是一个拥有31个整形元素的数组,因此ap就是一个指向这样的数组的指针

*拼接字符串s和t

char *t;   strcpy(r,s);  strcat(r,t);错误,因为不能确定r指向何处

 char r[100];  strcpy(r,s);  strcat(r,t);  只要r和s执行的字符串不太大, 就正确

C语言会自动的将作为参数的数组声明转换为相应的指针声明

 int strlen(char s[]){}等价于int strlen(char *s){}

extern char *hello 与extern char hello[]完全不同

*main (int argc, char *argv[]){}等价于main(int argc,char **argv){}前一种写法强调的重点在于argv是一个指向某数组的起始元素的指针,该数组的元素为字符指针类型

* char *p,*q; 

   p = "xyz";

   p的值是一个指向由‘x',’y',‘z','/0'4个字符组成的数组的起始元素的指针

*赋值指针并不同时复制指针所指向的数据

*由0转换来的指针不等于任何有效地指针,当我们将0赋值给一个指针变量时,绝对不能企图使用该指针所指向的内存中存储的内容

*变量是对程序中存储空间的抽象

*指针变量定义:存储类型   数据类型   *指针名

  存储类型指指针本身的存储类型,数据类型指指针的目标变量的数据类型

*不能用auto类型变量的地址初始化static型指针,如int i; static int *p = &i;错误

*void *类型指针表示不指定p是指向那一种类型数据的指针变量,使用时要进行强制类型转换

*程序中不能返回形参或局部变量的地址

*scanf("%d,%d,%d",&a,&b,&c);与scanf("%d,%d,%d/n",&a,&b,&c);运行结果不同

*函数在编译时被分配的入口地址(程序段的存储地址)称为函数指针,用函数名表示

*指向函数的指针变量:数据类型  (*指针变量名)();

                                数据类型是函数返回值的类型

*再给一个指向函数的指针变量赋值时,不用带参数,如p = max而不能p = max(x,y)

*p = a + i;是p指向二维数组的第i行

  二维数组形参实际上是一位数组指针变量int a[][10]  int (*x)[10]

  二维数组存储空间固定,字符指针数组相当于可变列长的二维数组

*调出调试界面:F5,设置断点F9,F10把函数调用当成一个语句,而F11一步步执行

*int *p[3];指针数组

  int (*p)[3];指向一位数组的指针

  int *p(int);返回整型值真的函数

  int (*p)(int)指向函数的指针,函数返回int型变量

  int *(*p)(int);指向函数的指针,函数返回int型指针

  int (*p[3])(int);函数指针数组,函数返回int型变量

  int *(*p[3])(int);函数指针数组,返回int型指针

*若有struct student *p = &stu_1;

  (*p).n等价于p->n;

  p->n++ 等价于(p->n)++;

  ++p->n 等价于++(p->n)

*判断二进制文件是否结束:int feof(FILE *fp)

*%d整数形式转换输出

  %ld长整数形式转换输出

  %f带小数点形式转换输出(默认精确到小数点后6为,四舍五入)

  %10d会有5个空格

  %7.2f  7表示所有数字及小数点多占的位数,不够7位右对齐,2表示精确到小数点后2位

* /0产生一个空字符

*scanf中转换字符串:%d,%ld,%f,%lf

*float tax_rate; 在内存中分配一块32位存储空间,存储空间使用名称tax_rate标识

*scanf提供变量的地址

*fopen若不能打开指定的文件,则返回一个空指针NULL,其值为头文件stdio.h中被定义为0,正常退出时“程序状态值”为零,非零表示程序出错退出

*对二进制文件进行读写操作时,必须使用feof()函数来判断是否遇到文件尾,即:

   !feof(input),其中input为指向源文件的指针

 

* if ( p == (char *) 0)正确

   if ( strcmp(p ,(char *) 0 ) == 0)错误,因为strcmp函数的实现中包括查看他的指针参数所指向内存中的内容操作

*避免栏杆错误:用第一个入界点和第一个出界点表示一个数值范围

*另一种考虑不对称边界的方式:把上界视为某序列中第一个被占用的元素,把下界视为序列中第一个被释放的元素

* --n >= 0与n-- >= 0执行速度一样快,甚至更快。--n >= 0的大小先从n中减去1,然后将结果与0比较,第二个表达式首先保存n,从n中减去1,然后比较保存至与0的大小

*数组中实际不存在的“溢界”元素的地址位于数组所占内存之后,这个地址可以用于赋值和比较,若要引用该元素,那就是非法的啦

*C语言中只有四个运算符(&&,||,?:,,)存在规定的求值顺序,其他未定义。特别,赋值运算符并不保证任何求值顺序

*f(x,y)中的求值顺序是未定义的,g((x,y))的却是确定的先求x再求y,函数g只有一个参数,这个参数的值是这样求得的:先求x然后将x丢弃,再求y

*只有两个操作数都是有符号整数时,“溢出”才可能发生,为避免溢出,可将操作数都强制转化为无符号整数(unsigned)或者if( a > INT_MAX - b)   complain();

*一个返回值为整数的函数如果返回失败,实际上是隐含的返回了某个“垃圾”整数,大多数C语言的实现都通过函数main的返回值来告知操作系统该函数的执行是成功还是失败,0代表成功,非0代表失败。

*某些C语言的实现提供了一个称为lint的程序,可以捕获连接器解决不了的错误

*若干个源程序可以再不同的时候单独进行编译,然后由连接器整合到一起,连接器并不理解C语言,然而它却能够理解机器语言和内存布局,连接器通常是把目标模块看成是由一组外部对象组成,每个外部对象代表着机器内存中的某个部分,并通过一个外部名称来标识,因此程序中的每个函数和每个外部变量,如果没有别声明为static,就都是一个外部对象

*连接器的输入是一组目标模块和库文件,输出是一个载入模块

*当连接器读入一个目标模块时,它必须解析出这个目标模块中定义的所有外部对象的引用,并作出标记,说明这些外部对象不再是未定义的

*int a;如果其位置出现在所有函数体之外,那么它就被称为外部对象a的定义

*C中未指定初始值的外部变量被初始化为0

*extern int a; 从连接器的角度看,上述声明是一个外部对象a的引用,而不是对a 的定义,即使它出现在函数的内部,也仍然具有同样的含义

*每个外部对象都必须在程序某个地方进行定义,因此,如果一个程序中包括了语句extern int a ,那么这个程序就必须在某个地方包括语句int a,这两个语句既可以出现在同一个源文件中,也可以位于不同的源文件中

*为了避免可能出现的命令冲突,如果一个函数仅仅被同一个源文件中的其它函数调用,我们就应声明改函数为static

*如果一个函数在被定义或声明之前被调用,那么他的返回类型就默认为整型

*如果一个函数没有float,short,char类型的参数,在函数声明中完全可以省略参数类型的说明,但函数定义中不能省略参数类型的说明

*声明可以有多次,定义只有一次

*一些内置函数:

  double sqrt(double x);

  double pow(double x, double y);

  double ceil(double x);不小于x的最小整数

  double floor(double x);不大于x的最大整数

  int toupper(int x);

  int tolower(int x);

  int rand(void);

  void exit(int retval)

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值