结构体的基本使用方法

编程规范:结构体名以大写字母开头,函数、变量名以小写字母开头

结构体作为函数的返回值可避免函数需要返回多个值的情况。

  1. #include <stdio.h>
  2. #include <math.h>
  3. struct Point { //C++写法
  4. double x, y;
  5. }; //注意最后有个分号;
  6. double calcDistance(Point a, Point b) {
  7. double deltaX = a.x - b.x;
  8. double deltaY = a.y - b.y;
  9. return sqrt(deltaX * deltaX + deltaY * deltaY);
  10. }
  11. Point calcMidPoint(Point a, Point b) {
  12. Point result;
  13. result.x = (a.x + b.x) / 2;
  14. result.y = (a.y + b.y) / 2;
  15. return result;
  16. }
  17. int main() {
  18. Point a, b; //声明两个Point结构体
  19. a.x = 1; a.y = 1; //赋初始值
  20. b.x = 4; b.y = 5;
  21. printf("sizeof(Point) = %d\n", sizeof(Point));
  22. double dist = calcDistance(a, b);
  23. printf("dist = %.2f\n", dist);
  24. Point middlePoint = calcMidPoint(a, b);
  25. printf("midPoint: x = %.2f, y = %.2f\n", middlePoint.x, middlePoint.y);
  26. return 0;
  27. }

当函数需要返回多个值时,可以使用结构体进行封装

以之前的求解一元二次方程的题为例

#include <stdio.h>
#include <math.h>
#define eps 0.000001

struct Result {
    int flag;
    double x1, x2;
    double delta;
};

Result calculate(double a, double b, double c) {
    Result result;
    double delta = b * b - 4 * a * c;
    result.delta = delta;

    if (delta > eps) {    // delta > 0
        double d = sqrt(delta);
        result.x1 = (-b + d) / (2 * a);
        result.x2 = (-b - d) / (2 * a);
        result.flag = 2;
    }
    else if (fabs(delta) < eps) {    // delta = 0
        result.x1 = -b / (2 * a);
        result.x2 = result.x1;
        result.flag = 1;
    }
    else {    // delta < 0
        result.flag = 0;
    }
    return result;
}

int main() {
    double a, b, c;
    scanf("%lf %lf %lf", &a, &b, &c);

    Result result = calculate(a, b, c);
    if (result.flag == 2) {
        printf("x1 = %f, x2 = %f\n", result.x1, result.x2);
    }
    else if (result.flag == 1) {
        printf("x1 = x2 = %f\n", result.x1);
    }
    else {    //flag == 0
        printf("no real roots solution\n");
    }
    return 0;
}
#include <stdio.h>
#define MAXN 1005

struct Student {
    char name[64];
    char stuNumber[6];
    int chinese, math;
};

Student students[MAXN];

int main() {
    int N;
    scanf("%d", &N);
    //输入学生数据
    for (int k = 0; k < N; k++) {
        scanf("%s", students[k].name);
        scanf("%s", students[k].stuNumber);
        scanf("%d", &students[k].chinese);
        scanf("%d", &students[k].math);
    }
    //可以进行学生数据的增删查改
    //.........
    printf("***********学生成绩***************\n");
    printf("学号\t姓名\t语文\t数学\n");
    for (int i = 0; i < N; i++) {
        printf("%s\t%s\t%d\t%d\n", students[i].name, students[i].stuNumber, students[i].chinese, students[i].math);
    }
    return 0;
}
2
1234 aadd 50 60
1002 bafd 80 100

结构体作为函数参数、指向结构体的指针

#include <stdio.h>

struct Student {
    char name[64];
    int chinese, math;
    int totalScore;
};

void calcTotalScore_1(Student stu) {
    stu.totalScore = stu.chinese + stu.math;
}

void calcTotalScore_2(Student* stu) {
    (*stu).totalScore = (*stu).chinese + (*stu).math;
    stu->totalScore = stu->chinese + stu->math;
}

void calcTotalScore_3(Student &stu) {
    stu.totalScore = stu.chinese + stu.math;
}

int main() {
    Student stu1;
    stu1.chinese = 70; stu1.math = 80; stu1.totalScore = 0;
    calcTotalScore_1(stu1);
    printf("stu1: totalScore: %d\n", stu1.totalScore);

    Student stu2;
    stu2.chinese = 70; stu2.math = 80; stu2.totalScore = 0;
    calcTotalScore_2(&stu2);
    printf("stu2: totalScore: %d\n", stu2.totalScore);

    return 0;
}

访问结构体的成员,什么时候用.什么时候用->?

如果stu是一个结构体变量(包括引用类型),则访问其内部成员使用.运算符,如stu.chinese

如果stu是一个指针,其指向一个结构体变量,则访问该指针所指向的结构体变量的成员chinese,因使用(*p).chinese,该语法可简化为p->chinese,两种写法完全等价。

typedef关键字

C 语言提供了 typedef关键字,可以使用它来为类型取一个新的名字

bool

#include <stdbool.h>:在stdbool.h头文件中已经包含了如下定义,可在C语言中使用bool类型

#include <stdio.h>

typedef int bool;    //给int型取一个新的名字叫bool型
#define false 0
#define true 1

bool isEvenNumber(int num) {
    return n % 2 == 0;
}

size_t

//size_t 可以表示 字符串长度、所占内存空间字节大小、数组的个数 等等不需要使用负数的场景
typedef unsigned int size_t;    //给unsigned int型取一个新的名字叫size_t型

size_t getSize();
void* malloc(size_t size);    //malloc函数原型

NodePosi

typedef Node* NodePosi;

struct Node {    //链表节点
    int data;
    //Node* next;
    NodePosi next;
};

void insertNode(NodePosi p);

ElemType(泛型思想)

typedef unsigned int size_t;
typedef double ElemType;
//typedef struct Student ElemType;
//typedef struct Node* ElemType;

struct Array {
    ElemType data[1000];
    size_t size;
};

C语言中结构体的语法细节

//将本代码保存为.c
#include <stdio.h>

struct Student {
    char name[20];
    int chinese, math;
    struct Student* deskmate;    //正确
    //Student* deskmate;    //报错,未定义标识符"Student"
};

int main() {
    //Student temp;    //报错,未定义标识符"Student"
    //Student stu[10];    //报错,未定义标识符"Student"

    struct Student temp;    //正确
    struct Student stu[10];    //正确
    return 0;
}
#include <stdio.h>
#include <string.h>

typedef struct Student {
    char name[20];
    int chinese, math;
    struct Student* deskmate;
}Student;

void printStuentInfo(Student* stu) {
    printf("name: %s, deskmate: %s\n", stu->name, stu->deskmate ? stu->deskmate->name : "null");
    printf("chinese: %d, math: %d\n", stu->math, stu->math);
    printf("\n");
}

int main() {
    Student stu1, stu2;

    //stu1.name = "aaa";    //错误
    strcpy(stu1.name, "aaa");
    stu1.chinese = 20; stu1.math = 80;
    stu1.deskmate = &stu2;

    strcpy(stu2.name, "bbb");
    stu2.chinese = 50; stu2.math = 50;
    stu2.deskmate = NULL;

    printStuentInfo(&stu1);
    printStuentInfo(&stu2);

    return 0;
}

手动实现一个栈stack

1. 完全不封装

#include <stdio.h>
#define MAXN 10010

int Stack[MAXN];
int size;

void push(int x) {
    if (size >= MAXN) {
        printf("栈满");
        return;
    }
    Stack[size++] = x;
}

bool isEmpty() {
    return size == 0;
}

int pop() {
    if (isEmpty()) {
        printf("栈空");
        return -1;
    }
    return Stack[--size];
}

int top() {    //访问栈顶元素
    if (isEmpty()) {
        return -1;
    }
    return Stack[size - 1];
}

int getSize() {
    return size;
}

int main() {
    for (int i = 1; i <= 10; i++) {
        push(i);
    }
    pop();
    //..........
    return 0;
}

2. 使用结构体部分封装

使用引用(C++)

#include <stdio.h>
#include <stdlib.h>
#define MAXN 1005

struct Stack {
    //int data[MAXN];
    int* data;
    int size;
};

void initStack(Stack &S) {
    S.data = (int*)malloc(MAXN * sizeof(int));
    S.size = 0;
}

//准确来说是将stack恢复为未初始化状态
void destoryStack(Stack &S) {
    free(S.data);
    S.data = NULL;
}

void clearStack(Stack &S) {
    S.size = 0;
}

bool isEmptyStack(const Stack &S) {
    return S.size == 0;
}

void pushStack(Stack &S, int x) {
    if (S.size >= MAXN) {
        printf("栈满");
        return;
    }
    S.data[S.size++] = x;
}

int popStack(Stack &S) {
    if (isEmptyStack(S)) {
        printf("栈空");
        return -1;
    }
    return S.data[--S.size];
}

int topStack(const Stack &S) {
    if (isEmptyStack(S)) {
        return -1;
    }
    return S.data[S.size - 1];
}

int getStackSize(const Stack &S) {
    return S.size;
}

void printStack(const Stack &S) {
    for (int i = 0; i < S.size; i++) {
        printf("%d ", S.data[i]);
    }
    printf("\n");
}

int main() {
    Stack s1;
    initStack(s1);

    for (int i = 1; i <= 10; i++) {
        pushStack(s1, i);
    }
    printStack(s1);
    destoryStack(s1);

    Stack s2;
    initStack(s2);
    //.........
    return 0;
}

使用指针(C语言)

#include <stdio.h>
#include <stdlib.h>
#define MAXN 1005

typedef struct Stack {
    int* data;
    int size;
}Stack;

//初始化一个栈,init是initialize缩写
void initStack(Stack* S) {
    S->data = (int*)malloc(MAXN * sizeof(int));
    S->size = 0;
}

//准确来说是将stack恢复为未初始化状态
//该函数是配合 initStack函数 来使用的
void destoryStack_1(Stack* S) {
    free(S->data);
    S->data = NULL;
}

//函数直接返回一个创建并初始化好的一个栈(的指针)
Stack* createStack() {
    Stack* s = (Stack*)malloc(sizeof(Stack));
    s->data = (int*)malloc(MAXN * sizeof(int));
    s->size = 0;
    return s;
}

//真正意义上的销毁栈
//该函数是配合 createStack函数 来使用的
void destoryStack_2(Stack* S) {
    free(S->data);
    free(S);
}

void clearStack(Stack* S) {
    S->size = 0;
}

bool isEmptyStack(const Stack* S) {
    return S->size == 0;
}

void pushStack(Stack* S, int x) {
    if (S->size >= MAXN) {
        printf("栈满");
        return;
    }
    S->data[S->size++] = x;
}

int popStack(Stack* S) {
    if (isEmptyStack(S)) {
        printf("栈空");
        return -1;
    }
    return S->data[--S->size];
}

int topStack(const Stack* S) {
    if (isEmptyStack(S)) {
        return -1;
    }
    return S->data[S->size - 1];
}

int getStackSize(const Stack* S) {
    return S->size;
}

void printStack(const Stack* S) {
    for (int i = 0; i < S->size; i++) {
        printf("%d ", S->data[i]);
    }
    printf("\n");
}

int main() {
    //栈s1是局部变量
    Stack s1;
    initStack(&s1);
    for (int i = 1; i <= 10; i++) {
        pushStack(&s1, i);
    }
    printStack(&s1);
    destoryStack_1(&s1);

    //栈s2是由 createStack函数 动态内存分配创建的
    //s2是一个指向Stack结构体变量的指针
    Stack* s2 = createStack();
    for (int i = 1; i <= 5; i++) {
        pushStack(s2, i);
    }
    printStack(s2);
    destoryStack_2(s2);
    return 0;
}

关于stack的定义通常有两种写法:

struct Stack {
    int* data;
    int size;
};

void initStack(Stack &S) {
    S.data = (int*)malloc(MAXN * sizeof(int));
    S.size = 0;
}

void destoryStack(Stack &S) {
    free(S.data);
    S.data = NULL;
}

//该操作是只读操作,这种写法Stack比较轻量,不加引用也不会影响性能(但是不安全)
bool isEmptyStack(Stack S) {
    return S.size == 0;
}
struct Stack {
    int data[MAXN];
    int size;
};

void initStack(Stack &S) {
    S.size = 0;
}

void destoryStack(Stack &S) {
    //操作为空,这种写法并不需要destory操作
}

//尽管该操作是只读操作,但这种写法Stack比较重,不加引用会影响性能
bool isEmptyStack(const Stack &S) {
    return S.size == 0;
}

数据结构教材上的实现方式

  1. 泛型
    typedef double ElemType;
    
  1. 异常处理
    所有函数均返回SuccessError等用来表示操作是否执行成功,而原本需要通过return返回的结果则通过指针/引用来返回。 ```c //使用枚举语法 enum Status {Success, Error};

//相当于以下语句 typedef int Status;

define Success 0

define Error 1


```c
//甚至还可以添加更细分的异常类型,非0统统表示异常情况
enum Status {Success, Error, StackIsFull, StackIsEmpty};
#include <stdio.h>
#include <stdlib.h>
#define MAXN 1005

typedef double ElemType;
enum Status {Success, Error};

struct Stack {
    ElemType* data;
    int size;
};

Status initStack(Stack& S) {
    ElemType* temp = (ElemType*)malloc(MAXN * sizeof(int));
    if (temp == NULL) return Error;
    S.data = temp;
    S.size = 0;
    return Success;
}

//准确来说是将stack恢复为未初始化状态
Status destoryStack(Stack& S) {
    free(S.data);
    S.data = NULL;
    return Success;
}

Status clearStack(Stack& S) {
    S.size = 0;
    return Success;
}

bool isEmptyStack(const Stack& S) {
    return S.size == 0;
}

Status pushStack(Stack& S, ElemType e) {
    if (S.size >= MAXN) {
        return Error;
    }
    S.data[S.size++] = e;
    return Success;
}

Status popStack(Stack& S, ElemType &e) {
    if (isEmptyStack(S)) {
        return Error;
    }
    e = S.data[--S.size];
    return Success;
}

Status topStack(const Stack& S, ElemType& e) {
    if (isEmptyStack(S)) {
        return Error;
    }
    e = S.data[S.size - 1];
    return Success;
}

int getStackSize(const Stack& S) {
    return S.size;
}

void printStack_intType(const Stack& S) {
    for (int i = 0; i < S.size; i++) {
        printf("%d ", S.data[i]);
    }
    printf("\n");
}

void printStack_doubleType(const Stack& S) {
    for (int i = 0; i < S.size; i++) {
        printf("%.2f ", S.data[i]);
    }
    printf("\n");
}

int main() {
    Stack s;
    if (initStack(s) != Success) {
        printf("error\n");
        return -1;    //提前中断程序的运行,向操作系统返回-1(非0表示异常)
    }

    for (int i = 1; i <= 5; i++) {
        if (pushStack(s, i) != Success) {
            printf("error\n");
            return -1;
        }
    }

    double x;
    popStack(s, x);    //调用函数的时候也可以忽略函数的返回值
    printf("%.2f\n", x);

    printStack_doubleType(s);
    destoryStack(s);


    return 0;
}

习题

  1. 在定义结构体时,下列叙述正确的是( A )
    A)系统不会分配空间 
    B) 系统会按成员大小分配空间
    C) 系统会按最大成员大小分配空间 
    D) 以上说法均不正确

  2. 如果有下面的定义和赋值,则使用( A )不可以输出n中data的值。

    struct SNode {
     unsigned int id;
     int data;
    }n, *p;
    p = &n;
    


A) p.data  B) n.data  C) p->data  D)  (*p).data