數據結構與算法複習-C語言回顧

C語言

數組

定義一維數組

類型符 數組名[常量表達式];

int a[10];
int b[5] = {1, 2, 3, 4, 5};
void func(int n)
{
    int a[2 * n];
}

指定數組爲靜態static存儲方式,則不能用“可變長數組”

static int a[2 * n]; // 不合法,a數組指定爲`static`存儲方式

指針

存儲地址的變量:類型+地址

類型決定了指針加1時,地址移動多少個字節,例如:

int *p, a = 10;
p = &a;
p = p + 1; // 移動4個字節,因爲一個正數在內存中佔4個字節

指針+1,是加上一個元素所佔用的字節數

定義指針

int *p1, *p2, a, b;

引用指針

  • & 取址運算符
  • * 指針運算符,或間接訪問運算符,*p代表指針變量p指向的對象。
int a = 100, b = 10;
int *p1, *p2;
p1 = &a;
p2 = &b;

指針引用數組

int a[5] = {1, 2, 3, 4, 5};
int *p;
p = &a[0]; // p的值是a[0]的地址
int a[5] = {1, 2, 3, 4, 5};
int *p;
p = a; // p的值是數組首元素(即a[0])的地址

數組引用的三種方法

  1. 下標法

    #inlcude <stdio.h>
    
    int main()
    {
        int a[10];
        int i;
        printf("please enter 10 integer numbers: ");
        for(i=0;i<10;i++)
        {
            scanf("%d", &a[i]);
        }
        for(i=0;i<10;i++)
        {
            printf("%d", a[i]);
        }
        printf("\n");
        return 0;
    }
    
  2. 通過數組名計算元素地址,找出元素的值

    #inlcude <stdio.h>
    
    int main()
    {
        int a[10];
        int i;
        printf("please enter 10 integer numbers: ");
        for(i=0;i<10;i++)
        {
            scanf("%d", &a[i]);
        }
        for(i=0;i<10;i++)
        {
            printf("%d", *(a+i));
        }
        printf("\n");
        return 0;
    }
    
  3. 用指針變量指向數組元素

    #inlcude <stdio.h>
    
    int main()
    {
        int a[10];
        int *p, i;
        printf("please enter 10 integer numbers: ");
        for(i=0;i<10;i++)
        {
            scanf("%d", &a[i]);
        }
        for(p=a;p<(a+10);p++)
        {
            printf("%d", *p);
        }
        printf("\n");
        return 0;
    }
    

方法1、2效率相同,C編譯系統是將a[i]轉換爲*(a+1)處理的,即先計算元素地址,因此方法1、2找數組元素費時較多。第3種方法快於1、2方法,用指針變量直接指向元素,不用每次都重新計算地址,像p++這樣的自加操作是比較快的。有規律地改變地址值能大大提高執行效率。不過方法1、2,能更直觀判斷處理到第幾個元素了,而第3種方法則不那麼直觀。

指針,數組作爲形參

void func(int a[]);
void func(int *p);

C編譯都是將形參數組作爲指針變量來處理的

指針引用字符串

char string[] = "I love China";
char *string = "I love China";

指向函數的指針

編譯系統爲函數代碼分配一段存儲空間,這段存儲空間的起始地址(又稱入口地址)稱爲這個函數的指針

類型名 (*指針變量名)(函數參數列表);

int (*p) (int, int);

用函數指針變量調用函數

#include <stdio.h>

int main()
{
    int max(int, int);
    int (*p) (int, int);
    int a, b, c;
    p = max;
    printf("please enter a and b: ");
    scanf("%d,%d", &a, &b);
    c = (*p)(a, b);
    return 0;
}

動態爲函數指針綁定結構相同(返回類型,參數個數,每個參數的類型相同)的函數

#include <stdio.h>

int main()
{
    int max(int, int);
    int min(int x, int y);
    int (*p) (int, int);
    int a, b, c, n;
    printf("please enter a and b: ");
    scanf("%d,%d", &a, &b);
    printf("please choose 1 or 2: ");
    scanf("%d", &n);
    if (n == 1) p = max;
    else p = min;
    c = (*p)(a, b);
    return 0;
}

用指向函數的指針作爲函數參數

void fun(int (*x1)(int), int (*x2)(int, int))
{
    int a, b, i = 3, j = 5;
    a = (*x1)(i);
    b = (*x2)(i, j);
}

一個完整的例子:

#include <stdio.h>

int main()
{
    int fun(int x, int y, int (*p)(int, int));
    int max(int, int);
    int min(int, int);
    int add(int, int);
    int a = 34, b = -21, n;
    printf("please choose 1, 2 or 3: ");
    scanf("%d", &n);
    if (n == 1) fun(a, b, max);
    else if (n == 2) fun(a, b, min);
    else if (n == 3) fun(a, b, add);
    return 0;
}

in fun(int x, int y, int (*p)(int, int))
{
    int result;
    result = (*p)(x, y);
    printf("%d\n", result);
}

int max(int x, int y)
{
    int z;
    if (x > y) z = x;
    else z = y;
    return z;
}

int min(int x, int y)
{
    int z;
    if (x > y) z = y;
    else z = x;
    return z;
}

int add(int x, int y)
{
    return x + y;
}

返回指針值的函數

類型名 *函數名(參數列表)

float *search((*p)[4], int n);

動態內存分配

void *malloc(unsigned size);
void *calloc(unsigned n, unsigned int size);
void free(void *p);
void *realloc(void *p, unsigned int size);

void指針

可以指向任意類型的指針

int a = 3;
int *p1 = &a;
char *p2;
void *p3;
p3 = (void *)p1;
p2 = (char *)p3;
p3 = &a;
printf("%d", *p3); // p3得到純地址,但並不指向a,不能通過*p3輸出a的值

結構體

定義使用結構體

struct 結構體名
{
    成員列表
} 變量名列表;

例:

struct Student
{
    int num;
    char name[20];
    char sex;
    int age;
    float score;
    char addr[30];
} student1, student2;

初始化和引用:

struct Student
{
    int num;
    char name[20];
    char sex;
    int age;
    float score;
    char addr[30];
} student1={101, "Li Lin", 'M', 8, 0.1, "123 Nanhu Road"};

某一成員初始化:

struct Student b = {.name="Zhang"};

引用和可進行的操作:

student1.num = 10001;
student1.num;
student1.birthday.month;
student1.age++;
sum = student1.score + student2.score;
student1 = student2;
scanf("%d",&student1.num); // 輸入&student1.num的值
printf("%o", &student1); // 結構體的首地址

結構體數據和結構體指針

結構體數組

struct Votes
{
    char name[20];
    int count;
} leader[3] = {"A", 10, "B", 8, "C", 5};
struct Votes
{
    char name[20];
    int count;
};
struct Votes leader[3];
struct Votes leader[3] = {"A", 10, "B", 8, "C", 5};

結構體指針

struct Votes v;
struct Votes *p;
p = &v;
(*p).count = 1000;
p->count = 1000;

指向運算符:->p->count等價於(*p).count

指向結構體數組的指針

struct Votes *p;
struct Votes leader[3] = {"A", 10, "B", 8, "C", 5};
for (p = leader;p<(leader+3);p++)
{
    printf("%s: %d", p->name, p->count);
}

共同體(聯合體)

所有成員從同一起始地址開始存儲。

union 共同體名
{
    成員列表;
} 變量列表;

特點:

  1. 在某一瞬間,只能存放其中一個成員的值,而不能同時存放多個。

  2. 可以初始化共同體,但只能有一個變量

    union Data
    {
        int i;
        char ch;
        float f;
    } d = {1, 'a', 1.5}; // 錯誤,不能初始化3個成員,因爲它們佔用同一塊存儲單元
    union Data d = {16}; // 初始化第一個成員
    union Data d = {.ch = 'j'}; // 初始化指定的成員
    
  3. 起作用的成員是最後一次被賦值的成員

枚舉(enumeration)

聲明和使用枚舉類型

enum [枚舉名] {枚舉元素列表};

enum Weekday
{
    sun, mon, tue, wed, thu, fri, sat
};

然後可以用此類型來定義枚舉類型變量:

enum Weekday workday, weekend;
// 他們的直只能來自枚舉類型定義中的枚舉常量(枚舉元素)之一:sun, mon, tue, wed, thu, fri, sat
workday = mon; // 正確
weekend = sum; // 正確
workday = monday; // 錯誤,monday不是枚舉類型Weekday的枚舉常量(枚舉元素)

也可以不聲明有名字的枚舉類型,而直接定義枚舉變量:

enum {sun, mon, tue, wed, thu, fri, sat} workday, weekend;

性質:

  1. 枚舉常量不能賦值

  2. 每個枚舉元素都代表一個整數,C語言按照定義時的順序默認它們的值爲0,1,2,3…

    指定元素值的定義:

    enum Weekday
    {
        sun=7, mon=1, tue, wed, thu, fri, sat // sun爲7,mon爲1,後依序加1,sat爲6
    } workday, weekend;
    
  3. 枚舉元素可以比較大小

    if (workday == mon) ;
    if (workday > sun) ;
    

使用typedef聲明新類型

  1. 簡單地用一個新的類型名代替原有類型名

    typedef int Integer;
    typedef float Real;
    
  2. 用簡單的類型名代替複雜的類型表示方法

    (1). 新類型名代表結構體類型

    typedef struct {
        int month;
        int day;
        int year
    } Date;
    
    Date birthday; // 不能再寫成struct DateStruct birthday;
    Date *p;
    

    (2). 新類型名代表數組類型

    typedef int Num[100]; // 聲明Num爲數組類型名
    Num a; // a爲100個元素大小的整數數組
    

    (3). 新類型名代表指針類型

    typedef char * String;
    String p, s[10]; // 定義p爲字符指針變量,s爲字符指針數組
    

    (4). 新類型名代表指向函數的指針

    typedef int (* Pointer)(); // 聲明Pointer爲指向函數的指針類型,該函數返回整型值
    Pointer p1, p2; // p1, p2爲Pointer類型的指針
    

文件操作

ANSI C標準採用”緩衝文件系統“處理數據文件,自動在內存中爲程序中每個文件開闢一個文件緩衝區。內存到硬盤輸出必須先送到內存緩衝區,內存緩衝區裝滿後寫入硬盤,從硬盤到內存,一次從硬盤讀取文件的的一部分到內存緩衝區(寫滿),然後從緩衝區逐個地將數據送到程序數據區(給程序變量)。

文件類型指針

由一個結構體保存文件信息:文件名,文件狀態,大小,當前位置等。一種C編譯環境提供的stdio.h有以下文件類型聲明:

typedef struct
{
    short level; // 緩衝區“滿”或“空”的程度
    unsigned flags; // 文件狀態標誌
    char fd; // 文件描述符
    unsigned char hold; // 如緩衝區無內容不讀取字符
    short bsize; // 緩衝區的大小
    unsigned char * buffer; // 數據緩衝區的位置
    unsigned char * curp; // 指針當前的指向
    unsigned istemp; // 臨時文件指示器
    short token; // 用於有效性檢查
} FILE;

打開關閉文件

#include <stdio.h>

FILE *fp;
fp = fopen("filename", "r");

文件打開模式:

模式字符 含義 如果文件不存在
“r” 讀取 出錯
“w” 寫入 新建文件
“a” 追加 出錯
“rb” 二進制讀取 出錯
“wb” 二進制寫入 新建文件
“ab” 二進制追加 出錯
“r+” 讀寫 出錯
“w+” 讀寫 新建文件
“a+” 讀追加 出錯
“rb+” 二進制讀寫 出錯
“wb+” 二進制讀寫 新建文件
“ab+” 二進制讀追加 出錯
fclose(fp);

順序讀寫數據文件

讀寫單個字符:fgetcfputc

char fgetc(FILE *fp);
int fputc(char ch, FILE *fp)

讀寫字符串: fgetsfputs

char * fgets(char *str, int n, FILE *fp); // 讀取長度爲n-1的字符串,存放到str裏面
int fputs(char *str, FILE *fp)

格式化讀寫

fprintf(FILE *fp, char *fmt_str,...);
fscanf(FILE *fp, char *fmt_str,...);

fprintf(fp, "%d,%6.2f", i, f);
fscanf(fp, "%d,%6.2f", &i, &f);

輸入時需要將文件中的編碼字符轉換爲二進制形式再保存在變量中;輸出時又需要將二進制形式轉換成編碼字符。轉換操作需要耗費較多時間,因此對於內存與硬盤頻繁交換數據的場景,儘量避免使用fprintffscanf函數。

二進制讀寫

fread(buffer, size, count, fp);
fwrite(buffer, size, count, fp);

buffer: 地址,存放從文件讀入的數據

size: 數據項大小(字節數),通常通過sizeof()函數得到

count: 要讀寫多少個數據項(每個數據項的長度爲size)

fp: FILE指針

隨機讀寫

rewind(FILE *fp); // 返回到文件頭
fseek(FILE *fp, int count, int start) // 偏移位置
ftell(FILE *fp); // 文件當前位置
起始點 變量名 數字
文件開始位置 SEEK_SET 0
文件當前位置 SEEK_CUR 1
文件結尾位置 SEEK_END 2

文件讀寫錯誤檢測

ferror(FILE *fp);
clearerr(FILE *fp);
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章