在linux c編程中,拷貝函數(shù)可以說是無處不用,結(jié)合我最近的一些實踐,簡單研究一下這幾個函數(shù)。說說實際使用中容易出錯的地方。
strcpy: 最常用的字符串拷貝函數(shù),但是要注意這個函數(shù)不會自己判斷源字符串是否比目標空間大,必須要程序員自己檢查,否則很容易造成拷貝越界,下面是幾個例子:
char *a = “0123456789”, *b = “abcdefghijk”;
char c[5];
輸出: strcpy(c,a)=0123456789 //數(shù)組c只有5個字節(jié)的空間,但是經(jīng)過strcpy后a的剩余字符也拷貝過去了,如果c后面是系統(tǒng)程序空間,那就要出問題了。
strncpy:strcpy的改進版本,多了一個拷貝長度的參數(shù)。需要注意的是長度參數(shù)應該為目的空間的大小,并且這個函數(shù)不會自己附加字符串結(jié)束符‘\0’,要自己加。看下面的例子:
strncpy(c,b,strlen(b))=abcdefghijkw //拷貝長度不對,還是越界
strncpy(c,a,sizeof(c))=01234fghijkw //拷貝長度正確,但是因為拷貝長度內(nèi)不包括‘\0’,所以輸出的時候還是會把原本的空間內(nèi)容輸出,知道遇到一個結(jié)束符‘\0’。
所以正確的做法應該是: strncpy(c, a, sizeof(c)-1); c[5] = ‘\0’;
memcpy: 最后說一下這個函數(shù),這個函數(shù)是個很強大的工具,因為它可以簡單的根據(jù)字節(jié)數(shù)拷貝內(nèi)存空間內(nèi)容,所以經(jīng)常被用于結(jié)構(gòu)體的拷貝。需要注意兩點:1、memcpy拷貝的時候源空間的長度和目標空間的長度都需要程序員自己考慮,如果按照源空間的長度拷貝,要注意是否會寫溢出,如果按照目標空間的長度拷貝,則要考慮是否造成讀溢出(把不該拷貝的內(nèi)容也拷貝過去了),而讀溢出在某些系統(tǒng)環(huán)境下(比如AIX),可能會造成coredump(當讀到不該讀的地址);2、源空間和目標空間不能重疊。如下例:
char src1[] = “src1”, src2[]=“source2, this is a long src”;
char dest[] = “destination”;
輸出:
memcpy(dest, src1, strlen(dest)) = src1 //讀越界
memcpy(dest, src2, strlen(src2)) = source2, this is a long srcis is a long src //寫越界
memcpy(dest, dest+2, strlen(dest2) = stination // 重疊,結(jié)果是混亂
copy函數(shù)的使用細節(jié)
strcpy是拷貝字符串,以\0為標志結(jié)束(即一旦遇到數(shù)據(jù)值為0的內(nèi)存地址拷貝過程即停止)
strcpy的原型為
char *strcpy(char *dest, const char *src)
而memcpy是給定來源和目標后,拷貝指定大小n的內(nèi)存數(shù)據(jù),而不管拷貝的內(nèi)容是什么(不僅限于字符)
memcpy的原型為
void *memcpy(void *dest, const void *src, size_t n);
C語言中的exit()函數(shù),括號中有時為1,有時為0在main函數(shù)中我們通常使用return (0);這樣的方式返回一個值。
但這是限定在非void情況下的也就是void main()這樣的形式。
exit()通常是用在子程序中用來終結(jié)程序用的,使用后程序自動結(jié)束跳會操作系統(tǒng)。
但在如果把exit用在main內(nèi)的時候無論main是否定義成void返回的值都是有效的,并且exit不需要考慮。括號中有時為1,有時為0
遇到1 就代表出錯后結(jié)束程序~其實不一定是1的~非0值也可以!遇到0就表示正常退出~~
例如:
你定義一個文件的指針fp
if (fp=fopen (“c:\\abc.txt”,“r”))==NULL)
{
printf(“Can not open the file.\n”);
exit(1);
}
如果文件不存在那么就跳出程序了 如果用struct的話,注意以下幾點:
1.盡量使用占為少的類型,如,在可能的時候使用short代替int
2.按數(shù)據(jù)類型本身占用的位置從大到小排列
例如
struct{
int a;
char b;
int c;
char d;
}
應該寫為:
struct{
int a;
int c;
char b;
char d;
};
一般的編譯器會采取一種叫做填充(padding)的方式來對齊數(shù)據(jù)。
以一個機器字(比如在32-bit的機器上為word = 32bit.)為基礎(chǔ)進行填充。
像上面的struct會這樣存儲:
?。▁p,vc6.0)
a 32bit = 4byte
c 32bit = 4byte
b,d,*,* 32bit = 4byte
其中,兩個*號表示填充(但是并沒有使用那兩個位置。)
system函數(shù):參數(shù)放入的是一個命令。
#include 《sys/types.h》
#include 《sys/wait.h》
#include 《errno.h》
#include 《unistd.h》
int system(const char * cmdstring)
{ pid_t pid;
int status;
if(cmdstring == NULL){
return (1);
}
if((pid = fork())《0){
status = -1;
}
else if(pid = 0){
execl(“/bin/sh”, “sh”, “-c”, cmdstring, (char *)0);
-exit(127); //子進程正常執(zhí)行則不會執(zhí)行此語句
}
else{
while(waitpid(pid, &status, 0) 《 0){
if(errno != EINTER){
status = -1;
break;
}
}
}
return status;
}
先分析一下原理,然后再看上面的代碼大家估計就能看懂了:
當system接受的命令為NULL時直接返回,否則fork出一個子進程,因為fork在兩個進程:父進程和子進程中都返回,這里要檢查返回的pid,fork在子進程中返回0,在父進程中返回子進程的pid,父進程使用waitpid等待子進程結(jié)束,子進程則是調(diào)用execl來啟動一個程序代替自己,execl(“/bin/sh”, “sh”, “-c”, cmdstring, (char*)0)是調(diào)用shell,這個shell的路徑是/bin/sh,后面的字符串都是參數(shù),然后子進程就變成了一個shell進程,這個shell的參數(shù)
是cmdstring,就是system接受的參數(shù)。在windows中的shell是command,想必大家很熟悉shell接受命令之后做的事了。
rewind函數(shù)用于把fp所指文件的內(nèi)部位置指針移到文件頭
評論
查看更多