c primer plus 專題14:結構和其他數據形式

1 創建圖書目錄

#include <stdio.h>
#include <string.h>

char *s_gets(char * st, int n);
#define MAXTITL		41		/* 書名最大長度 + 1 */
#define MAXAUTL		31		/* 作者姓名的最大長度 + 1 */

struct book					/* 結構模板:標記是 book	*/
{
	char title[MAXTITL];
	char author[MAXAUTL];
	float value;
};							/* 結構模板結束			*/

int main(void)
{
	struct book library;	/* 把 library 聲明爲一個 book 類型的變量 */

	puts("Please enter the book title.");
	s_gets(library.title, MAXTITL);		/* 訪問 title 部分 */
	puts("Now enter the author.");
	s_gets(library.author, MAXAUTL);
	puts("Now enter the value.");
	scanf("%f", &library.value);
	printf("%s by %s: $%.2f\n", library.title, library.author, library.value);
	printf("%s: \"%s\" ($%.2f)\n", library.author, library.title, library.value);
	puts("Done!!!");
	
	return 0;
}

char *s_gets(char * st, int n)
{
	int i = 0;
	char * ret_val;
	
	ret_val = fgets(st, n, stdin);
	if (ret_val)
	{
		while (st[i] != '\n' && st[i] != '\0')
			i++;
		if (st[i] == '\n')
			st[i] = '\0';
		else
			while (getchar() != '\n')
				continue;
	}

	return ret_val;
}

程序執行結果

2 定義結構聲明

1 建立結構聲明

2 初始化結構

3 訪問數組成員

4 結構體的初始化器

3 結構體數組

#include <stdio.h>
#include <string.h>

#define MAXTITL		40		
#define MAXAUTL		30
#define MAXBKS		100

struct book							/* 簡歷 book 模板 */
{		
	char title[MAXTITL];
	char author[MAXAUTL];
	float value;
};							

int main(void)
{
	int i;
	char title[MAXTITL];
	char author[MAXAUTL];
	struct book library[MAXBKS];	/* book 類型結構體數組 */
	
	/* 結構體數組賦值 */
	for (i = 0; i < MAXBKS; i++)
	{
		sprintf(title, "title %d", i);
		sprintf(author, "author %d", i);
		strncpy(library[i].title, title, MAXTITL);
		strncpy(library[i].author, author, MAXAUTL);
		library[i].value = i + 0.5f;
	}
	/* 打印輸出 */
	for (i = 0; i < MAXBKS; i++)
	{
		printf("library[%d].title = %s\tlibrary[%d].author = %s\t"
			"library[%d].value = %.2f\n", i, library[i].title,
			i, library[i].author, i, library[i].value);
	}                                 
	puts("Done!!!");
	
	return 0;
}

聲明結構體數組

4 結構體嵌套

5 指向結構體的指針

#include <stdio.h>

#define LEN 20

struct names			/* 姓名結構體 */
{
	char first[LEN];
	char last[LEN];
};

struct guy				/* 個人信息結構體 */
{
	struct names handle;
	char favfood[LEN];
	char job[LEN];
	float income;
};

int main(void)
{
	struct guy fellow[2] = {	/* 結構體數組 */
		{ { "Even", "Villard" },
		    "grilled salmon",
		    "personality coach",
		    68112.00
		},
		{ { "Rodney", "Swillbelly" },
		    "tripe",
		    "tabloid editor",
		    432400.00
		}
	};
	struct guy * him;			/* 指向結構體的指針 */
	printf("address #1: %p #2: %p\n", &fellow[0], &fellow[1]);
	him = &fellow[0];			/* 告訴編譯器指針該指向何處 */
	printf("pointer #1: %p #2: %p\n", him, him + 1);
	printf("him->income is $%.2f: (*him).income is $%.2f\n",
		him->income, (*him).income);
	him++;
	printf("him->favfood is %s: him->handle.last is %s\n",
		him->favfood, him->handle.last);

	return 0;
}

程序執行結果:

1 聲明和初始化結構體指針(和數組不同,結構體名並不是結構體地址)

在有些系統中,一個結構體大小可能大於它各成員大小之和。這是因爲系統對數據進行校準的過程中產生了間隔。例如,有些系統把結構體的每個成員放在偶數地址上,或4的倍數地址上。在這種系統中,結構體的內部就存在未使用的空間。

2 用指針訪問結構體成員

6 函數與結構體

1 結構體允許直接賦值,如下所示

這意味着,我們既可以將結構體本身作爲參數傳遞,也可以傳遞結構體指針。如何選擇?

2 結構體中的字符數組和字符指針

當字符指針沒有指向預先設置好的確定地址時,程序可能會出問題。如下所示:

3 結構體、指針和malloc() 函數

如果在結構體中使用字符串指針,一個合理的做法是 malloc() 申請一塊內存,將地址傳遞給字符串指針。

完整的程序如下所示

#include <stdio.h>
#include <string.h>		/* 提供 strlen() strcpy() 函數原型 */
#include <stdlib.h>		/* 提供 malloc() free() 函數原型 */

#define SLEN	81

struct namect 
{						/* 使用字符串指針 */
	char * fname;		
	char * lname;
	int letters;
};

void getinfo(struct namect * pst);
void makeinfo(struct namect * pst);
void showinfo(struct namect * pst);
void cleanup(struct namect * pst);
char *s_gets(char *st, int n);

int main(void)
{
	struct namect person;

	getinfo(&person);
	makeinfo(&person);
	showinfo(&person);
	cleanup(&person);

	return 0;
}

void getinfo(struct namect * pst)
{
	char temp[SLEN];

	puts("Please enter your first name.");
	s_gets(temp, SLEN);
	/* 分配剛好容納字符串大小的內存空間 */
	pst->fname = (char *)malloc(strlen(temp) + 1);
	/* 將讀取到的字符串,從臨時數組拷貝至分配的內存 */
	strcpy(pst->fname, temp);

	puts("Please enter your last name.");
	s_gets(temp, SLEN);
	pst->lname = (char *)malloc(strlen(temp) + 1);
	strcpy(pst->lname, temp);
}

void makeinfo(struct namect * pst)
{
	pst->letters = strlen(pst->fname) + strlen(pst->lname);
}

void showinfo(struct namect * pst)
{
	printf("%s %s, your name contains %d letters.\n",
		pst->fname, pst->lname, pst->letters);
}

void cleanup(struct namect * pst)
{
	free(pst->fname);		/* 釋放動態內存 */
	free(pst->lname);
}

char *s_gets(char *st, int n)
{
	char * find;
	char * ret_val;

	ret_val = fgets(st, n, stdin);
	if (ret_val)
	{
		find = strchr(st, '\n');	/* 查找換行符 */
		if (find)					/* 如果地址不是NULL */
			*find = '\0';			/* 在此處放置一個空字符 */
		else
			while (getchar() != '\n')
				continue;			/* 清除緩衝區 */
	}
	return ret_val;
}

程序執行結果

4 使用結構體數組的函數

假設一個函數要處理一個結構體數組。由於數組名就是該數組的地址,所以可以把它傳遞給函數。

#include <stdio.h>

#define FUNDLEN		50
#define N			2

struct funds {
	char	bank[FUNDLEN];
	double	bankfund;
	char	save[FUNDLEN];
	double	savefund;
};

double sum(const struct funds * money, int n);

int main(void)
{
	struct funds jones[N] = {
		{
			"Garlic-Melon bank",
			4032.27,
			"Lucky's Saving and Loan",
			8543.94
		},
		{
			"Honest Jack's bank",
			3620.88,
			"Party Time Savings",
			3802.91
		}
	};
	printf("The joneses have a total of %.2f.\n", sum(jones, N));

	return 0;
}

double sum(const struct funds * money, int n)
{
	int i;
	double total = 0;

	for (i = 0; i < n; i++)
	{
		// total += money[i].bankfund + money[i].savefund;			/* 數組表示 */
		total += (*(money + i)).bankfund + (*(money + i)).savefund;	/* 指針表示 */
	}
		
	return total;
}

7 聯合簡介 union

當多個數據需要共享內存或者多個數據每次只取其一時,可以利用聯合體(union)。

使用 聯合體 union

使用 聯合體 union 的一個有用的做法:用一個成員把值存儲在一個聯合中,然後用另一個成員查看內容。這種方式非常重要,在各種通信協議(如 MQTT)中非常流行。

8 枚舉類型 enum

1 enum 類型的本質:

2 使用 enum 的很精妙的程序

9 typedef

1 typedef 簡介

2 typedef 其他用處

2.1 聲明指針

2.2 typedef 定義結構體

10 複雜類型聲明

1 數組和指針

2 函數和指針(重點理解!!!)

11 函數指針

1 函數指針有什麼用?

2 從函數到函數指針

3 函數地址賦值給函數指針

4 通過函數指針,來進行函數調用

5 函數調用函數指針(回調函數)

下面是使用函數指針的實例:

/* func_ptr.c -- 函數指針 */
#include <stdio.h>
#include <string.h>
#include <ctype.h>		/* 提供 toupper() tolower() isupper() 等函數原型 */

#define LEN		81

char showmenu(void);
void eatline(void);				/* 讀取至行末尾 */
void ToUpper(char * str);		/* 把字符串轉換爲大寫 */
void ToLower(char * str);		/* 把字符串轉換爲小寫 */
void Transpose(char * str);		/* 大小寫轉置 */
void Dummy(char * str);			/* 不改變字符串 */
void show(void(*fp)(char *), char * str);
char * s_gets(char * st, int n);

int main(void)
{
	char line[LEN];
	char copy[LEN];
	char choice;
	void(*pfun)(char *);		/* 聲明函數指針 */

	pfun = NULL;				/* 缺少這行,編譯器可能會報錯 */
	puts("Enter a string (empty line to quit):");
	while (s_gets(line, LEN) != NULL && line[0] != '\0')
	{
		while ((choice = showmenu()) != '\n')
		{
			switch (choice)
			{
			case 'u': pfun = ToUpper; break;
			case 'l': pfun = ToLower; break;
			case 't': pfun = Transpose; break;
			case 'o': pfun = Dummy; break;
			default: break;
			}
			strcpy(copy, line);		/* 爲 show() 函數拷貝一份 */
			show(pfun, copy);
		}
		puts("Enter a string (empty line to quit):");
	}
	puts("Bye!!!");

	return 0;
}

char showmenu(void)
{
	char ans;

	puts("Enter menu choice:");
	puts("u) uppercase          l) lowercase");
	puts("t) transposed case    o) original case");
	puts("n) next string");
	ans = getchar();			/* 獲取用戶輸入 */
	ans = tolower(ans);			/* 轉換爲小寫 */
	eatline();					/* 清除緩衝區 */
	while (strchr("ulton", ans) == NULL)
	{
		puts("Please enter a u, l, t, o, or a n:");
		ans = tolower(getchar());
		eatline();
	}

	return ans;
}

void eatline(void)
{
	while (getchar() != '\n')
		continue;
}

void ToUpper(char * str)
{
	while (*str)
	{
		*str = toupper(*str);
		str++;
	}
}

void ToLower(char * str)
{
	while (*str)
	{
		*str = tolower(*str);
		str++;
	}
}

void Transpose(char * str)
{
	while (*str)
	{
		if (isupper(*str))
			*str = tolower(*str);
		else if (islower(*str))
			*str = toupper(*str);
		str++;
	}
}

void Dummy(char * str)
{
	/* 空函數體,不改變字符串 */
}

void show(void(*fp)(char *), char * str)
{	
	fp(str);		/* 把用戶選定的函數作用於 str */
	puts(str);		/* 顯示結果 */
}

char * s_gets(char * st, int n)
{
	char * find, *ret_val;

	ret_val = fgets(st, n, stdin);
	if (ret_val)
	{
		find = strchr(st, '\n');
		if (find)
			*find = '\0';
		else
			while (getchar() != '\n')
				continue;
	}

	return ret_val;
}

程序執行結果

程序改進:

使用 typedef 定義函數指針,作爲回調函數的參數傳遞,如下所示:

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章