定义一个结构的一般形式为:
struct 结构名
{
成员表列
};
对每个成员也必须作类型说明,其形式为:
类型说明符 成员名;
struct student
{
int num;
char name[20];
char sex;
int age;
float score;
char addr[30];
}
可以采取以下3种方法定义结构体类型变量:
(1)先声明结构体类型再定义变量名
结构类型变量
在定义了结构体变量后,系统会为之分配内存单元。
例如:
student1和student2在内存中各占 ? 个字节。
(4 + 20 + 1 + 4 + 4 + 30 = 67)。
这个东西不对呀。我用
#include <stdio.h>
struct student
{
int num;
char name[20];
char sex;
int age;
float score;
char addr[30];
} student1, student2; \\这其实是下面第二种方法
void main()
{
printf("size:%d", sizeof(student));
}
结果是68
(2)在声明类型的同时定义变量
这种形式的定义的一般形式为:
struct 结构体名
{
成员表列
} 变量名表列;
例如:
struct student
{
int num;
char name[20];
char sex;
int age;
float score;
char addr[30];
}student1, student2;
(3) 直接定义结构体类型变量
其一般形式为:
struct
{
成员表列
}变量名表列;
即不出现结构体名
看这图
嵌套的定义
结论:这是一个嵌套的定义
struct date
{
int month;
int day;
int year;
};
struct
{
int num;
char name[20];
char sex;
struct date birthday;
float score;
} boy1, boy2;
正确引用结构体变量中成员的方式为:
结构体变量名.成员名
student1.num表示student1变量中的num成员,即student1的num(学号)项。
可以对变量的成员赋值,例如:student1.num=100;
“.”是成员(分量)运算符,它在所有的运算符中优先级最高,因此可以把student1.num作为一个整体来看待。
上面赋值语句的作用是将整数100赋给student1变量中的成员num。
boy2 = boy1;是把boy1所有值传给boy2
(2) 如果成员本身又属一个结构体类型,则要用若干个成员运算符,一级一级地找到最低的一级的成员。只能对最低级的成员进行赋值或存取以及运算。
对上面定义的结构体变量student1, 可以这样访问各成员:
student1.num
student1.birthday.month
(3) 对结构体变量的成员可以像普通变量一样进行各种运算(根据其类型决定可以进行的运算)。
例如:
student2.score = student1.score;
sum = student1.score + student2.score;
student1.age++;
++student2.age;
(4) 可以引用结构体变量成员的地址,也可以引用结构体变量的地址。
例
#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 );
}
结构体的名称和他的第一个元素地址相同
结构体的初始化
#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);
}
结构体数组
一个结构体变量中可以存放一组数据(如一个学生的学号、姓名、成绩等数据)。
如果有10个学生的数据需要参加运算,显然应该用数组,这就是结构体数组。
结构体数组与以前介绍过的数值型数组不同之处在于每个数组元素都是一个结构体类型的数据,它们都分别包括各个成员(分量)项。
#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);
}
}
结构体数组初始化
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[]{{…},{…},{…}};
即先声明结构体类型,然后定义数组为该结构体类型,在定义数组时初始化。
指向结构体类型数据的指针
一个结构体变量的指针就是该结构体变量所占据的内存段的起始地址。
结构指针变量说明的一般形式为:
struct 结构名 *结构指针变量名
例如,在前面的例题中定义了stu这个结构,如要说明一个指向stu的指针变量pstu,可写为:
struct stu *pstu
赋值是把结构变量的首地址赋予该指针变量,不能把结构句赋予该指针量
如
struct student
{
char name[30];
int age;
} boy;
struct student *pstu;
pstu = &boy; //这是正确的
pstu = &student; //这是错误的
其访问的一般形式为:
(*结构指针变量).成员名
或为:
结构指针变量->成员名
例如:
(*pstu).num
或者:
pstu->num
结构指针变量作函数参数
将一个结构体变量的值传递给另一个函数,有3个方法:
(1)用结构体变量的成员作参数
(2)用结构体变量作实参
(3)用指向结构体变量(或数组)的指针作实参,将结构体变量(或数组)的地址传给形参
先用结构体变量作函数参数:
#include <stdio.h>
#include <string.h>
struct student
{
int num;
char name[20];
//char *name;
float score[3];
};
void print(struct student);
void main()
{
struct student stu;
stu.num = 8;
strcpy(stu.name, "Fishc.com");
//stu.name = "Fishc.com!"; //如果用这个前面必须定义为 char *name; 把"Fishc.com" 这个常量的地址赋给指针
stu.score[0] = 98.5;
stu.score[1] = 99.0;
stu.score[2] = 99.5;
print( stu );
}
void print( struct student stu )
{
printf("\tnum : %d\n", stu.num);
printf("\tname : %s\n", stu.name);
printf("\tscore_1 : %5.2f\n", stu.score[0]);
printf("\tscore_2 : %5.2f\n", stu.score[1]);
printf("\tscore_3 : %5.2f\n", stu.score[2]);
printf("\n");
}
改用指向结构体变量的指针作实参:
#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 = 8;
strcpy(stu.name, "Fishc.com!");
stu.score[0] = 98.5;
stu.score[1] = 99.0;
stu.score[2] = 99.5;
print( &stu );
}
void print( struct student *p )
{
printf("\tnum : %d\n", p -> num);
printf("\tname : %s\n", p -> name);
printf("\tscore_1 : %5.2f\n", p -> score[0]);
printf("\tscore_2 : %5.2f\n", p -> score[1]);
printf("\tscore_3 : %5.2f\n", p -> score[2]);
printf("\n");
}
动态存储分配
常用的内存管理函数有以下三个:
1. 分配内存空间函数 malloc、calloc
2. 释放内存空间函数 free
malloc函数
函数原型为 void *malloc(unsigned int size);
其作用是在内存的动态存储区中分配一个长度为size的连续空间(size是一个无符号数)。
此函数的返回值是一个指向分配域起始地址的指针(类型为void)。
如果此函数未能成功地执行(例如内存空间不足),则返回空指针(NULL)。
calloc函数
函数原型为 void *calloc (unsigned n, unsigned size);
其作用是在内存的动态存储区中分配n个长度为size的连续空间。
函数返回一个指向分配域起始地址的指针;
如果分配不成功,返回NULL。
用calloc函数可以为一维数组开辟动态存储空间,n为数组元素个数,每个元素长度为size。
free函数
函数原型为 void free (void *p);
其作用是释放由p指向的内存区,使这部分内存区能被其他变量使用。
p是最近一次调用calloc或malloc函数时返回的值。
free函数无返回值。
链表
链表的组成:
头指针:存放一个地址,该地址指向第一个元素
结点:用户需要的实际数据和链接节点的指针
链表的组成
实践
我们尝试根据下图建立链表:
链表的组成
例
#include <stdio.h>
struct student
{
long num;
float score;
struct student *next;
};
void main()
{
struct student a, b, c, *head;
a.num = 10101;
a.score = 89.5;
b.num = 10103;
b.score = 90;
c.num = 10107;
c.score = 85;
head = &a;
a.next = &b;
b.next = &c;
c.next = NULL;
do
{
printf("%ld %5.1f\n", head->num, head->score);
head = head->next;
}while( head != NULL );
}
链表的输出
#include <stdio.h>
#include <malloc.h>
#include <stdlib.h>
#define LEN sizeof(struct student) // student结构的大小
struct student *creat(); //创建链表
void print(struct student *head); //打印链表
struct student
{
int num;
float score;
struct student *next;
};
int n; //全局变量,用来记录存放了多少数据。
void main()
{
struct student *stu;
stu = creat();
print( stu );
printf("\n\n");
system("pause");
}
struct student *creat()
{
struct student *head;
struct student *p1, *p2;
p1 = p2 = (struct student *)malloc(LEN); // LEN是student结构的大小
printf("Please enter the num :");
scanf("%d", &p1->num);
printf("Please enter the score :");
scanf("%f", &p1->score);
head = NULL;
n = 0;
while( 0 != p1->num )
{
n++;
if( 1 == n )
{
head = p1;
}
else
{
p2->next = p1;
}
p2 = p1;
p1 = (struct student *)malloc(LEN);
printf("\nPlease enter the num :");
scanf("%d", &p1->num);
printf("Please enter the score :");
scanf("%f", &p1->score);
}
p2->next = NULL;
return head;
}
void print(struct student *head)
{
struct student *p;
printf("\nThere are %d records!\n\n", n);
p = head;
if( NULL != head )
{
do
{
printf("学号为 %d 的成绩是: %f\n", p->num, p->score);
p = p->next;
}while( NULL != p );
}
}
对链表的删除操作
从一个动态链表中删去一个结点,并不是真正从内存中把它抹掉,而是把它从链表中分离开来,只要撤销原来的链接关系即可。
链表的删除操作
作业
#include <stdio.h>
#include <malloc.h>
#include <stdlib.h>
#define LEN sizeof(struct student) // student结构的大小
struct student *creat(); // 创建链表
struct student *del( struct student *head, int num); // del函数用于删除结点, *head即链表
// 的头指针, num是要删除的结点num。
void print(struct student *head); // 打印链表
struct student
{
int num;
float score;
struct student *next;
};
int n; // 全局变量,用来记录存放了多少数据。
void main()
{
struct student *stu, *p;
int n;
stu = creat();
p = stu;
print( p );
printf("Please enter the num to delete: ");
scanf("%d", &n);
print( del(p, n) );
printf("\n\n");
system("pause");
}
struct student *creat()
{
struct student *head;
struct student *p1, *p2;
p1 = p2 = (struct student *)malloc(LEN); // LEN是student结构的大小
printf("Please enter the num :");
scanf("%d", &p1->num);
printf("Please enter the score :");
scanf("%f", &p1->score);
head = NULL;
n = 0;
while( p1->num )
{
n++;
if( 1 == n )
{
head = p1;
}
else
{
p2->next = p1;
}
p2 = p1;
p1 = (struct student *)malloc(LEN);
printf("\nPlease enter the num :");
scanf("%d", &p1->num);
printf("Please enter the score :");
scanf("%f", &p1->score);
}
p2->next = NULL;
return head;
}
void print(struct student *head)
{
struct student *p;
printf("\nThere are %d records!\n\n", n);
p = head;
if( head )
{
do
{
printf("学号为 %d 的成绩是: %f\n", p->num, p->score);
p = p->next;
}while( p );
}
}
struct student *del( struct student *head, int num)
{
struct student *p1, *p2;
if( NULL == head ) // 如果头结点指向NULL,这是一个空链表。纯属忽悠T_T
{
printf("\nThis list is null!\n");
goto END;
}
p1 = head;
while( p1->num != num && p1->next != NULL)
{
p2 = p1;
p1 = p1->next;
}
if( num == p1->num )
{
if( p1 == head ) // 当将要删除的结点位于头结点的时候
{
head = p1->next;
}
else // 一般情况
{
p2->next = p1->next;
}
printf("\nDelete No: %d succeed!\n", num);
n = n-1; // n是作为一个全局变量,用来记录链表的数据数。
}
else
{
printf("%d not been found!\n", num);
}
END:
return head;
}
对链表的插入操作
对链表的插入是指将一个结点插入到一个已有的链表中。
为了能做到正确插入,必须解决两个问题:
① 怎样找到插入的位置;
② 怎样实现插入。
我们可以先用指针变量p0指向待插入的结点,p1指向第一个结点。将p0->num与p1->num相比较,如果p0->num>p1-> num ,此时将p1后移,并使p2指向刚才p1所指的结点。
对链表的插入操作
对链表的插入操作
#include <stdio.h>
#include <malloc.h>
#include <stdlib.h>
#define LEN sizeof(struct student) // student结构的大小
struct student *creat(); //创建链表
struct student *del(struct student *head, int num); //del函数用于删除结点, *head即链表
//的头指针, num是要删除的结点num。
struct student *insert(struct student *head, struct student *stu_2); // 第一个参数需要被插入的链表
// 第二个参数待插入的结构的地址
void print(struct student *head); //打印链表
struct student
{
int num;
float score;
struct student *next;
};
int n; //全局变量,用来记录存放了多少数据。
void main()
{
struct student *stu, *p, stu_2;
int n;
stu = creat();
p = stu;
print( p );
printf("\nPlease input the num to delete: ");
scanf("%d", &n);
print( del(p, n) );
printf("\nPlease input the num to insert: ");
scanf("%d", &stu_2.num);
printf("Please input the score: ");
scanf("%f", &stu_2.score);
p = insert(stu, &stu_2);
print( p );
printf("\n\n");
system("pause");
}
struct student *creat()
{
struct student *head;
struct student *p1, *p2;
p1 = p2 = (struct student *)malloc(LEN); // LEN是student结构的大小
printf("Please enter the num :");
scanf("%d", &p1->num);
printf("Please enter the score :");
scanf("%f", &p1->score);
head = NULL;
n = 0;
while( p1->num )
{
n++;
if( 1 == n )
{
head = p1;
}
else
{
p2->next = p1;
}
p2 = p1;
p1 = (struct student *)malloc(LEN);
printf("\nPlease enter the num :");
scanf("%d", &p1->num);
printf("Please enter the score :");
scanf("%f", &p1->score);
}
p2->next = NULL;
return head;
}
void print(struct student *head)
{
struct student *p;
printf("\nThere are %d records!\n\n", n);
p = head;
if( head )
{
do
{
printf("学号为 %d 的成绩是: %f\n", p->num, p->score);
p = p->next;
}while( p );
}
}
struct student *del( struct student *head, int num)
{
struct student *p1, *p2;
if( NULL == head )
{
printf("\nThis list is null!\n");
goto end;
}
p1 = head;
while( p1->num != num && p1->next != NULL)
{
p2 = p1;
p1 = p1->next;
}
if( num == p1->num )
{
if( p1 == head )
{
head = p1->next;
}
else
{
p2->next = p1->next;
}
printf("Delete No: %d succeed!\n", num);
n = n-1;
}
else
{
printf("%d not been found!\n", num);
}
end:
return head;
}
struct student *insert(struct student *head, struct student *stu_2)
{
struct student *p0, *p1, *p2;
p1 = head;
p0 = stu_2;
if( NULL == head )
{
head = p0;
p0->next = NULL;
}
else
{
while( (p0->num > p1->num) && (p1->next != NULL) ) //两种情况推出while,一:
{
p2 = p1;
p1 = p1->next;
}
if( p0->num <= p1->num )
{
if( head == p1 ) // p1是头结点,插入头部
{
head = p0;
}
else // 普通情况,插入中间
{
p2->next = p0;
}
p0->next = p1;
}
else // p0的num最大,插入到末尾
{
p1->next = p0;
p0->next = NULL;
}
}
n = n+1; // 由于插入了,所以增加了一位数据成员进入链表中。
return head;
}
共用体
定义共用体类型变量的一般形式为:
union 共用体名
{
成员表列
}变量表列;
例如:
union data
{
int i;
char ch;
float f;
}a, b, c;
或
union data
{
int i;
char ch;
float f;
};
union data a, b, c;
应用情况
设有若干个人员的数据,其中有学生和教师。学生的数据中包括:姓名、号码、性别、职业、班级。
教师的数据包括:姓名、号码、性别、职业、职务。可以看出,学生和教师所包含的数据是不同的。
现要求把它们放在同一表格中。
共用体类型数据的特点
处理算法
#include <stdio.h>
struct
{
int num;
char name[10];
char sex;
char job;
union
{
int banji;
char position[10];
}category;
}person[2]; //为了方便先假设一个学生一个老师。
void main()
{
int i;
for(i=0; i < 2; i++)
{
printf("Please input the num: ");
scanf("%d", &person[i].num);
printf("Please input the name: ");
scanf("%s", person[i].name);
fflush(stdin);
printf("Please input the sex(M/F): ");
scanf("%c", &person[i].sex);
fflush(stdin);
printf("Please input the job(s/t): ");
scanf("%c", &person[i].job);
fflush(stdin);
if( person[i].job == 's' )
{
printf("Please input the class: ");
scanf("%d", &person[i].category.banji);
fflush(stdin);
}
else if( person[i].job == 't' )
{
printf("Please input the position: ");
scanf("%s", person[i].category.position);
fflush(stdin);
}
else
{
printf("Input Error!!\n");
}
printf("\n");
}
// 以下是打印数据……
printf("No. name sex job class/position\n");
for( i=0; i < 2; i++ )
{
if( person[i].job == 's')
{
printf("%-6d%-10s%-3c%-3c%10d\n", person[i].num,
person[i].name, person[i].sex, person[i].job,
person[i].category.banji);
}
else
{
printf("%-6d%-10s%-3c%-3c%10s\n", person[i].num,
person[i].name, person[i].sex, person[i].job,
person[i].category.position);
}
}
}
枚举变量的声明
设有变量a,b,c被说明为上述的weekday,可采用下述任一种方式:
enum weekday{ sun,mou,tue,wed,thu,fri,sat };
enum weekday a, b, c;
或者为:
enum weekday{ sun,mou,tue,wed,thu,fri,sat }a, b, c;
或者为:
enum { sun,mou,tue,wed,thu,fri,sat }a, b, c;
枚举还要百度一下看看
用typedef定义类型
用typedef声明新的类型名来代替已有的类型名
声明INTEGER为整型
typedef int INTEGER
声明结构类型
typedef struct{
int month;
int day;
int year;
}DATE;
例:
#include <stdio.h>
typedef struct
{
int month;
int day;
int year;
}DATE;
void main()
{
DATE date_one; //注意这用DATE 声明个结构变量
date_one.month = 12;
date_one.day = 31;
date_one.year = 2012;
printf("%d - %d - %d \n", date_one.year, date_one.month, date_one.day);
}
声明NUM为整型数组类型
例:
typedef int NUM[100];
NUM num = {0};
声明STRING为字符指针类型
typedef char* STRING;
STRING p1;
p1 = "I Love Fishc.com";
声明POINTER为指向函数的指针类型,该函数返回整型值。
typedef int (*POINTER)();
例:
#include <stdio.h>
typedef void (*P)();
void fun();
void main()
{
char a[10] = "Fishc.com!";
P p1; // 相当于void (*p1)();
p1 = fun; //p1 = &fun;也行和上面的数组相似
(p1)();
printf("%d %d\n", a, &a);
}
void fun()
{
printf("I love Fishc.com!\n");
}
注意,纯C,变量只能定义在函数头
下面看一下fun指针传递的反汇编
用typedef定义类型的方法
先按定义变量的方法写出定义体(如:int i)
将变量名换成新类型名(例如:将i换成COUNT)。
在最前面加 typedef
(例如:typedef int COUNT)
然后可以用新类型名去定义变量
(例如:COUNT i, j;)
区别: typedef 和 define
typedef (int*) p1;
和
#define p2 int*
如果用 p2 x1,x2;
相当于int *x1,x2; 只有x1为指针 x2为int