網(wǎng)站首頁(yè) 編程語(yǔ)言 正文
引言
當(dāng)前文章介紹動(dòng)態(tài)堆空間內(nèi)存分配與釋放,C語(yǔ)言結(jié)構(gòu)體定義、初始化、賦值、結(jié)構(gòu)體數(shù)組、結(jié)構(gòu)體指針的相關(guān)知識(shí)點(diǎn),最后通過(guò)一個(gè)學(xué)生管理系統(tǒng)綜合練習(xí)結(jié)構(gòu)體數(shù)組的使用。
1. 動(dòng)態(tài)內(nèi)存管理
C語(yǔ)言代碼----->編譯----->鏈接------>可執(zhí)行的二進(jìn)制文件(windows下xxx.exe) 二進(jìn)制文件中的數(shù)據(jù)是如何擺放的? 文本數(shù)據(jù)段、靜態(tài)數(shù)據(jù)段、全局?jǐn)?shù)據(jù)段。
堆棧空間: 代碼在運(yùn)行的時(shí)候才有的空間。 棧空間: 系統(tǒng)負(fù)責(zé)申請(qǐng),負(fù)責(zé)釋放。比如: 函數(shù)形參變量、數(shù)組…… 堆空間: 程序員負(fù)責(zé)申請(qǐng),負(fù)責(zé)釋放。
#include <stdlib.h> //標(biāo)準(zhǔn)庫(kù)頭文件 void *malloc(int size); //內(nèi)存申請(qǐng)。 形參表示申請(qǐng)的空間大小,返回值:申請(qǐng)的空間的地址 void free(void *p); //內(nèi)存釋放。 形參就是要釋放的空間首地址。
動(dòng)態(tài)空間申請(qǐng)示例。
動(dòng)態(tài)空間申請(qǐng) #include "stdio.h" #include "string.h" #include <stdlib.h> int main() { int *p=malloc(sizeof(int)); //申請(qǐng)空間 if(p!=NULL) { printf("申請(qǐng)的空間地址: 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 定義語(yǔ)法
結(jié)構(gòu)體的概念: 可存放不同數(shù)據(jù)類型的集合。 比如: 存放一個(gè)班級(jí)學(xué)生的信息。 可以使用一個(gè)結(jié)構(gòu)體存放一個(gè)學(xué)生的信息。 一個(gè)結(jié)構(gòu)體數(shù)組存放整個(gè)班級(jí)的學(xué)習(xí)信息。 數(shù)組的概念: 可存放相同數(shù)據(jù)類型的集合。
結(jié)構(gòu)體的定義語(yǔ)法:
//聲明一種新類型-----數(shù)據(jù)類型 struct <結(jié)構(gòu)體的名稱> { <結(jié)構(gòu)體的成員>1; <結(jié)構(gòu)體的成員>2; ………… }; //最后有分號(hào)結(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)部成員的語(yǔ)法: . 點(diǎn)運(yùn)算符 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)部成員的語(yǔ)法: . 點(diǎn)運(yùn)算符 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)部成員的語(yǔ)法: . 點(diǎn)運(yùn)算符 #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)體類型的變量 //成員單獨(dú)賦值 data.a='A'; data.b=123; data.c=456.789; strcpy(data.str,"abcd"); //數(shù)組賦值 ? //結(jié)構(gòu)體變量訪問內(nèi)部成員的語(yǔ)法: . 點(diǎn)運(yùn)算符 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)體賦值分為兩種標(biāo)準(zhǔn): 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]; ? //成員單獨(dú)賦值 data[0].a='A'; data[0].b=123; data[0].c=456.789; strcpy(data[0].str,"abcd"); //數(shù)組賦值 ? //結(jié)構(gòu)體變量訪問內(nèi)部成員的語(yǔ)法: . 點(diǎn)運(yùn)算符 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)部成員的變量 通過(guò) -> 運(yùn)算符。 printf("%c\n",data->a); printf("%d\n",data->b); printf("%f\n",data->c); printf("%s\n",data->str); return 0; }
3. 學(xué)生管理系統(tǒng)
作業(yè): 學(xué)生管理系統(tǒng)
需求: (每一個(gè)功能都是使用函數(shù)進(jìn)行封裝) 1.實(shí)現(xiàn)從鍵盤上錄入學(xué)生信息。 (姓名、性別、學(xué)號(hào)、成績(jī)、電話號(hào)碼) 2.將結(jié)構(gòu)體里的學(xué)生信息全部打印出來(lái)。 3.實(shí)現(xiàn)根據(jù)學(xué)生的姓名或者學(xué)號(hào)查找學(xué)生,查找到之后打印出學(xué)生的具體信息。 4.根據(jù)學(xué)生的成績(jī)對(duì)學(xué)生信息進(jìn)行排序。 5.根據(jù)學(xué)號(hào)刪除學(xué)生信息。
示例:
#include "stdio.h" #include "string.h" #include <stdlib.h> //定義存放學(xué)生信息的結(jié)構(gòu)體類型 struct StuDentInfo { char Name[20]; //姓名 int number; //學(xué)號(hào) char phone[20];//電話號(hào)碼 }; //全局變量區(qū)域 unsigned int StuDentCnt=0; //記錄已經(jīng)錄入的全部學(xué)生數(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位學(xué)生的信息 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("選擇錯(cuò)誤!\n\n"); break; } } return 0; } /* 函數(shù)功能: 打印學(xué)生管理系統(tǒng)的功能列表 */ void PrintStuDentInfoList(void) { printf("\n--------------學(xué)生管理系統(tǒng)功能列表----------------\n"); printf("1. 錄入學(xué)生信息\n"); printf("2. 根據(jù)學(xué)號(hào)查找學(xué)生信息\n"); printf("3. 根據(jù)學(xué)號(hào)排序\n"); printf("4. 打印所有學(xué)生信息\n"); printf("5. 刪除指定的學(xué)生信息\n"); printf("請(qǐng)選擇功能序號(hào):"); } /* 函數(shù)功能: 錄入學(xué)生信息 */ void InputStuDentInfo(struct StuDentInfo*info) { printf("輸入學(xué)生姓名:"); scanf("%s",info[StuDentCnt].Name); printf("輸入學(xué)號(hào):"); scanf("%d",&info[StuDentCnt].number); printf("輸入電話號(hào)碼:"); scanf("%s",info[StuDentCnt].phone); StuDentCnt++; //數(shù)量自增 } /* 函數(shù)功能: 查找學(xué)生信息 */ void FindStuDentInfo(struct StuDentInfo*info) { int num,i; printf("輸入查找的學(xué)號(hào):"); scanf("%d",&num); for(i=0; i<StuDentCnt; i++) { if(info[i].number==num) { printf("信息查找成功,該學(xué)生的信息如下:\n"); printf("姓名:%s\n",info[i].Name); printf("學(xué)號(hào):%d\n",info[i].number); printf("電話號(hào)碼:%s\n",info[i].phone); printf("\n"); break; } } if(i==StuDentCnt) { printf("----------%d學(xué)號(hào)不存在!---------\n",num); } } /* 函數(shù)功能: 根據(jù)學(xué)號(hào)排序 */ void SortStuDentInfo(struct StuDentInfo*info) { int i,j; struct StuDentInfo tmp; //保存臨時(shí)信息 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ù)功能: 打印所有學(xué)生信息 */ void PrintStuDentInfo(struct StuDentInfo*info) { int i=0; printf("-----------所有學(xué)生的信息列表------------\n"); for(i=0;i<StuDentCnt;i++) { printf("姓名:%s\n",info[i].Name); printf("學(xué)號(hào):%d\n",info[i].number); printf("電話號(hào)碼:%s\n",info[i].phone); printf("\n"); } }
附:結(jié)構(gòu)體變量的存儲(chǔ)原理
1)結(jié)構(gòu)體數(shù)據(jù)成員對(duì)齊的意義
內(nèi)存是以字節(jié)為單位編號(hào)的,某些硬件平臺(tái)對(duì)特定類型的數(shù)據(jù)的內(nèi)存要求從特定的地址開始,如果數(shù)據(jù)的存放不符合其平臺(tái)的要求,就會(huì)影響到訪問效率。所以在內(nèi)存中各類型的數(shù)據(jù)按照一定的規(guī)則在內(nèi)存中存放,就是對(duì)齊問題。而結(jié)構(gòu)體所占用的內(nèi)存空間就是每個(gè)成員對(duì)齊后存放時(shí)所占用的字節(jié)數(shù)之和。
計(jì)算機(jī)系統(tǒng)對(duì)基本數(shù)據(jù)類型的數(shù)據(jù)在內(nèi)存中存放的限制是:這些數(shù)據(jù)的起始地址的值要求是某個(gè)數(shù)K的倍數(shù),這就是內(nèi)存對(duì)齊,而這個(gè)數(shù) K 就是該數(shù)據(jù)類型的對(duì)齊模數(shù)(alignment modulus)。這樣做的目的是為了簡(jiǎn)化處理器與內(nèi)存之間傳輸系統(tǒng)的設(shè)計(jì),并且能提升讀取數(shù)據(jù)的速度。
結(jié)構(gòu)體對(duì)齊不僅包括其各成員的內(nèi)存對(duì)齊(即相對(duì)結(jié)構(gòu)體的起始位置),還包括結(jié)構(gòu)體的總長(zhǎng)度。
2)結(jié)構(gòu)體大小的計(jì)算方法和步驟
i. 將結(jié)構(gòu)體內(nèi)所有數(shù)據(jù)成員的長(zhǎng)度值相加,記為 sum_a ;
ii. 將各數(shù)據(jù)成員為了內(nèi)存對(duì)齊,按各自對(duì)齊模數(shù)而填充的字節(jié)數(shù)累加到sum_a上,記為sum_b。
對(duì)齊模數(shù)是 #pragma pack 指定的數(shù)值與該數(shù)據(jù)成員自身長(zhǎng)度相比較得到的數(shù)值較小者。該數(shù)據(jù)相對(duì)起始位置應(yīng)該是對(duì)齊模數(shù)的整數(shù)倍。
iii. 將和 sum_b 向結(jié)構(gòu)體模數(shù)對(duì)齊。
該模數(shù)則是 #pragma pack 指定的數(shù)值與結(jié)構(gòu)體內(nèi)最大的基本數(shù)據(jù)類型成員長(zhǎng)度相比較得到的數(shù)值較小者。結(jié)構(gòu)體的長(zhǎng)度應(yīng)該是該模數(shù)的整數(shù)倍。
數(shù)據(jù)類型自身對(duì)齊:
所謂“對(duì)齊在N上”,是指“存放的起始位置是%N = 0”.
總結(jié)
原文鏈接:https://juejin.cn/post/7080696319807291423
相關(guān)推薦
- 2022-05-05 Flutter如何保證數(shù)據(jù)操作原子性詳解_Android
- 2022-07-04 PyG搭建GCN模型實(shí)現(xiàn)節(jié)點(diǎn)分類GCNConv參數(shù)詳解_python
- 2022-06-18 C#多線程的ResetAbort()方法_C#教程
- 2023-07-02 Python配置文件管理之ini和yaml文件讀取的實(shí)現(xiàn)_python
- 2023-02-03 VB十七種可用一行代碼完成判斷的技巧代碼_vb
- 2022-02-03 gogs倉(cāng)庫(kù)代碼拉取不需要用戶賬號(hào)驗(yàn)證問題
- 2023-10-16 input框錄入身份證自動(dòng)填寫性別年齡
- 2023-10-09 如何搭建小程序項(xiàng)目,uniApp搭建,uView組件庫(kù)的引入和請(qǐng)求配置
- 最近更新
-
- window11 系統(tǒng)安裝 yarn
- 超詳細(xì)win安裝深度學(xué)習(xí)環(huán)境2025年最新版(
- Linux 中運(yùn)行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲(chǔ)小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎(chǔ)操作-- 運(yùn)算符,流程控制 Flo
- 1. Int 和Integer 的區(qū)別,Jav
- spring @retryable不生效的一種
- Spring Security之認(rèn)證信息的處理
- Spring Security之認(rèn)證過(guò)濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權(quán)
- redisson分布式鎖中waittime的設(shè)
- maven:解決release錯(cuò)誤:Artif
- restTemplate使用總結(jié)
- Spring Security之安全異常處理
- MybatisPlus優(yōu)雅實(shí)現(xiàn)加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務(wù)發(fā)現(xiàn)-Nac
- Spring Security之基于HttpR
- Redis 底層數(shù)據(jù)結(jié)構(gòu)-簡(jiǎn)單動(dòng)態(tài)字符串(SD
- arthas操作spring被代理目標(biāo)對(duì)象命令
- Spring中的單例模式應(yīng)用詳解
- 聊聊消息隊(duì)列,發(fā)送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠(yuǎn)程分支