C語言常見的函式呼叫
C語言常見的函式呼叫
isatty,函式名,主要功能是檢查裝置型別,判斷檔案描述詞是否為終端機。
函式名: isatty
用 法: int isatty(int desc);
返回值:如果引數desc所代表的檔案描述詞為一終端機則返回1,否則返回0。
程式例:
#include <stdio.h>
#include <io.h>
int main(void)
{
int handle;
handle = fileno(stdout);
if (isatty(handle))
printf("Handle %d is a device type\n", handle);
else
printf("Handle %d isn't a device type\n", handle);
re
函式名稱:fileno(在VC++6.0下為_fileno)
函式原型:int _fileno( FILE *stream );
函式功能:fileno()用來取得引數stream指定的檔案流所使用的檔案描述符
返回值:某個資料流的檔案描述符
標頭檔案:stdio.h
相關函式:open,fopen,fclose
void *memset(void *s, int ch, size_t n);
函式解釋:將s中當前位置後面的n個位元組 (typedef unsigned int size_t )用 ch 替換並返回 s 。
memset:作用是在一段記憶體塊中填充某個給定的值,它是對較大的結構體或陣列進行清零操作的一種最快方法
函式原型
char *fgets(char *buf, int bufsize, FILE *stream);
引數
*buf: 字元型指標,指向用來儲存所得資料的地址。
bufsize: 整型資料,指明儲存資料的大小。
*stream: 檔案結構體指標,將要讀取的檔案流。
返回值
- 成功,則返回第一個引數buf;
- 在讀字元時遇到end-of-file,則eof指示器被設定,如果還沒讀入任何字元就遇到這種情況,則buf保持原來的內容,返回NULL;
- 如果發生讀入錯誤,error指示器被設定,返回NULL,buf的值可能被改變。
chdir 是C語言中的一個系統呼叫函式(同cd),用於改變當前工作目錄,其引數為Path 目標目錄,可以是絕對目錄或相對目錄。
exec函式
linux下c語言程式設計exec函式使用
2012年04月10日 09:39:27
閱讀數:19800
exec用被執行的程式完全替換呼叫它的程式的影像。fork建立一個新的程序就產生了一個新的PID,exec啟動一個新程式,替換原有的程序,因此這個新的被exec執行的程序的PID不會改變,和呼叫exec函式的程序一樣。
下面來看下exec函式族:
#include <uniSTd.h>
int execl(cONst char *path, const char *arg, ...);
int execlp(const char *file, const char *arg, ...);
int execle(const char *path, const char *arg, ..., char *const envp[]);
int execv(const char *path, char *const argv[]);
int execvp(const char *file, char *const argv[]);
int execve(const char *path, char *const argv[], char *const envp[]);
exec函式族裝入並執行程式pathname,並將引數arg0(arg1,arg2,argv[],envp[])傳遞給子程式,出錯返回-1。在exec函式族中,字尾l、v、p、e新增到exec後,所指定的函式將具有某種操作能力有後綴:
execl("/bin/ls","ls","-a",NULL)
execv("/bin/ls",arg)
execlp("ls","ls","-a",NULL)
execvp("ls",arg)
execle("/bin/ls","ls","-a",NULL,envp)
execve("/bin/ls",arg,envp)
assert()使用
assert()是一個除錯程式時經常使用的巨集,在程式執行時它計算括號內的表示式,如果表示式為FALSE (0), 程式將報告錯誤,並終止執行。如果表示式不為0,則繼續執行後面的語句,它的作用是終止程式以免導致嚴重後果,同時也便於查詢錯誤。
linux程式設計之dup與dup2
在linux下,通過open開啟以檔案後,會返回一個檔案描述符,檔案描述符會指向一個檔案表,檔案表中的節點指標會指向節點表。看下圖:
開啟檔案的核心資料結構
dup和dup2兩個函式都可以用來複制開啟的檔案描述符,複製成功後和複製源共享同一個檔案表。看下錶
執行dup後的核心資料結構
dup函式
fd1=dup(fd)
fd1和fd共享一個檔案表(對df進行什麼操作,fd1也會有相應的操作,fd和fd1是同步的)
具體解釋:
#inclue<stdio.h>
#include<sys/types.h>
#include<unistd.h>
#include<fcntl.h>
#include<stdlib.h>
int main()
{
char buf[6]={0};
char buf1[6]={0};
int fd = open("file",O_RDWR|O_CREAT,0644);
if(fd < 0)
printf("open error");
printf("fd:%d\n",fd);
//輸出fd=3;
write(fd,"hello,world",12);
lseek(fd,0,SEEK_SET); //將檔案偏移量置為0,就是從第一個字元開始讀(h開始)
read(fd,buf,5);
printf("fd:%s",buf);//輸出hello
int fd1 = dup(fd);
read(fd1,buf1,5); //之前做的是對fd的讀寫操作,並沒有對fd1做任何操作。但在這對fd1進行了讀,如果輸出資料。說明fd和fd1是同步的(fd做了什麼相當於fd1也做了什麼)
printf("fd1:%s\n",buf1); //輸出,worl
//既然輸出的是fd中的內容,說明fd和fd1共用一個檔案表,讀到的是,worl,而不是hello(我們在上面將偏移量從第一個字元開始,輸出hello之後,fd的偏移量距離開始有5個字元當我們再次讀fd的時候,它是從第6個字元開始讀的,很明顯,第6個是逗號,往後讀5個,就是,worl),說明偏移量是一致的。(其實不用寫偏移量,因為共用檔案表就意味著檔案偏移量也共用)
printf("fd1:%d\n",fd1);//輸出fd1 = 4
//fd=3不等於fd1說明不共用同一個檔案描述符。這也是dup和dup2的區別。
close(fd);
close(fd1);
return 0;
}
(2)dup2函式
fd2 = dup2(fd,fd1);
fd2用的fd1(第二個引數)的描述符,用的fd(第一個引數)的檔案(和fd共享一個檔案表,當然也共享檔案偏移量)
強調第幾個引數是因為如果你寫成fd2=dup2(fd1,fd);那麼fd2 =fd,和fd1共享同一個檔案表。
#inclue<stdio.h>
#include<sys/types.h>
#include<unistd.h>
#include<fcntl.h>
#include<stdlib.h>
int main()
{
int fd = open("file",O_RDWR|O_CREAT,0644);
if(fd < 0)
printf("open error");
printf("fd:%d\n",fd);
//輸出fd=3;
int fd1 =open("text",,O_RDWR|O_CREAT,0644);
if(fd1 < 0)
printf("open error");
printf("fd1:%d\n",fd1);
//輸出fd1=4;
int fd2 = dup2(fd,fd1);
printf("fd2:%d\n",fd2);
//輸出fd2=4;
//fd1 =fd2=4;說明fd2使用了fd1的檔案描述符。
char buf[12]="hello,world";
write(fd,buf,12); //我們對fd進行了寫,並沒有對fd2進行寫
read(fd2,buf,12);//但是我們對fd2讀的時候,如果沒有寫,怎麼可能讀出來呢
printf("fd2:%s\n",buf);//事實是讀出來了
//輸出fd2:hello,world //說明fd和fd2共用一個檔案表。
lseek(fd,5,SEEK_SET);//距離開始偏移5位,說明下次讀的時候是從第6個開始,注意我們是對fd進行偏移,沒有對fd2偏移
read(fd2,buf,5); //但是如果讀fd2結果是從第6個字元開始的
buf[5]=0; //如果不寫這句,輸出的buf是按照12個字元輸出的。因為定義buf的時候陣列中可以放12個字元。
printf("fd2:%s\n",buf);//輸出fd2:,worl //說明fd2和fd共享檔案偏移量。
close(fd);
close(fd2);
return 0;
}
dup和dup2的區別
dup:fd1= dup(fd);目標描述符使用了fd的檔案表
dup2:fd2 = dup2(fd1,fd)目標描述符使用了fd1的描述符,使用了fd的檔案表
linux程式設計之pipe()函式
管道是一種把兩個程序之間的標準輸入和標準輸出連線起來的機制,從而提供一種讓多個程序間通訊的方法,當程序建立管道時,每次都需要提供兩個檔案描述符來操作管道。其中一個對管道進行寫操作,另一個對管道進行讀操作。對管道的讀寫與一般的IO系統函式一致,使用write()函式寫入資料,使用read()讀出資料。
#include<unistd.h>
int pipe(int filedes[2])
返回值:成功,返回0,否則返回-1。引數陣列包含pipe使用的兩個檔案的描述符。fd[0]:讀管道,fd[1]寫管道。
必須在fork()中呼叫pipe(),否則子程序不會繼承檔案描述符。兩個程序不共享祖先程序,就不能使用pipe。但是可以使用命名管道。
1 #include<stdio.h>
2 #include<stdlib.h>
3 #include<string.h>
4 #include<unistd.h>
5 #include<sys/types.h>
6 int main(void){
7 int result=-1;
8 int fd[2],nbytes;
9 pid_t pid;
10 char string[]="hell world, my pipe!";
11 char readbuffer[100];
12 int *write_fd=&fd[1];
13 int *read_fd=&fd[0];
14 result=pipe(fd);;
15 if(-1==result){
16 printf("fail to create pipe\n");
17 return -1;
18 }
19 pid=fork();
20 if(-1==pid){
21 printf("fail to fork\n");
22 return -1;
23 }
24 if(0==pid){
25 close(*read_fd);
26 result=write(*write_fd,string,strlen(string));
27 return 0;
28 }else{
29 close(*write_fd);
30 nbytes=read(*read_fd,readbuffer,sizeof(readbuffer));
31 printf("the parent receive %d bytes data: %s \n",nbytes,readbuffer);
32 }
33 return 0;
34 }
the parent receive 20 bytes data: hell world, my pipe!
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#define COUNT (10)
int main(int argc, char *argv[])
{
int pipefd[2];
int read_count = 0;
char buf[COUNT] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9'};;
if (pipe(pipefd) == -1) {
perror("call pipe failed \n");
exit(EXIT_FAILURE);
}
printf("write %d chars to pipe1 \n", COUNT);
write(pipefd[1], buf, COUNT);
while (read(pipefd[0], &buf, 1) > 0)
{
printf("read %c from pipe0\n", buf[0]);
read_count++;
if(read_count == COUNT)
{
printf("total read %d chars \n", read_count);
break;
}
}
close(pipefd[0]);
close(pipefd[1]);
}
編譯及執行結果:
[[email protected] core_dump]# gcc pipe.c
[[email protected] core_dump]# ./a.out
write 10 chars to pipe1
read 0 from pipe0
read 1 from pipe0
read 2 from pipe0
read 3 from pipe0
read 4 from pipe0
read 5 from pipe0
read 6 from pipe0
read 7 from pipe0
read 8 from pipe0
read 9 from pipe0
total read 10 chars
從shell中執行一個程序,預設會有3個檔案描述符存在(0、1、2), 0與程序的標準輸入相關聯,1與程序的標準輸出相關聯,2與程序的標準錯誤輸出相關聯,一個程序當前有哪些開啟的檔案描述符可以通過/proc/程序ID/fd目錄檢視
C語言提供了幾個標準庫函式,可以將任意型別(整型、長整型、浮點型等)的數字轉換為字串。
1.int/float to string/array:
C語言提供了幾個標準庫函式,可以將任意型別(整型、長整型、浮點型等)的數字轉換為字串,下面列舉了各函式的方法及其說明。
● itoa():將整型值轉換為字串。
● ltoa():將長整型值轉換為字串。
● ultoa():將無符號長整型值轉換為字串。
● gcvt():將浮點型數轉換為字串,取四捨五入。
● ecvt():將雙精度浮點型值轉換為字串,轉換結果中不包含十進位制小數點。
● fcvt():指定位數為轉換精度,其餘同ecvt()。
除此外,還可以使用sprintf系列函式把數字轉換成字串,其比itoa()系列函式執行速度慢
2. string/array to int/float
C/C++語言提供了幾個標準庫函式,可以將字串轉換為任意型別(整型、長整型、浮點型等)。
● atof():將字串轉換為雙精度浮點型值。
● atoi():將字串轉換為整型值。
● atol():將字串轉換為長整型值。
● strtod():將字串轉換為雙精度浮點型值,並報告不能被轉換的所有剩餘數字。
● strtol():將字串轉換為長整值,並報告不能被轉換的所有剩餘數字。
● strtoul():將字串轉換為無符號長整型值,並報告不能被轉換的所有剩餘數字。
以下是用itoa()函式將整數轉換為字串的一個例子:
# include <stdio.h>
# include <stdlib.h>
void main (void)
{
int num = 100;
char str[25];
itoa(num, str, 10);
printf("The number 'num' is %d and the string 'str' is %s. \n" ,
num, str);
}
itoa()函式有3個引數:第一個引數是要轉換的數字,第二個引數是要寫入轉換結果的目標字串,第三個引數是轉移數字時所用 的基數。在上例中,轉換基數為10。10:十進位制;2:二進位制...
C語言pthread_create傳遞帶多個引數的函式& pthread_join
pthread_create是類Unix作業系統(Unix、Linux、Mac OS X等)的建立執行緒的函式,標頭檔案在pthread.h中。函式的宣告如下:
int pthread_create(pthread_t *tidp,const pthread_attr_t *attr,
(void*)(*start_rtn)(void*),void *arg);
//返回值:若成功則返回0,否則返回錯誤編號
引數
第一個引數為指向執行緒識別符號的指標。
第二個引數用來設定執行緒屬性。
第三個引數是執行緒執行函式的起始地址。
最後一個引數是執行函式的引數。
從第三個函式可以看到,傳入的函式引數需要為void*型別。但是很多情況下需要執行緒處理的函式是多引數的。可以通過把引數封裝成結構體的方式來實現傳遞帶多個引數的函式。
struct fun_para
{
var para1;//引數1
var para2;//引數2
.......
}
將這個結構體指標,作為void *形參的實際引數傳遞
struct fun_para para;
pthread_create(&ntid, NULL, thr_fn,¶);
接著線上程的呼叫函式thr_fn中可以通過下面的方式使用通過para傳入的引數。
void *thr_fn(void *arg)
{
fun_para *para;
para = (fun_para *) arg;
para->para1;//引數1
para->para2;//引數2
......
//pthread_exit(0);
return ((void *)0);
}
Additional Mark: 程式碼中如果沒有pthread_join,主執行緒會很快結束從而使整個程序結束,從而使建立的執行緒沒有機會開始執行就結束了。加入pthread_join後,主執行緒會一直等待直到等待的執行緒結束自己才結束,使建立的執行緒有機會執行。
函式定義:
int pthread_join(pthread_t thread, void **retval);
- 1
描述 : pthread_join()函式,以阻塞的方式等待thread指定的執行緒結束。當函式返回時,被等待執行緒的資源被收回。如果執行緒已經結束,那麼該函式會立即返回。並且thread指定的執行緒必須是joinable的。
引數: thread: 執行緒識別符號,即執行緒ID,標識唯一執行緒。retval: 使用者定義的指標,用來儲存被等待執行緒的返回值。
返回值 : 0代表成功。 失敗,返回的則是錯誤號。
tmp1 = pthread_join(tid, &retval);
if (tmp1 != 0)
{
printf("cannot join with thread1\n");
}
多執行緒下變數-原子操作 __sync_fetch_and_add等等
當然我們知道,count++這種操作不是原子的。一個自加操作,本質是分成三步的:
1 從快取取到暫存器
2 在暫存器加1
3 存入快取。
由於時序的因素,多個執行緒操作同一個全域性變數,會出現問題。這也是併發程式設計的難點。在目前多核條件下,這種困境會越來越彰顯出來。
最簡單的處理辦法就是加鎖保護,這也是我最初的解決方案。看下面的程式碼:
pthread_mutex_t count_lock = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_lock(&count_lock);
global_int++;
pthread_mutex_unlock(&count_lock);
後來在網上查詢資料,找到了__sync_fetch_and_add系列的命令,發現這個系列命令講的最好的一篇文章,英文好的同學可以直接去看原文。Multithreaded simple data type access and atomic variables
__sync_fetch_and_add系列一共有十二個函式,有加/減/與/或/異或/等函式的原子性操作函式,__sync_fetch_and_add,顧名思義,現fetch,然後自加,返回的是自加以前的值。以count = 4為例,呼叫__sync_fetch_and_add(&count,1),之後,返回值是4,然後,count變成了5.
有__sync_fetch_and_add,自然也就有__sync_add_and_fetch,呵呵這個的意思就很清楚了,先自加,在返回。他們哥倆的關係與i++和++i的關係是一樣的。被譚浩強他老人家收過保護費的都會清楚了。
有了這個寶貝函式,我們就有新的解決辦法了。對於多執行緒對全域性變數進行自加,我們就再也不用理執行緒鎖了。下面這行程式碼,和上面被pthread_mutex保護的那行程式碼作用是一樣的,而且也是執行緒安全的。
__sync_fetch_and_add( &global_int, 1 );
下面是這群函式的全家福,大家看名字就知道是這些函式是幹啥的了。
在用gcc編譯的時候要加上選項 -march=i686
type __sync_fetch_and_add (type *ptr, type value);
type __sync_fetch_and_sub (type *ptr, type value);
type __sync_fetch_and_or (type *ptr, type value);
type __sync_fetch_and_and (type *ptr, type value);
type __sync_fetch_and_xor (type *ptr, type value);
type __sync_fetch_and_nand (type *ptr, type value);
type __sync_add_and_fetch (type *ptr, type value);
type __sync_sub_and_fetch (type *ptr, type value);
type __sync_or_and_fetch (type *ptr, type value);
type __sync_and_and_fetch (type *ptr, type value);
type __sync_xor_and_fetch (type *ptr, type value);
type __sync_nand_and_fetch (type *ptr, type value);