第二章------BUG
一.多做过之(不该做的做了)**
1.一个L和两个L的区别
‘NUL’ —> 表示结束一个acsii字符串(’\0’)
‘NULL’ —> 表示空指针
2.switch … case…问题:
(1) case里出现的值过于任性,可以避免在switch{ }再去声明一些变量,再对这些变量赋予初值,是没有必要的。
(2)switch内部语句可以添加任何标签,可以用goto,但是会破坏程序的结构化。
(3)不会在执行完每个case之后自动终止,需要在每个case后面加break,但要注意使用break会带来什么影响,如果没有break,则会造成fall through。
3.合并相邻字符串
(1) 旧风格
printf("you are a boy, but you should good \
study day day up.so you must be work hard \
everyday!\n");
//除了最后一行,其余每一行字符最后的'\0'会被删掉,编译时自动合并
(2)新风格
printf("you are a boy, but you should good"
"study day day up.so you must be work hard "
"everyday!\n");
//除了最后一行,其余每一行字符最后的'\0'会被删掉,编译时自动合并
tips:使用时要注意,如果需要逗号分隔的,不能遗漏
(3) 是否需要预留逗号(古老方法)
–其实本无必要,没有意义
可以用以下方式解决:
func(char *string)
{
static char str1=' ';
printf("%c %s\n",str1,string);
str1=',';
}
//开头给个空格,之后就自动在字符串前面加个逗号,
//比一开始让逗号加在后面方便多了
4.缺省可见性
在缺省情况下,函数的名字是全局可见的,前面可以加个多余的extern 关键字.
func apple() //全局可见
extern func pear() //全局可见
static func water() //在此文件之外不可见
//如果要限制函数的可见性的话就加上 static(局部作用域和全局生命期)
二.误做之过(该做的没做)
1.符号重载
int i = 1 , 2;
//逗号运算符最低,约等于int (i = 1),2;--->i=1
tips:记住两个优先级即可,乘法除法先与加减,其余的一律加括号
3.gets()缺陷
gets( )是从流中读入一个字符串,但不检查缓冲空间,很容易出问题
—解决方法用fgets( ),对读取字符设置了限制
:char *fgets (char *buf, int n, FILE *fp)
// n 表示空间长度 可以用 sizeof(buf)
三.少做之过(该做但做的不合适)
1.空格
"\ "是转义字符,可以和回车使用,当做下一行对当前行的延续,用于连接长字符串。
—但是\newline 和 \ newline 是不一样的
注意:空格不要随便省略,否则会造成错误
eg:z = y+++x -->会导致error
2.要合理运用"//"注释符
3.编译器日期被破坏
//把源文件的timetamp转换为表示当地格式日期的字符串
char *localized_time(char *filename)
{
struct tm *tm_ptr;
struct stat stat_block;
char buffer[120];
//获得原文件的timestamp,格式为time_t
stat(filename,&stat_block);
//把UNIX的time_t转换成tm结构,里面保存当地时间
tm_ptr = localtime(&stat_block.st_mtime);
//把tm结构转换成以当地日期格式表示的字符串
strftime(buffer,sizeof(buffer),"%a %b %e %T %Y",tm_ptr);
return buffer;
}
----问题出在数组buffer是局部变量,局部作用域和生存期,是自动分配内存的数组,一旦离开函数就是自动销毁内存!!
改进方法:
(1) 返回一个指向字符串常量的指针
char *fun(){return "you are good!"};
//缺点是字符串常量存储于只读内存区是不能改的
(2) 使用全局声明的数组
该情况只适用于自己创建字符串的情况,缺点是任何人都可以修改这个全局数组,而且函数下一次调用的时候也会覆盖内容.
(3)使用静态数组
char *func()
{
static char buffer[20];
....
return buffer;
}
–可以防止其他人修改数组,只有拥有指向该数组的指针的函数才能修改这个静态数组,但下一次调用也会覆盖内容,所以需要调用函数将其备份。
—如果大型的缓冲区闲置的话是非常浪费空间的
(4)用动态分配内存,保存返回的值`
char *func()
{
char *s = malloc(120);
...
return s;
}
----该函数每次调用时都会创建一个新的缓冲区,而且不会覆盖以前的返回值,适合多线程的代码;
—但是对内存管理非常严格,如果还在使用就释放或者内存泄漏(用了没有free)就会发生错误!!
(5)调用者分配内存来保存函数的返回值,还应该同时指定缓冲区的大小
void func(char *result , int size)
{
strncpy (result,"you are good!",size);
}
buffer = malloc(size);
func(buffer,size);
....
free(buffer);
拓展:
1.char ** 和 const char**
char *cp;
const char *ccp;
ccp = cp;
//是合法的,左操作数是一个指向有const限定符的char的指针
//右操作数是一个指向无限定符的char的指针
****************
char **cp;
const char **ccp;
ccp = cp;
//是不合法的,操作数不同,两者不相容
//char ** 所指是 char *
//const char ** 所指是 const char*
2.const与指针
int const *p ---> 不能通过p指针去修改所指的变量,但那个变量不是const
int * const p --->指的是p只能指向那个变量,不能指向其他变量