C語言學習篇(21)----結構體初探

引言

不管什麼樣的編程語言,數據類型的不斷衍生都是為了不同場合對其進行不同處理或管理。 比如單一的變量,我們可以定義成char, short,,int,float, double等;而如果需要管理多個同一類型的數據就可以使用數組來統一管理;那麼如果是不同的數據類型,但是彼此是相關聯的呢? 此時就可以使用結構體來統一管理,這也是面對對象的基本思想。比如一個學生,他有如下信息: 名字(char *), 年齡(uint8), 成績(float)等。今天我們就來說說結構體的基本使用,後續再深入研究。

結構體的定義

  1. 使用struct關鍵字定義原生結構體類型
<code>struct people{

\tchar name[20];
\tint age;
};
/<code>
  1. 使用typedef類型自定義結構體類型
<code>typedef struct people1{
\t
\tchar name[20];
\tint age;
\t
}people1_t;/<code>

兩種方式的有何不同呢? 第一種屬於原生結構體類型,在定義變量之前,都需要加上struct people

<code>struct people p1;/<code>

而第二種使用typedef關鍵字自定義了people_t類型(people1_t等同於struct people1), 即在定義變量時,只需要在變量之前寫上people_t即刻。

<code>people1_t p2;/<code>

這兩種方式都可,用戶根據自己的習慣選擇其中一種即刻,個人推薦第二種,定義比較方便~

定義結構體變量和初始化

  • 如上所述,使用第一種struct people定義結構體變量時,有如下方式:
<code>struct people{
\t
\tchar name[20];
\tint age;
};

int main(void)
{
\tstruct people p1; //使用struct people定義變量p1
\t
\treturn 0;
}
/<code>

或:

<code>//定義類型的同時定義變量
struct student{
\t
\tchar name[20];
\tint age;
}std;

int main(void)
{
\tstd.age =23;\t//直接使用std結構體變量\t
\t
\treturn 0;
}/<code>
  • 使用typedef方式定義結構體變量
<code>typedef struct people1{
\t
\tchar name[20];
\tint age;
\t

}people1_t;


int main(void)
{
\tpeople1_t p2;
\t

\treturn 0;
}/<code>

接下來我們再介紹結構體的兩種方式初始化:

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

struct people{
\t
\tchar name[20];
\tint age;
};

typedef struct people1{
\t
\tchar name[20];
\tint age;
\t
}people1_t;

int main(void)
{
\t//方式一:在定義的變量的同時初始化
\tstruct people p1 ={\t\t\t
\t\t.name = "xiaoming",
\t\t.age = 23
\t};
\t
\tpeople1_t p2;
\t//方式二: 定義變量後,再對其初始化
\tstrcpy(&p2.name[0], "xiaohong");
\tp2.age = 45;
\t
\tprintf("p1.name = %s, age = %d.\\n", p1.name, p1.age);
\tprintf("p2.name = %s, age = %d.\\n", p2.name, p2.age);
\t

\treturn 0;
}
/<string.h>/<stdio.h>/<code>

編譯運行:

C語言學習篇(21)----結構體初探

結構體的元素訪問

在C語言中有兩種方式訪問,分別是"."和"->", 具體參考如下代碼:

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

struct people{
\t
\tchar name[20];
\tint age;
};

typedef struct people1{
\t
\tchar name[20];
\tint age;
\t
}people1_t;

int main(void)
{
\t//定義結構體變量,並初始化
\tstruct people p1 ={\t\t\t
\t\t.name = "xiaoming",
\t\t.age = 18
\t};
\t//定義結構體指針變量
\tpeople1_t *p2 = NULL;
\t
\t//申請people1_t結構體大小的堆內存空間,並將得到的起始地址賦予p2
\tp2 = (people1_t *)malloc(sizeof(people1_t));
\tif(NULL != p2)
\t{
\t\t//初始化
\t\tstrcpy(&p2->name[0], "xiaohong");
\t\tp2->age = 26;
\t}
\t

\t
\t//結構體變量通過'.'來訪問其元素
\tprintf("p1.name = %s, age = %d.\\n", p1.name, p1.age);
\t
\t//結構體變量通過'->'來訪問其元素
\tprintf("p2.name = %s, age = %d.\\n", p2->name, p2->age);
}/<stdlib.h>/<string.h>/<stdio.h>/<code>

編譯運行結果:

C語言學習篇(21)----結構體初探

以上兩種方式都是使用下標式訪問結構體元素, 那麼如何使用指針方式訪問呢?

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


struct my_test{
\tint a;\t\t\t//4
\tdouble b;\t\t//8
\tchar c;\t\t\t//1
};

int main(void)
{
\tstruct my_test s1;
\t
\ts1.a = 12;
\ts1.b = 3.4;
\ts1.c = 'a';
\t
\tint *p1 = (int *)&s1;
\tdouble *p2 = (double *)((long unsigned int)&s1 + 8);
\tchar *p3 = (char *)((long unsigned int)&s1 + 8 + 8);
\t
\tprintf("s1.a = %d.\\n", s1.a);
\tprintf("s1.b = %.1f.\\n", s1.b);
\tprintf("s1.c = %c.\\n", s1.c);
\t
\tprintf("=====================\\n");
\t
\tprintf("*p1 = %d.\\n", *p1);
\tprintf("*p2 = %.1f.\\n", *p2);
\tprintf("*p3 = %c.\\n", *p3);
}/<stdlib.h>/<string.h>/<stdio.h>/<code>

分析:

  • int *p1 = (int *)&s1,其中&s1為結構體的起始地址,也是首元素a的地址,因此可以通過類型轉化後賦值給p1(int *類型,指向int類型的變量a)
  • double *p2 = (double *)((long unsigned int)&s1 + 8); 其中因為&s1是作為結構體地址,本身是帶有數據類型的,我們通過(long unsigned int)將其轉化成普通的長整型數值,然後再加上a(8字節)的長度,之後的地址就是結構體第二個元素b的地址了,於是乎將得到的地址轉化成double *類型賦值給p2,通過p2來訪問。
  • char *p3 = (char *)((long unsigned int)&s1 + 8 + 8); 與上步驟分析一致, 首先將&s1轉化成普通的普通的長整型數值,然後加上元素a 和 元素b的數據類型長度,就得到了元素c的地址,再賦值給p3,通過p3來訪問結構體元素c。

編譯運行結果:

C語言學習篇(21)----結構體初探

總結

從數組到結構體的進步之處:數組有2個明顯的缺陷:第一個是定義時必須明確給出大小,且這個大小在以後不能再更改(這裡不考慮可變數組);第二個是數組要求所有的元素的類型必須一致。

結構體就完美解決了數組的第二個缺陷的,可以將結構體理解為一個其中元素類型可以不相同的數組。結構體完全可以取代數組,只是在數組可用的範圍內數組比結構體更簡單,使用更方便。


C語言學習篇(21)----結構體初探


分享到:


相關文章: