程序猿教你学C语言/C++编程——结构化数据

C语言是面向过程的,而C++是面向对象的

C和C++的区别:

C是一个结构化语言,它的重点在于算法和数据结构。C程序的设计首要考虑的是如何通过一个过程,对输入(或环境条件)进行运算处理得到输出(或实现过程(事务)控制)。

C++,首要考虑的是如何构造一个对象模型,让这个模型能够契合与之对应的问题域,这样就可以通过获取对象的状态信息得到输出或实现过程(事务)控制。 所以C与C++的最大区别在于它们的用于解决问题的思想方法不一样。之所以说C++比C更先进,是因为“ 设计这个概念已经被融入到C++之中 ”。

C与C++的最大区别:在于它们的用于解决问题的思想方法不一样。之所以说C++比C更先进,是因为“ 设计这个概念已经被融入到C++之中 ”,而就语言本身而言,在C中更多的是算法的概念。那么是不是C就不重要了,错!算法是程序设计的基础,好的设计如果没有好的算法,一样不行。而且,“C加上好的设计”也能写出非常好的东西。

很多小伙伴都老是会碰到疑问,其实还是基础没打扎实,这些题如果你不看答案你能知道多少呢?如果还有很多不知道就证明基础没打扎实,如果你还在入门纠结,如果你还在苦恼怎么入门!小编推荐一个学C语言/C++的学习裙【 六九九,四七零,五九六 】,无论你是大牛还是小白,是想转行还是想入行都可以来了解一起进步一起学习!裙内有开发工具,很多干货和技术资料分享!

第六章 结构化数据

简单便捷效率高,是C语言的语法处理方式,C语言的作者对于编程语言中凡是稍微复杂一点的特性,要么就放入了标准库进行处理,要么就干脆没有加进来。放入标准库里的我们只需要简单调用函数的方式就能使用了,比如我们初学时用的最多的printf,这其实就是一个复杂的函数,我们在讲函数的最后才讲到这种参数个数可变的函数;没有加进来的语法特性,我们也可以很容易的使用C语言的原有语法进行扩展,struct几乎就是专门为了进行相关扩展而诞生的,使用它甚至可以实现C语言的面向对象。以前我们要处理一组类型相同的数据时,我们通常都会定义一个数组,但是如果要处理一组类型不同的数据呢?比如说要处理一匹马的数据,一匹马有名字、高度、出生日期等等,这些数据中有些是字符串,有些是数值,这时候就需要结构了。关键字struct能定义各种类型的变量集合,称为结构,并把它们看做一个单元,下面是一个简单的结构声明例子:

struct Horse

{

int age;

int height;

}silver;

这个例子声明了一个结构类型Horse。Horse不是一个变量名,而是一个新的类型,我们可以使用这个新的类型来定义变量(这里的silver就是我们定义的结构体变量)。当然,我们的结构类型名和结构类型变量名的命名方式和我们熟悉的普通变量名相同。Horse结构内的变量名称age和height称为成员或字段。在这个例子中,它们都是int类型。结构成员出现在结构体名Horse后的大括号内。结构的一个实例silver是在定义结构时声明的,它是一个Horse类型的变量,只要使用变量名silver,它都包含两个结构成员:age和height。

下面我们再把Horse结构类型复杂化一点:

struct Horse

{

int age;

int height;

char name[20];

char father[20];

char mother[20];

}dobbin = {24,17,"Dobbin","Trigger","Flossie"};

结构内的成员可以使任何类型的变量,甚至是数组。在Horse结构类型的这个版本中,有5个成员:整数成员age和height,char数组成员name、father和mother。在Horse结构定义的闭括号后定义了一个实例变量dobbin,给dobbin赋初值的方式和数组类似。注意我们不能在Horse结构体里面初始化值,因为这里Horse只是定义了一个模板或称一个蓝图,在我们定义struct Horse dobbin的时候才定义了一个具体的变量dobbin,我们才给dobbin赋了初值。

可以将结构的声明和结构变量的声明分开,取代前面的例子如下:

struct Horse

{

int age;

int height;

char name[20];

char father[20];

char mother[20];

};

struct Horse dobbin = {24,17,"Dobbin","Trigger","Flossie"};

第一个语句定义了结构类型Horse,第二个声明该类型的变量dobbin。结构定义和结构变量声明语句都用分号结束。在dobbin结构成员的初始值中,dobbin的父亲是Trigger,母亲是Flossie。

也可以给前面两个例子添加第三条语句,定义另一个Horse类型的变量:

struct Horse trigger = {30,15,"Trigger","Smith","Wesson"};

这句定义了变量trigger,它其实就是dobbin的父亲,而dobbin的爷爷奶奶就是Smith和Wesson。当然,也可以像普通变量那样在一行语句中声明多个结构变量。例如:

struct Horse piebald,bandy;

这行语句声明了两个Horse类型的变量。和普通变量声明相比,这个声明只增加了关键字struct。为了使代码跟简洁,我们可以使用typedef定义,这样在声明结构变量时就不需要struct关键字了,如下:

typedef struct Horse Horse;

这个语句把Horse定义为struct Horse,我们就可以直接使用Horse来定义Horse类型的变量了:

Horse trigger = {30,15,"Trigger","Smith","Wesson"};

这样就不再需要每次定义结构变量时都使用struct关键字了。

访问结构成员

现在知道如何定义结构和声明结构变量了,还必须能够引用结构的成员,在C语言里我们在结构变量名的后面加一个句点,再加上变量名称,就可以引用结构成员了,比如我们使用dobbin.age=12就可以把dobbin的age重新赋值为12,结构变量名和成员名间的句点称为成员选择运算符,前面我们在初始化trigger时需要以正确的顺序,其实在初始化列表中可以指定成员名,这样就可以忽略顺序了,如下:

Horse trigger = {.height=15,.age=30,.name="Trigger",.mother="Wesson",.father="Smith"};

下面我们来看一个使用结构体的例子:

#include

typedef struct Horse Horse;

struct Horse

{

int age;

int height;

char name[20];

char father[20];

char mother[20];

};

int main(int argc, char *argv[])

{

Horse my_horse;

printf("Enter the name of the horse:");

scanf("%s",my_horse.name);

printf("How old is %s?",my_horse.name);

scanf("%d",&my_horse.age);

printf("How high is %s (in hands)?",my_horse.name);

scanf("%d",&my_horse.height);

printf("Who is %s's father?",my_horse.name);

scanf("%s",my_horse.father);

printf("Who is %s's mother?",my_horse.name);

scanf("%s",my_horse.mother);

printf("%s is %d years old,%d hands high,",

my_horse.name,my_horse.age,my_horse.height);

printf(" and has %s and %s as parents. ",

my_horse.father,my_horse.mother);

return 0;

}

这个程序也比较简单,一开始把Horse定义为struct Horse,然后定义了结构体Horse,然后在main函数里给Horse变量my_horse的每个成员输入了值,最后打印出来

结构指针:

结构指针的声明方式和声明其他类型的指针变量相同,例如:Horse *pHorse = NULL;这条语句声明了一个pHorse指针变量,它可以存储Horse类型的结构地址。需要给Horse添加typedef语句来省略struct关键字。没有typedef,就必须把语句写成:

struct Horse *pHorse = NULL;下面来看看具体使用结构指针的例子:

#include

typedef struct Horse Horse;

struct Horse

{

int age;

int height;

char name[20];

char father[20];

char mother[20];

};

int main(int argc, char *argv[])

{

Horse my_horse = {3,11,"Jimbo","Trigger","Nellie"};

Horse *pHorse = &my_horse;

printf("%s is %d years old,%d hands high,",

my_horse.name,my_horse.age,my_horse.height);

printf(" and has %s and %s as parents. ",

my_horse.father,my_horse.mother);

printf("%s is %d years old,%d hands high,",

pHorse->name,pHorse->age,pHorse->height);

printf(" and has %s and %s as parents. ",

pHorse->father,pHorse->mother);

return 0;

}

可以看到我们把结构变量my_horse的地址赋值给了结构指针pHorse,下面就可以使用结构指针了,注意使用结构指针引用结构成员时要使用->符号,->叫做“指向结构体成员运算符”,它是C语言和C++语言的一个运算符,用处是使用一个指向结构体或对象的指针访问其内成员。最后可以看到使用结构变量使用.运算符和结构指针使用->运算符打印的结果是相同的。

小编推荐一个学C语言/C++的学习裙【 六九九,四七零,五九六 】,无论你是大牛还是小白,是想转行还是想入行都可以来了解一起进步一起学习!裙内有开发工具,很多干货和技术资料分享!

结构的嵌套定义:

可以在Horse结构的定义中声明Date结构,如下:

struct Horse

{

struct Date

{

int day;

int month;

int year;

}dob;

int age;

int height;

char name[20];

char father[20];

char mother[20];

};

这个声明将Date结构声明放在Horse结构的定义内,因此不能在Horse结构的外部声明Date变量。当然,每个horse类型的变量都包含Date类型的成员dob。但下面的语句:

struct Date my_date;

会导致编译错误,错误信息会说明Date结构类型未定义。如果需要在Horse结构的外部使用Date,就必须将它定义在Horse结构之外。

结构指针用作结构成员:

假设我们所处的环境是一个大型的牧场,这里好很多批马,我们要把这些马的数据都存储起来使用,我们该怎么做呢,首先会想到的是结构数组,但是在使用时我们会发现结构数组很不好用,因为牧场几乎每天都有马儿老死病死或者卖给其他人,这样我们就需要从原来的数组里删除掉这匹马的信息,同样牧场里会有小马出生,我们也需要在原来的数组基础上添加新增马儿的信息,但是我们的数组长度是固定的,每当我们要增加一个新的数据或删除一个原有的数据时我们都要大费周章。所以前人就发明了一种结构,可以很方便的解决数据的插入和删除操作。我们知道任何指针都可以是结构的成员,包括结构指针在内,结构成员指针可以指向相同类型的结构。例如,Horse类型的结构可以含有一个指向horse类型结构的指针。这样就可以把马匹链接起来,下面我们来看具体的程序:

#include

typedef struct Horse Horse;

struct Horse

{

int age;

int height;

char name[20];

char father[20];

char mother[20];

Horse *next;

};

int main(int argc, char *argv[])

{

Horse *pDobbin = (Horse *)malloc(sizeof(Horse));

Horse *pTrigger = (Horse *)malloc(sizeof(Horse));

Horse *pJimbo = (Horse *)malloc(sizeof(Horse));

Horse *current;

//pDobbin赋值

pDobbin->age=24,pDobbin->height=17,strcpy(pDobbin->name,"Dobbin"),

strcpy(pDobbin->father,"Trigger"),strcpy(pDobbin->mother,"Flossie");

//pTrigger赋值

pTrigger->age=30,pTrigger->height=15,strcpy(pTrigger->name,"Trigger"),

strcpy(pTrigger->father,"Smith"),strcpy(pTrigger->mother,"Wesson");

//pJimbo赋值

pJimbo->age=3,pJimbo->height=11,strcpy(pJimbo->name,"Jimbo"),

strcpy(pJimbo->father,"Trigger"),strcpy(pJimbo->mother,"Nellie");

//将三个结构指针变量串在一起

pDobbin->next = pTrigger;

pTrigger->next = pJimbo;

pJimbo->next = NULL;

//循环打印链表

for(current = pDobbin;current != NULL;current = current->next){

printf("%s is %d years old,%d hands high,",

current->name,current->age,current->height);

printf(" and has %s and %s as parents. ",

current->father,current->mother);

}

return 0;

}

可以看到,我们在结构体Horse中添加了新的成员Horse *next;这是一个Horse结构指针,下面的代码中就是通过

pDobbin->next = pTrigger;

pTrigger->next = pJimbo;

pJimbo->next = NULL;

将这三个结构变量串在了一起,它有个更常见的名字,在"数据结构"中叫做"链表",可以看到我们在for循环里,通过current = current->next来不断指向下一个结构指针变量,这样就能遍历打印出了整个链表的信息,pDobbin、pTrigger、pJimbo都叫做链表的节点,循环结束条件的非空判断就是我们上面设定的pJimbo->next = NULL,pJimbo叫做链表的尾节点,因为它的next指向的是NULL。函数malloc 向系统申请分配指定size个字节的内存空间。返回值是这段空间的首地址,返回类型是 void* 类型。void* 表示未确定类型的指针,C,C++规定,void* 类型可以强制转换为任何其它类型的指针。可能有人不太理解为什么要定义结构指针变量,还使用malloc函数,而不是直接定义结构变量,像这样Horse dobbin不是更方便吗?其实是因为malloc申请的空间更方便我们控制,我们只需要调用free(指针变量)就可以释放掉malloc函数申请的动态内存了,如果是普通变量,我们都没法自己控制变量空间的释放,这样就可能会占用多余的内存空间,这在我们马上要说的链表删除操作就会讲到。当然,malloc的功能很强大,使用它也可以实现比如可变长数组这样的东西。下面有个图可以更好的理解链表:

链表的插入和删除:

所谓数据,无非就是增删查改四个操作,就算你以后学了数据库,你也会发现是这样。数组对于查找和修改这两个操作特别方便,因为我们只需要数组名+下标就可以很容易的访问到一个数据,但是如果涉及到增加和删除操作,使用数组就不太方便了;链表则恰恰相反,增加和删除操作很容易,但是查找和修改就反而不如在数组里这么简洁了,链表插入:假设我们的链表是head->A->B->NULL,分为三种情况,插入链首、插入链尾和插入链表中两个节点中间。插入中间的情况,比如我们有节点C要插入A、B之间,那么我们需要操作,A->C,然后是C->B;插入链尾的情况,我们也只需要两步,B->C;C->NULL这样就插入C成为了链表的尾节点;插入链首的情况也差不多。链表删除:假设链表head->A->B->C->NULL,我们要删除节点B,我们需要两步,A->C;free(B),这样就可以把节点B删除并且释放了它占用的内存,这也是我们上面为什么说要使用malloc创建结构指针变量的最主要原因。来看具体的例子:

#include

typedef struct Horse Horse;

struct Horse

{

int age;

int height;

char name[20];

char father[20];

char mother[20];

Horse *next;

};

int main(int argc, char *argv[])

{

Horse *pDobbin = (Horse *)malloc(sizeof(Horse));

Horse *pTrigger = (Horse *)malloc(sizeof(Horse));

Horse *pJimbo = (Horse *)malloc(sizeof(Horse));

Horse *pNew = (Horse *)malloc(sizeof(Horse));

Horse *current = NULL;

//pDobbin赋值

pDobbin->age=24,pDobbin->height=17,strcpy(pDobbin->name,"Dobbin"),

strcpy(pDobbin->father,"Trigger"),strcpy(pDobbin->mother,"Flossie");

//pTrigger赋值

pTrigger->age=30,pTrigger->height=15,strcpy(pTrigger->name,"Trigger"),

strcpy(pTrigger->father,"Smith"),strcpy(pTrigger->mother,"Wesson");

//pJimbo赋值

pJimbo->age=3,pJimbo->height=11,strcpy(pJimbo->name,"Jimbo"),

strcpy(pJimbo->father,"Trigger"),strcpy(pJimbo->mother,"Nellie");

//将三个结构指针变量串在一起

pDobbin->next = pTrigger;

pTrigger->next = pJimbo;

pJimbo->next = NULL;

//循环打印链表

for(current = pDobbin;current != NULL;current = current->next){

printf("%s is %d years old,%d hands high,",

current->name,current->age,current->height);

printf(" and has %s and %s as parents. ",

current->father,current->mother);

}

//pNew赋值

pNew->age=7,pNew->height=12,strcpy(pNew->name,"Lucy"),

strcpy(pNew->father,"pDobbin"),strcpy(pNew->mother,"Meluk");

//插入操作,这里我们将新节点pNew插入到pDobbin和pTrigger中间

pDobbin->next = pNew;

pNew->next = pTrigger;

//删除操作,这里我们将节点pTrigger删除

pNew->next = pTrigger->next;

free(pTrigger);

//再次循环打印链表

for(current = pDobbin;current != NULL;current = current->next){

printf("%s is %d years old,%d hands high,",

current->name,current->age,current->height);

printf(" and has %s and %s as parents. ",

current->father,current->mother);

}

return 0;

}

小编推荐一个学C语言/C++的学习裙【 六九九,四七零,五九六 】,无论你是大牛还是小白,是想转行还是想入行都可以来了解一起进步一起学习!裙内有开发工具,很多干货和技术资料分享!

我们在上面就插入了新节点pNew并且删除了原来的节点pTrigger。其实我们的程序大可不必定义那么多的指针变量pDobbin、pTrigger、pJimbo,我们可以直接使用一个指针变量比如Horse *pNode = NULL就可以了,大不了我们每次创建新节点时使用pNode = (Horse *)malloc(sizeof(Horse))就可以了,这样因为每次pNode都会指向新的malloc分配的地址,所以我们就相当于创建了一个新节点;另外我们的链表的创建、遍历、插入、删除等等操作都可以单独做成不同的函数,放在另一个源文件里,这样更体现了程序的结构化,这也是我们学过程化程序设计语言的原因。可能你觉得这些不重要,事实上它们大有用处,我们以后要学到的数据结构和算法,就是这样做的

今天就到这里,欲知后事如何且听下回分解(手动滑稽)~

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,457评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,837评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,696评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,183评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,057评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,105评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,520评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,211评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,482评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,574评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,353评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,213评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,576评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,897评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,174评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,489评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,683评论 2 335

推荐阅读更多精彩内容