数据结构与算法复习-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);
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章