Android 音視頻入門:C語言基礎

概述

C語言對於Android開發來講仍是很是必要的,無論你是要閱讀源碼,仍是想要學習NDK,音視頻,性能優化等,都不可避免須要接觸到C,並且C語言屬於系統級的語言,操做系統內核都有C的身影,因此我今天學習一下C語言,本篇博客做爲筆記,以防之後忘記java

C簡介

C語言最初適用於系統開發工做的,特別是組成操做系統的程序,因爲C語言產生的代碼運行速度與彙編編寫的代碼運行速度幾乎相同,因此採用C語言做爲系統開發語言,下面列舉幾個使用C的實例編程

  • 操做系統
  • 文本編輯器
  • 打印機
  • 網絡驅動器等

C環境的設置

寫在源文件的中代碼,使咱們人類能夠看懂的,他須要編譯轉換爲機器語言,這樣CPU能夠按照指令執行程序,而C語言能夠經過GUN編譯器,把源碼編譯爲機器語言數組

Mac上直接下載Xcode就可使用GUN編譯器,開發工具可使用CLion或者直接用文本編譯器緩存

先寫一個HelloWord

#include <stdio.h>
 
int main() {
   /* 個人第一個 C 程序 */
   printf("第一個c程序Hello, World! \n");
   
   return 0;
}
複製代碼

一個C程序包括性能優化

  • 預處理器指令 : 程序的第一行#include <stdio.h>是預處理指令,告訴C編譯器實際編譯以前須要包括stdio.h文件
  • 函數 : 下一行int main()是主函數,程序從這裏開始運行,printf()是程序中另外一個可用的函數做用是在屏幕上顯示Hello, World!
  • 變量
  • 語句&表達式
  • 註釋: /* ... */這就是一個註釋
  • return 0;標識此函數結束,並返回0

編譯執行C程序

  • 首先打開文本編輯器添加上方代碼
  • 保存文件爲hello.c
  • 打開命令行,進入文件的文件夾
  • 命令行輸入gcc hello.c,編譯代碼
  • 若是沒有錯誤的話,文件夾就會生成a.out的可執行文件
  • 命令行輸入a.out,運行程序
  • 而後屏幕上就能夠看到Hello, World!
Last login: Mon Feb 17 14:53:00 on ttys002
L-96FCG8WP-1504:~ renxiaohui$ cd Desktop/
L-96FCG8WP-1504:Desktop renxiaohui$ cd c/
L-96FCG8WP-1504:c renxiaohui$ gcc hello.c 
L-96FCG8WP-1504:c renxiaohui$ a.out 
第一個c程序 Hello, World! 
L-96FCG8WP-1504:c renxiaohui$ 
複製代碼

C語言入門

1 基本語法

標識符

標記符是用來標記變量,函數或者任何用戶自定的變量名稱,一個標識符以字母A—Z,a-z,或者_下劃線開始,後面跟0個或多個字母,數字,下劃線bash

標識符中不容許出現標點字符,好比@%,標識符是區分大小寫的網絡

關鍵字

這些關鍵字不能做爲常量名或者變量名,其餘標識符的名稱編輯器

關鍵字 說明
continue 結束當前循環,開始下一輪循環
switch 用於開關語句
case 開關語句分支
default 開關語句中的其餘分支
break 跳出當前循環
do 循環語句的循環體
while 循環語句的循環條件
if 條件語句
else 條件語句否認分支與if一塊兒使用
for 一種循環語句
goto 無條件跳轉語句
return 子程序返回語句,可帶參數可不帶參數
char 聲明字符變量或者返回值類型
double 聲明雙精度浮點類型對象或函數返回值類型
float 聲明浮點型變量或返回值類型
short 聲明短整形變量或者返回值類型
int 聲明整形變量或者返回值類型
long 聲明長整形變量或返回值類型
unsigned 聲明無符號類型變量或返回值類型
void 聲明函數無返回值或無參數,聲明無類型指針
enum 聲明枚舉類型
static 聲明靜態變量
auto 自動聲明變量
const 定義常量,若是一個變量被const修飾,則它的值不能被改變
extern 聲明變量或函數在其餘文件或本文件的其餘位置定義
register 聲明寄存器變量
signed 聲明有符號類型的變量
sizeof 計算數據類型或變量的長度
struct 聲明結構體類型
union 聲明共用體
typedef 給數據類型取別名
volatile 說明變量在執行過程當中能夠隱含的改變

能夠看到70%都是java中有的,學習起來並非很難ide

2 數據類型

C中的數據類型能夠分爲如下幾種函數

類型 描述
基本數據類型 他們是算術類型,包含倆種類型,整數類型和浮點數類型
枚舉類型 他們也是算術類型,被用來定義在程序中只能賦予其必定的離散整數值得變量
void 標識沒有可用的值
派生類型 他包括指針類型,數組類型,結構類型,共用體類型和函數類型

咱們先介紹基本數據類型

整數類型

類型 存儲大小 值範圍
char 1字節 -128-127或0-255
unsigned char 1字節 0-255
signed char 1字節 -128-127
int 2或4字節 -32,768 到 32,767 或 -2,147,483,648 到 2,147,483,647
unsigned int 2或4字節 0 到 65,535 或 0 到 4,294,967,295
short 2字節 -32,768 到 32,767
unsigned short 2字節 0 到 65,535
long 4字節 -2,147,483,648 到 2,147,483,647
unsigned long 4字節 0 到 4,294,967,295

各種型的儲存大小,與系統位數有關,爲了獲得準確大小能夠用sizeof來計算

#include <stdio.h>
#include <limits.h>
int main(){
    printf("int 存儲大小 : %lu \n", sizeof(int));
    return 0 ;
}
複製代碼
int 存儲大小 : 4 
複製代碼

浮點類型

類型 存儲大小 值範圍 精度
float 4字節 1.2E-38 到 3.4E+38 6 位小數
double 8字節 2.3E-308 到 1.7E+308 15位小數
long double 16字節 3.4E-4932 到 1.1E+4932 19位小數

void類型

類型 描述
函數返回爲空 C 中有各類函數都不返回值,或者您能夠說它們返回空。不返回值的函數的返回類型爲空。例如 void exit (int status);
函數參數爲空 C 中有各類函數不接受任何參數。不帶參數的函數能夠接受一個 void。例如 int rand(void);
指針指向void 類型爲void*指針表明對象的地址,而不是類型,例如,內存分配函數 void *malloc( size_t size ); 返回指向 void 的指針,能夠轉換爲任何數據類型。

3 變量

變量是程序可操做的儲存區名稱,C中每一個變量都有特定的類型,類型決定了變量的大小和佈局,該範圍內的值均可以儲存在內存中

C語言中有基本數據類型的變量,也能夠有其餘類型的變量,好比數組,指針,枚舉,結構體,共用體等

變量的定義

變量的定義就是告訴儲存器何處建立變量的儲存,以及如何建立變量的儲存,變量定義指定數據類型,幷包含該類型的一個或多個變量列表

type variable_list;
複製代碼

type必須是一個有效的C數據類型,variable_list爲變量的名字,能夠有多個

int    i, j, k;
char   c, ch;
float  f, salary;
double d;
複製代碼

上方i,j,k等聲明並定義了變量i,j,k

變量能夠在聲明的時候初始化

type variable_name = value;

例如

extern int d = 3, f = 5;    // d 和 f 的聲明與初始化
int d = 3, f = 5;           // 定義並初始化 d 和 f
byte z = 22;                // 定義並初始化 z
char x = 'x';
複製代碼

C中變量的聲明

變量的聲明,像編輯器保證變量以指定的類型和名稱存在,這樣編輯器就能夠在不知道變量完整細節的狀況下,也能進一步編譯

變量聲明有倆種狀況

  • 一種是須要創建儲存空間的,例如:int a 在聲明的時候就已經創建了存儲空間。
  • 另外一種是不須要創建儲存空間的,經過extern關鍵字聲明變量名而不定義它,例如:extern int a 其中變量 a 能夠在別的文件中定義的
  • 除非有extern關鍵字,其餘的都是變量的定義
extern int i; //聲明,不是定義
int i; //聲明,也是定義
複製代碼

4 常量

常量是固定的值,在程序期間不會改變,這些固定的值又叫作字面量

常量能夠是任何基本數據類型,常量在值定義以後不會修改

定義常量

在C中有倆種定義常量的方式

  • 使用#define預處理器
#define identifier value

#include <stdio.h>
#include <limits.h>

#define FFF 10
#define DDD 20
#define HHH '\n'
int main(){
    int a ;
    a =FFF*DDD;
    printf("值,%d",a);
    printf("\n字符串,%c",HHH);

    return 0 ;
}
複製代碼
值,200
字符串,
複製代碼
  • 使用const關鍵字
const type variable = value;

int main() {
    const int FFF =10;
    const int DDD=20;
    const char HHH='\n';
    int a;
    a = FFF * DDD;
    printf("值,%d", a);
    printf("\n字符串,%c", HHH);

    return 0;
}
複製代碼
值,200
字符串,
複製代碼

5 儲存類

儲存類定義C中變量,函數的範圍和生命週期,這些說明符放在他們所修飾的類型以前,下面列出可用的儲存類

  • auto
  • register
  • static
  • extern

auto儲存類

auto儲存類是全部局部變量默認的儲存類

{
   int mount;
   auto int month;
}
複製代碼

上面的兩種寫法都是同樣的,auto只能用在函數內,即只能修飾局部變量

register

用於定義儲存在寄存器中而不是RAM的局部變量,這意味着變量的最大大小是寄存器的大小

{
   register int  miles;
}
複製代碼

寄存器只用於須要快速訪問的變量,好比計數器

static

編譯器在聲明週期內保持保持局部變量的值,而不須要每次進入和離開做用域是建立和銷燬,所以使用static修飾局部變量,能夠函數調用間保持局部變量的值

static也能夠用於全局變量,當static修飾全局變量時,變量的做用域會限制在他的本文件中,也就是隻有本文件才能夠訪問(普通的全局變量,使用extern外部聲明後任何文件均可以訪問)

static函數:和上方的全局變量同樣(非靜態函數能夠在另外一個文件中直接引用,甚至沒必要使用extern聲明)

extern

用於提供一個全局變量的引用,全局變量對全部的程序文件均可見

當在A文件定義一個全局變量a,B文件想要使用A文件的變量a,能夠在B文件使用extern關鍵字拿到a的引用

第一個文件

#include <stdio.h>

int count =5;

extern void add();

int main() {
    add();
    return 0;
}
複製代碼

第二個文件

#include <stdio.h>

extern int count;

void add(){
    printf("count=%d\n",count);
}
複製代碼

運行後

bogon:untitled renxiaohui$ gcc text.c tex1.c
bogon:untitled renxiaohui$ a.out 
count=5
複製代碼

6 運算符

同java,不在記錄

7 判斷

同java

8 循環

同java

9 函數

定義函數

return_type function_name( parameter list ) {
   body of the function
}
複製代碼

一個函數的組成部分

  • 返回值類型:一個函數能夠返回一個值,return_type是一個函數返回值的數據類型,有些函數不返回數據,這種狀況下return_type的關鍵字是void
  • 函數名稱:函數的實際名稱function_name
  • 函數參數:當調用函數是,須要向函數傳遞一個值,就是參數,參數能夠有多個,也能夠沒有
  • 函數主體:函數內實現邏輯的語句
int Max(int num1,int num2){
    int result;
    if(num1>num2){
        result=num1;
    }else{
        result=num2;
    }
    
    return result;
}
複製代碼

這就是一個函數的簡單實例

函數聲明

函數的聲明會告訴編譯器,函數的名稱和如何調用函數,函數的實際主體能夠單獨定義

函數聲明包括如下幾個部分

return_type function_name( parameter list );
複製代碼

針對上方的函數咱們能夠聲明

int Max(int num1,int num2);
複製代碼

在函數聲明中,參數名稱並非很重要,能夠省略掉

int Max(int ,int);
複製代碼

當你在一個源文件定義一個函數,在另外一個文件調用這個函數時,函數聲明是必要的,這種狀況下,你須要在調用函數文件的頂部聲明函數

調用函數

#include <stdio.h>

int Max(int,int);
int main() {
    int num1 = 100;
    int num2 = 120;
    int rec;
    rec = Max(num1,num2);
    printf("比較結果爲%d\n",rec);
    return 0;
}

int Max(int num1, int num2) {
    int result;
    if (num1 > num2) {
        result = num1;
    } else {
        result = num2;
    }

    return result;
}
複製代碼

結果爲

比較結果爲120
複製代碼

C的做用域規則

局部變量

在某個函數塊內聲明的變量爲局部變量,他只能被該函數或者該代碼塊內的函數語句使用,局部變量外部是不可知的

#include <stdio.h>
 
int main () {
  /* 局部變量聲明 */
  int a, b;
  int c;
 
  /* 實際初始化 */
  a = 10;
  b = 20;
  c = a + b;
 
  printf ("value of a = %d, b = %d and c = %d\n", a, b, c);
 
  return 0;
}
複製代碼

這裏的a,b,c 都是局部變量

全局變量

全局變量定義在函數的外部,一般是程序的頂部,全局變量在整個程序的生命週期內都是有效的,在任意的函數內部能夠訪問全局變量。

全局變量能夠被任意函數訪問,也就是說全局變量在聲明後在整個程序中都是可用的

局部變量和全局變量名稱能夠相同,可是在函數內,若是倆個名字相,會使用局部變量,不會使用全局變量

#include <stdio.h>

//全局變量
int a = 10;
int c = 40;
int main(){
    //局部變量
    int a = 20;
    int b =30;
    printf("a=%d\n",a);
    printf("b=%d\n",b);
    printf("c=%d\n",c);
    return 0;
}
複製代碼

輸出

a=20
b=30
c=40
複製代碼

全局變量與局部變量的區別

  • 全局變量保存在內存的全局儲存區,佔用靜態的儲存單元
  • 局部變量保存在棧中,只有在函數被調用時,才動態的爲變量分配儲存單元

10 數組

聲明數組

type arrayName [ arraySize ];

複製代碼

這是一個一維數組,arraySize必須是大於0的常量,type是任意有效的c數據類型,聲明一個含有10個double數據的nums數組

double nums [10];
複製代碼

初始化數組

初始化固定數量的數組

int nums [3] = {10,2,3}
複製代碼

初始化數量不固定的數組

int nums []={1,2,3,4,5}
複製代碼

爲某個數組賦值

nums[3] = 6;
複製代碼

訪問數組元素

int a = num[3];
複製代碼

實例

#include <stdio.h>


int main(){
    int n[10];
    int i,j;

    for(i=0;i<10;i++){
        n[i]=i*10;
    }

    for (int j = 0; j < 10; ++j) {
        printf("%d = %d\n",j,n[j]);
    }

}
複製代碼

結果

0 = 0
1 = 10
2 = 20
3 = 30
4 = 40
5 = 50
6 = 60
7 = 70
8 = 80
9 = 90

複製代碼

11 枚舉

枚舉是C語言中的一種基本數據類型

enum&emsp;枚舉名&emsp;{枚舉元素1,枚舉元素2,……};
複製代碼

假如咱們定義一週七天,假如不用枚舉,用常量

#define MON 1
#define TUE 2
#define WED 3
#define THU 4
#define FRI 5
#define SAT 6
#define SUN 7
複製代碼

假如用枚舉

enum  Day{
      MON=1, TUE, WED, THU, FRI, SAT, SUN
}
複製代碼

這樣看起來會很簡潔

注意:默認第一個成員變量爲0,後面的順序加1,若是第一個是1,後面是2,以此類推

枚舉變量的定義

1 先定義枚舉類型在定義變量

enum DAY{
  MON=1, TUE, WED, THU, FRI, SAT, SUN
};

enum DAY day;
複製代碼

2 定義枚舉類型的同時定義枚舉變量

enum DAY
{
      MON=1, TUE, WED, THU, FRI, SAT, SUN
} day;
複製代碼

3 省去枚舉名稱直接定義枚舉變量

enum
{
      MON=1, TUE, WED, THU, FRI, SAT, SUN
} day;
複製代碼

實例

#include <stdio.h>
enum DAY {
    MON = 1, TUE, WED, THU, FRI, SAT, SUN
};
int main() {
   enum DAY day;
   day=THU;
   printf("enun= %d\n",day);
    return 0;
}
複製代碼

結果

enun= 4
複製代碼

在C語言中,枚舉是被當作int或者 unsigned int來處理的,因此按照C語言規範是沒有辦法遍歷枚舉的

枚舉在switch中使用

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

enum Color {
    red=1, green, blue
};

int main() {
    enum Color enumColor;

    printf("請選擇你喜歡的顏色(1 紅色 2 綠色 3 藍色)");
    scanf("%d",&enumColor);

    switch (enumColor){
        case red:
            printf("你喜歡紅色\n");
            break;
        case green:
            printf("你喜歡綠色\n");
            break;
        case blue:
            printf("你喜歡藍色\n");
            break;
    }
    return 0;
}
複製代碼

結果

請選擇你喜歡的顏色(1 紅色 2 綠色 3 藍色)1
你喜歡紅色
複製代碼

12 指針

每個變量都有一個內存位置,每個內存位置都定義了可以使用&符號訪問地址,他表示在內存中的一個地址

#include <stdio.h>

int main(){

    int a ;

    int b[10];

    printf("a 的內存地址=%p\n",&a);
    printf("b 的內存地址=%p\n",&b);

    return 0;
}
複製代碼

結果

a 的內存地址=0x7ffee5e086c8
b 的內存地址=0x7ffee5e086d0
複製代碼

什麼是指針

指針是一個變量,其值爲另外一個變量的內存地址,使用指針以前須要對其進行聲明

type *var-name;
複製代碼

type是指針的類型,他必須是一個有效的C數據類型,var-name是指針的名稱,*用來表示這個變量是指針。

int *aa;//一個整形指針
double  *bb;//一個double類型的指針
複製代碼

如何使用指針

使用指針會頻繁的進行以下幾個操做,定義一個指針變量,把變量地址賦值給指針,訪問指針變量中的可用地址

#include <stdio.h>

int main(){

    int a =20;//定義int值
    int *b;//定義指針

    b=&a;//爲指針賦值

    printf("變量的地址值=%p\n",&a);
    printf("指針的值=%p\n",b);
    printf("指針的地址值=%d\n",*b);

    return 0;

}
複製代碼

結果

變量的地址值=0x7ffeef4356f8
指針的值=0x7ffeef4356f8
指針的地址值=20
複製代碼

C中的NULL指針

變量聲明的時候,若是沒有明確的地址能夠賦值,爲指針賦值一個NULL是一個良好的習慣,賦值爲NULL被稱爲空指針

int main(){
    int *var = NULL;

    printf("var的地址爲=%p\n",var);

    return 0;
}
複製代碼
var的地址爲=0x0
複製代碼

指針指向指針

#include <stdio.h>

int main(){
    int a;
    int *b;
    int **c;

    a=40;

    b=&a;

    c=&b;

    printf("a的值爲=%d\n",a);

    printf("b的值爲=%d\n",*b);

    printf("c的值爲=%d\n",**c);

    return 0;
}
複製代碼
a的值爲=40
b的值爲=40
c的值爲=40
複製代碼

傳遞數組給函數,函數返回數組

#include <stdio.h>

int sum(int *arr,int size);

int* getarr();

int main() {
    int a[4] = {1, 2, 3, 4};
    int v = sum(a,4);
    printf("sum=%d\n",v);

    int *p  = getarr();

    for (int i = 0; i < 4; ++i) {
        printf("數組=%d\n",*(p+i));
        printf("數組=%d\n",p[i]);

    }
    return 0;
}

int sum(int *arr, int size) {
     int sum = 0;
    for (int i = 0; i < size; ++i) {
        printf("i=%d\n", arr[i]);
        printf("i=%d\n", *(arr+i));

        sum+=arr[i];
    }
    return sum;
}

int * getarr(){
    static int arr[4]={2,4,5,7};

    return arr;
}
複製代碼
i=1
i=1
i=2
i=2
i=3
i=3
i=4
i=4
sum=10
數組=2
數組=2
數組=4
數組=4
數組=5
數組=5
數組=7
數組=7

複製代碼

指針運算

C指針用數字表示地址,所以能夠進行算術運算,++,--,+,-等,假如prt是一個int類型的地址1000,那麼執行prt++,prt將指向1004,即當前位置移動4個字節,假如prt是一個char類型的地址1000,那麼執行prt++,prt將指向1001,這個跟類型也是相關的

  • 指針的每一次遞增,他實際上會指向下一個元素的儲存單元
  • 指針的每一次遞減,他實際上指向上一個元素的儲存單元
  • 指針在遞增和遞減跳躍的字節數,取決於指針所指向變量的數據類型的長度,好比int就是4個字節

遞增一個指針

#include <stdio.h>

const int Max = 3;

int main() {
    int arr[3] = {1, 2, 3};
    int i ,*p;
    //給p指針賦值數組中第一個元素的地址
    p = arr;

    for (int i = 0; i < Max; ++i) {
        printf("元素的地址=%p\n", p);
        printf("元素的地址=%d\n", *p);
        //移動到下個位置
        p++;
    }
    return 0;
}
複製代碼
元素的地址=0x7ffee165b6ac
元素的地址=1
元素的地址=0x7ffee165b6b0
元素的地址=2
元素的地址=0x7ffee165b6b4
元素的地址=3
複製代碼

指針數組

有一種狀況,咱們數組內能夠存儲內存地址值

#include <stdio.h>

const int Max= 3;
int main(){
    int arr[3]={1,2,3};
    int *p[Max];

    for (int i = 0; i <Max ; ++i) {
        p[i]=&arr[i];
    }

    for (int j = 0; j < Max; ++j) {
        printf("指針數組數據=%p\n",p[j]);
        printf("指針數組數據值=%d\n",*p[j]);

    }
    return 0;
}
複製代碼
指針數組數據=0x7ffee7cda6ac
指針數組數據值=1
指針數組數據=0x7ffee7cda6b0
指針數組數據值=2
指針數組數據=0x7ffee7cda6b4
指針數組數據值=3

複製代碼

指向指針的指針

指針也能夠指向指針

#include <stdio.h>

int main(){
    int a = 10;
    int *b;
    int **c;

    b=&a;
    c=&b;

    printf("a的值爲=%d\n",a);
    printf("b的值爲=%d\n",*b);
    printf("c的值爲=%d\n",**c);

}
複製代碼
a的值爲=10
b的值爲=10
c的值爲=10
複製代碼

傳遞指針給函數

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

void getlong(unsigned long *a);
int main() {
 unsigned long b;

 getlong(&b);

 printf("b的值爲=%ld\n",b);
}

void getlong(unsigned long *a) {
    *a = time(NULL);
    return;
}
複製代碼
b的值爲=1585048748
複製代碼

13 函數指針和回調函數

函數指針是指向函數的指針變量,函數指針能夠向普通函數同樣,傳遞參數,調用函數

函數返回值類型 (* 指針變量名) (函數參數列表);
複製代碼
#include <stdio.h>

int max(int, int);

int main() {
    //p是函數指針
    int (*p)(int, int) = &max;//&能夠省略
    int a, b, c, d;

    printf("請輸入三個數字:");
    scanf("%d,%d,%d", &a, &b, &c);
    d = p(p(a, b), c);//至關於調用max(max(a,b),c);
    printf("d的值爲=%d\n", d);
    return 0;
}

int max(int a, int b) {
    return a > b ? a : b;
}
複製代碼

輸出

請輸入三個數字:1,2,3
d的值爲=3
複製代碼

將指針函數當作參數傳遞給函數

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


void setvalue(int *arr, int b, int(*p)(void)) {
    for (int i = 0; i < b; ++i) {
        arr[i] = p();
    }
}

int stett(int(*p)(void)){
    return p();
}

int getvalue(void) {
    return rand();
}

int main() {
    int arr[10];

    setvalue(arr, 10, getvalue);
    for (int i = 0; i < 10; ++i) {
        printf("i=%d\n", arr[i]);
    }

    int b;
    b= stett(getvalue);
    printf("b=%d\n", b);


    return 0;
}


複製代碼

結果

i=16807
i=282475249
i=1622650073
i=984943658
i=1144108930
i=470211272
i=101027544
i=1457850878
i=1458777923
i=2007237709
b=823564440
複製代碼

14 字符串

C語言定義字符串Hello,兩種形式,字符串和字符數組的區別:最後一位是不是空字符

#include <stdio.h>

int main(){
    char hello[6]= {'H','e','l','l','o','\0'};
    char hello1[]= "hello";
    char *hello2="hello";

    printf("測試=%s\n",hello);
    printf("測試=%s\n",hello1);
    printf("測試=%s\n",hello2);

    return 0;
}
複製代碼
測試=Hello
測試=hello
測試=hello
複製代碼

15 結構體

結構體能夠存儲不一樣類型的數據項

定義一個結構體

struct tag { 
    member-list
    member-list 
    member-list  
    ...
} variable-list ;
複製代碼
  • tag:結構體標籤
  • member-list:結構體數據項
  • variable-list:結構體變量
struct Book {
    int book_id ;
    char title[50];
    char author[50];
    char subject[50];
} book;
複製代碼

在通常狀況下,tag、member-list、variable-list 這 3 部分至少要出現 2 個。如下爲實例:

//此聲明聲明瞭擁有3個成員的結構體,分別爲整型的a,字符型的b和雙精度的c
//同時又聲明告終構體變量s1
//這個結構體並無標明其標籤
struct {
    int a;
    char b;
    double c;
} s1;
 
//此聲明聲明瞭擁有3個成員的結構體,分別爲整型的a,字符型的b和雙精度的c
//結構體的標籤被命名爲SIMPLE,沒有聲明變量
struct SIMPLE {
    int a;
    char b;
    double c;
};
//用SIMPLE標籤的結構體,另外聲明瞭變量t一、t二、t3
struct SIMPLE t1, t2[20], *t3;
 
//也能夠用typedef建立新類型
typedef struct {
    int a;
    char b;
    double c; 
} Simple2;
//如今能夠用Simple2做爲類型聲明新的結構體變量
Simple2 u1, u2[20], *u3;
複製代碼

結構體中能夠含有其餘結構體和指針

//結構體包含其餘結構體
struct AA{
    int a;
    struct Book b;
};
//結構體包含本身的指針
struct BB{
    int b;
    struct BB *nextbb };

複製代碼

結構體的初始化

struct Book {
    int book_id ;
    char title[50];
    char author[50];
    char subject[50];
} book= {1,"c語言","小明","bb"};
複製代碼
title : c語言
author: 小明
subject: bb
book_id: 1
複製代碼

訪問結構體的成員

訪問結構體的成員能夠用.符號,好比上方的book.title;

int main(){

    struct Book book1;

    strcpy(book1.title,"學習書");
    strcpy(book1.author,"小紅");
    strcpy(book1.subject,"111");

    book1.book_id=222;


    printf("title : %s\nauthor: %s\nsubject: %s\nbook_id: %d\n", book1.title, book1.author, book1.subject, book1.book_id);

    return 0;
}
複製代碼
title : 學習書
author: 小紅
subject: 111
book_id: 222
複製代碼

結構體做爲函數的參數

void printstuct(struct Book book);
int main(){

    struct Book book1;

    strcpy(book1.title,"學習書");
    strcpy(book1.author,"小紅");
    strcpy(book1.subject,"111");

    book1.book_id=222;


    printf("title : %s\nauthor: %s\nsubject: %s\nbook_id: %d\n", book1.title, book1.author, book1.subject, book1.book_id);
    printstuct(book1);
    return 0;
}


void printstuct(struct Book book){
    printf( "Book title : %s\n", book.title);
    printf( "Book author : %s\n", book.author);
    printf( "Book subject : %s\n", book.subject);
    printf( "Book book_id : %d\n", book.book_id);
}
複製代碼
title : 學習書
author: 小紅
subject: 111
book_id: 222
Book title : 學習書
Book author : 小紅
Book subject : 111
Book book_id : 222
複製代碼

指向結構體的指針

定義,賦值,調用

struct Books *struct_pointer;

struct_pointer = &Book1;

struct_pointer->title;
複製代碼
int main() {

    struct Book book1;

    strcpy(book1.title, "學習書");
    strcpy(book1.author, "小紅");
    strcpy(book1.subject, "111");

    book1.book_id = 222;
    printstuct1(&book1);
    return 0;
}

void printstuct1(struct Book *book) {
    printf("Book title : %s\n", book->title);
    printf("Book author : %s\n", book->author);
    printf("Book subject : %s\n", book->subject);
    printf("Book book_id : %d\n", book->book_id);
}
複製代碼
Book title : 學習書
Book author : 小紅
Book subject : 111
Book book_id : 222
複製代碼

16 共用體

共用體是一個特殊的數據類型,容許在相同的儲存位置,儲存不一樣的數據類型,能夠定義一個帶有多個成員的共用體,可是任什麼時候候只能一個成員帶值

定義共用體

union定義共用體,

union [union tag]
{
   member definition;
   member definition;
   ...
   member definition;
} [one or more union variables];
複製代碼

union tag是可選的,每一個member definition;都是標準的變量定義,如int i char b等,在分號以前能夠定義一個或多個共用體變量是可選的

定義一個成員有int,float,char[]的共用體

#include <stdio.h>
#include <string.h>


union Data {
    int a;
    char b[100];
    float c;
};

int main() {

    union Data data;

    printf("數據長度=%lu\n", sizeof(data));

    data.a = 1;
    data.c = 10.00;
    strcpy(data.b, "測試數據");
    
    printf("數據%d\n",data.a);
    printf("數據%f\n",data.c);
    printf("數據%s\n",data.b);
  

    return 0;
}
複製代碼
數據長度=100
數據-393497114
數據-5278115000342806695772160.000000
數據測試數據

複製代碼

咱們看到數據a,c成員的值有損壞,是由於最後賦值的變量佔用了的內存位置,這也是b變量能夠正確輸出的緣由

咱們同一時間只能使用一個變量

#include <stdio.h>
#include <string.h>


union Data {
    int a;
    char b[100];
    float c;
};

int main() {

    union Data data;

    printf("數據長度=%lu\n", sizeof(data));

    data.a = 1;
    printf("數據%d\n",data.a);

    data.c = 10.00;
    printf("數據%f\n",data.c);

    strcpy(data.b, "測試數據");
    printf("數據%s\n",data.b);




    return 0;
}
複製代碼
數據長度=100
數據1
數據10.000000
數據測試數據
複製代碼

在這裏全部的成員均可以正確輸出,是由於同一時間只用到了一個變量

17 typedef

C語言提供typedef關鍵字,可使用它爲類型起一個新的名字

#include <stdio.h>

typedef unsigned int TEXT;
int main(){
    TEXT a = 11;

    printf("參數爲=%d\n",a);
    return 0;
}
複製代碼
參數爲=11
複製代碼

也能夠爲用戶自定義的數據類型去一個新的名字,好比結構體

#include <stdio.h>
#include <string.h>

typedef unsigned int TEXT;

typedef struct BOOKS {
    char a[50];
    char b[50];
} Book;

int main() {
    TEXT a = 11;
    printf("參數爲=%d\n", a);

    Book book;

    strcpy(book.a, "測試1");
    strcpy(book.b, "測試2");
    printf("a=%s\n", book.a);
    printf("b=%s\n", book.b);


    return 0;
}
複製代碼
參數爲=11
a=測試1
b=測試2
複製代碼

typedef和#define 的區別

#define是一個C指令,用於爲各類數據類型定義別名,與typedef相似,可是他有如下幾種不一樣

  • typedef僅限於爲類型定義符號名稱,#define不只爲類型定義別名,也能夠爲數值定義別名
  • typedef爲編譯器解釋執行,#define爲預編譯器進行處理
#define TRUE 0
#define FALSE 1

int main() {
    printf("數值爲=%d\n", TRUE);
    printf("數值爲=%d\n", FALSE);
    return 0;
}
複製代碼
數值爲=0
數值爲=1
複製代碼

18 輸入和輸出

getchar() & putchar() 函數

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

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

#include <stdio.h>

int main(){
    printf("請輸入一個字符\n");

    int  a = getchar();

    printf("你的輸入爲\n");

    putchar(a);

    printf("\n");
}
複製代碼
請輸入一個字符
text
你的輸入爲
t
複製代碼

gets() & puts() 函數

char *gets(char *s)函數從stdin讀取一行到s指向的緩存區,直到一個終止符或一個EOF int puts(const char *s)函數吧一個字符串s和一個尾隨的換行符寫入stdout

#include <stdio.h>

int main(){
    char a[100];

    printf("輸入你的字符\n");
    gets(a);

    printf("你輸入的字符爲\n");

    puts(a);
    return 0;
}
複製代碼
輸入你的字符
text
你輸入的字符爲
text

複製代碼

scanf() 和 printf() 函數

int scanf(const char *format, ...)函數從標準輸入流 stdin 讀取輸入,並根據提供的 format 來瀏覽輸入。

int printf(const char *format, ...)函數把輸出寫入到標準輸出流 stdout ,並根據提供的格式產生輸出。

format 是一個簡單的常量字符串,可是你能夠定義%s,%d,%c,%f來讀取字符串,數字,字符,浮點數

#include <stdio.h>

int main() {
    char a[100];
    int b;

    printf("輸入文字\n");

    scanf("%s%d", a, &b);

    printf("你輸入的文字=%s,%d\n",a,b);
    return 0;
}
複製代碼
輸入文字
text 123
你輸入的文字=text,123
複製代碼

19 文件讀寫

#include <stdio.h>

int main() {
    FILE *file;

    //打開文件容許讀寫,若是不存在則建立該文件
    file = fopen("/Users/renxiaohui/Desktop/test.txt", "w+");
    //寫入一行
    fprintf(file, "this is a text\n");
    //再寫入一行
    fputs("this is a text aaaa\n", file);

    fclose(file);

}
複製代碼

會在建立文件test.txt,並寫入倆行文字

#include <stdio.h>

int main() {
    FILE *file1;

    char a[255];
    //打開一個文件,只讀
    file1 = fopen("/Users/renxiaohui/Desktop/test.txt", "r");
    //讀取文件,當遇到第一個空格或者換行符會中止讀取
    fscanf(file1, "%s", a);
    printf("1= %s\n", a);

    //讀取字符串到緩衝區,遇到換行符或文件的末尾 EOF返回
    fgets(a, 255, file1);
    printf("2= %s\n", a);

    fgets(a, 255, file1);
    printf("3= %s\n", a);

    //關閉文件 
    fclose(file1);
}
複製代碼

讀取剛纔建立的文件

1= this
2=  is a text
3= this is a text aaaa
複製代碼

20 預處理器

C預處理器不是編譯器的組成部分,他是編譯過程當中的一個單獨步驟,他們會在編譯器實際編譯以前完成所需的預處理

全部的預處理器都是以(#)開頭的,他必須是第一個非空字符,爲了加強可讀性,預處理器應該從第一列開始,下面列出比較重要的預處理器

指令 描述
#define 定義宏
#undef 取消定義的宏
#include 包含一個源文件代碼
#ifdef 若是宏已經定義則返回真
#ifndef 若是宏沒有定義則返回真
#if 若是給定條件爲真則編譯一下代碼
#else #if的替代方案
#elseif 若是前面的 #if 給定條件不爲真,當前條件爲真,則編譯下面代碼
#endif 結束一個#if。。#else 語句
#error 遇到標準錯誤是,輸出錯誤消息
#pragma 使用標準化方法,向編譯器發佈特殊指令到編譯器中去

預編譯器實例

#define MAX 20
複製代碼

這個指令表示,把全部的MAX定義爲20,使用#define定義常量能夠增長可讀性

#include <stdio.h>
#include "myheader.h"
複製代碼

第一個表示從系統庫獲取stdio.h庫,並添加到源文件中,一個是從本地目錄獲取myheader.h,並添加到源文件中

#undef FILE 
#define FILE 99
複製代碼

取消已經定義的FILE,並把它從新定義爲99

#ifdef MESSAGE
    #define MESSAGE "you wish"
#endif
複製代碼

這個表示只有MESSAGE未定義時,才定義MESSAGE爲you wish

預約義宏

ANSI C中預約義宏,咱們能夠在編程中使用這些宏,但不能夠改變他的值

描述
1__DATE__ 當前日期,一個以 "MMM DD YYYY" 格式表示的字符常量。
1__TIME__ 當前時間,一個以 "HH:MM:SS" 格式表示的字符常量。
1__FILE__ 這會包含當前文件名,一個字符串常量。
1__LINE__ 這會包含當前行號,一個十進制常量。
1__STDC__ 當編譯器以 ANSI 標準編譯時,則定義爲 1。

看下使用

#include <stdio.h>
int main() {

    printf("data=%s\n",__DATE__);
    printf("file=%s\n",__FILE__);
    printf("time=%s\n",__TIME__);
    printf("line=%d\n",__LINE__);
    printf("stdic=%d\n",__STDC__);

    return 0;
}
複製代碼
data=Mar 31 2020
file=text27.c
time=15:31:49
line=19
stdic=1
複製代碼

參數化宏

參數化宏能夠模擬函數,例以下面是一個計算數字平方的方法

int square(int x) {
   return x * x;
}
複製代碼

能夠用參數化宏來表示

#define square(x) ((x)*(x))
複製代碼

在使用參數化宏以前必須使用#define來定義,參數列表必須包含在圓括號內的,而且必須緊跟在宏名稱以後,不容許有空格

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

int main() {

    printf("平方爲=%d\n",square(5));

    return 0;
}
複製代碼
平方爲=25
複製代碼

預處理運算符

宏延續運算符(\)

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

#define message_for(a,b) \ printf(#a"and "#b"we are friend\n")
複製代碼

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

在宏定義中,須要把一個宏的參數轉化爲字符串常量時,則使用字符串常量化運算符(#)例如:

#define message_for(a,b) \ printf(#a"and "#b"we are friend\n")

int main() {

    message_for("xiaoming","xiaohong");

    return 0;
}
複製代碼
"xiaoming"and "xiaohong"we are friend
複製代碼

** 標記粘貼運算符(##)**

直接看代碼比較

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


int main() {

    int token34 =40;

    add(34);

    return 0;
}
複製代碼
token34 = 40
複製代碼

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

printf ("token34 = %d", token34);
複製代碼

defined()運算符

預處理器defined運算符是在常量表達式中的,用來肯定一個標識符是否已經#define定義過,若是定義過爲真,若是沒有定義過值爲假

#if !defined(TEXT)
#define TEXT "text\n"
#endif


int main() {

    printf(TEXT);

    return 0;
}
複製代碼
text
複製代碼

21 頭文件

相關文章
相關標籤/搜索