文章出處

以下代碼在Federo9上試驗成功。

 

一、格式化輸入16進制字符串

printf("Format:%.2x\n",10);

輸入結果:

image

 

二、測試各類型的占用的字節數

int main(int argc, char *argv[])
{
    int OutputHex = 1;
    unsigned char aValue=10;
    char Buffer[10];
    int len=sprintf(Buffer, OutputHex ? "%.2X  " : "%c", aValue);
    printf("Len:%d  Format:%s\n",len,Buffer);
    printf("Size Of char:%d\n",sizeof(char));
    printf("Size Of unsigned char:%d\n",sizeof(unsigned char));
    printf("Size Of int:%d\n",sizeof(int));
    return EXIT_SUCCESS;
}

輸入結果:

image

三、使用getopt函數來獲取參數

當我們運行Linux下的C語言程序的時候,就可以非常方便地用getopt()這個函數將main參數中的argv提取出來,按需進行處理。函數的使用見以下代碼段。

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>

int main(int argc, char *argv[])
{
    int opt;

    while((opt = getopt(argc, argv, ":if:lr")) != -1) {
        switch(opt) {
        case 'i':
        case 'l':
        case 'r':
            printf("option: %c\n", opt);
            break;
        case 'f':
            printf("filename: %s\n", optarg);
            break;
        case ':':
            printf("option needs a value\n");
            break;
        case '?':
            printf("unknown option: %c\n", optopt);
            break;
        }
    }
    for(; optind < argc; optind++)
        printf("argument: %s\n", argv[optind]);
    exit(0);
}

image

四、exit()和_exit()函數

1、_exit()執行后立即返回給內核,而exit()要先執行一些清除操作,然后將控制權交給內核。

2、exit中的參數exit_code為0代表進程正常終止,若為其他值表示程序執行過程中有錯誤發生。

五、用fprintf實現printf

大家可以通過以下代碼看看兩者的異同。

#include <stdio.h>
int main(void){
   printf("Hello world using printf\n");
   fprintf(stdout, "Hello world To stdout\n");
   fprintf(stderr, "Hello world To stderr\n");
}
stdoutstderr都是打印在屏幕的,因為標準錯誤輸出流對象(stderr)就是定義為屏幕

輸出結果:

image

小記:

關于 stdin、stdout、stderr 的說明如下:

stdout(Standard Output Stream)標準輸出

stdin(Standard Input Stream)標準輸入

stderr(Standard Error Output)標準錯誤輸出

默認情況下,標準輸入(stdin)指的從鍵盤上讀數據,而標準輸出(stdout)標準錯誤輸出(stderr)是指屏幕

六、捕捉Esc按鍵

捕捉其他按鍵的程序跟這個差不多,總的思路就是通過判斷鍵盤掃描碼進行對應的處理。

#include <stdio.h>
int main ( void )
{
    unsigned char aValue;
    printf ( "Enter a Char\n" );
    aValue = getchar();
    if ( aValue == '\x1b' )
    {
        printf ( "Esc pressed %c\n",aValue );
    }else
    {
        printf ( "Esc Not pressed%c\n",aValue );
    }
}

結果:

image

七、避免getchar()讀入用于結束輸入的換行符

#include <stdio.h>
int main ( void )
{
    unsigned char aValue;
    do{
        printf ( "Enter a Char\n" );
        do{
            aValue = getchar();
        }while(aValue=='\n' );//避免讀入\n
        if ( aValue == '\x1b' )
        {
            printf ( "Esc pressed.\t \n" );
        }else
        {
            printf ( "Esc Not pressed.\t%c Pressed\n",aValue );
        }
    }while(aValue!='q' );//讀到q的時候,退出
}

八、如果stdout已經被重定向,可以將消息寫到stderr(標準錯誤輸出),stderr不會被重定向。

另外,Linux是一個多用戶的系統,我們可以通過利用’/dev/tty‘正確地將信息輸出到用戶正在使用的終端上。

九、fileno()函數

功    能:把文件流指針轉換成文件描述符
相關函數:open, fopen
表頭文件:#include <stdio.h>
定義函數:int fileno(FILE *stream)
函數說明:fileno()用來取得參數stream指定的文件流所使用的文件描述詞
返回值  :返回和stream文件流對應的文件描述符。如果失敗,返回-1。
范例:

#include <stdio.h> 
main() 
{ 
     FILE   *fp; 
     int   fd; 
     fp = fopen("/etc/passwd", "r"); 
     fd = fileno(fp); 
     printf("fd = %d\n", fd); 
     fclose(fp); 
}

    文件描述詞是Linux編程中的一個術語。當一個文件打開后,系統會分配一部分資源來保存該文件的信息,以后對文件的操作就可以直接引用該部分資源了。文件描述詞可以認為是該部分資源的一個索引,在打開文件時返回。在使用fcntl函數對文件的一些屬性進行設置時就需要一個文件描述詞參數。
    以前知道,當程序執行時,就已經有三個文件流打開了,它們分別是標準輸入stdin,標準輸出stdout和標準錯誤輸出stderr。和流式文件相對應的是,也有三個文件描述符被預先打開,它們分別是0,1,2,代表標準輸入、標準輸出和標準錯誤輸出。
    需要指出的是,上面的流式文件輸入、輸出和文件描述符的輸入輸出方式不能混用,否則會造成混亂。

 

十、fgets會自動將字符串的最后一位置為’\0’

請看一下下面的代碼,我們期待輸入’12345678’(8個數),用fgets函數輸出’12345678’(8個數).

#include <stdio.h> /*標準輸入輸出定義*/
#define PASSWORD_LEN 8

int main(){
        char password[PASSWORD_LEN+1];
        printf("請輸入您的密碼\n");
        fgets(password,PASSWORD_LEN,stdin);
        printf("您輸入的密碼是:%s\n",password);
}

輸出結果:

image

我們可以看出,輸出的結果并不是我們所期望的,但是我們也命名在fget里面寫了,我們期望的獲取8個數(PASSWORD_LEN==8)。原來fget一次調用只能傳輸n-1個字符,因為它必須把空字節’\0’加上以結束字符串。也就是,如果我們要讓輸出跟我們的期望相符,那么我們要修改fget函數的參數,如下代碼段所示。

fgets(password,PASSWORD_LEN+1,stdin);

 

 

十一、malloc函數的學習

#include <stdlib.h>
#include <string.h>
int main(){
    char * s="123.33";    
    int i=0;
    char **endp= (char **)malloc(sizeof(int)*5);//先分配五個整型數據空間,這5個空間就可以用來存儲每個二級指針的首地址了。
   
    for(i=0;i<5;i++){
        endp[i]=(char *)malloc(sizeof(char)*5);//再為每個二級指針分配5個char型數據空間,這樣,每個元素也就擁有了各自的家。只是這5個空間是用來存儲char型的!!endp[0]5個,endp[1]5個等等
    }

    strcpy(endp[0],"12345");
    for(i=0;i<5;i++){
        printf("endp[%d] is :%s\t,endp的地址是%d\n",i,*(endp+i),endp+i);//可以看出剛分配完的時候,所有的5個endp一維數組的內容都是空
    }
    for(i=0;i<5;i++){
        printf("endp[0][%d] is :%c,\tendp[0][%d]的地址是%d\n",i,*((*endp)+i),i,((*endp)+i)    );
    }

}

其實可以用malloc動態分配二維數組空間的。

運行結果:

image

 

十二、從一段代碼中看printf的執行順序(編譯環境為gcc)

printf("++a=%d,a++=%d\n",++a,a++);

結果分析:

如果輸出++a=1,a++=1,那么說明函數是從左執行到右的;如果++a=2,a++=0,那么說明函數是從右執行到左的。

結果截圖:

image

 

 

十三、獲取系統當前的時間

首先,讓我們先看看兩個跟時間有關的類型time_t,tm。

time_t:它是一個大到能容納以秒計算的日期和時間的整數類型。在32位系統上就是32位的的。

tm結構體被定義為至少包含下表所示的成員。

image

以下代碼段展示的是如何獲取當前的時間

#include <time.h>
#include <stdio.h>
#include <stdlib.h>

int main ( int argc, char *argv[] )
{
    struct tm *tm_ptr,timestruct;
    time_t the_time;
    char buf[256];      //用來保存時間字符串
    char *result;       //用來保存不能成功轉化的字符串指針
    ( void ) time ( &the_time );
    printf ( "the current time is : %s",ctime ( &the_time ) );//獲取當前的時間
    tm_ptr = &timestruct;
    result = strptime ( ctime ( &the_time ),"%a %b %d %H:%M:%S %Y", tm_ptr );
    printf ( "strptime gives:\n" );
    printf ( "date: %02d/%02d/%02d\n",
             tm_ptr->tm_year % 100, tm_ptr->tm_mon+1, tm_ptr->tm_mday );
    printf ( "time: %02d:%02d:%02d\n",
             tm_ptr->tm_hour, tm_ptr->tm_min,tm_ptr->tm_sec );
    exit ( EXIT_SUCCESS );
}

十四、設置文件的訪問模式,可以利用fcntl.

十五、利用inline可以解決一些頻繁調用的小涵數大量消耗棧空間或是叫棧內存的問題

      在c中,為了解決一些頻繁調用的小涵數大量消耗棧空間或是叫棧內存的問題,特別的引入了inline修飾符,表示為內聯涵數。
      可能說到這里,很多人還不明白什么是棧空間,其實棧空間就是指放置程式的局部數據也就是函數內數據的內存空間,在系統下,棧空間是有限的,假如頻繁大量的使用就會造成因棧空間不足所造成的程式出錯的問題,涵數的死循環遞歸調用的最終結果就是導致棧內存空間枯竭。
下面我們來看一個例子

#include <stdio.h>

inline char* dbtest ( int a ); //函數原形聲明為inline即:內聯涵數

int main()
{
    int i = 0;
    for ( i=1;i<=10;i )
    {
        printf ( "%d is %s\n",i,dbtest ( i ) );
    }
    return 0;
}

char* dbtest ( int a ) //這里不用再次inline,當然加上inline也是不會出錯的
{
    return ( a%2>0 ) ?"":"";
}

 

     上面的例子就是標準的內聯涵數的用法,使用inline修飾帶來的好處我們表面看不出來,其實在內部的工作就是在每個for循環的內部任何調用 dbtest(i)的地方都換成了(i%2>0)?"奇":"偶"這樣就避免了頻繁調用函數對棧內存重復開辟所帶來的消耗。
      說到這里很多人可能會問,既然inline這么好,還不如把所謂的函數都聲明成inline,嗯,這個問題是要注意的,inline的使用是有所限制的,inline只適合涵數體內代碼簡單的涵數使用,不能包含復雜的結構控制語句例如while switch,并且不能內聯函數本身不能是直接遞歸函數(自己內部還調用自己的函數)。
      說到這里我們不得不說一下在c語言中廣泛被使用的#define語句,是的define的確也能夠做到inline的這些工作,但是define是會產生副作用的,尤其是不同類型參數所導致的錯誤,由此可見inline有更強的約束性和能夠讓編譯器檢查出更多錯誤的特性,在c 中是不推薦使用define的。

 

參考

《Linux程序設計 第四版》

 

 

 

 

 

linux c inline


文章列表


不含病毒。www.avast.com
arrow
arrow
    全站熱搜
    創作者介紹
    創作者 大師兄 的頭像
    大師兄

    IT工程師數位筆記本

    大師兄 發表在 痞客邦 留言(0) 人氣()