无论什么时候,如果遇到了这样一条语句malloc(strlen(str)),几乎可以断定它是错误的,而malloc(strlen(str + 1))才是正确的,这是因为其他的字符串处理库函数几乎都包含一个额外空间,用于容纳字符串结尾的'\0'字符,所以,人们很容易忽略strlen这个特殊情况。
分析编程语言缺陷的一种方法就是把所有的缺陷归于3类:不该做的做了;该做的没做;该做但做得不合适
switch语句的一般形式如下:
switch(表达式) {
case 常量表达式 : 零条或多条语句
default : 零条或多条语句
case 常量表达式 : 零条或多条语句
}
当表达式的值与case中的常量匹配时,该case后面的语句就会执行。default(如果有的话)可以出现在case列表的任何位置,它在其他的case均无法匹配时被选中执行。如果没有default,而且所有的case均不匹配,那么整个switch语句便什么也不做。
#include
int main(void)
{
switch(3) {
default : printf("3\n");
case 1 : printf("1\n");
case 2 : printf("2\n");
}
return 0;
}
/*
打印
3
1
2
*/
switch对case可能出现的情况太过于放纵了,例如:可以在switch的左花括号之后声明一些变量,从而进行一些局部存储的分配。在switch语句中为这些变量加上初始值是没有什么用处的,因为它就不会被执行——语句从匹配表达式的case开始执行。
#include
int main(void)
{
int a = 1;
switch(3) {
a = 2;
case 1 : printf("1\n");
case 2 : printf("2\n");
}
printf("%d\n",a);
return 0;
}
/*
a仍然输出1
*/
switch的另一个问题是它内部的任何语句都可以加上标签,并在执行时跳转到那里,这就有可能破话程序流的结构化:
switch(i) {
case 5 + 3 : do_again:;
case 2 : printf("3333333333333333\n"); goto do_again;
case 3 : ;
}
switch最大的缺点是它不会再每个case标签后面的语句执行完毕后自动中止。一旦执行某个case语句,程序将会依次执行后面所有的case,除非遇到break语句,这称之为“fall through”,它的意思是:如果case语句后面不加break,就依次执行下去,以满足某些特殊情况的需求。但实际上,这是一个非常不好的特性,因为几乎所有的case都需要以break结尾。
break语句事实上跳出的是最近的那层循环语句或switch语句
//旧风格
printf("1111111 \
2222222 \
333333");
//新风格
printf("1111111"
"2222222222"
"33333333");
新风格可以用一连串相邻的字符串常量来代替它,它们会在编译时自动合并,除了最后一个字符串外,其余每个字符串末尾的的'\0'将会去掉。这种合并意味着字符串数组在初始化时,如果不小心漏掉了一个逗号,编译器将不会发出错误信息,而是悄无声息的把两个字符串合并在一起。
x = f() + g() * h()
g()和h()的返回值先组成一个意群,执行乘法运算,但g()和h()的调用可能以任何顺序出现(g的调用不一定早于h),类似,f可能在乘法之前也可能在乘法之后调用,也可能在g与h之间调用。唯一确定的就是乘法会在加法之前执行
有些专家建议在C语言中牢记两个优先级就行了:乘法和除法先于加法和减法,在涉及其他的操作符时一律加上括号。
结合性:它是仲裁者,在几个操作符具有相同优先级时决定先执行哪一个
所有赋值符(包括复合赋值符)都具有右结合性,具有左结合性的操作符如& |
结合性只用于表达式中出现两个以上相同优先级的操作符的情况,用于消除歧义,事实上,所有优先级相同的操作员符,它们的结合性也相同。
gets函数原型:char*gets(char*buffer);//读取字符到数组:gets(str);str为数组名。
gets函数功能:从键盘上输入字符,直至接受到换行符或EOF时停止,并将读取的结果存放在buffer指针所指向的字符数组中。读取的换行符被转换为null值,做为字符数组的最后一个字符,来结束字符串。
注意:gets函数由于没有指定输入字符大小,所以会无限读取,一旦输入的字符大于数组长度,就会发生内存越界,从而造成程序崩溃或其他数据的错误。
fgets函数原型:char *fgets(char *s, int n, FILE *stream);//我们平时可以这么使用:fgets(str, sizeof(str), stdin);其中str为数组首地址,sizeof(str)为数组大小,stdin表示我们从键盘输入数据。
fgets函数功能:从文件指针stream中读取字符,存到以s为起始地址的空间里,知道读完N-1个字符,或者读完一行。
注意:调用fgets函数时,最多只能读入n-1个字符。读入结束后,系统将自动在最后加'\0',并以str作为函数值返回。
看看这个函数的官方说明:
/***
*char *fgets(string, count, stream) - input string from a stream
*
*Purpose:
* get a string, up to count-1 chars or '\n', whichever comes first,
* append '\0' and put the whole thing into string. the '\n' IS included
* in the string. if count<=1 no input is requested. if EOF is found
* immediately, return NULL. if EOF found after chars read, let EOF
* finish the string as '\n' would.
*
*Entry:
* char *string - pointer to place to store string
* int count - max characters to place at string (include \0)
* FILE *stream - stream to read from
*
*Exit:
* returns string with text read from file in it.
* if count <= 0 return NULL
* if count == 1 put null string in string
* returns NULL if error or end-of-file found immediately
*
*Exceptions:
*
*******************************************************************************/
char *fgets(char *s, int n, FILE *stream)
{
register int c;
register char *cs;
cs=s;
while(--n>0 &&(c = getc(stream))!=EOF)
if ((*cs++= c) =='\n')
break;
*cs ='\0';
return (c == EOF && cs == s) ?NULL :s ;
}
z = y+++x
程序员的意图可能是z = y + ++x,但也可能是z = y++ + x。ANSI C规定了一种逐渐为人熟知的“maximal munch strategy(最大一口策略)”。这种策略表示如果下一个标记有超过一种的解释方案,编译器将选取能组成最长字符序列的方案,以上这个例子被解析为z = y++ + x
编译器日期被破坏
char * localized_time(char * filename)
{
struct tm *tm_ptr;
struct stat stat_block;
char buffer[120];
stat(filename,&stat_block);
tm_ptr = localtime(&stat_block.st_mtime);
strftime(buffer,sizeof(buffer),"%a %b %e %T %Y",tm_ptr);
return buffer;
}
问题就出在函数的最后一行,也就是返回buffer的那行。buffer是一个自动分配内存的数组,是该函数的局部变量。当控制流 离开申明自动变量的范围时,自动变量便自动失效。这就意味着即使返回一个指向局部变量的指针,当函数结束时,由于该变量已被销毁,谁也不知道这个指针所指向的地址的内容是什么。
在C语言中,自动变量在堆栈中分配内存。当包含自动变量的函数或代码块退出时,他们所占用的内存便被回收,他们的内容肯定会被下一个所调用的函数覆盖。这一切取决于堆栈中先前自动变量位于何处,活动函数申明了什么变量,写入了什么内容等。原先自动变量地址的内容可能被立即覆盖,也可能稍后才被覆盖。
解决这个问题有几种方案:
1、返回一个指向字符串常亮的指针;
2、使用全局申明的数组;
3、使用静态数组;
4、显示分配一些内存,保存返回的值(注意内存的释放)
5、调用者分配内存来保存函数的返回值。为了提高安全性,调用者应该同时指定缓冲区的大小。
如果可以在同一代码块中同时进行“malloc”和“free”操作,内存管理是最为轻松的