c陷阱和缺陷(重要知识点)

来源:互联网 发布:淘宝晒图福利 编辑:程序博客网 时间:2024/04/28 20:53
一、词法“陷阱”        
1、  =不同于==
        一般而言,赋值运算相对于比较运算出现得更频繁,所以c语言将赋值运算符设置为=,比较设置为==。为了避免出现将比较运算符错写成赋值运算符=,将数值写在左边,变量写在右边。
2、   ~、&、|与!、&&、||
        前者为按位运算符:将其视作一个二进制的位序列,分别对其每个位进行操作。
        后者为逻辑运算符:将其视为要么true,要么false。并且&&和||在左侧操作数的值能够确定最终结果时不会对右侧操作数进行求值。
3、词法分析中的“贪心法”
        单字符符号:只有一个字符,多字符符号:有多个字符。
        例:(1)a---b被解释为a-- -b。(2)若想将x除以*p的值赋给y,则y=x/*p表示方式是错误的,编译器将解释成y=x,而/*p将被看做是注释符号
        读入单、多字符判断规则:每个符号应包含尽可能多的字符。如果输入流戒指至某个字符之前都已经被分解为一个个符号,那么下一个字符将包括从该字符之后可能组成一个符号的最长字符串。
        注意:除字符串与字符常量,符号的中间不能嵌有空白(空格、制表符和换行符)
二、语法“陷阱”
1、运算符问题······
        例:while(c=get(in)!=EOF)应该为while((c=get(in))!=EOF)
2、注意不要多加分号: while();
                                                    {}
也不要少加分号:struct
                                        {
                                         };
可以将struct结构体定义看做一个像int一样的类型,在int n之后要加分号,所以struct后也要加。
三、语义“陷阱”
1、c语言中只有一位数组。
例:int calendar[12][13];
        int *p;
        p=calendar;(错误)
        int **p;
        p=calendar;(错误)
        int *p[13];
        p=calendar;(正确)
2、例:将字符串s和t连接成单个字符串r:
char *r;
r=(char *)malloc(strlen(s)+strlen(t));
strcpy(r,s);
strcat(r,t);
········
错误一:没有考虑开辟空间发生错误的情况
错误二:没有想到r还有最后一个存放'/'的字节
错误三:没有用free释放不用的内存空间
改正:
char *r;
if(!(r=(char *)malloc(strlen(s)+strlen(t)+1))
{
    printf("error!\n");
    exit(1);
}
````
free(r);
3、边界计算与不对称边界
        栏杆错误(差一错误):100英尺长的围栏每隔10英尺需用一根支撑用的栏杆,共需多少杆?
        两种考虑方法:(1)支撑10英尺长的围栏实际需2根
                                    (2)除了最右侧的一段围栏,其他每一段10英尺长的围栏在左侧有一根栏杆;例外的最右侧的一段围栏不仅左侧有,右侧也有。
        通用原则:(1)首先考虑最简单情况下的特例,然后将结果外推
                            (2)仔细计算边界,绝对不掉以轻心。
解决数组和堆栈问题时:用第一个入界点和第一个出界点来表示一个数值范围,这样取值范围就是上界和下界之差。由此而推出:如果取值范围为空,那么上界=下界,即使取值范围为空,上界也不会小于下界。例:13=<x<=34应表示为13=<<35;
例:将长度无规律的输入数据到缓冲区中去,每当这块内存被“填满”时,就将缓冲区的内容写出。
定义缓冲区:#define N 1024
                        static char buffer[N];
然后考虑将数据写到缓冲区中:*bufptr++=c;
初始化缓冲区bufptr=buffer;
判断缓冲区是否满:if(bufptr==&buffer[N])(不对称边界,即引用第一个出界元素)
假设函数flushbuffer()是将缓冲区的内容输出并且刷新缓冲区,则函数为:
void bufwrite(char *p,int n)//*p指向要写入缓冲区的第一个字符,n代表要写入缓冲区的字符数
{
    while(--n>=0)
    {
        if(bufptr==&buffer[N])flushbuffer();
        *bufptr++=*p++;
    }
}
程序优化:
程序的时间复杂度主要用在了两次比较中:
(1)对要写入缓冲区字符数的判断(即循环计数器n是否达到终值的判断)
(2)对缓冲区是否已满的判断
为了优化程序,则可以一次移动多个字符(memcpy)
改进的程序:
void bufferwrite(char *p,int n)
{
    while(n>0)
    {
        if(bufptr==&buffer[N])flushbuffer();
        int k,rem;//k是每次移动的最大字符个数,rem代表缓冲区剩余空余的字符数
        rem=N-(bufptr-buffer);
        k=rem>n?n:rem;
        memcpy(bufptr,p,k);
        p+=k;
        n-=k;
        bufptr+=k;
    }
}
4、求值顺序与运算优先级
运算优先级:a+b*c=》a+(b*c)
求值顺序:if(count!=0&&sum/count<small)是先求count!=0还是先求sum/count<small。注意:即使是count为0时也不会产生用0做除数的错误。
四、链接
static int a与int a
前者的作用域限制在一个源文件内,对于其他源文件,a是不可见的。如果一个函数仅仅被同一个源文件中的其他函数调用,我们就应该声明该函数为static。
char filename[]="sdfds";与char *filename="sdfds";不同
前者
filename
     
后者
filename
 
     
五、除法运算时发生的截断
若a除以b的商为q,余数为r。则我们希望:
1、满足余数的定义
2、如果我们该变a的正负号,我们希望q的正负号改变,但q的绝对值不会改变。
3、当b>0时,我们希望保证r>=0且r<b。例如,如果余数用于哈希表的索引,确保它是一个有效的索引值很重要。
但这三条不能同时成立,大多数编译器在实现整数除法截断运算时,放弃第3条,而改为要求余数与被除数的正负号相同。
六、可移植性的一个例子
把给出的long型整数转换为10进制表示,并且对10进制表示中的每个字符都调用函数指针所指向的函数
void printnum(long n,void (*p)())
{
    if(n>0)
        {
            (*p)('-');//打印出负号
            n=-n;
        }
    if(n>=10)printnum(n/10,p);
    (*p)((int)(n%10)+'0');
}
存在的可移植性错误:
(1)将n%10得到的末尾数字转换为字符形式时所用的方法若是在不用ASCII字符集和EBCDIC字符集的计算机上就是不正确的。解决办法:用一张代表数字的字符表。因为一个字符串常量可以用来表示一个字符数组,所以在数组名出线的地方都可以用字符串常量来代替。
即:"0123456789"[n%10]来代替(int)(n%10)+'0'
(2)n<0时可能发生溢出。因为基于2的代码的计算机一般运行表示的负数范围要大于正数的取值范围。下界-2的n次方,上界2的n次方-1(因为有0)。解决办法:都对将其转化为负数求:
void printneg(long n,void (*p)())
{
    if(n<=-10)
        printneg(n/10,p);
    (*p)("0123456789"[-n%10]);
}
void printnum(long n,void (*p)())
{
    if(n<0)
    {
        (*p)('-');
        printneg(n,p);
    }
    else printneg(-n,p);
}
但此时还有些可移植性的问题:当除法运算中的一个操作数为负时,它的行为表现与具体的实现有关。因此当n为负数时,n%10有可能是一个正数。此时,-(n%10)就是一个负数,0123456789"[-n%10])就不在数组之中。要解决这个问题,可以创建两个临时变量来分别保存商和余数。在除法运算完成后,检查余数是否在合理的范围内,如果不是,调整两个变量。
printnum不需要修改,只要改动printneg函数:
void printneg(long n,void (*p)())
{
    long q;
    int r;
    q=n/10;
    r=n%10;
    if(r>0)
    {
        r-=10;
        q++;
    }
    if(n<=-10)
        printneg(q,p);
    (*p)("0123456789"[-r]);
}