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 定義函數指針,作爲回調函數的參數傳遞,如下所示: