C、C++语言容易出错的几个地方
1. sizeof()和strlen()函數(shù)
sizeof常見標準用法(1)、(2)、(3) [摘自C++ Reference]:
//(1)
char buff[6];
strncpy(buff, argv[1], sizeof(buff));
//(2)
int array[] = { 3, 1, 4, 1, 5, 9 };
unsigned int array_size = sizeof(array) / sizeof(array[0]);
//(3)
typedef struct data_type {
?????? int age;
?????? char name[20];
} data;
data *bob;
bob = (data*) malloc( sizeof(data) );
if( bob != NULL ) {
?????? bob->age = 22;
?????? strcpy( bob->name, "Robert" );
?????? printf( "%s is %d years old\n", bob->name, bob->age );
}
free( bob );
?
可見,sizeof主要用于求某種數(shù)據(jù)(例如int,數(shù)組,字符串,指針,結(jié)構(gòu)體…)的size,例如:
char str[]="hello";
char *p1=str;
此時,用sizeof(str)得到的是6,因為hell0是5個字符,系統(tǒng)儲存的時候會在hello的末尾加上結(jié)束標識\0,一共為6個字符;
?????? 而sizeof(p1)得到的卻是4,它求得的是指針變量p1的長度,在32位機器上,一個地址都是32位,即4個字節(jié)。
?????? 用sizeof(*p1)得到的是1,因為*p1定義為char,相當于一個字符,所以只占一個字節(jié)。
?????? 用strlen(str),得到的會是5,因為strlen求得的長度不包括最后的\0。
??? 用strlen(p1),得到的是5,與strlen(str)等價。
上面的是sizeof和strlen的區(qū)別,也是 指針字符串和 數(shù)組字符串 的區(qū)別。
?
?
編程時這種錯誤非常隱秘,見下面的一個例子。
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main( )
{
??? char *src="hello world";
??? char *dest=NULL;
??? int len=strlen(src);//這里很容易出錯,寫成sizeof(src)就是求指針的長度,即4
??? dest=(char *)malloc(len+1);//這里很容易出錯,寫成len
??? char *d=dest;
??? char *s=&src[len-1]; //這里很容易出錯,寫成len
??? while(len--!=0)
??????? *d++=*s--;
??? *d='\0'; //這句很容易漏寫
??? printf("%s\n", dest);
??? free(dest); //這句很容易漏寫
??? return 0;
}
?
注意,我上面這個C語言程序是在Linux平臺下gcc編譯的,Windows平臺下的VC6不支持即用即聲明的形式,必須先定義后使用。用VC6編譯可以改成下面的形式:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main( )
{
??? char *src, *dest, *d, *s;
??? int len;
??? src="hello world";
??? dest=NULL;
??? len=strlen(src);
??? dest=(char *)malloc(len+1);
??? d=dest;
??? s=&src[len-1];
??? while(len--!=0)
??????? *d++=*s--;
??? *d='\0';
??? printf("%s\n", dest);
??? free(dest);
??? return 0;
}
首先說明一下malloc函數(shù)和free函數(shù)的使用www.2cto.com
??? #include <stdlib.h>
??? void *malloc( unsigned int size );
它的功能是在內(nèi)存的動態(tài)存儲區(qū)分配一個長度為size字節(jié)的連續(xù)空間。函數(shù)返回的是一個指向分配域其實地址的指針,這個指針的類型是void類型。如果函數(shù)未能執(zhí)行成功則返回一個空指針NULL,使用這個函數(shù)必須包含頭文件stdio.h。
?
??? void類型的指針,指向一個類型未定的變量,也就是說它可以指向char類型變量,也可以指向int類型或其它類型。因此在將它的值賦值給另外一個指針時要進行強制類型轉(zhuǎn)換,例如:
char *p1="123456";
void *p2="abcdef";
p1=(char *)p2;//兩者類型必須相同,也可以p2=(void *)p1;
?????? malloc函數(shù)必須和free函數(shù)成對出現(xiàn),使用完了free(dest);
?
2. 數(shù)組中易錯的地方
?
分析下面這段小程序:
#include<stdio.h>
void main()
{
?????? int a[5]={1,2,3,4,5};
?????? int *ptr=(int *)(&a+1);
?????? printf("%d, %d\n", *(a+1), *(ptr-1));
}
以及下面這個程序:
#include <stdlib.h>
#include <stdio.h>
static void show_str_pointer(const char **ppstr)
{
??? printf("%s\n", *ppstr);
}
int main()
{
??? char array[4] = "abc";
??? char *pointer = "abc";
??? show_str_pointer(&pointer);
??? show_str_pointer(&array);
??? return 0;
}
?
執(zhí)行結(jié)果為:
abc
7�
?????? &array與array的語義相同。在這里指針與數(shù)組是不能互換的。&pointer為指針的地址,與show_str_pointer參數(shù)char **ppstr指向指針的指針的變量類型相同。而&array仍然為數(shù)組地址,與參數(shù)char **ppstr的類型不符。
?
?
3. 數(shù)組的最大長度問題
?????? int n[1000000];這樣肯定是不行的,因為這樣定義的數(shù)組用的是棧內(nèi)存,系統(tǒng)默認值為最大1Mb,一個int型占4字節(jié)這樣最大可以申請1024*1024/4=264144個,如果考慮到系統(tǒng)自身的占用最大值約為25000個。
?????? int *p=(int *)malloc(1000000*sizeof(int));,這樣用的是堆內(nèi)存,只要你內(nèi)存有那么多的連續(xù)空間就可以。例子如下:
#include<stdio.h>
#include<malloc.h>
int main()
{
??? int *p=(int *)malloc(1000000*sizeof(int));
??? //int p[1000000];
??? int i=0;
??? for(;i<1000000;i++)
??????? printf("%d\n",p[i]=i);
??????????????? free(p);
??? return 0;
}
?
如果非要用數(shù)組的話,一般這樣寫,不能再大了:
#define MAXSIZE 250000
int a[MAXSIZE];
?
2. fscanf和fprintf函數(shù)
?????? 將文件中的數(shù)據(jù)讀出來存入指定的指針位置,例如fscanf (fp,"%d", &a[i]);
?????? 將數(shù)據(jù)存入文件中可以用fprintf函數(shù),但是對于二進制文件,寫成fprintf(fp, "%d", a[i]);就不對,因為存入的是%d格式是十進制的,所以此時不要用fprintf,換成fwrite即可。
?有關(guān)二進制文件的讀寫參加下面一個程序:
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#define DATASIZE 250000
int main(int argc, char ** argv)
{
????? const char * file_name = "out.dat";
????? FILE * fp = fopen(file_name, "wb");//必須放在其他變量的定義之前
????? int i, a, b[DATASIZE];
????? srand( time(NULL) );
????? for(i=0; i<DATASIZE; i++)
????? {
????????? a=rand()%100;
????????? fwrite(&a, sizeof(int), 1, fp);
????? }
????? fclose(fp);
????? fp = fopen(file_name, "rb");
????? fread(&b, sizeof(int), DATASIZE, fp);
?????
????? for(i=0; i<200; i++)
????????? printf("%d? ", b[i]);
????? printf("\n");
????? return 0;
}
程序說明:隨機生成25萬個0~100的整數(shù)以二進制的形式存入文件out.dat中,然后再讀取文件out.dat中的整數(shù)挨個賦值給數(shù)組b[DATASIZE],最后打印出該數(shù)組的前200個數(shù)。
?
?3. 遞歸棧空間溢出
?????? 我們知道快速排序通常是用遞歸算法寫的,雖然說號稱是速度最快的(其實也不是最快的),但是我排20萬個整數(shù)它就受不鳥了(貌似系統(tǒng)給棧空間分配的大小為2M或者8M,快速排序最壞情況下,遞歸深度為n,所需要的棧空間為O(n),一個整數(shù)4位32個字節(jié),100W*32那就有30多M了,棧空間必定溢出),排18萬個整數(shù)的時候近似時間復(fù)雜度約為1.4億,運行了0.497秒,而VC6自帶庫函數(shù)里面的那個qsort()要慢很多,用了1.942秒,堆排序只用了0.032秒(太給力了!),還有就是哥那個優(yōu)化過的希爾排序,用了8.518秒,復(fù)雜度約為16億,傷不起啊!
?????? 我把快速排序稍微改進一下,用首中尾三者取中作為基準的辦法,速度肯定是提高了不少,但是可排序的元素個數(shù)銳減到了3萬個,4萬個元素都可能導(dǎo)致遞歸棧空間溢出,5萬個元素想都別想了,所以呀這個快速排序的極限是排3萬個元素。而當我用3萬個元素去測試的時候,快排用了0.169秒,堆排序用了0.006秒,快慢一眼明了。
?????? 當待排序的數(shù)據(jù)量比較大時,你就別想用什么冒泡排序、直接選擇排序、直接插入排序等,效率太低了!最好是用堆排序。
?
補充一點:
?????? 遞歸算法的實現(xiàn)過程:是通過棧實現(xiàn)的,例如下面一個求階乘的算法:
int Fac(n)
int n;
{
? if( n==1 ) return 1;
? else return n*Fac(n-1);
}
?????? 系統(tǒng)最初是不會去計算的,它會在內(nèi)存中開辟一個棧空間,假如說n=3,那么首先3*Fac(2)入棧,占據(jù)棧底的位置,然后2*Fac(1)入棧,此時Fac(1)就不再遞歸了,所以沒有元素再入棧了,Fac(1)返回值1,然后2*Fac(1)、3*Fac(2)依次出棧,最終出棧的是數(shù)字6,這就是3的階乘。
?????? 由此可見,遞歸的函數(shù)越復(fù)雜,數(shù)據(jù)量越大,變量越多,那么占用的棧空間就越大,因為棧的每一層都要保存這些,其實很多的重復(fù)的,但是依然保存了,只有到該層出棧的時候才釋放。編譯器就是這樣的,沒有太多的優(yōu)化,內(nèi)存開銷相當?shù)拇?#xff0c;所以——少用遞歸,慎用遞歸!
總結(jié)
以上是生活随笔為你收集整理的C、C++语言容易出错的几个地方的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: C语言的指针*和引用
- 下一篇: 两个栈来实现一个队列的C++代码