九、結構體
構造類型:
不是基本類型的數據結構也不是指針類型, 它是若干個相同或不同類型的數據構成的集合
結構體類型:
結構體是一種構造類型的數據結構,是一種或多種基本類型或構造類型的數據的集合。
1.結構體類型定義
定義:
(1).先定義結構體類型, 再去定義結構體變量
struct 結構體類型名{
成員列表;
};
struct stu{
int num;
char name[20];
char sex;
};
//有了結構體類型后, 就可以用類型定義變量了
struct stu lucy,bob,lilei;//定義了三個 struct stu 類型的變量,每個變量都有三個成員, 分別是 num name sex
(2).在定義結構體類型的時候順便定義結構體變量, 以后還可以定義結構體變量
struct 結構體類型名{
成員列表;
}結構體變量 1,變量 2;
struct 結構體類型名 變量 3, 變量 4;
struct stu{
int num;
char name[20];
char sex;
}lucy,bob,lilei;
struct stu xiaohong,xiaoming;
無結構體類型名
在定義結構體類型的時候, 沒有結構體類型名, 順便定義結構體變量,
因為沒有類型名, 所以以后不能再定義相關類型的數據了
struct {
成員列表;
}變量 1, 變量 2;
struct {
int num;
char name[20];
char sex;
}lucy,bob;
//以后沒法再定義這個結構體類型的數據了, 因為沒有類型名
最常用:
通常咱們將一個結構體類型重新起個類型名, 用新的類型名替代原先的類型
typedef struct stu{
int num;
char name[20];
char sex;
}STU;
//以后 STU 就相當于 struct stu
STU lucy;和 struct stu lucy;是等價的。
2.結構體變量的定義初始化及使用
1、 結構體變量的定義和初始化
(1):在定義結構體變量之前首先得有結構體類型, 然后再定義變量
(2):在定義結構體變量的時候, 可以順便給結構體變量賦初值, 被稱為結構體的初始化
(3):結構體變量初始化的時候, 各個成員順序初始化
struct stu{
int num;
char name[20];
char sex;
};struct stu boy;
struct stu lucy={
101,
"lucy",
'f'
};
//結構體內后面的變量可以不初始化,中間的變量必須初始化
2、 結構體變量的使用
(1).結構體變量成員的引用方法 :結構體變量.成員名
struct stu{
int num;
char name[20];
char sex;
char* addr;
};
struct stu bob;
bob.num=101;//bob 是個結構體變量,但是 bob.num 是個 int 類型的變量
bob.name 是個字符數組,是個字符數組的名字,代表字符數組的地址,是個常量
//bob.name ="bob";是不可行,是個常量
strcpy(bob.name,"bob");strcpy(bob.addr,"beijing")//錯誤,bob.addr是個野指針
bob.addr="beijing";//正確
#include <stdio.h>
struct stu{
int num;
char name[20];
int score;
char *addr;
};
int main(int argc, char *argv[])
{
struct stu bob;
printf("%d\n",sizeof(bob));
printf("%d\n",sizeof(bob.name));
printf("%d\n",sizeof(bob.addr));
return 0;
}
//結構體大小,為各元素的大小之和
3、相同類型的結構體變量相互賦值
#include <stdio.h>
struct stu{
int num;
char name[20];
char sex;
};int main(int argc, char *argv[])
{
struct stu bob={101,"bob",'m'};
struct stu lilei;
lilei=bob;//類型相同
printf("%d %s %c\n",lilei.num,lilei.name,lilei.sex);
return 0;
}
3.結構體數組
結構體數組是個數組, 由若干個相同類型的結構體變量構成的集合
1、結構體數組的定義方法
struct 結構體類型名 數組名[元素個數];
struct stu{
int num;
char name[20];
char sex;
};
struct stu edu[3];//定義了一個 struct stu 類型的結構體數組 edu,
這個數組有3個元素分別是 edu[0]、 edu[1]、edu[2]
結構體數組元素的引用: 數組名[下標]
數組元素的使用
edu[0].num =101;//用 101 給 edu 數組的第 0 個結構體變量的 num 賦值
strcpy(edu[1].name,"lucy");
#include <stdio.h>
typedef struct student
{
int num;
char name[20];
float score;
}STU;
STU edu[3]={
{101,"Lucy",78},
{102,"Bob",59.5},
{103,"Tom",85}
};
int main()
{
int i;
float sum=0;
for(i=0;i<3;i++)
{sum+=edu[i].score;}
printf("平均成績為%f\n",sum/3);
return 0;
}
4.結構體指針
即結構體的地址, 結構體變量存放內存中, 也有起始地址。咱們定義一個變量來存放這個地址, 那這個變量就是結構體指針變量。
結構體指針變量也是個指針, 既然是指針在 32 位環境下, 指針變量的占 4 個字節, 存放一個地址編號。
1、 結構體指針變量的定義方法:
struct 結構體類型名 * 結構體指針變量名;
struct stu{
int num;
char name[20];
};
struct stu * p;//定義了一個 struct stu *類型的指針變量
//變量名是p,p占4個字節,用來保存結構體變量的地址編號
struct stu boy;
p=&boy;boy.num=101;//可以,通過 結構體變量名.成員名
(*p).num=101;//可以,*p 相當于 p 指向的變量 boy
p->num=101;//可以,指針->成員名
//通過結構體指針來引用指針指向的結構體的成員, 前提是指針必須先指向一個結構體變量。
2、應用場景
(1): 保存結構體變量的地址
typedef struct stu{
int num;
char name[20];
float score;
}STU;int main()
{
STU *p,lucy;
p=&lucy;
p->num=101;
strcpy(p->name,"baby");
//p->name="baby";//錯誤,因為 p->name 相當于 lucy.name 是個字符數組的名字,是個常量
}
(2): 函數傳結構體變量的地址
#include<stdio.h>
#include<string.h>
typedef struct stu{
int num;
char name[20];
float score;
}STU;
void fun(STU *p)
{
p->num=101;
(*p).score=87.6;
strcpy(p->name,"lucy");
} int main()
{
STU girl;
fun(&girl);
printf("%d %s %f\n",girl.num,girl.name,girl.score);
return 0;
}
(3): 傳結構體數組的地址
結構體數組, 是由若干個相同類型的結構體變量構成的集合。 存放在內存里,
也有起始地址, 其實就是第 0 個結構體變量的地址。
#include<stdio.h>
#include<string.h>
typedef struct stu{
int num;
char name[20];
float score;
}STU;
void fun(STU *p)
{
p[1].num=101;
(*(p+1)).score=88.6;
}
int main()
{
STU edu[3];
fun(edu);
printf("%d %f\n",edu[1].num,edu[1].score);
return 0;
}
注意:
(1): 結構體變量的地址編號和結構體第一個成員的地址編號相同, 但指針的類型不同
#include <stdio.h>
typedef struct stu{
int num;
char name[20];
int score;
}STU;
int main(int argc, char *argv[])
{
STU bob;
printf("%p\n",&bob);//STU*
printf("%p\n",&(bob.num));//int*
return 0;
}
(2): 結構體數組的地址就是結構體數組中第 0 個元素的地址
#include <stdio.h>
struct stu{
int num;
char name[20];
int score;
};int main(int argc, char *argv[])
{
struct stu edu[3];
printf("%p\n",edu);//struct stu *
printf("%p\n",&(edu[0]));//struct stu *
printf("%p\n",&(edu[0].num));//int *
return 0;
}//各地址相同
5.結構體內存分配
規則 1: 以多少個字節為單位開辟內存
(1): 成員中只有 char 型數據 , 以 1 字節為單位開辟內存。
(2): 成員中出現了 short int 類型數據, 沒有更大字節數的基本類型數據。以 2 字節為單位開辟內存
(3): 出現了 int ,float 沒有更大字節的基本類型數據的時候以 4 字節為單位開辟內存。
(4): 出現了 double 類型的數據
情況 1:
在 vc6.0 和 Visual Studio 中里, 以 8 字節為單位開辟內存。
情況 2:
在 Linux 環境 gcc 里, 以 4 字節為單位開辟內存。
無論是那種環境, double 型變量, 占 8 字節。
(5):如果出現指針的話, 沒有占字節數更大的類型的, 以 4 字節為單位開辟內存。
struct stu{
char sex;
int age;
}lucy;
//lucy 的大小是 4 的倍數。
規則 2: 字節對齊
(1): char 1 字節對齊 , 即存放 char 型的變量, 內存單元的編號是 1 的倍數即可。
(2): short int 2 字節對齊 , 即存放 short int 型的變量, 起始內存單元的編號是 2 的倍數即可。
(3): int 4 字節對齊 , 即存放 int 型的變量, 起始內存單元的編號是 4 的倍數即可
(4): long int 在 32 位平臺下,4 字節對齊,即存放 long int 型的變量, 起始內存單元的編號是 4
的倍數即可
(5): float 4 字節對齊 , 即存放 float 型的變量, 起始內存單元的編號是 4 的倍數即可
(6): double
vc6.0 和 Visual Studio 環境下
8 字節對齊, 即存放 double 型變量的起始地址, 必須是 8 的倍數, double 變量占 8 字節
gcc 環境下
4 字節對齊, 即存放 double 型變量的起始地址, 必須是 4 的倍數, double 變量占 8 字節。
注意 3: 當結構體成員中出現數組的時候, 可以看成多個變量。
注意 4: 開辟內存的時候, 從上向下依次按成員在結構體中的位置順序開辟空間
#include<stdio.h>
struct stu{
char a;
short int b;
int c;
}temp;
int main()
{
printf("%d\n",sizeof(temp));
printf("%p\n",&(temp.a));
printf("%p\n",&(temp.b));
printf("%p\n",&(temp.c));
return 0;
}
結果分析:
a 的地址和 b 的地址差 2 個字節
b 的地址和 c 的地址差 2 個字節
#include<stdio.h>
struct stu{
char a;
int c;
short int b;
}temp;
int main()
{
printf("%d\n",sizeof(temp));
printf("%p\n",&(temp.a));
printf("%p\n",&(temp.b));
printf("%p\n",&(temp.c));
return 0;
}
結果分析:
a 和 c 的地址差 4 個字節
c 和 b 的地址差 4 個字節
struct stu{
char buf[10];
int a;
}temp;
//temp 占 16 個字節
在 vc 和 Visual Studio 中占 16 個字節 a 和 b 的地址差 8 個字節
在 gcc 中占 12 個字節 a 和 b 的地址差 4 個字節
#include<stdio.h>
struct stu{
char a;
double b;
}temp;
int main()
{
printf("%d\n",sizeof(temp));
printf("%p\n",&(temp.a));
printf("%p\n",&(temp.b));
return 0;
}