一、结构(struct)
结构类似于 java 中的类,只不过它只有属性,没有方法
1.1 定义、声明、初始化结构,访问结构成员
#include <stdio.h>
#define SIZE 20
//定义结构
struct book
{
char name[SIZE];
char author[SIZE];
double price;
};
int main(void)
{
//声明结构
struct book book_b;
struct book book_c;
//声明结构同时初始化
struct book book_a = {
"C Primer Plus",
"Stephen Prata",
60
};
//使用初始化器初始化,这种方式会指定的成员初始化,其他成员使用默认值
struct book book_d = {
.author = "d"
};
//使用访问结构成员的方式进行初始化,这种方式初始化会导致如果访问未被初始化的属性发生内存泄漏
gets_s(book_c.name, SIZE);
gets_s(book_c.author, SIZE);
book_c.price = 50;
//把一个结构初始化为另一个结构
book_b = (struct book){
"b",
"B",
60
};
printf("book_a: \n");
printf("name: %s, author: %s, price: %.2f", book_a.name, book_a.author, book_a.price);
printf("\n--------------------------------\n");
printf("book_b: \n");
printf("name: %s, author: %s, price: %.2f", book_b.name, book_b.author, book_b.price);
printf("\n--------------------------------\n");
printf("book_c: \n");
printf("name: %s, author: %s, price: %.2f", book_c.name, book_c.author, book_c.price);
printf("\n--------------------------------\n");
printf("book_d: \n");
printf("name: %s, author: %s, price: %.2f", book_d.name, book_d.author, book_d.price);
printf("\n--------------------------------\n");
getchar();
}
/*运行结果
c
C
book_a:
name: C Primer Plus, author: Stephen Prata, price: 60.00
--------------------------------
book_b:
name: b, author: B, price: 60.00
--------------------------------
book_c:
name: c, author: C, price: 50.00
--------------------------------
book_d:
name: , author: d, price: 0.00
--------------------------------
*/
代码详解:
结构有两层含义。一层含义是 ”结构布局“,结构布局告诉编译器如何表示数据,但是它并未让编译器为数据分配空间。另一层含义是结构变量,创建一个结构变量:
struct book book_a;
编译器执行这行代码便创建了一个结构变量 book_a。编译器使用 book 模板为该变量分配空间:两个内含 SIZE个元素的 char 数组和一个 double 类型的变量。
1.5 结构数组
结构数组就是一个数组里面存储的是结构。
例:
#include <stdio.h>
#define SIZE 20
struct book
{
char name[SIZE];
char author[SIZE];
double price;
};
int main(void)
{
int i;
//创建结构数组
struct book books[SIZE] =
{
{"aa", "AA", 20.5},
{"bb", "BB", 30},
{"cc", "CC", 40}
};
//输出数组
printf("%15s %15s %15s\n", "name", "autor", "price");
for(i = 0; i < 3; i++)
printf("%15s %15s %13.2f\n", books[i].name, books[i].author, books[i].price);
return 0;
}
/*运行结果
name autor price
aa AA 20.50
bb BB 30.00
cc CC 40.00
*/
1.6 指向结构的指针
本例演示了两种方法:
Ⅰ. 将指针指向一个已经声明的结构
Ⅱ. 使用 malloc 为指针分配空间
例1:
#include <stdio.h>
#define SIZE 20
struct book
{
char name[SIZE];
char author[SIZE];
double price;
};
int main(void)
{
struct book book_a ={
"C Primer Plus",
"Stephen Prata",
60
};
//声明结构指针
struct book * book_ptr_a;
struct book * book_ptr_b;
//指针指向 book_a
book_ptr_a = &book_a;
//输出
printf("%15s %15s %15s\n", "name", "autor", "price");
printf("%15s %15s %13.2f\n", book_ptr_a->name, book_ptr_a->author, book_ptr_a->price);
return 0;
}
/*结果
name autor price
C Primer Plus Stephen Prata 60.00
*/
例2:
#include <stdio.h>
#include <stdlib.h>
#define SIZE 20
struct book
{
char name[SIZE];
char author[SIZE];
double price;
};
int main(void)
{
struct book* book_ptr_b;
double* p = (double *) malloc(5 * sizeof(double));
//使用 malloc 分配空间
book_ptr_b = (struct book*) malloc(sizeof(struct book));
//设置属性
scanf("%s%s", book_ptr_b->name, book_ptr_b->author);
book_ptr_b->price = 50;
printf("%15s %15s %15s\n", "name", "autor", "price");
printf("%15s %15s %13.2f\n", book_ptr_b->name, book_ptr_b->author, book_ptr_b->price);
//malloc 分配的空间不会自动释放,养成好的编程习惯,要使用 free 释放空间
free(p);
return 0;
}
/*运行结果
bb cc
name autor price
bb cc 50.00
*/
例3:伸缩性数组成员,就是结构中的数组在定义时并未初始化
#include <stdio.h>
#include <stdlib.h>
struct class
{
int grade;
int stdunts[]; //stdunts的长度未指定
};
int main(void)
{
//声明并初始化一个 students 的长度未5 的结构 class
struct class * c = (struct class *) malloc(sizeof(struct class) + 5 * sizeof(int));
free(c);
return 0;
}
1.7 向函数传递结构的信息
可以传递三种信息:
Ⅰ. 传递结构成员
Ⅱ. 传递结构指针(按地址传递)
Ⅲ. 传递一个结构(按值传递)
1.8 使用 malloc() 为结构中的指针成员分配内存
例:
#include <stdio.h>
#include <stdlib.h>
#define SIZE 20
struct book
{
char * name; //name 是一个指针,它存储字符串的地址
char * author;
double price;
};
int main(void)
{
//声明一个 book 结构
struct book mybook;
//分配 name 指向的内存
mybook.name = (char *) malloc(SIZE);
//分配 author 指向的内存
mybook.author= (char *) malloc(SIZE);
//为 name 指向的内存设置一个值
scanf("%s %s", mybook.name, mybook.author);
//输出值
printf("%s, %s\n", mybook.name, mybook.author);
return 0;
}
/*运行结果
aaa AAA(输入)
aaa, AAA (输出)
*/
1.10 把结构保存到文件中
/*fwrite 把结构保存到文件中,注:保存到文件中的是二进制格式*/
#include <stdio.h>
#define SIZE 20
//定义结构体
struct book
{
char name[SIZE];
char author[SIZE];
double price;
};
int main(void)
{
FILE *bookfile;
int i;
//定义一个结构数组
struct book books[3] = {
{"aa", "AA", 20},
{"bb", "BB", 30},
{"cc", "CC", 40}
};
bookfile = fopen("books", "wb");
fwrite(books, sizeof(struct book), 3, bookfile); //把数组中内容写到 bookfile 文件中
fclose(bookfile);
return 0;
}
/*使用 fread() 从文件中读取结构*/
#include <stdio.h>
#define SIZE 20
//定义一个结构体
struct book
{
char name[SIZE];
char author[SIZE];
double price;
};
int main(void)
{
int i;
FILE *bookfile; //文件指针
struct book books[3]; //结构数组
bookfile = fopen("books", "rb"); //打开books
fread(books, sizeof(struct book), 3, bookfile); //把文件中的内容读到结构数组中
//输出
printf("books: \n");
for(i = 0; i < 3; i++)
printf("name: %s, author: %s, price: %.2f\n", books[i].name, books[i].author, books[i].price);
return 0;
}
/*结果:
books:
name: aa, author: AA, price: 20.00
name: bb, author: BB, price: 30.00
name: cc, author: CC, price: 40.00
*/
二、联合(union)
联合是一种数据类型,它能在同一个内存空间中存储不同的数据类型(不是同时存储)。
2.1 使用联合
例:
#include <stdio.h>
//定义一个联合
union data {
int a;
double b;
};
int main(void)
{
//声明联合
union data a = {88}; //初始化联合成员
union data b = {.b = 55.5}; //使用初始化器初始化元素 b
union data c;
//查看数据
printf("union a: \n");
printf("a = %d, b = %.2f\n", a.a, a.b);
printf("union b: \n");
printf("a = %d, b = %.2f\n", b.a, b.b);
printf("union c: \n");
c.a = 1;
printf("before set b, a = %d, b = %.2f \n", c.a, c.b);
c.b = 2;
printf("after set b, a = %d, b = %.2f \n", c.a, c.b);
//查看联合大小
printf("--------------------------------\n");
printf("size of union: %zd\n", sizeof(union data));
return 0;
}
/*运行结果
union a:
a = 88, b = 0.00
union b:
a = 0, b = 55.50
union c:
before set b, a = 1, b = 0.00
after set b, a = 0, b = 2.00
--------------------------------
size of union: 8
*/
分析结果:
实验结果符合定义,联合只能存储一个变量,其他变量是默认值。联合所占的空间大小是最大的一个数据类型所占的空间。
三、枚举(enum)
可以用枚举类型(enmuerated type)声明符号名称来表示整型常量。枚举类型的目的是提高程序的可读性。
例:
#include <stdio.h>
enum spectrum { red, orange, yellow, green, blue, violet};
int main(void)
{
enum spectrum color;
//查看枚举的值
printf("red = %d, orange = %d, yellow = %d, green = %d, blue = %d, violet = %d\n", red, orange, yellow, green, blue, violet);
color = 1; //使用整形常量给 color 赋值
switch(color)
{
case 0: printf("I'm red\n");
break;
case orange: printf("I'm orange\n"); //使用枚举来接受整形常量
break;
default: printf("I'm others");
break;
}
return 0;
}
/*运行结果
red = 0, orange = 1, yellow = 2, green = 3, blue = 4, violet = 5
I'm orange
*/
四、函数指针
普通变量有地址,函数也有地址。指向函数的指针中存储着函数代码起始的地址,函数指针主要的用法就是把函数作为另一个函数的参数。
声明函数指针如同声明函数一样,也要指定名称,参数,返回值类型。
例:void (*pf) (char *)
例1:
/*练习声明、使用函数指针*/
#include <stdio.h>
void (*pf) (char *); //声明一个函数指针
void show(char *); //声明一个函数,用函数指针指向它
int main(void)
{
pf = show;
//ANSI 认为这两种方法等价
pf("I'm 'pf'");
(*pf)("I'm '*pf'");
return 0;
}
void show(char * str)
{
printf("%s\n", str);
return;
}
/*运行结果
I'm 'pf'
I'm '*pf'
*/
例2:
#include <stdio.h>
void (*pf) (char *); //声明一个函数指针
void show(char *); //声明一个函数,用函数指针指向它
void usePf (void (*pf)(char *), char *, int choice); //声明一个函数,它的参数是一个函数参数为 char* 的函数指针、一个 char* 、一个 int 值
int main(void)
{
pf = show;
int choice;
char str[20];
//写一个死循环接受用户选择,当输入的不是数字则停止
printf("enter your choice: ");
while(scanf("%d", &choice) == 1)
{
printf("enter a string: ");
scanf("%s", str);
usePf(pf, str, choice); //调用 usePf
printf("enter your choice: ");
}
return 0;
}
void show(char * str)
{
printf("%s\n", str);
return;
}
void usePf(void (*pf)(char *), char * str, int choice)
{
switch(choice)
{
case 1: printf("No.1, ");
break;
case 2: printf("No.2, ");
break;
case 3: printf("No.3, ");
break;
}
pf(str); //使用函数指针来调用 show 函数
}
/*运行结果
enter your choice: 1
enter a string: hello
No.1, hello
enter your choice: 2
enter a string: chi
No.2, chi
enter your choice: 3
enter a string: hh
No.3, hh
*/
参考书籍
C Primer Plus (第六版)中文版