C/C++編程筆記:C語言入門知識點(三),請收藏C語言最全筆記!

今天咱們繼續來學習C語言的入門知識點,第一課:C/C++編程筆記:C語言入門知識點(二),請收藏C語言最全筆記!程序員

21. 輸入 & 輸出

當咱們提到輸入時,這意味着要向程序填充一些數據。輸入能夠是以文件的形式或從命令行中進行。C 語言提供了一系列內置的函數來讀取給定的輸入,並根據須要填充到程序中。express

當咱們提到輸出時,這意味着要在屏幕上、打印機上或任意文件中顯示一些數據。C 語言提供了一系列內置的函數來輸出數據到計算機屏幕上和保存數據到文本文件或二進制文件中。編程

標準輸出數組

C 語言把全部的設備都看成文件。因此設備(好比顯示器)被處理的方式與文件相同。如下三個文件會在程序執行時自動打開,以便訪問鍵盤和屏幕。微信

 

 

文件指針是訪問文件的方式,本節將講解如何從屏幕讀取值以及如何把結果輸出到屏幕上。編程語言

C 語言中的 I/O (輸入/輸出) 一般使用 printf() 和 scanf() 兩個函數。編輯器

scanf() 函數用於從標準輸入(鍵盤)讀取並格式化, printf() 函數發送格式化輸出到標準輸出(屏幕)。ide

例子:函數

void main(){工具

      float f;

    printf("Enter a float number: \n");

    // %f 匹配浮點型數據

    scanf("%f",&f);

    printf("Value = %f", f);

}

輸出:

Enter a float number:

12.3

Value = 12.300000

 

getchar()&putchar() 函數

int getchar(void)函數從屏幕讀取下一個可用的字符,並把它返回爲一個整數。這個函數在同一個時間內只會讀取一個單一的字符。您能夠在循環內使用這個方法,以便從屏幕上讀取多個字符。

int putchar(int c)函數把字符輸出到屏幕上,並返回相同的字符。這個函數在同一個時間內只會輸出一個單一的字符。您能夠在循環內使用這個方法,以便在屏幕上輸出多個字符。

void main(){

    int c;

    printf( "\nEnter a value :");

    //函數從屏幕讀取下一個可用的字符,並把它返回爲一個整數。這個函數在同一個時間內只會讀取一個單一的字符。您能夠在循環內使用這個方法,以便從屏幕上讀取多個字符。

    c = getchar( );

    printf( "\nYou entered: ");

    //讀取第一個字符

    putchar( c );

}

輸出:

Enter a value :abcdef

You entered: a

 

gets() & puts() 函數

char *gets(char *s)函數從stdin讀取一行到s所指向的緩衝區,直到一個終止符或 EOF。

int puts(const char *s)函數把字符串 s 和一個尾隨的換行符寫入到stdout

void main(){

    char str[100];

    printf( "\nEnter a value :");

    //讀取一行

    gets( str );

    printf( "\nYou entered: ");

    puts( str );

}

輸出:

Enter a value :你們好,纔是真的好!

You entered: 你們好,纔是真的好!

 

22. 文件讀寫

上一節咱們講解了 C 語言處理的標準輸入和輸出設備。本章咱們將介紹 C 程序員如何建立、打開、關閉文本文件或二進制文件。

一個文件,不管它是文本文件仍是二進制文件,都是表明了一系列的字節。C 語言不只提供了訪問頂層的函數,也提供了底層(OS)調用來處理存儲設備上的文件。本章將講解文件管理的重要調用。

打開文件

您可使用fopen( )函數來建立一個新的文件或者打開一個已有的文件,這個調用會初始化類型FILE的一個對象,類型FILE包含了全部用來控制流的必要的信息。下面是這個函數調用的原型:

FILE *fopen( const char * filename, const char * mode );

在這裏,filename 是字符串,用來命名文件,訪問模式 mode 的值能夠是下列值中的一個:

 

 

若是處理的是二進制文件,則須要使用下面的訪問模式來取代上面的訪問模式:

"rb", "wb", "ab", "rb+", "r+b", "wb+", "w+b", "ab+", "a+b"

 

關閉文件

爲了關閉文件,請使用 fclose( ) 函數。函數的原型以下:

int fclose( FILE *fp );

若是成功關閉文件,fclose( )函數返回零,若是關閉文件時發生錯誤,函數返回EOF。這個函數實際上,會清空緩衝區中的數據,關閉文件,並釋放用於該文件的全部內存。EOF 是一個定義在頭文件stdio.h中的常量。

C 標準庫提供了各類函數來按字符或者以固定長度字符串的形式讀寫文件。

 

寫入文件

下面是把字符串寫入到流中的最簡單的函數:

int fputc(int c,FILE *fp);

函數 fputc() 把參數 c 的字符值寫入到 fp 所指向的輸出流中。若是寫入成功,它會返回寫入的字符,若是發生錯誤,則會返回 EOF。您可使用下面的函數來把一個以 null 結尾的字符串寫入到流中:

int fputs( const char *s, FILE *fp );

函數fputs()把字符串s寫入到 fp 所指向的輸出流中。若是寫入成功,它會返回一個非負值,若是發生錯誤,則會返回EOF。您也可使用int fprintf(FILE *fp,const char *format, ...)函數來寫把一個字符串寫入到文件中。嘗試下面的實例:

 

 

void main(){

      //定義一個空指針文件

    FILE *fp = NULL;

    //打開文件,打開一個文本文件,容許讀寫文件。

    // 若是文件不存在,則會建立一個新文件。

    // 讀取會從文件的開頭開始,寫入則只能是追加模式。

    fp = fopen("/Users/devyk/Data/ClionProjects/NDK_Sample/README.md","a+");

    fprintf(fp, " fprintf 我是添加進來的1\n");

    fprintf(fp, "fprintf 我是添加進來的2\n");

    fputs("fputs 我是添加進來的1\n", fp);

    fputs("fputs 我是添加進來的2\n", fp);

    fclose(fp);

}

 

讀取文件

下面是從文件讀取單個字符的最簡單的函數:

int fgetc( FILE * fp );

fgetc() 函數從 fp 所指向的輸入文件中讀取一個字符。返回值是讀取的字符,若是發生錯誤則返回 EOF。下面的函數容許您從流中讀取一個字符串:

char *fgets( char *buf, int n, FILE *fp );

函數fgets()從 fp 所指向的輸入流中讀取 n - 1 個字符。它會把讀取的字符串複製到緩衝區buf,並在最後追加一個null字符來終止字符串。

若是這個函數在讀取最後一個字符以前就遇到一個換行符 '\n' 或文件的末尾 EOF,則只會返回讀取到的字符,包括換行符。您也可使用int fscanf(FILE *fp, const char *format, ...)函數來從文件中讀取字符串,可是在遇到第一個空格和換行符時,它會中止讀取。

 

 

void main(){

    FILE *fp = NULL;

    //讀取文件

    char buff[255];

    fp = fopen("/Users/devyk/Data/ClionProjects/NDK_Sample/README.md","r");

    fscanf(fp,"%s",buff);

    printf("1: %s\n", buff);

    fgets(buff, 255, (FILE*)fp);

    printf("2: %s\n", buff);

    fgets(buff, 255, (FILE*)fp);

    printf("3: %s\n", buff );

    fclose(fp);

}

 

23. 預處理器

C 預處理器不是編譯器的組成部分,可是它是編譯過程當中一個單獨的步驟。簡言之,C 預處理器只不過是一個文本替換工具而已,它們會指示編譯器在實際編譯以前完成所需的預處理。咱們將把 C 預處理器(C Preprocessor)簡寫爲 CPP。

全部的預處理器命令都是以井號(#)開頭。它必須是第一個非空字符,爲了加強可讀性,預處理器指令應從第一列開始。下面列出了全部重要的預處理器指令:

 

 

 

 

 

例子:

分析下面的實例來理解不一樣的指令。

#define MAX_ARRAY_LENGTH 20

(1)這個指令告訴 CPP 把全部的 MAX_ARRAY_LENGTH 替換爲 20。使用 #define 定義常量來加強可讀性。

#include <stdio.h>

#include "utils.h"

(2)這些指令告訴 CPP 從系統庫中獲取 stdio.h,並添加文本到當前的源文件中。下一行告訴 CPP 從本地目錄中獲取 utils.h,並添加內容到當前的源文件中。

#undef FILE_SIZE

#define FILE_SIZE 42

(3)這個指令告訴 CPP 取消已定義的 FILE_SIZE,並定義它爲 42。

#ifndef MESSAGE

  #define MESSAGE "You wish!"

#endif

這個指令告訴 CPP 只有當 MESSAGE 未定義時,才定義 MESSAGE。

#ifdef DEBUG

  /* Your debugging statements here */

#endif

這個指令告訴 CPP 若是定義了 DEBUG,則執行處理語句。在編譯時,若是您向 gcc 編譯器傳遞了-DDEBUG開關量,這個指令就很是有用。它定義了 DEBUG,您能夠在編譯期間隨時開啓或關閉調試。

 

預約義宏

ANSI C 定義了許多宏。在編程中您可使用這些宏,可是不能直接修改這些預約義的宏。

 

 

 

預處理器運算符

C 預處理器提供了下列的運算符來幫助您建立宏:

宏延續運算符()

一個宏一般寫在一個單行上。可是若是宏太長,一個單行容納不下,則使用宏延續運算符(\)。例如:

#define message_for(a, b) \

    printf(#a " and " #b ": We love you!\n")

 

字符串常量化運算符(#)

在宏定義中,當須要把一個宏的參數轉換爲字符串常量時,則使用字符串常量化運算符(#)。在宏中使用的該運算符有一個特定的參數或參數列表。例如:

#include <stdio.h>

#define  message_for(a, b)  \

    printf(#a " and " #b ": We love you!\n")

int main(void)

{

  message_for(Carole, Debra);

  return 0;

}

當上面的代碼被編譯和執行時,它會產生下列結果:

Carole and Debra: We love you!

 

標記粘貼運算符(##)

宏定義內的標記粘貼運算符(##)會合並兩個參數。它容許在宏定義中兩個獨立的標記被合併爲一個標記。例如:

#include <stdio.h>

#define tokenpaster(n) printf ("token" #n " = %d", token##n)

int main(void)

{

  int token34 = 40;

 

  tokenpaster(34);

  return 0;

}

當上面的代碼被編譯和執行時,它會產生下列結果:

token34 = 40

這是怎麼發生的,由於這個實例會從編譯器產生下列的實際輸出:

printf ("token34 = %d", token34);

這個實例演示了 token##n 會鏈接到 token34 中,在這裏,咱們使用了字符串常量化運算符(#)和標記粘貼運算符(##)

 

defined() 運算符

預處理器defined運算符是用在常量表達式中的,用來肯定一個標識符是否已經使用 #define 定義過。若是指定的標識符已定義,則值爲真(非零)。若是指定的標識符未定義,則值爲假(零)。下面的實例演示了 defined() 運算符的用法:

#include <stdio.h>

#if !defined (MESSAGE)

  #define MESSAGE "You wish!"

#endif

int main(void)

{

  printf("Here is the message: %s\n", MESSAGE); 

  return 0;

}

當上面的代碼被編譯和執行時,它會產生下列結果:

Here is the message: You wish!

參數化的宏

CPP 一個強大的功能是可使用參數化的宏來模擬函數。例如,下面的代碼是計算一個數的平方:

int square(int x) {

  return x * x;

}

咱們可使用宏重寫上面的代碼,以下:

#define square(x) ((x) * (x))

在使用帶有參數的宏以前,必須使用 #define 指令定義。參數列表是括在圓括號內,且必須緊跟在宏名稱的後邊。宏名稱和左圓括號之間不容許有空格。例如:

#include <stdio.h>

#define MAX(x,y) ((x) > (y) ? (x) : (y))

int main(void)

{

  printf("Max between 20 and 10 is %d\n", MAX(10, 20)); 

  return 0;

}

當上面的代碼被編譯和執行時,它會產生下列結果:

Max between 20 and 10 is 20

 

24. 頭文件

頭文件是擴展名爲.h的文件,包含了 C 函數聲明和宏定義,被多個源文件中引用共享。有兩種類型的頭文件:程序員編寫的頭文件和編譯器自帶的頭文件。

在程序中要使用頭文件,須要使用 C 預處理指令#include來引用它。前面咱們已經看過stdio.h頭文件,它是編譯器自帶的頭文件。

引用頭文件至關於複製頭文件的內容,可是咱們不會直接在源文件中複製頭文件的內容,由於這麼作很容易出錯,特別在程序是由多個源文件組成的時候。

A simple practice in C 或 C++ 程序中,建議把全部的常量、宏、系統全局變量和函數原型寫在頭文件中,在須要的時候隨時引用這些頭文件。

引用頭文件的語法

使用預處理指令#include能夠引用用戶和系統頭文件。它的形式有如下兩種:

#include <file>

這種形式用於引用系統頭文件。它在系統目錄的標準列表中搜索名爲 file 的文件。在編譯源代碼時,您能夠經過 -I 選項把目錄前置在該列表前。

#include "file"

這種形式用於引用用戶頭文件。它在包含當前文件的目錄中搜索名爲 file 的文件。在編譯源代碼時,您能夠經過 -I 選項把目錄前置在該列表前。

 

引用頭文件的操做

#include指令會指示 C 預處理器瀏覽指定的文件做爲輸入。預處理器的輸出包含了已經生成的輸出,被引用文件生成的輸出以及#include指令以後的文本輸出。例如,若是您有一個頭文件 char_manger.h,以下:

char *test(void);

和一個使用了頭文件的主程序 char_manager.c,以下:

#include "char_manger.h"

int x;

int main (void)

{

  puts (test ());

}

編輯器會看到以下的代碼信息:

char *test (void);

int x;

int main (void)

{

  puts (test ());

}

 

只引用一次頭文件

若是一個頭文件被引用兩次,編譯器會處理兩次頭文件的內容,這將產生錯誤。爲了防止這種狀況,標準的作法是把文件的整個內容放在條件編譯語句中,以下:

#ifndef HEADER_FILE

#define HEADER_FILE

the entire header file file

#endif

這種結構就是一般所說的包裝器#ifndef。當再次引用頭文件時,條件爲假,由於 HEADER_FILE 已定義。此時,預處理器會跳過文件的整個內容,編譯器會忽略它。

 

有條件引用

有時須要從多個不一樣的頭文件中選擇一個引用到程序中。例如,須要指定在不一樣的操做系統上使用的配置參數。您能夠經過一系列條件來實現這點,以下:

#if SYSTEM_1

  # include "system_1.h"

#elif SYSTEM_2

  # include "system_2.h"

#elif SYSTEM_3

  ...

#endif

可是若是頭文件比較多的時候,這麼作是很不穩當的,預處理器使用宏來定義頭文件的名稱。這就是所謂的有條件引用。它不是用頭文件的名稱做爲 #include 的直接參數,您只須要使用宏名稱代替便可:

#define SYSTEM_H "system_1.h"

...

#include SYSTEM_H

 

25. 強制類型轉換

強制類型轉換是把變量從一種類型轉換爲另外一種數據類型。例如,若是您想存儲一個 long 類型的值到一個簡單的整型中,您須要把 long 類型強制轉換爲 int 類型。您可使用強制類型轉換運算符來把值顯式地從一種類型轉換爲另外一種類型,以下所示:

(type_name) expression

請看下面的實例,使用強制類型轉換運算符把一個整數變量除以另外一個整數變量,獲得一個浮點數:

void main(){

  void main(){

    int sum = 20,count = 3;

    double  value,value2;

    value = (double)sum / count;

    value2 = sum / count;

    printf("Value 強轉 : %f Value2 wei強轉 : %f\n ", value ,value2);

}

}

輸出:

Value 強轉 : 6.666667 Value2 wei強轉 : 6.000000

 

整數提高

整數提高是指把小於 int 或 unsigned int 的整數類型轉換爲 int 或 unsigned int 的過程。請看下面的實例,在 int 中添加一個字符:

void main(){

 

    //整數提高

    int i= 17;

    char c = 'c'; //在 ascii 中的值表示 99

    int sum2;

    sum2 = i + c;

    printf("Value of sum : %d\n", sum2 );

}

輸出:

Value of sum : 116

在這裏,sum 的值爲 116,由於編譯器進行了整數提高,在執行實際加法運算時,把 'c' 的值轉換爲對應的 ascii 值。

 

26. 錯誤處理

C 語言不提供對錯誤處理的直接支持,可是做爲一種系統編程語言,它以返回值的形式容許您訪問底層數據。在發生錯誤時,大多數的 C 或 UNIX 函數調用返回 1 或 NULL,同時會設置一個錯誤代碼errno,該錯誤代碼是全局變量,表示在函數調用期間發生了錯誤。您能夠在 errno.h 頭文件中找到各類各樣的錯誤代碼。

因此,C 程序員能夠經過檢查返回值,而後根據返回值決定採起哪一種適當的動做。開發人員應該在程序初始化時,把 errno 設置爲 0,這是一種良好的編程習慣。0 值表示程序中沒有錯誤。

errno、perror() 和 strerror()

C 語言提供了perror()strerror()函數來顯示與errno相關的文本消息。

(1)perror()函數顯示您傳給它的字符串,後跟一個冒號、一個空格和當前 errno 值的文本表示形式。

(2)strerror()函數,返回一個指針,指針指向當前 errno 值的文本表示形式。

讓咱們來模擬一種錯誤狀況,嘗試打開一個不存在的文件。您可使用多種方式來輸出錯誤消息,在這裏咱們使用函數來演示用法。另外有一點須要注意,您應該使用stderr文件流來輸出全部的錯誤。

例子:

void main(){

    int dividend = 20;

    int divsor = 0;

    int quotient;

    if (divsor == 0){

        fprintf(stderr,"除數爲 0 退出運行。。。\n");

        exit(EXIT_FAILURE);

    }

    quotient = dividend / divsor;

    fprintf(stderr,"quotient 變量的值爲 : %d\n", quotient);

    exit(EXIT_SUCCESS);

}

輸出:除數爲 0 退出運行。。。

 

27. 遞歸

遞歸指的是在函數的定義中使用函數自身的方法。

語法格式以下:

void recursion()

{

  statements;

  ... ... ...

  recursion(); /* 函數調用自身 */

  ... ... ...

}

int main()

{

  recursion();

}

 

數的階乘

double factorial(unsigned int i){

    if (i <= 1){

        return 1;

    }

  return i * factorial(i - 1);

}

void main(){

    int i = 15;

    printf("%d 的階乘 %ld \n",i ,factorial(i));

}

輸出:15 的階乘 140732727129776

 

斐波拉契數列

//斐波拉契數列

int fibonaci(int i){

    if (i == 0){

        return 0;

    }

    if (i == 1){

        return 1;

    }

    return fibonaci(i - 1) + fibonaci( i -2);

}

void main(){

    for (int j = 0; j < 10; j++) {

        printf("%d\t\n", fibonaci(j));

    }

}

輸出:

0

1

1

2

3

5

8

13

21

34

 

28. 可變參數

有時,您可能會碰到這樣的狀況,您但願函數帶有可變數量的參數,而不是預約義數量的參數。C 語言爲這種狀況提供了一個解決方案,它容許您定義一個函數,能根據具體的需求接受可變數量的參數。下面的實例演示了這種函數的定義。

int func(int, ... )

{

  .

  .

  .

}

int main()

{

  func(2, 2, 3);

  func(3, 2, 3, 4);

}

請注意,函數func()最後一個參數寫成省略號,即三個點號(...),省略號以前的那個參數是int,表明了要傳遞的可變參數的總數。爲了使用這個功能,您須要使用stdarg.h頭文件,該文件提供了實現可變參數功能的函數和宏。具體步驟以下:

(1)定義一個函數,最後一個參數爲省略號,省略號前面能夠設置自定義參數。

(2)在函數定義中建立一個va_list類型變量,該類型是在 stdarg.h 頭文件中定義的。

(3)使用int參數和va_start宏來初始化va_list變量爲一個參數列表。宏 va_start 是在 stdarg.h 頭文件中定義的。

(4)使用va_arg宏和va_list變量來訪問參數列表中的每一個項。

(5)使用宏va_end來清理賦予va_list變量的內存。

如今讓咱們按照上面的步驟,來編寫一個帶有可變數量參數的函數,並返回它們的平均值:

double average(int num,...){

    va_list  vaList;

    double  sum = 0.0;

    int i ;

    //爲 num 個參數初始化 valist

    va_start(vaList,num);

    //訪問全部賦給 vaList 的參數

    for (int j = 0; j < num; j++) {

        sum += va_arg(vaList, int);

    }

    //清理爲valist 保留的內存

    va_end(vaList);

    return sum/num;

}

void main(){

    printf("Average of 2, 3, 4, 5 = %f\n", average(4, 2,3,4,5));

    printf("Average of 5, 10, 15 = %f\n", average(3, 5,10,15));

}

輸出:

Average of 2, 3, 4, 5 = 3.500000

Average of 5, 10, 15 = 10.000000

 

29. 內存管理

本章將講解 C 中的動態內存管理。C 語言爲內存的分配和管理提供了幾個函數。這些函數能夠在 <stdlib.h> 頭文件中找到。

**注意: ** void * 類型表示未肯定類型的指針。C、C++ 規定 void * 類型能夠經過類型轉換強制轉換爲任何其它類型的指針。

 

動態分配內存

編程時,若是您預先知道數組的大小,那麼定義數組時就比較容易。例如,一個存儲人名的數組,它最多容納 100 個字符,因此您能夠定義數組,以下所示:

char name[100];

可是,若是您預先不知道須要存儲的文本長度,例如您向存儲有關一個主題的詳細描述。在這裏,咱們須要定義一個指針,該指針指向未定義所需內存大小的字符,後續再根據需求來分配內存,以下所示:

void main() {

    char name[100];

    char *description;

    //將字符串 copy 到 name 中

    strcpy(name, "迎娶白富美!");

    //開始動態分配內存

    description = (char *) malloc(200 * sizeof(char));

    if (description == NULL) {

        fprintf(stderr, "Error - unable to allocate required memory\n");

    } else {

        strcpy(description, "開始添加數據到 description 中");

    }

    printf("Name = %s\n", name );

    printf("Description: %s sizeOf 大小 :%d\n", description , sizeof(description));

//    使用 free() 函數釋放內存

    free(description);

}

輸出:

Name = 迎娶白富美!

Description: 開始添加數據到 description 中 sizeOf 大小 :8

 

30. 命令行參數

執行程序時,能夠從命令行傳值給 C 程序。這些值被稱爲命令行參數,它們對程序很重要,特別是當您想從外部控制程序,而不是在代碼內對這些值進行硬編碼時,就顯得尤其重要了。

命令行參數是使用 main() 函數參數來處理的,其中,argc是指傳入參數的個數,argv[]是一個指針數組,指向傳遞給程序的每一個參數。下面是一個簡單的實例,檢查命令行是否有提供參數,並根據參數執行相應的動做:

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

    if (argc ==1){

        printf("argv[%d] == %d",0,*argv[0]);

    }

    else if (argc ==2){

        printf("argv[%d] == %d",1,*argv[1]);

    } else{

        printf("匹配失敗...");

    }

}

輸出:argv[0] == 47

 

總結

到了這裏,咱們連續三天的C語言入門知識點分享,到這裏就結束了,大家收穫了多少呢?筆者也知道大家若是初學的話確定一時間也記不住什麼,因此仍是要記得收藏哈!這個很重要的。

不知道你們在看完 C 基礎內容以後在對比下 Java 語法,是否是大部分都差很少,以前有的人說學了 C 在學其它語言都是小菜一碟,如今看來好像是這麼回事。我的以爲其實只要會編程語言中的任何一門在學其它語言都會比較容易上手。

自學C/C++編程難度很大,不妨和一些志同道合的小夥伴一塊兒學習成長!

C語言C++編程學習交流圈子,【點擊進入微信公衆號:C語言編程學習基地

有一些源碼和資料分享,歡迎轉行也學習編程的夥伴,和你們一塊兒交流成長會比本身琢磨更快哦!

相關文章
相關標籤/搜索