1.結構體
(1)內容
定義:
1.用戶自定義的數據類型
2.可以包含若干不同數據類型(可相同)的成員變量
3.這些數據項組合起來反應某一信息
格式:
struct 結構體名 (用戶自定義的數據類型)
{
????????數據類型 成員變量1;
????????數據類型 成員變量2;
????????數據類型 成員變量3;
????????...
};
(2)結構體變量
(結構體類型定義的變量)
格式:
struct 結構體 變量名
定義方式:
①先定義結構體類型,再定義其變量
struct 結構體名
{
????????成員變量;
};
struct 結構體名 變量名;
②定義結構體同時,定義結構體變量
③缺省結構體名定義結構體變量
(3)賦值
① 定義變量的同時直接大括號賦值 (初始化)
②定義變量時未初始化,然后對成員變量單獨賦值
③點等法賦值
(4)訪問
通過 .訪問:結構體變量名.成員變量名
scanf("%s %d %d %d", stu.name, &stu.id, &stu.score, &stu.age);
printf("%s %d %d %d\n", stu.name, stu.id, stu.score, stu.age);
(5)重定義 typedef
對變量進行重命名
typedef int int _num;
int == int_num
int a; == int_num a;
對指針地址進行重命名
typedef int * int _p
int * == int_p
int *p = &a; == int _p p = &a;
① 定義結構體的同時重定義
typedef struct student
{
????????int id;
????????int score;
} STU; // STU是結構體類型重定義的名字
struct student stu; == STU stu;
② 先定義結構體,然后重定義
struct student
{
????????int id;
????????int age;
};
typedef struct student STU;
2.結構體數組
(結構體相類型變量組成的數組)
(1)格式
① 定義結構體的同時定義結構體數組
②先定義結構體,然后定義結構體數組
struct student
{char name[32];int id;int score;int age;
};struct student stu[3];
(2)賦值:
????????① 定義結構體數組的同時賦值
????????②先定義結構體數組。再對結構體數組的每一個人元素分別賦值
(3)結構體數組的輸入輸出:(for循環遍歷)
#include <stdio.h>
#include <string.h>struct student
{char name[32];int id;int score;int age;
};int main(int argc, char const *argv[])
{struct student stu[3];for(int i = 0; i < 3; i++){scanf() \ printf();}return 0;
}
(4)結構體數組的大小
????????sizeof(結構體數組名);
????????元素個數 * 結構體類型大小
3.結構體指針
(指向結構體的指針)
(1)格式:
????????存儲類型 數據類型 *指針變量名
????????struct 結構體名 *結構體指針變量名
(2)賦值:
????????格式:結構體指針變量 -> 成員變量
????????-> 指向的
#include <stdio.h>
#include <string.h>struct student
{int id;int age;
} stu;struct work
{int id;int score;
} w;int main(int argc, char const *argv[])
{struct student *p1 = &stu;
p1->id = 1;
p1->age = 18;printf("%d %d\n", stu.id, stu.age);return 0;
}
(*p1).成員變量
練習:
????????創建一個結構體數組,數組名為student,成員包含學號,姓名,成績(數據類型自己設定)從終端輸入學生信息,封裝函數實現按成績從低到高打印學生信息。
#include <stdio.h>
#include <string.h>#define N 3struct student
{int id;char name[32];int score;
};void fun(struct student *p)
{struct student temp;for(int i = 0; i < N-1; i++){for(int j = 0; j < N-1-i; j++){if((p+j)->score > (p+j+1)->score){
temp = *(p+j);*(p+j) = *(p+j+1);*(p+j+1) = temp;}// if(p[j].score > p[j+1].score)// {// temp = p[j];// p[j] = p[j+1];// p[j+1] = temp;// }}}for(int i = 0; i < N; i++)//printf("%d %s %d\n", p[i].id, p[i].name, p[i].score);printf("%d %s %d\n", (p+i)->id, (p+i)->name, (p+i)->score);
}int main(int argc, char const *argv[])
{struct student stu[N];for (int i = 0; i < N; i++)scanf("%d %s %d", &stu[i].id, stu[i].name, &stu[i].score);fun(stu);return 0;
}
(2)結構體大小
sizeof(struct 結構體名) // 結構體類型大小
struct student
{char a;short w;char y;int b;char c;
} stu;
結構體大小遵循字節對齊原則
? ? ? ? 1.字節對齊:
在實際使用中,訪問特定數據類型變量時需要在特定的內存起始地址進行訪問,這就需要各種數據類型按照一定的規則在空間上進行排列,而不是順序地一個接一個地存放,這就是字節對齊
????????2.字節對齊原則:
- 在64位系統下默認的value值為8字節,判斷結構體類型中最大成員變量的字節大小,和默認的value值進行比較,按小的數進行對齊
- 結構體成員進行對齊時遵循地址偏移量是成員類型的整數倍
- 結構體成員按順序進行存儲,如果不滿足以上條件時,需要填充空字節
? ? ? ? 3.進行字節對齊的原因:
- 平臺原因:不是所有的硬件平臺都能訪問任意地址上的任意數據的;某些硬件平臺只能在某些地址處取某些特定類型的數據,否則拋出硬件異常。(提高程序的移植性)
- 性能原因:數據結構(尤其是棧)應該盡可能地在自然邊界上對齊。原因在于,為了訪問未對齊的內存,處理器需要作兩次內存訪問;而對齊的內存訪問僅需要一次訪問。
- 內存原因:在設計結構體時,通過對齊規則盡可能優化結構體的空間大小至最小空間
例子:
struct student
{char a;short w;char y;int b;char c;
} stu; // 大小 16
第一步:找出成員變量的大小,讓其與編譯器的默認的對齊數進行比較,取較小的值作為該成員變量的對齊數
第二步:根據每個成員對應的對齊數畫出他們在內存中的相對位置
第三步:計算結構體大小
4.枚舉
維基百科的理解:枚舉類型用于聲明一組命名的常數,當一個變量有幾種可能的取值時,可以將它定義為枚舉類型。 定義:是指將變量的值一一列出來,變量的值只限于列舉出來的值的范圍內。
我的理解:枚舉類型就是將一些比較固定的值一一列舉出來,枚舉類型和宏定義是差不多的,只有細微區別,宏運行是在預處理階段完成的,枚舉類型是在與編譯階段完成的。
定義:
用戶自定義數據類型,用于聲明一組常數
格式:
enum 枚舉名
{
????????value1,
????????value2,
????????value3
????????...
};
未賦初值時,第一個常數默認是0,后續的常數依次加一(如果第一個值賦值為1,從賦值的1開始遞增)
#include <stdio.h>
#include <string.h>
enum week
{
MON = 1,
TUE,
WED,
Thurs,
Fri
};
int main(int argc, char const *argv[])
{int day;scanf("%d", &day);switch (day){case MON: printf("Monday"); break;case TUE:printf("Tuesday"); break;case WED:printf("Wednesday"); break;case Thurs:printf("Thursday"); break;case Fri:printf("Friday"); break;default:break;}return 0;
}
5.共用體
不同類型的成員變量共用同一塊地址空間
格式:
union 共用體名
{
????????成員列表;
}
(1)定義共用體變量
union 共用體名 變量名;
#include <stdio.h>
#include <string.h>union val
{int a;char ch;
} v;int main(int argc, char const *argv[])
{
v.a = 10;// 他倆不能同時出現,是以最后一次賦值為準
v.ch = 'a';printf("%d\n", v.a);return 0;
}
(2)特性:
- 共用體成員共用同一塊地址空間
- 賦值順序以最后一次為準
- 共用體大小為成員中類型最大的數據的大小
(3)使用共用體測試大小端
#include <stdio.h>
#include <string.h>union val
{int num;char ch;
} v;int main(int argc, char const *argv[])
{
v.num = 0x12345678;if(v.ch == 0x78){printf("小端\n");}else{printf("大端\n");}return 0;
}