C语言.docx
- 文档编号:12467340
- 上传时间:2023-04-19
- 格式:DOCX
- 页数:17
- 大小:24.85KB
C语言.docx
《C语言.docx》由会员分享,可在线阅读,更多相关《C语言.docx(17页珍藏版)》请在冰豆网上搜索。
C语言
1.不用库函数实现字符串的操作
(1)strcpy函数
⒈strcpy的实现代码
char*strcpy(char*strDest,constchar*strSrc)
{char*strDestCopy=strDest;//[3]
if((NULL==strDest)||(NULL==strSrc))//[1]
throw"Invalidargument(s)";//[2]
while((*strDest++=*strSrc++)!
='\0');//[4]
returnstrDestCopy;}
错误的做法:
[1]
(A)不检查指针的有效性,说明答题者不注重代码的健壮性。
(B)检查指针的有效性时使用((!
strDest)||(!
strSrc))或(!
(strDest&&strSrc)),说明答题者对C语言中类型的隐式转换没有深刻认识。
在本例中char*转换为bool即是类型隐式转换,这种功能虽然灵活,但更多的是导致出错概率增大和维护成本升高。
所以C++专门增加了bool、true、false三个关键字以提供更安全的条件表达式。
(C)检查指针的有效性时使用((strDest==0)||(strSrc==0)),说明答题者不知道使用常量的好处。
直接使用字面常量(如本例中的0)会减少程序的可维护性。
0虽然简单,但程序中可能出现很多处对指针的检查,万一出现笔误,编译器不能发现,生成的程序内含逻辑错误,很难排除。
而使用NULL代替0,如果出现拼写错误,编译器就会检查出来。
[2]
(A)returnnewstring("Invalidargument(s)");,说明答题者根本不知道返回值的用途,并且他对内存泄漏也没有警惕心。
从函数中返回函数体内分配的内存是十分危险的做法,他把释放内存的义务抛给不知情的调用者,绝大多数情况下,调用者不会释放内存,这导致内存泄漏。
(B)return0;,说明答题者没有掌握异常机制。
调用者有可能忘记检查返回值,调用者还可能无法检查返回值(见后面的链式表达式)。
妄想让返回值肩负返回正确值和异常值的双重功能,其结果往往是两种功能都失效。
应该以抛出异常来代替返回值,这样可以减轻调用者的负担、使错误不会被忽略、增强程序的可维护性。
[3]
(A)忘记保存原始的strDest值,说明答题者逻辑思维不严密。
[4]
(A)循环写成while(*strDestCopy++=*strSrc++);,同[1](B)。
(B)循环写成while(*strSrc!
='\0')*strDest++=*strSrc++;,说明答题者对边界条件的检查不力。
循环体结束后,strDest字符串的末尾没有正确地加上'\0'。
⒉返回strDest的原始值使函数能够支持链式表达式,增加了函数的“附加值”。
同样功能的函数,如果能合理地提高的可用性,自然就更加理想。
链式表达式的形式如:
intiLength=strlen(strcpy(strA,strB));
又如:
char*strA=strcpy(newchar[10],strB);
返回strSrc的原始值是错误的。
其一,源字符串肯定是已知的,返回它没有意义。
其二,不能支持形如第二例的表达式。
其三,为了保护源字符串,形参用const限定strSrc所指的内容,把constchar*作为char*返回,类型不符,编译报错。
在上面的语句中,循环语句
while((*strDestCopy++=*strSrc++)!
='\0');
较难理解,可以把这句理解为以下操作。
第一种:
while
(1){chartemp;temp=*strDestCopy=*strSrc;strDestCopy++;strSrc++;if('\0'==temp)break;}
第二种:
while(*strSrc!
='\0')
{
*strDestCopy=*strSrc;
strDestCopy++;
strSrc++;
}
*strDestCopy=*strSrc++;
也即:
while(*strSrc!
='\0')
{
*strDestCopy++=*strSrc++;
}
*strDestCopy=‘\0’;
(2)strcmp函数
intstrcmp(constchar*str1,constchar*str2)
{
/*不可用while(*str1++==*str2++)来比较,当不相等时仍会执行一次++,return返回的比较值实际上是下一个字符。
应将++放到循环体中进行。
*/
while(*str1==*str2)
{
if(*str1=='\0')
return0;
str1++;
str2++;
}
return*str1-*str2;
}
(3)strcat函数
//将源字符串加const,表明其为输入参数
char*strcat(char*strDest,constchar*strSrc)
{
//后文returnaddress,故不能放在assert断言之后声明address
char*address=strDest;
assert((strDest!
=NULL)&&(strSrc!
=NULL));//对源地址和目的地址加非0断言
while(*strDest)//是while(*strDest!
=’\0’)的简化形式
{
//若使用while(*strDest++),则会出错,因为循环结束后strDest还会执行一次++,
//那么strDest将指向'\0'的下一个位置。
/所以要在循环体内++;因为要是*strDest最后指
//向该字符串的结束标志’\0’。
strDest++;
}
while(*strDest++=*strSrc++)
{
NULL;//该循环条件内可以用++,
}//此处可以加语句*strDest=’\0’;无必要
returnaddress;//为了实现链式操作,将目的地址返回
}
(4)strlen()函数与sizeof的区别
strlen(char*)函数求的是字符串的实际长度,它求得方法是从开始到遇到第一个'\0',如果你只定义没有给它赋初值,这个结果是不定的,它会从aa首地址一直找下去,直到遇到'\0'停止。
charaa[10];cout< charaa[10]={'\0'};cout< charaa[10]="jun";cout< 而sizeof()返回的是变量声明后所占的内存数,不是实际长度,此外sizeof不是函数,仅仅是一个操作符,strlen是函数。 sizeof(aa)返回10 inta[10];sizeof(a)返回40(根据语言int型c是两个字节c++是四个java是两个) ⒈sizeof操作符的结果类型是size_t,它在头文件中typedef为unsigned int类型。 该类型保证能容纳实现所建立的最大对象的字节大小。 ⒉sizeof是操作符(关键字),strlen是函数。 ⒊sizeof可以用类型做参数,strlen只能用char*做参数,且必须是以''\0''结尾的。 sizeof还可以用函数做参数,比如: shortf(); printf("%d\n",sizeof(f())); 输出的结果是sizeof(short),即2。 ⒋数组做sizeof的参数不退化,传递给strlen就退化为指针了。 ⒌大部分编译程序在编译的时候就把sizeof计算过了是类型或是变量的长度这就是sizeof(x)可以用来定义数组维数的原因 charstr[20]="0123456789"; inta=strlen(str);//a=10; intb=sizeof(str);//而b=20; 6.strlen的结果要在运行的时候才能计算出来,是用来计算字符串的长度,不是类型占内存的大小。 7.sizeof后如果是类型必须加括弧,如果是变量名可以不加括弧。 这是因为sizeof是个操作符不是个函数。 ⒏当适用了于一个结构类型时或变量,sizeof返回实际的大小, 当适用一静态地空间数组,sizeof归还全部数组的尺寸。 sizeof操作符不能返回动态地被分派了的数组或外部的数组的尺寸 ⒐数组作为参数传给函数时传的是指针而不是数组,传递的是数组的首地址, 如: fun(char[8]) fun(char[]) 都等价于fun(char*) 在C++里参数传递数组永远都是传递指向数组首元素的指针,编译器不知道数组的大小 如果想在函数内知道数组的大小,需要这样做: 进入函数后用memcpy拷贝出来,长度由另一个形参传进去 fun(unsigedchar*p1,intlen){unsignedchar*buf=newunsignedchar[len+1]memcpy(buf,p1,len);} 我们能常在用到sizeof和strlen的时候,通常是计算字符串数组的长度 看了上面的详细解释,发现两者的使用还是有区别的,从这个例子可以看得很清楚: charstr[20]="0123456789"; inta=strlen(str);//a=10;>>>>strlen计算字符串的长度,以结束符0x00为字符串结束。 intb=sizeof(str);//而b=20;>>>>sizeof计算的则是分配的数组str[20]所占的内存空间的大小,不受里面存储的内容改变。 上面是对静态数组处理的结果,如果是对指针,结果就不一样了 char*ss="0123456789"; sizeof(ss)结果4>>>>ss是指向字符串常量的字符指针,sizeof获得的是一个指针的值所占的空间,应该是长整型的,所以是4 sizeof(*ss)结果1>>>>*ss是第一个字符其实就是获得了字符串的第一位'0'所占的内存空间,是char类型的,占了1位 strlen(ss)=10>>>>如果要获得这个字符串的长度,则一定要使用strlen sizeof返回对象所占用的字节大小.//正确 strlen返回字符个数.//正确 在使用sizeof时,有一个很特别的情况,就是数组名到指针蜕变, charArray[3]={'0'}; sizeof(Array)==3; char*p=Array; strlen(p)==1;//sizeof(p)结果为4 在传递一个数组名到一个函数中时,它会完全退化为一个指针 看完以上你是否很清楚sizeof和strlen的区别了呢? 还不明白的话,我们看下面几个例子: 第一个例子 char*ss="0123456789"; sizeof(ss)结果4===》ss是指向字符串常量的字符指针 sizeof(*ss)结果1===》*ss是第一个字符 大部分编译程序在编译的时候就把sizeof计算过了是类型或是变量的长度 这就是sizeof(x)可以用来定义数组维数的原因 charstr[20]="0123456789"; inta=strlen(str);//a=10; intb=sizeof(str);//而b=20; charss[]="0123456789"; sizeof(ss)结果11===》ss是数组,计算到\0位置,因此是10+1 sizeof(*ss)结果1===》*ss是第一个字符 charss[100]="0123456789"; sizeof(ss)结果是100===》ss表示在内存中的大小100×1 strlen(ss)结果是10===》strlen是个函数,内部实现是用一个循环计算到\0之前为止 intss[100]="0123456789"; sizeof(ss)结果400===》ss表示在内存中的大小100×4 strlen(ss)错误===》strlen的参数只能是char*且必须是以'\0'结尾的 charq[]="abc"; charp[]="a\n"; sizeof(q),sizeof(p),strlen(q),strlen(p); 结果是4332 第二个例子 classX{inti;intj;chark;};Xx; cout< cout< 第三个例子 charszPath[MAX_PATH] 如果在函数内这样定义,那么sizeof(szPath)将会是MAX_PATH,但是将szPath作为虚参声明时(voidfun(charszPath[MAX_PATH])),sizeof(szPath)却会是4(指针大小) 还有一位网友的说明也很好: 其实理解sizeof只需要抓住一个要点: 栈 程序存储分布有三个区域: 栈、静态和动态。 能够从代码直接操作的对象,包括任何类型的变量、指针,都是在栈上的;动态和静态存储区是靠栈上的指针来间接操作的。 sizeof操作符,计算的是对象在栈上的投影体积;记住这个就很多东西都很清楚了。 charconst*static_string="Hello"; sizeof(static_string)是sizeof一个指针,所以在32bitsystem是4 charstack_string[]="Hello"; sizeof(stack_string)是sizeof一个数组,所以是6*sizeof(char) char*string=newchar[6]; strncpy(string,"Hello",6"); sizeof(string)是sizeof一个指针,所以还是4。 和第一个不同的是,这个指针指向了动态存储区而不是静态存储区。 不管指针指向的内容在什么地方,sizeof得到的都是指针的栈大小 C++中对引用的处理比较特殊;sizeof一个引用得到的结果是sizeof一个被引用的对象的大小;所以 structO{inta,b,c,d,e,f,g,h;};intmain(){O&r=*newO;cout< r引用的是整个的O对象而不是指向O的指针,所以sizeofr的结果和sizeofO完全相同。 下面几种实现strlen函数的源代码大家参考 例1 1 2 3 4 5 6 7 8 9 #include #include Typedefunsignedintu_int; u_intMystrlen(constchar*str) { u_inti; assert(str! =NULL); for(i=0;str[i]! ='\0';i++); returni; } 例2 1 2 3 4 5 6 7 intstrlen(constchar*str) { assert(str! =NULL); intlen=0; while((*str++)! ='\0') len++; returnlen; } 例3 1 2 3 4 5 6 intstrlen(constchar*str) { assert(str); constchar*p=str; while(*p++! =NULL); returnp-str-1; } 例4 1 2 3 4 5 6 7 intstrlen(constchar*str) { assert(str); if(*str==NULL) return0; else return(1+strlen(++str)); } 例5 1 2 3 4 5 6 /***strlen-Findthelengthofastring*@s: Thestringtobesized*/ size_tstrlen(constchar*s) { constchar*sc; for(sc=s;*sc! ='\0';++sc)/*nothing*/; returnsc-s; } 2.内存的操作 (1)malloc()函数 原型为externvoid*malloc(unsignedintnum_bytes)。 动态内存分配,包含在malloc.h或者stdlib.h(vc++6.0)中。 分配长度为num_bytes字节的内存块。 如果分配成功则返回指向被分配内存的指针(此存储区中的初始值不确定),否则返回空指针NULL。 当内存不再使用时,应使用free()函数将内存块释放。 函数返回的指针一定要适当对齐,使其可以用于任何数据对象。 [1]malloc()函数与new的区别 从本质上来说,malloc(Linux上具体实现可以参考manmalloc,glibc通过brk()&mmap()实现)是libc里面实现的一个函数,如果在sourcecode中没有直接或者间接include过stdlib.h,那么gcc就会报出error: ‘malloc’wasnotdeclaredinthisscope。 如果生成了目标文件(假定动态链接malloc),如果运行平台上没有libc(Linux平台,手动指定LD_LIBRARY_PATH到一个空目录即可),或者libc中没有malloc函数,那么会在运行时(Run-time)出错。 new则不然,是c++的关键字,它本身不是函数。 new不依赖于头文件,c++编译器就可以把new编译成目标代码(g++4.6.3会向目标中插入_Znwm这个函数,另外,编译器还会根据参数的类型,插入相应的构造函数)。 在使用上,malloc和new至少有两个不同: new返回指定类型的指针,并且可以自动计算所需要大小。 比如: 1 2 3 int*p; p=newint; //返回类型为int*类型(整数型指针),分配大小为sizeof(int); 或: 1 2 3 int*parr; parr=newint[100]; //返回类型为int*类型(整数型指针),分配大小为sizeof(int)*100; 而malloc则必须要由我们计算字节数,并且在返回后强行转换为实际类型的指针。 1 2 3 4 5 6 7 int*p; p=(int*)malloc(sizeof(int)*128); //分配128个(可根据实际需要替换该数值)整型存储单元, //并将这128个连续的整型存储单元的首地址存储到指针变量p中 double*pd=(double*)malloc(sizeof(double)*12); //分配12个double型存储单元, //并将首地址存储到指针变量pd中 第一、malloc函数返回的是void*类型。 对于C++,如果你写成: p=malloc(sizeof(int));则程序无法通过编译,报错: “不能将void*赋值给int*类型变量”。 所以必须通过(int*)来将强制转换。 而对于C,没有这个要求,但为了使C程序更方便的移植到C++中来,建议养成强制转换的习惯。 第二、函数的实参为sizeof(int),用于指明一个整型数据需要的大小。 在Linux中可以有这样: malloc(0),这是因为Linux中malloc有一个下限值16Bytes,注意malloc(-1)是禁止的; 但是在某些系统中是不允许malloc(0)的。 在规范的程序中我们有必要按照这样的格式去使用malloc及free: 1 2 3 4 5 6 7 8 9 10 type*p; if(NULL==(p=(type*)malloc(sizeof(type)))) /*请使用if来判断,这是有必要的*/ { perror("error..."); exit (1); } .../*其它代码*/ free(p); p=NULL;/*请加上这句*/ malloc也可以达到new[]的效果,申请出一段连续的内存,方法无非是指定你所需要内存大小。 比如想分配100个int类型的空间: 1 2 int*p=(int*)malloc(sizeof(int)*100); //分配可以放得下100个整数的内存空间。 另外有一点不能直接看出的区别是,malloc只管分配内存,并不能对所得的内存进行初始化,所以得到的一片新内存中,其值将是随机的。 除了分配及最后释放的方法不一样以外,通过malloc或new得到指针,在其它操作上保持一致。 (2)calloc()函数 函数原型: void*calloc(size_tn,size_tsize); 功能: 在内存的动态存储区中分配n个长度为size的连续空间,函数返回一个指向分配起始地址的指针;如果分配不成功,返回NULL。 跟malloc的区别: calloc在动态分配完内存后,自动初始化该内存空间为零,而malloc不初始化,里边数据是随机的垃圾数据。 用法: void*calloc(size_tn,size_tsize); 一般使用后要使用free(起始地址的指针)对内存进行释放,不然内存申请过多会影响计算机的性能,以至于得重启电脑。 如果使用过后不清零,还可以使用指针对该块内存进行访问。 头文件: stdlib.h或malloc.h 编写一个calloc(n,size),返回一个指向n个大小为size的对象的指针,用malloc(size)写 void *calloc(size_t num, size_t size) { void *ptr = null; size_t nbytes = 0; nbytes = num*size; ptr = malloc(nbytes); if(ptr! =null){ memset(ptr, 0×0,nbytes); } return ptr; } void*memset(void*s,intch, size_t n); 函数解释: 将s中前n个字节(typedefunsignedintsize_t)用ch替换并返回s。 memset: 作用是在一段内存块中填充某个给定的值,它是对较大的结构体或数组进行清零操作的一种最快方法[1] 。 (3)realloc()函数 realloc原型是externvoid*reall
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 语言