C语言学习之九——结构体

  1. 结构体概述
    1.1 概念:有时需要将不同类型的数据组合成一个有机的整体,以便于引用。因此C语言采用结构体形式将不同类型的数据包含到一个整体中。
    1.2 定义一个结构的一般形式为:
    struct 结构名
    {
    成员表列
    };
    (注:成员表列由若干个成员组成,每个成员都是该结构的一个组成部分。对每个成员也必须作类型说明,其形式为: 类型说明符 成员名;)

//例1,定义一个结构体date
struct date
{
int year;
int month;
int day;
};

  1. 定义结构体类型变量的方法
    2.1 先声明结构体类型再定义变量名,见例2

//例2
/#include<stdio.h>

void main()
{
struct date //定义结构体
{
int year;
int month;
int day;
};

struct date D; // 先声明结构体类型struct date,再定义变量名D,即D 有struct date的结构

printf("Please input year:");
scanf("%d", &D.year);
printf("Please input month:");
scanf("%d", &D.month);
printf("Please input day:");
scanf("%d", &D.day);
printf("%d, %d, %d\n", D.year, D.month, D.day);
}
//注:
struct (类型名) date(结构体名) D(变量名);

2.2 在定义结构体类型的同时定义变量,一般形式如下:

struct 结构体名
{
成员列表
}变量名列表;

//例3
/#include<stdio.h>

void main()
{
struct date //定义结构体
{
int year;
int month;
int day;
}D; //定义变量D

printf("Please input year:");
scanf("%d", &D.year);
printf("Please input month:");
scanf("%d", &D.month);
printf("Please input day:");
scanf("%d", &D.day);

printf("%d, %d, %d\n", D.year, D.month, D.day);
}

2.3 直接定义结构体类型变量,其一般形式如下:

struct
{
成员列表
}变量名列表;

//例4

include<stdio.h>

void main()
{
struct //定义结构体,但省去结构体名
{
int year;
int month;
int day;
}D; //定义变量D

printf("Please input year:");
scanf("%d", &D.year);
printf("Please input month:");
scanf("%d", &D.month);
printf("Please input day:");
scanf("%d", &D.day);

printf("%d, %d, %d\n", D.year, D.month, D.day);
}

  1. 结构体变量的引用
    3.1引用结构体变量应注意:
    1)不能将一个结构体变量作为一个整体进行输入和输出。
    如:printf("%d, %d, %d\n", D); 是错误的!!!!!!!!
    正确引用结构体变量中成员的方式为: 结构体变量名 . 成员名
    D.year 表示 D, 变量中的year成员 。可以对变量的成员赋值 , 例如:D.year= 2017;
    “.” 是成员 ( 分量 ) 运算符 , 它在所有的运算符中优先级最高 , 因此可以把 D.year作为一个整体来看待。上面赋值语句的作用是将年份2017赋给 变量中的成员 year 。
    2)如果成员本身又属一个结构体类型 , 则要用若干个成员运算符 , 一级一级地找到最低的一级的成员。只能对最低级的成员进行赋值或存取以及运算。见例5.

//例5
/#include <stdio.h>

void main()
{
struct date
{
int month;
int day;
int year;
};

struct student
{
int num;
char name[20];
char sex;
struct date birthday; //结构体student内嵌套结构体date
float score;
} boy1, boy2;

printf("Please input birthday(YY:) ");
scanf("%d", &boy1.birthday.year); //对最低级year进行赋值
printf("Please input birthday(MM:) ");
scanf("%d", &boy1.birthday.month);
printf("Please input birthday(DD:) ");
scanf("%d", &boy1.birthday.day);
printf("\n");

boy2 = boy1;

printf("boy1's birthday is %d-%d-%d\n", boy1.birthday.year, boy1.birthday.month, boy1.birthday.day);
printf("boy2's birthday is %d-%d-%d\n", boy2.birthday.year, boy2.birthday.month, boy2.birthday.day);
}

3)对结构体变量的成员可以像普通变量一样进行各种运算(根据其类型决定可以进行的运算)。
例如:
student2.score = student1.score;
sum = student1.score + student2.score;
student1.age++;
++student2.age;

4)可以引用结构体变量成员的地址,也可以引用结构体变量的地址。见例6.

//例6
/#include <stdio.h>

void main()
{
struct student
{
int num;
char *name;
char sex;
float score;
} boy1;

boy1.num = 007;
boy1.name = "Jane";

printf("The address of struct is: %o \n", &boy1 );
printf("The address of num is: %o \n", &boy1.num );
}
//The address of struct is 12006276760
The address of num is 12006276760
(由此可见,结构体变量的首地址即为结构体变量的首成员的地址。而且引用结构体变量的成员是从结构体变量的首地址为入口开始查找,直到查找出目标成员)

  1. 结构体变量的初始化,见例7.

//例7.
/#include <stdio.h>

void main()
{
struct student /定义结构/
{
int num;
char *name;
char sex;
float score;
}boy1, boy2 = { 102, "Jane", 'M', 98.5 };

boy1 = boy2;

printf("Number = %d\nName = %s\nScore = %d\n", boy1.num, boy1.name, boy1.score);
printf("\n\n");
printf("Number = %d\nName = %s\nScore = %d\n", boy2.num, boy2.name, boy2.score);
}

  1. 结构体数组
    一个结构体变量中可以存放一组数据(如一个学生的学号、姓名、成绩等数据)。
    如果有10个学生的数据需要参加运算,显然应该用数组,这就是结构体数组。
    5.1概念:结构体数组与以前介绍过的数值型数组不同之处在于,每个数组元素都是一个结构体类型的数据,它们都分别包括各个成员(分量)项。见例8.

//例8.
/#include"stdio.h"

/#define NUM 3

struct person
{
char name[20];
char phone[10];
};

void main()
{
struct person man[NUM];
int i;

for( i=0; i < NUM; i++)
{
printf("input name:\n");
gets(man[i].name);
printf("input phone:\n");
gets(man[i].phone);
}

printf("name\t\t\tphone\n\n");

for( i=0; i < NUM; i++)
{
printf("%s\t\t\t%s\n",man[i].name,man[i].phone);
}
}
//输出:
name phone

jack 123
tom 123
peter 123

5.2 定义结构体数组
和定义结构体变量的方法相仿,只需说明其为数组即可。(三种定义结构体变量的方式)

5.3 结构体数组的初始化
与其他类型的数组一样,对结构体数组可以初始化。例如:

struct student
{
int num;
char name[20] ;
char sex ;
int age ;
float score;
char addr[30] ;
} stu [ 2 ]= {
{101 ,′′ LiLin′′ ,′ M′ , 18 , 87.5 ,′′ Beijing′′ },
{102 ,′′ Zhang′′ ,′ F′ , 19 , 99 ,′′ Shanghai′′}
} ;

当然,数组的初始化也可以用以下形式:

struct student
{
int num ;

...
};
struct student str[]{{...},{...},{...}} ;
(即先声明结构体类型,然后定义数组为该结构体类型,在定义数组时初始化。)

  1. 指向结构体类型数据的指针
    6.1 定义:一个结构体变量的指针就是该结构体变量所占据的内存段的起始地址。
    6.2结构指针变量说明的一般形式为:
    struct 结构名 * 结构指针变量名

例如,在前面定义了 student 这个结构,如要说明一个指向 student 的指针变量 pstu ,可写为:struct student *pstu;
当然也可在定义 student 结构时同时说明 pstu 。

6.3 与其他各类指针变量相同,结构指针变量也必须要先赋值后才能使用。赋值是把结构变量的首地址赋予该指针变量,不能把结构名赋予该指针变量。
不能把结构名赋予该指针变量。
如果 boy 是被说明为 stu 类型的结构变量,则:pstu = &boy 是正确的。pstu=&student 是错误的。
因为,结构名和结构变量是两个不同的概念,不能混淆。结构名只能表示一个结构形式,编译系统并不对它分配内存空间。只有当某变量被说明为这种类型的结构时,才对该变量分配存储空间。因此上面 &student 这种写法是错误的,不可能去取一个结构名的首地址。有了结构指针变量,就能更方便地访问结构变量的各个成员。

6.4 指向结构体类型数据的指针访问结构体变量成员的一般形式为:
(* 结构指针变量 ). 成员名
或为:
结构指针变量 -> 成员名
例如:(*pstu).num 或者:pstu->num

//例9
/#include <stdio.h>

struct student
{
int num;
char *name;
char sex;
float score;
} boy1 = {102, "Jack", 'M', 78.5};

void main()
{
struct student *pstu;
pstu = &boy1;

printf("Number = %d\nName = %s\n", boy1.num, boy1.name);
printf("Sex = %c\nScore = %f\n\n", boy1.sex, boy1.score);

printf("Number = %d\nName = %s\n", (pstu).num, (pstu).name);
printf("Sex = %c\nScore = %f\n\n", (pstu).sex, (pstu).score);

printf("Number = %d\nName = %s\n", pstu->num, pstu->name);
printf("Sex = %c\nScore = %f\n\n", pstu->sex, pstu->score);
}
//输出:
Number = 102
Name = Jack
Sex = M
Score = 78.500000

Number = 102
Name = Jack
Sex = M
Score = 78.500000

Number = 102
Name = Jack
Sex = M
Score = 78.500000

6.5 结构指针变量做函数参数
将一个结构体变量的值传递给另一个函数,有 3 个方法 :
1)用结构体变量的成员作参数
2)用结构体变量作实参,见例10.

//例10
/#include <stdio.h>
/#include <string.h>

struct student
{
int num;
char *name;
float score[3];
};

void print(struct student);

void main()
{
struct student stu;

stu.num = 102;
stu.name = "Jack";
stu.score[0] = 98.5;
stu.score[1] = 99.0;
stu.score[2] = 99.5;

print( stu );
}

void print( struct student stu )
{
printf("num : %d\n", stu.num);
printf("name : %s\n", stu.name);
printf("score_1 : %5.2f\n", stu.score[0]);
printf("score_2 : %5.2f\n", stu.score[1]);
printf("score_3 : %5.2f\n", stu.score[2]);
}
//输出
num : 102
name : Jack
score_1 : 98.50
score_2 : 99.00
score_3 : 99.50

3)用指向结构体变量(或数组)的指针作实参,将结构体变量(或数组)的地址传给形参。见例11.

//例11
/#include <stdio.h>
/#include <string.h>

struct student
{
int num;
char name[20];
float score[3];
};

void print(struct student *);

void main()
{
struct student stu;

stu.num = 102;
strcpy(stu.name, "Jack"); /strcpy是将从存“Jack”字符串的内存单元的首地址开始,且含有'\0'结束符的字符串复制到以stu.name[0]开始的地址空间/
stu.score[0] = 98.5;
stu.score[1] = 99.0;
stu.score[2] = 99.5;

print( &stu );
}

void print( struct student *p )
{
printf("num : %d\n", p -> num);
printf("name : %s\n", p -> name);
printf("score_1 : %5.2f\n", p -> score[0]);
printf("score_2 : %5.2f\n", p -> score[1]);
printf("score_3 : %5.2f\n", p -> score[2]);
}
//输出:
num : 102
name : Jack
score_1 : 98.50
score_2 : 99.00
score_3 : 99.50

  1. 动态分配数组
    实际的编程中,往往会发生这种情况,即所需的内存空间取决于实际输入的数据,而无法预先确定。因此,C语言提供了一些内存管理函数,这些内存管理函数可以按需要动态地分配内存空间,也可把不再使用的空间回收待用,为有效地利用内存资源提供了手段。
    常用的内存管理函数有以下三个:
    1) 分配内存空间函数 malloc 、 calloc;
    2)释放内存空间函数 free

7.1 malloc函数
函数原型为: void *malloc(unsigned int size);
其作用是在内存的动态存储区中分配一个长度为 size 的连续空间( size 是一个无符号数)。此函数的返回值是一个指向分配域起始地址的指针(类型为 void )。如果此函数未能成功地执行(例如内存空间不足),则返回空指针 (NULL) 。

7.2 calloc函数
函数原型为:void *calloc ( unsigned n, unsigned size ) ;
其作用是在内存的动态存储区中分配n个长度为size 的连续空间。函数返回一个指向分配域起始地址的指针。如果分配不成功,返回 NULL 。
(用 calloc 函数可以为一维数组开辟动态存储空间, n 为数组元素个数,每个元素长度为 size 。)

7.3 free函数
函数原型为:void free ( void *p ) ;
其作用是释放由 p 指向的内存区,使这部分内存区能被其他变量使用。p 是最近一次调用 calloc 或 malloc 函数时返回的值。free 函数无返回值。

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

推荐阅读更多精彩内容