C基础系列教程10——结构体

该系列文章内容可能来源我本人或者zhrmoe(他的主页:http://zhrmoe.iflab.org/)的编写。文章如果有错误欢迎批评指正,谢谢!转载请注明来自本站,另外,本系列教程中的代码建议初学者自己手打一遍,不要直接复制(由于某些奇怪的原因可能会导致你复制的代码出现错误!相信自己的双手吧=-=
如果你觉得进度太快,可以等你熟悉了前面的内容后再继续往下看

 

先思考一个问题:
(亲爱的小明出场) 假如一个小组有10个同学,需要记录在程序中,其中几个同学分别叫小明、小红、小绿、小然、小琪、小玮、小霄·········(这些只是随便起的名字,臻的是随便起的233333)
现在需要分别记录TA们的编号、身高、体重什么的,为了方便写代码所以只出现数字,都是int,应该不会有越界情况发生(逃)。请思考如何声明变量。
先想想,再往下看
————————— 我 是 萌 萌 哒 分 割 线 ●▽● —————————
你可能写出这样的代码(这是个错误的示范)

int xiaomingdeid;

int xiaomingdeshengao;

int xiaomingdetizhong;

int xiaoludeid;

int xiaoludeshengao;

int xiaoludetizhong; 


每个人的每个数据(以后可以称之为属性)都声明一个变量。这样当然是可以的(如果你不嫌累)。
顺便一说,变量的命名要简洁明了,不要这么啰嗦=-=并且不要用拼音。

不多嘛,30行就写完了。然后呢,等你写完之后突然有人又告诉你(客户改需求了),需要每个人再增加一个年龄。。你内心一定是这样的图片
————————— 我 是 萌 萌 哒 分 割 线 ●▽● —————————
现在遇到的问题就是这样,今天介绍一个新的东西,struct。叫做结构体,一个结构体可以包括多个变量, 定义方式如下。
你需要新建一个文件,文件名叫做person.h,这种后缀的文件我们称作头文件,内容是这样的

#ifndef _person_h

#define _person_h 

struct person {

    
int id;

    
int height;

    
int weight; 

};

#endif


这样就定义了1个结构体类型叫做person,注意不要丢了右大括号后面的那个分号。在主程序中我们需要在#include<stdio.h>的后面加一行 #include “person.h” ,这种自己写的头文件我们用双引号引起来以便于和C自带的头文件混淆。

接下来就可以定义变量了,代码是这样的。

int main(){

    
struct person xiaoming;

    
struct person xiaohong;

    xiaoming.id = 
0;

    xiaoming.height = 
180;

    xiaoming.weight = 
150;

    xiaohong.id = 
1;

    xiaohong.height = 
160;

    xiaohong.weight = xiaoming.weight;

    system(
"pause");

    
return 0;

}


第一行声明了一个叫做xiaoming的变量,这是个特殊的变量,因为它是个结构体,里面包含了刚刚定义的三个属性,id、身高、体重。我们使用.(这是一个点儿)来访问结构体变量中的属性给它赋值(当然也可以读取,比如最后一行)。

这样的话定义起来就会方便很多,增加属性的话直接去更改头文件的定义就好了。

————————— 我 是 萌 萌 哒 分 割 线 ●▽● —————————
接下来会出现一个问题,你每次定义的时候都要敲一个struct person,这令人十分讨厌(主要还是懒)。所以呢,我们在把头文件改成这样

#ifndef _person_h

#define _person_h

struct _person {

    
int id;

    
int height;

    
int weight;

};

typedef struct _person person;

#endif


我把原来的struct person改成了struct _person,加了一个下划线,是为了下面这样做,typedef的意思是定义一个新的类型,把struct _person 叫做person,也就是说你待会输入person就相当于输入了struct _person,这样就少写了一个单词 (我臻是太懒了=-=)
那么这样做之后你会发现原来的程序报错了,因为我们加了个下划线,所以源程序有两种改法
第一种,跟着加下划线,这种方法显然更麻烦
第二种,直接删掉struct,这也是我们这样做的目的(其实还是为了偷懒)。

更改后代码如下,小红就删了啊=-=

int main(){

    person xiaoming;



    xiaoming.id = 
0;

    xiaoming.height = 
180;

    xiaoming.weight = 
150;



    printf(
"xiaoming.id = %d\n", xiaoming.id);

    printf(
"xiaoming.height = %d\n", xiaoming.height);

    printf(
"xiaoming.weight = %d\n\n", xiaoming.weight);



    system(
"pause");

    
return 0;

}


————————— 我 是 萌 萌 哒 分 割 线 ●▽● —————————
今天有时间,就接着往下写吧=-=一次看不完可以下次接着看

在数据结构中,不光要储存数据,还要对应数据的一些操作,这些操作的集合我们叫做操作集,也就是一堆函数。
所以,在数据结构中很少通过定义一个本地的变量来操作,都是通过函数来进行的,那么这里就有一个问题,在函数中对本地变量操作无法直接传参数,需要传地址,也就是指针。

所以需要更改声明为

//person xiaoming;

//person* addressOfxiaoming;

person* xiaoming =  NULL;


第一行是原来的声明,需要改写成第二行的样子,之所以注释掉是因为太长(虽然更好理解,也就是小明所在内存中的地址),所以依然叫做xiaoming(还是太懒) ,现在你要注意,接下来的 “xiaoming” 的意思是小明所在的地址,不再是之前的xiaoming了。还有一点要注意,指针的声明要初始化为NULL,否则叫做野指针,不知道指向哪里。

我们来写一个函数来进行和之前同样的操作。主程序是这样的,注意多加了一个stdlib.h

#include <stdio.h>

#include <stdlib.h>

#include "person.h"

int main(){

    person* xiaoming;

    

    xiaoming = (person *)malloc(
sizeof(person));

    fun(xiaoming);

    

    system(
"pause");

    
return 0;

}

其中需要解释的是malloc这一行,意思是分配一块大小刚好能放得下person结构体的内存,这时候电脑并不知道这块内存要放什么,你需要强制类型转换成person指针类型来告诉电脑这里要放的是一个person。其中sizeof求出的是一个person占多大的内存,malloc负责向系统申请一块这么大的内存,然后返回一个内存的地址,但是是不知道类型的地址(也就是void *),最后强制转换成person*
这样的做法就相当于之前的person xiaoming;然后把小明的地址记下来。最后一行把小明的地址传给fun函数处理。

接下来写fun函数,我们接收到的是person的指针,叫做sb(somebody-_-||不要多想),这个sb就是一个指向了person的指针。

void fun(person * sb) { //这的意思是somebody-_-||不要多想

    (*sb).id = 0;

    (*sb).height = 
180;

    (*sb).weight = 
150;

    printf(
"xiaoming.id = %d\n", (*sb).id);

    printf(
"xiaoming.height = %d\n", (*sb).height);

    printf(
"xiaoming.weight = %d\n\n", (*sb).weight);

}

我们的做法和之前的整数交换差不多,*sb代表的是间接取出sb指向的内容,也就是小明,之后在用.(这是一个点儿)来访问小明的id、身高、体重。之所以加括号是因为那个“点”的运算优先级要高于间接取值的星号。
但是人总是懒的=-=又要打括号星号还有点太麻烦了→_→所以呢就有这这种写法,和上面完全等价

void fun(person * sb) { //这的意思是somebody-_-||不要多想

    sb->id = 0;

    sb->height = 
180;

    sb->weight = 
150;

    printf(
"xiaoming.id = %d\n", sb->id);

    printf(
"xiaoming.height = %d\n", sb->height);

    printf(
"xiaoming.weight = %d\n\n", sb->weight);

}


是的你没看错就是一个箭头,一个减号和一个大于号。
注意这俩符号中间不能加空格
注意这俩符号中间不能加空格
注意这俩符号中间不能加空格
重要的事情说三遍。

————————— 我 是 萌 萌 哒 分 割 线 ●▽● —————————
补充说明:.(这是一个点)运算用在访问一个结构体变量的属性时,那个箭头用于指针指向的结构体属性时候,一个是直接访问一个是间接访问,后续还可能会出现结构体的属性指向结构体,就可能出现先箭头再一个点来访问,比如链表。。。这是小然提出来的补充哦(逃
————————— 我 是 萌 萌 哒 分 割 线 ●▽● —————————
至此,数据结构基础告一段落,如果还有补充的我会继续发=-=
下面是我的github,这里有一些数据结构的源码。。。还在学习ing=-=(请勿抄袭!请勿抄袭!请勿抄袭!)

https://github.com/956237586/DataStructure-C/tree/master/DataStructure-C
————————— 我 是 萌 萌 哒 分 割 线 ●▽● —————————
题外话,关于箭头还有个黑魔法,依然转自知乎 http://www.zhihu.com/question/27417946

发表评论

电子邮件地址不会被公开。 必填项已用*标注

4 + 6 =