C語言結(jié)構(gòu)體(struct)的詳細講解
引言
當前文章介紹動態(tài)堆空間內(nèi)存分配與釋放,C語言結(jié)構(gòu)體定義、初始化、賦值、結(jié)構(gòu)體數(shù)組、結(jié)構(gòu)體指針的相關(guān)知識點,最后通過一個學生管理系統(tǒng)綜合練習結(jié)構(gòu)體數(shù)組的使用。
1. 動態(tài)內(nèi)存管理
C語言代碼----->編譯----->鏈接------>可執(zhí)行的二進制文件(windows下xxx.exe) 二進制文件中的數(shù)據(jù)是如何擺放的? 文本數(shù)據(jù)段、靜態(tài)數(shù)據(jù)段、全局數(shù)據(jù)段。
堆??臻g: 代碼在運行的時候才有的空間。 ??臻g: 系統(tǒng)負責申請,負責釋放。比如: 函數(shù)形參變量、數(shù)組…… 堆空間: 程序員負責申請,負責釋放。
#include <stdlib.h> //標準庫頭文件 void *malloc(int size); //內(nèi)存申請。 形參表示申請的空間大小,返回值:申請的空間的地址 void free(void *p); //內(nèi)存釋放。 形參就是要釋放的空間首地址。
動態(tài)空間申請示例。
動態(tài)空間申請
#include "stdio.h"
#include "string.h"
#include <stdlib.h>
int main()
{
int *p=malloc(sizeof(int)); //申請空間
if(p!=NULL)
{
printf("申請的空間地址: 0x%X\n",p);
*p=888;
printf("%d\n",*p);
}
free(p); //釋放空間
return 0;
}
示例2:
#include "stdio.h"
#include "string.h"
#include <stdlib.h>
?
char *func(void)
{
char*str=malloc(100); //char str[100];
if(str!=NULL)
{
strcpy(str,"1234567890");
printf("子函數(shù)打印:%s\n",str);
//free(str); //釋放空間
return str;
}
else
{
return NULL;
}
}
?
int main()
{
char *p=func();
printf("主函數(shù)打印:%s\n",p);
return 0;
}2. 結(jié)構(gòu)體
2.1 定義語法
結(jié)構(gòu)體的概念: 可存放不同數(shù)據(jù)類型的集合。 比如: 存放一個班級學生的信息。 可以使用一個結(jié)構(gòu)體存放一個學生的信息。 一個結(jié)構(gòu)體數(shù)組存放整個班級的學習信息。 數(shù)組的概念: 可存放相同數(shù)據(jù)類型的集合。
結(jié)構(gòu)體的定義語法:
//聲明一種新類型-----數(shù)據(jù)類型
struct <結(jié)構(gòu)體的名稱>
{
<結(jié)構(gòu)體的成員>1;
<結(jié)構(gòu)體的成員>2;
…………
}; //最后有分號結(jié)束
?
struct MyStruct
{
char a;
int b;
float c;
char str[100];
};2.2 定義示例
結(jié)構(gòu)體如何賦值? 如何訪問結(jié)構(gòu)體內(nèi)部成員
#include "stdio.h"
#include "string.h"
#include <stdlib.h>
?
//定義結(jié)構(gòu)體數(shù)據(jù)類型
struct MyStruct
{
char a;
int b;
float c;
char str[100];
};
int main()
{
struct MyStruct data={'A',123,456.789,"abcd"}; //data就是結(jié)構(gòu)體類型的變量
//結(jié)構(gòu)體變量訪問內(nèi)部成員的語法: . 點運算符
printf("%c\n",data.a);
printf("%d\n",data.b);
printf("%f\n",data.c);
printf("%s\n",data.str);
return 0;
}2.3 初始化
#include "stdio.h"
#include "string.h"
#include <stdlib.h>
?
//定義結(jié)構(gòu)體數(shù)據(jù)類型
struct MyStruct
{
char a;
int b;
float c;
char str[100];
}data={'A',123,456.789,"abcd"}; //data就是結(jié)構(gòu)體類型的變量
?
int main()
{
//結(jié)構(gòu)體變量訪問內(nèi)部成員的語法: . 點運算符
printf("%c\n",data.a);
printf("%d\n",data.b);
printf("%f\n",data.c);
printf("%s\n",data.str);
return 0;
}2.4 結(jié)構(gòu)體賦值
//結(jié)構(gòu)體變量訪問內(nèi)部成員的語法: . 點運算符
#include "stdio.h"
#include "string.h"
#include <stdlib.h>
?
//定義結(jié)構(gòu)體數(shù)據(jù)類型
struct MyStruct
{
char a;
int b;
float c;
char str[100];
};
?
int main()
{
struct MyStruct data;//data就是結(jié)構(gòu)體類型的變量
//成員單獨賦值
data.a='A';
data.b=123;
data.c=456.789;
strcpy(data.str,"abcd"); //數(shù)組賦值
?
//結(jié)構(gòu)體變量訪問內(nèi)部成員的語法: . 點運算符
printf("%c\n",data.a);
printf("%d\n",data.b);
printf("%f\n",data.c);
printf("%s\n",data.str);
return 0;
}2.5 結(jié)構(gòu)體數(shù)組
結(jié)構(gòu)體賦值分為兩種標準: C89 、C99
?結(jié)構(gòu)體數(shù)組
#include "stdio.h"
#include "string.h"
#include <stdlib.h>
?
//定義結(jié)構(gòu)體數(shù)據(jù)類型
struct MyStruct
{
char a;
int b;
float c;
char str[100];
};
?
int main()
{
struct MyStruct data[100];//data就是結(jié)構(gòu)體數(shù)組類型變量
struct MyStruct data2[50];
?
//成員單獨賦值
data[0].a='A';
data[0].b=123;
data[0].c=456.789;
strcpy(data[0].str,"abcd"); //數(shù)組賦值
?
//結(jié)構(gòu)體變量訪問內(nèi)部成員的語法: . 點運算符
printf("%c\n",data[0].a);
printf("%d\n",data[0].b);
printf("%f\n",data[0].c);
printf("%s\n",data[0].str);
return 0;
}2.6 結(jié)構(gòu)體指針賦值
#include "stdio.h"
#include "string.h"
#include <stdlib.h>
//定義結(jié)構(gòu)體數(shù)據(jù)類型
struct MyStruct
{
char a;
int b;
float c;
char str[100];
};
?
int main()
{
//struct MyStruct buff[100];
//struct MyStruct *data=buff; //結(jié)構(gòu)體指針類型變量
?
struct MyStruct *data=malloc(sizeof(struct MyStruct));
data->a='A';
data->b=123;
data->c=456.789;
strcpy(data->str,"abcd");
?
//結(jié)構(gòu)體指針訪問內(nèi)部成員的變量 通過 -> 運算符。
printf("%c\n",data->a);
printf("%d\n",data->b);
printf("%f\n",data->c);
printf("%s\n",data->str);
return 0;
}3. 學生管理系統(tǒng)
作業(yè): 學生管理系統(tǒng)
需求: (每一個功能都是使用函數(shù)進行封裝) 1.實現(xiàn)從鍵盤上錄入學生信息。 (姓名、性別、學號、成績、電話號碼) 2.將結(jié)構(gòu)體里的學生信息全部打印出來。 3.實現(xiàn)根據(jù)學生的姓名或者學號查找學生,查找到之后打印出學生的具體信息。 4.根據(jù)學生的成績對學生信息進行排序。 5.根據(jù)學號刪除學生信息。
示例:
#include "stdio.h"
#include "string.h"
#include <stdlib.h>
//定義存放學生信息的結(jié)構(gòu)體類型
struct StuDentInfo
{
char Name[20]; //姓名
int number; //學號
char phone[20];//電話號碼
};
//全局變量區(qū)域
unsigned int StuDentCnt=0; //記錄已經(jīng)錄入的全部學生數(shù)量
//函數(shù)聲明區(qū)域
void PrintStuDentInfoList(void);
void InputStuDentInfo(struct StuDentInfo*info);
void FindStuDentInfo(struct StuDentInfo*info);
void SortStuDentInfo(struct StuDentInfo*info);
void PrintStuDentInfo(struct StuDentInfo*info);
int main()
{
struct StuDentInfo data[100]; //可以100位學生的信息
int number;
while(1)
{
PrintStuDentInfoList(); //打印功能列表
scanf("%d",&number);
printf("\n");
switch(number)
{
case 1:
InputStuDentInfo(data);
break;
case 2:
FindStuDentInfo(data);
break;
case 3:
SortStuDentInfo(data);
break;
case 4:
PrintStuDentInfo(data);
break;
case 5:
break;
default:
printf("選擇錯誤!\n\n");
break;
}
}
return 0;
}
/*
函數(shù)功能: 打印學生管理系統(tǒng)的功能列表
*/
void PrintStuDentInfoList(void)
{
printf("\n--------------學生管理系統(tǒng)功能列表----------------\n");
printf("1. 錄入學生信息\n");
printf("2. 根據(jù)學號查找學生信息\n");
printf("3. 根據(jù)學號排序\n");
printf("4. 打印所有學生信息\n");
printf("5. 刪除指定的學生信息\n");
printf("請選擇功能序號:");
}
/*
函數(shù)功能: 錄入學生信息
*/
void InputStuDentInfo(struct StuDentInfo*info)
{
printf("輸入學生姓名:");
scanf("%s",info[StuDentCnt].Name);
printf("輸入學號:");
scanf("%d",&info[StuDentCnt].number);
printf("輸入電話號碼:");
scanf("%s",info[StuDentCnt].phone);
StuDentCnt++; //數(shù)量自增
}
/*
函數(shù)功能: 查找學生信息
*/
void FindStuDentInfo(struct StuDentInfo*info)
{
int num,i;
printf("輸入查找的學號:");
scanf("%d",&num);
for(i=0; i<StuDentCnt; i++)
{
if(info[i].number==num)
{
printf("信息查找成功,該學生的信息如下:\n");
printf("姓名:%s\n",info[i].Name);
printf("學號:%d\n",info[i].number);
printf("電話號碼:%s\n",info[i].phone);
printf("\n");
break;
}
}
if(i==StuDentCnt)
{
printf("----------%d學號不存在!---------\n",num);
}
}
/*
函數(shù)功能: 根據(jù)學號排序
*/
void SortStuDentInfo(struct StuDentInfo*info)
{
int i,j;
struct StuDentInfo tmp; //保存臨時信息
for(i=0; i<StuDentCnt-1; i++)
{
for(j=0;j<StuDentCnt-i-1;j++)
{
if(info[j].number>info[j+1].number)
{
tmp=info[j];
info[j]=info[j+1];
info[j+1]=tmp;
}
}
}
}
/*
函數(shù)功能: 打印所有學生信息
*/
void PrintStuDentInfo(struct StuDentInfo*info)
{
int i=0;
printf("-----------所有學生的信息列表------------\n");
for(i=0;i<StuDentCnt;i++)
{
printf("姓名:%s\n",info[i].Name);
printf("學號:%d\n",info[i].number);
printf("電話號碼:%s\n",info[i].phone);
printf("\n");
}
}附:結(jié)構(gòu)體變量的存儲原理
1)結(jié)構(gòu)體數(shù)據(jù)成員對齊的意義
內(nèi)存是以字節(jié)為單位編號的,某些硬件平臺對特定類型的數(shù)據(jù)的內(nèi)存要求從特定的地址開始,如果數(shù)據(jù)的存放不符合其平臺的要求,就會影響到訪問效率。所以在內(nèi)存中各類型的數(shù)據(jù)按照一定的規(guī)則在內(nèi)存中存放,就是對齊問題。而結(jié)構(gòu)體所占用的內(nèi)存空間就是每個成員對齊后存放時所占用的字節(jié)數(shù)之和。
計算機系統(tǒng)對基本數(shù)據(jù)類型的數(shù)據(jù)在內(nèi)存中存放的限制是:這些數(shù)據(jù)的起始地址的值要求是某個數(shù)K的倍數(shù),這就是內(nèi)存對齊,而這個數(shù) K 就是該數(shù)據(jù)類型的對齊模數(shù)(alignment modulus)。這樣做的目的是為了簡化處理器與內(nèi)存之間傳輸系統(tǒng)的設(shè)計,并且能提升讀取數(shù)據(jù)的速度。
結(jié)構(gòu)體對齊不僅包括其各成員的內(nèi)存對齊(即相對結(jié)構(gòu)體的起始位置),還包括結(jié)構(gòu)體的總長度。
2)結(jié)構(gòu)體大小的計算方法和步驟
i. 將結(jié)構(gòu)體內(nèi)所有數(shù)據(jù)成員的長度值相加,記為 sum_a ;
ii. 將各數(shù)據(jù)成員為了內(nèi)存對齊,按各自對齊模數(shù)而填充的字節(jié)數(shù)累加到sum_a上,記為sum_b。
對齊模數(shù)是 #pragma pack 指定的數(shù)值與該數(shù)據(jù)成員自身長度相比較得到的數(shù)值較小者。該數(shù)據(jù)相對起始位置應(yīng)該是對齊模數(shù)的整數(shù)倍。
iii. 將和 sum_b 向結(jié)構(gòu)體模數(shù)對齊。
該模數(shù)則是 #pragma pack 指定的數(shù)值與結(jié)構(gòu)體內(nèi)最大的基本數(shù)據(jù)類型成員長度相比較得到的數(shù)值較小者。結(jié)構(gòu)體的長度應(yīng)該是該模數(shù)的整數(shù)倍。
數(shù)據(jù)類型自身對齊:
所謂“對齊在N上”,是指“存放的起始位置是%N = 0”.
總結(jié)
到此這篇關(guān)于C語言結(jié)構(gòu)體(struct)的文章就介紹到這了,更多相關(guān)C語言結(jié)構(gòu)體struct內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
C/C++數(shù)字與字符串互相轉(zhuǎn)換的實現(xiàn)示例
本文主要介紹了C/C++數(shù)字與字符串互相轉(zhuǎn)換的實現(xiàn)示例,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2025-02-02
C語言中的strdup()函數(shù)和其與strcpy()函數(shù)的區(qū)別
這篇文章主要介紹了C語言中的strdup()函數(shù)和其與strcpy()函數(shù)的區(qū)別,同樣用于拷貝字符串的兩個函數(shù)的異同值得注意,需要的朋友可以參考下2015-08-08
C++數(shù)據(jù)結(jié)構(gòu)之二叉搜索樹的實現(xiàn)詳解
二叉搜索樹作為一個經(jīng)典的數(shù)據(jù)結(jié)構(gòu),具有鏈表的快速插入與刪除的特點,同時查詢效率也很優(yōu)秀,所以應(yīng)用十分廣泛。本文將詳細講講二叉搜索樹的C++實現(xiàn),需要的可以參考一下2022-08-08
C++11的future和promise、parkged_task使用
這篇文章主要介紹了C++11的future和promise、parkged_task使用,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2020-04-04
一文詳解C++中的轉(zhuǎn)換構(gòu)造函數(shù)
在 C/C++ 中,不同的數(shù)據(jù)類型之間可以相互轉(zhuǎn)換,無需用戶指明如何轉(zhuǎn)換的稱為自動類型轉(zhuǎn)換(隱式類型轉(zhuǎn)換),需要用戶顯式地指明如何轉(zhuǎn)換的稱為強制類型轉(zhuǎn)換,本文就給大家詳細介紹一下C++的轉(zhuǎn)換構(gòu)造函數(shù),需要的朋友可以參考下2023-09-09
數(shù)據(jù)結(jié)構(gòu)之Treap詳解
這篇文章主要介紹了數(shù)據(jù)結(jié)構(gòu)之Treap詳解,本文講解了Treap的基本知識、Treap的基本操作、Treap的高級操作技巧等,需要的朋友可以參考下2014-08-08

