结构体的基本使用方法
编程规范:结构体名以大写字母开头,函数、变量名以小写字母开头
结构体作为函数的返回值可避免函数需要返回多个值的情况。
#include <stdio.h>
#include <math.h>
struct Point { //C++写法
double x, y;
}; //注意最后有个分号;
double calcDistance(Point a, Point b) {
double deltaX = a.x - b.x;
double deltaY = a.y - b.y;
return sqrt(deltaX * deltaX + deltaY * deltaY);
}
Point calcMidPoint(Point a, Point b) {
Point result;
result.x = (a.x + b.x) / 2;
result.y = (a.y + b.y) / 2;
return result;
}
int main() {
Point a, b; //声明两个Point结构体
a.x = 1; a.y = 1; //赋初始值
b.x = 4; b.y = 5;
printf("sizeof(Point) = %d\n", sizeof(Point));
double dist = calcDistance(a, b);
printf("dist = %.2f\n", dist);
Point middlePoint = calcMidPoint(a, b);
printf("midPoint: x = %.2f, y = %.2f\n", middlePoint.x, middlePoint.y);
return 0;
}
当函数需要返回多个值时,可以使用结构体进行封装
以之前的求解一元二次方程的题为例
#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;
}
数据结构教材上的实现方式
- 泛型
typedef double ElemType;
- 异常处理
所有函数均返回Success
、Error
等用来表示操作是否执行成功,而原本需要通过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;
}
习题
在定义结构体时,下列叙述正确的是( A )
A)系统不会分配空间
B) 系统会按成员大小分配空间
C) 系统会按最大成员大小分配空间
D) 以上说法均不正确如果有下面的定义和赋值,则使用( 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