C语言.docx

上传人:b****2 文档编号:1760595 上传时间:2023-05-01 格式:DOCX 页数:17 大小:24.85KB
下载 相关 举报
C语言.docx_第1页
第1页 / 共17页
C语言.docx_第2页
第2页 / 共17页
C语言.docx_第3页
第3页 / 共17页
C语言.docx_第4页
第4页 / 共17页
C语言.docx_第5页
第5页 / 共17页
C语言.docx_第6页
第6页 / 共17页
C语言.docx_第7页
第7页 / 共17页
C语言.docx_第8页
第8页 / 共17页
C语言.docx_第9页
第9页 / 共17页
C语言.docx_第10页
第10页 / 共17页
C语言.docx_第11页
第11页 / 共17页
C语言.docx_第12页
第12页 / 共17页
C语言.docx_第13页
第13页 / 共17页
C语言.docx_第14页
第14页 / 共17页
C语言.docx_第15页
第15页 / 共17页
C语言.docx_第16页
第16页 / 共17页
C语言.docx_第17页
第17页 / 共17页
亲,该文档总共17页,全部预览完了,如果喜欢就下载吧!
下载资源
资源描述

C语言.docx

《C语言.docx》由会员分享,可在线阅读,更多相关《C语言.docx(17页珍藏版)》请在冰点文库上搜索。

C语言.docx

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

展开阅读全文
相关资源
猜你喜欢
相关搜索
资源标签

当前位置:首页 > 总结汇报 > 学习总结

copyright@ 2008-2023 冰点文库 网站版权所有

经营许可证编号:鄂ICP备19020893号-2