在C語言中,結構體參數變量經常作為函數的參數來進行傳遞。但如果參數設置不當,會出現內存問題。
本文以實際的程序代碼為例,詳細地介紹如何正確地使用結構體參數變量,為相關的開發工作提供了參考。
一、前言
本文中的程序實現對員工信息結構體字段賦值並打印出來的功能。該結構體的定義如下:
// 員工信息結構體 typedef struct { INT8 szEmployeeName[100]; // 員工姓名 UINT16 iEmployeeAge; // 員工年齡 UINT32 iEmployeeNo; // 員工工號 } TEmployeeInfo;
函數GetEmployeeInfo用來對員工信息字段進行賦值,其聲明如下:
INT32 GetEmployeeInfo(TEmployeeInfo *ptEmployeeInfo);
在主函數main中,采用兩種參數傳遞的方法,一種是指針傳遞,另一種是非指針傳遞。
二、采用指針傳遞時的程序代碼
采用指針傳遞時的程序代碼如下:
* 修改記錄1:// 修改歷史記錄, 包括修改日期、版本號、修改人及修改內容 * 修改日期: 20140617 * 版本號: V1.0 * 修改人: Zhou Zhaoxiong * 修改內容:創建 **********************************************************************/ #include <stdio.h> #include <string.h> // 數據類型 typedef signed char INT8; typedef unsigned char UINT16; typedef unsigned int UINT32; typedef signed int INT32; // 員工信息結構體 typedef struct { INT8 szEmployeeName[100]; // 員工姓名 UINT16 iEmployeeAge; // 員工年齡 UINT32 iEmployeeNo; // 員工工號 } TEmployeeInfo; // 函數聲明 INT32 GetEmployeeInfo(TEmployeeInfo *ptEmployeeInfo); // 獲取員工信息函數 INT32 main(void); /**************************************************************** * 功能描述: 主函數 * 輸入參數: 無 * 輸出參數: 無 * 返回值: 0-執行成功 -1-執行失敗 * 其他說明: 無 * 修改日期 版本號 修改人 修改內容 * ---------------------------------------------------------------------------------------- * 20140617 V1.0 Zhou Zhaoxiong 創建 ****************************************************************/ INT32 main(void) { INT32 iRetValue = 0; // 該變量用於表示調用GetEmployeeInfo函數返回的值 TEmployeeInfo *ptEmployeeInfo = NULL; // 該變量用於存放員工信息 // 調用函數對員工信息字段賦值, 並打印出來 iRetValue = GetEmployeeInfo(ptEmployeeInfo); if (iRetValue != 0) { printf("exec GetEmployeeInfo failed.\n"); return -1; } printf("員工信息為: \n姓名: %s\n年齡: %d\n工號: %d\n", ptEmployeeInfo->szEmployeeName, ptEmployeeInfo->iEmployeeAge, ptEmployeeInfo->iEmployeeNo); return 0; } /********************************************************************** * 功能描述:對員工信息字段賦值 * 輸入參數: ptEmployeeInfo: 員工信息結構體 * 輸出參數: ptEmployeeInfo: 員工信息結構體 * 返回值: 0-成功 -1-失敗 * 其它說明:無 * 修改日期 版本號 修改人 修改內容 * -------------------------------------------------------------------------------------- * 20140617 V1.0 Zhou Zhaoxiong 創建 ***********************************************************************/ INT32 GetEmployeeInfo(TEmployeeInfo *ptEmployeeInfo) { // 先對輸入的指針參數進行異常判斷 if (ptEmployeeInfo == NULL) { printf("Input parameter is NULL.\n"); return -1; } strncpy((char *)ptEmployeeInfo->szEmployeeName, "Li Yuanfang", strlen("Li Yuanfang")); // 對姓名字段賦值 ptEmployeeInfo->iEmployeeAge = 100; // 對年齡字段賦值 ptEmployeeInfo->iEmployeeNo = 123456; // 對工號字段賦值 return 0; // 賦值成功, 返回0 }
程序的運行結果如圖1所示:
圖1 采用指針傳遞時的程序代碼運行結果
從圖1可以看出,函數GetEmployeeInfo的入參為空,不能實現賦值的功能。
三、改進後的采用指針傳遞時的程序代碼
既然程序打印出指針為空的信息,那麼我們先對傳入的指針進行賦值操作是不是就可以了呢?
改進後的采用指針傳遞時的程序代碼如下:
修改記錄1:// 修改歷史記錄, 包括修改日期、版本號、修改人及修改內容 * 修改日期: 20140617 * 版本號: V1.0 * 修改人: Zhou Zhaoxiong * 修改內容:創建 **********************************************************************/ #include <stdio.h> #include <string.h> // 數據類型 typedef signed char INT8; typedef unsigned char UINT16; typedef unsigned int UINT32; typedef signed int INT32; // 員工信息結構體 typedef struct { INT8 szEmployeeName[100]; // 員工姓名 UINT16 iEmployeeAge; // 員工年齡 UINT32 iEmployeeNo; // 員工工號 } TEmployeeInfo; // 函數聲明 INT32 GetEmployeeInfo(TEmployeeInfo *ptEmployeeInfo); // 獲取員工信息函數 INT32 main(void); /**************************************************************** * 功能描述: 主函數 * 輸入參數: 無 * 輸出參數: 無 * 返回值: 0-執行成功 -1-執行失敗 * 其他說明: 無 * 修改日期 版本號 修改人 修改內容 * -------------------------------------------------------------------------------------------- * 20140617 V1.0 Zhou Zhaoxiong 創建 ****************************************************************/ INT32 main(void) { INT32 iRetValue = 0; // 該變量用於表示調用GetEmployeeInfo函數返回的值 TEmployeeInfo *ptEmployeeInfo = NULL; // 該變量用於存放員工信息 // 先對員工信息字段賦值, 防止空指針的存在 strncpy((char *)ptEmployeeInfo->szEmployeeName, "Di Renjie", strlen("Di Renjie")); // 對姓名字段賦值 ptEmployeeInfo->iEmployeeAge = 150; // 對年齡字段賦值 ptEmployeeInfo->iEmployeeNo = 654321; // 對工號字段賦值 // 調用函數對員工信息字段賦值, 並打印出來 iRetValue = GetEmployeeInfo(ptEmployeeInfo); if (iRetValue != 0) { printf("exec GetEmployeeInfo failed.\n"); return -1; } printf("員工信息為: \n姓名: %s\n年齡: %d\n工號: %d\n", ptEmployeeInfo->szEmployeeName, ptEmployeeInfo->iEmployeeAge, ptEmployeeInfo->iEmployeeNo); return 0; } /********************************************************************** * 功能描述:對員工信息字段賦值 * 輸入參數: ptEmployeeInfo: 員工信息結構體 * 輸出參數: ptEmployeeInfo: 員工信息結構體 * 返回值: 0-成功 -1-失敗 * 其它說明:無 * 修改日期 版本號 修改人 修改內容 * ----------------------------------------------------------------------------------- * 20140617 V1.0 Zhou Zhaoxiong 創建 ***********************************************************************/ INT32 GetEmployeeInfo(TEmployeeInfo *ptEmployeeInfo) { // 先對輸入的指針參數進行異常判斷 if (ptEmployeeInfo == NULL) { printf("Input parameter is NULL.\n"); return -1; } strncpy((char *)ptEmployeeInfo->szEmployeeName, "Li Yuanfang", strlen("Li Yuanfang")); // 對姓名字段賦值 ptEmployeeInfo->iEmployeeAge = 100; // 對年齡字段賦值 ptEmployeeInfo->iEmployeeNo = 123456; // 對工號字段賦值 return 0; // 賦值成功, 返回0 }
程序的運行結果如圖2所示:
圖2 改進後的采用指針傳遞時的程序代碼運行結果
可見,程序出現了內存問題。原因是在傳遞之前,ptEmployeeInfo指針已經指向了確定的地址,不能讓同一個指針同時指向不同的地址。
四、第二次改進後的程序代碼
既然不能用指針作為參數進行傳遞,那麼我們就要考慮另外的方法。
以下代碼采用非指針的傳遞方式:
* 修改記錄1:// 修改歷史記錄, 包括修改日期、版本號、修改人及修改內容 * 修改日期: 20140617 * 版本號: V1.0 * 修改人: Zhou Zhaoxiong * 修改內容:創建 **********************************************************************/ #include <stdio.h> #include <string.h> // 數據類型 typedef signed char INT8; typedef unsigned char UINT16; typedef unsigned int UINT32; typedef signed int INT32; // 員工信息結構體 typedef struct { INT8 szEmployeeName[100]; // 員工姓名 UINT16 iEmployeeAge; // 員工年齡 UINT32 iEmployeeNo; // 員工工號 } TEmployeeInfo; // 函數聲明 INT32 GetEmployeeInfo(TEmployeeInfo *ptEmployeeInfo); // 獲取員工信息函數 INT32 main(void); /**************************************************************** * 功能描述: 主函數 * 輸入參數: 無 * 輸出參數: 無 * 返回值: 0-執行成功 -1-執行失敗 * 其他說明: 無 * 修改日期 版本號 修改人 修改內容 * -------------------------------------------------------------- * 20140617 V1.0 Zhou Zhaoxiong 創建 ****************************************************************/ INT32 main(void) { INT32 iRetValue = 0; // 該變量用於表示調用GetEmployeeInfo函數返回的值 TEmployeeInfo tEmployeeInfo = {0}; // 該變量用於存放員工信息 // 調用函數對員工信息字段賦值, 並打印出來 iRetValue = GetEmployeeInfo(&tEmployeeInfo); if (iRetValue != 0) { printf("exec GetEmployeeInfo failed.\n"); return -1; } printf("員工信息為: \n姓名: %s\n年齡: %d\n工號: %d\n", tEmployeeInfo.szEmployeeName, tEmployeeInfo.iEmployeeAge, tEmployeeInfo.iEmployeeNo); return 0; } /********************************************************************** * 功能描述:對員工信息字段賦值 * 輸入參數: ptEmployeeInfo: 員工信息結構體 * 輸出參數: ptEmployeeInfo: 員工信息結構體 * 返回值: 0-成功 -1-失敗 * 其它說明:無 * 修改日期 版本號 修改人 修改內容 * -------------------------------------------------------------- * 20140617 V1.0 Zhou Zhaoxiong 創建 ***********************************************************************/ INT32 GetEmployeeInfo(TEmployeeInfo *ptEmployeeInfo) { // 先對輸入的指針參數進行異常判斷 if (ptEmployeeInfo == NULL) { printf("Input parameter is NULL.\n"); return -1; } strncpy((char *)ptEmployeeInfo->szEmployeeName, "Li Yuanfang", strlen("Li Yuanfang")); // 對姓名字段賦值 ptEmployeeInfo->iEmployeeAge = 100; // 對年齡字段賦值 ptEmployeeInfo->iEmployeeNo = 123456; // 對工號字段賦值 return 0; // 賦值成功, 返回0 }
程序的執行結果如圖3所示:
圖3第二次改進後的程序代碼執行結果
從圖3可以看出,程序執行結果正確,得到了我們想要的結果。
五、總結
在編寫代碼的過程中,我們需要注意以下方面:
(1) 程序頭部、函數頭部及重要的程序語句處一定要有注釋,這體現了軟件開發人員的專業素養。
(2) 函數中出現的變量在定義的同時要進行初始化,函數在調用之前一定要先進行聲明。
(3) 對於函數中的指針變量參數,在使用之前一定要先進行異常判斷(即判斷其是否為NULL)。
(4) 對於有返回值的函數,要用不同的返回值來區別不同的執行結果,並在重要的地方打印出提示信息,方便對代碼的調試。
指針是C語言的精華所在,同時也是難點所在。對於一個合格的軟件開發工程師來說,一定要熟練掌握指針的使用方法。