提前认识软件开拓(13) 指针及布局体的利用
当前位置:以往代写 > C/C++ 教程 >提前认识软件开拓(13) 指针及布局体的利用
2019-06-13

提前认识软件开拓(13) 指针及布局体的利用

提前认识软件开拓(13) 指针及布局体的利用

副标题#e#

指针在C语言中占有很重要的职位,同时也是进修C语言的难点地址。布局体属于用户本身成立的数据范例,在实际的软件开拓项目中应用很遍及。

本文以实际的例子先容了C语言中指针和布局体的利用要领,为进一步的进修和应用提供了有益的参考。

1.指针和布局体简介

在C语言中,将地点形象化地称为指针,意即通过它可以或许找到以它为地点的内存单位。实际上,利用指针是对一个内存单位的间接会见。譬喻,有一个变量Var的值为1,利用一个变量Var_Pointer存放变量Var在内存中的地点3000,通过该地点可以或许找到变量Var在内存中的值,那么这种间接会见操纵的示意图如图1所示。

提前认识软件开辟(13) 指针及机关体的操作

图1指针操纵示意图

在诸如数组这样的数据布局中,所有的数据都是同一种范例,即不能存放差异范例(如整型和字符型)的数据。布局体(structure)的呈现办理了这个问题,它答允用户本身成立由差异范例数据构成的组合型的数据布局。

在实际的软件开拓项目中,指针和布局体都有很重要的应用,要成为一名及格的软件开拓工程师,必然要学会机动运用指针和布局体来编写C语言措施。

2.本文中利用的措施流程说明

本文中措施实现的成果为:从当地文件中读取以约命名目构成的员工的信息记录(包罗工号、姓名和年数,字段之间以“|”脱离),理会后将每个字段的内容输出到屏幕上。流程图如图2所示。

提前认识软件开辟(13) 指针及机关体的操作

图2本措施流程图

本措施文件定名为“Pointer.c”,利用的当地文件定名为“EmployeeInfo.ini”,文件内里的内容为形如“工号|姓名|年数”这样的记录,内容存放示譬喻图3所示。

提前认识软件开辟(13) 指针及机关体的操作

图3文件内容存放示例图

留意,在措施编译运行的时候,要将当地文件存放到与“Pointer.c”同级目次下,这样才气够读取到记录信息。


#p#副标题#e#

3.措施代码

*  版本       修改时间     修改人           修改内容
    
********************************************************************
    
*   V1.0        20140416    周兆熊             建设
    
**********************************************************************/
    
#include <stdio.h>
    
#include <stdlib.h>
    
#include <string.h>
    
     
    
//字段最大长度
    
#define MAX_RET_BUF_LEN     (1024)
    
     
    
//数据范例
    
typedef unsigned char       UINT8;
    
typedef unsigned short int  UINT16;
    
typedef unsigned int        UINT32;
    
typedef signed   int        INT32;
    
typedef unsigned char       BOOL;
    
     
    
//参数范例
    
#define MML_INT8_TYPE       0
    
#define MML_INT16_TYPE      1
    
#define MML_INT32_TYPE      2
    
#define MML_STR_TYPE        3
    
     
    
#define  TRUE         (BOOL)1
    
#define  FALSE        (BOOL)0
    
     
    
//员工信息布局体
    
typedef struct
    
{
    
    UINT8  szEmployeeID[1024];      //员工工号
    
    UINT8  szEmployeeName[1024];    //员工姓名
    
    UINT32 iEmployeeAge;            //员工年数
    
} T_EmployeeInfo;
    
     
    
/**********************************************************************
    
*成果描写:获取字符串中某一个字段的数据
    
*输入参数: iSerialNum-字段编号(为正整数)
    
             iContentType-需要获取的内容的范例
    
             pSourceStr-源字符串
    
             pDstStr-目标字符串(提取的数据的存放位置)
    
             cIsolater-源字符串中字段的脱离符
    
             iDstStrSize-目标字符串的长度
    
*输出参数:无
    
*返回值: TRUE-乐成  FALSE-失败
    
*其它说明:无
    
*修他日期        版本号            修改人         修改内容
    
* --------------------------------------------------------------
    
* 20140416         V1.0               zzx           建设
    
***********************************************************************/
    
BOOL GetValueFromStr(UINT16 iSerialNum, UINT8 iContentType, UINT8 *pSourceStr, UINT8 *pDstStr, UINT8 cIsolater, UINT32 iDstStrSize)
    
{
    
    UINT8  *pStrBegin                 = NULL;
    
    UINT8  *pStrEnd                   = NULL;
    
    UINT8   szRetBuf[MAX_RET_BUF_LEN] = {0}; //截取出的字符串放入该数组中
    
    UINT8  *pUINT8                    = NULL;
    
    UINT16 *pUINT16                   = NULL;
    
    UINT32 *pUINT32                   = NULL;
    
    UINT32  iFieldLen                 = 0;     //用于暗示每个字段的实际长度
    
     
    

    if (pSourceStr == NULL)           //对输入指针的异常环境举办判定
    
    {
    
        return FALSE;
    
}
    
    //字段首
    
    pStrBegin = pSourceStr;
    
    while (--iSerialNum != 0)
    
    {
    
        pStrBegin = strchr(pStrBegin, cIsolater);
    
        if (pStrBegin == NULL)
    
        {
    
            return FALSE;
    
        }
    
        pStrBegin ++;
    
    }
    
     
    
    //字段尾
    
    pStrEnd = strchr(pStrBegin, cIsolater);
    
    if (pStrEnd == NULL)
    
    {
    
        return FALSE;
    
    }
    
     
    
    iFieldLen = (UINT16)(pStrEnd - pStrBegin);
    
    if(iFieldLen >= MAX_RET_BUF_LEN) //举办异常掩护, 防备每个字段的值过长
    
    {
    
        iFieldLen = MAX_RET_BUF_LEN - 1;
    
    }
    
     
    
    memcpy(szRetBuf, pStrBegin, iFieldLen);
    
     
    
    //将需要的字段值放到pDstStr中去
    
    switch (iContentType)
    
    {
    
        case MML_STR_TYPE:                        //字符串范例
    
        {
    
            strncpy(pDstStr, szRetBuf, iDstStrSize);
    
            break;
    
        }
    
     
    
        case MML_INT8_TYPE:                       //字符范例
    
        {
    
            pUINT8   = (UINT8 *)pDstStr;
    
            *pDstStr = (UINT8)atoi(szRetBuf);
    
            break;
    
        }
    
     
    
        case MML_INT16_TYPE:                      // short int范例
    
        {
    
            pUINT16  = (UINT16 *)pDstStr;
    
            *pUINT16 = (UINT16)atoi(szRetBuf);
    
            break;
    
        }
    
     
    
        case MML_INT32_TYPE:                      // int范例
    
        {
    
            pUINT32  = (UINT32 *)pDstStr;
    
            *pUINT32 = (UINT32)atoi(szRetBuf);
    
            break;
    
        }
    
     
    
        default:                                  //必然要有default分支
    
        {
    
            return FALSE;
    
        }
    
    }
    
     
    
    return TRUE;
    
}
*修他日期        版本号       修改人        修改内容
    
* -------------------------------------------------------------------------------
    
* 20140416         V1.0          zzx           建设
    
****************************************************************/
    
INT32 main(void)
    
{
    
    UINT32  iInfoCount          = 0;      //该变量用于计较记录条数
    
    UINT8   szContentLine[1024] = {0};       //用于存放从文件中独到的每笔记录
    
    FILE   *hFile               = NULL;   //文件句柄指针
    
       
    
    //打开文件
    
    hFile = fopen("EmployeeInfo.ini", "r");
    
    if (!hFile)                     //打开失败
    
    {
    
        printf("Open EmployeeInfo.ini failed!\n");
    
        return -1;                  //异常退出
    
    }
    
     
    
    while (NULL != fgets(szContentLine, sizeof(szContentLine), hFile))
    
    {
    
        T_EmployeeInfo t_EmployeeInfo = {0};
    
     
    
        iInfoCount ++;           //每读取到一笔记录, 则记录条数加1
    
     
    
        //获取EmployeeID
    
        if (TRUE != GetValueFromStr(1, MML_STR_TYPE, szContentLine, t_EmployeeInfo.szEmployeeID, '|', sizeof(t_EmployeeInfo.szEmployeeID)))
    
        {
    
            printf("获取第%d位员工的工号失败.\n", iInfoCount);
    
            return -1;
    
        }
    
     
    
        //获取EmployeeName
    
        if (TRUE != GetValueFromStr(2, MML_STR_TYPE, szContentLine, t_EmployeeInfo.szEmployeeName, '|', sizeof(t_EmployeeInfo.szEmployeeName)))
    
        {
    
            printf("获取第%d位员工的姓名失败.\n", iInfoCount);
    
            return -1;
    
        }
    
     
    
        //获取EmployeeAge
    
        if (TRUE != GetValueFromStr(3, MML_INT32_TYPE, szContentLine, (UINT8 *)&(t_EmployeeInfo.iEmployeeAge), '|', sizeof(t_EmployeeInfo.iEmployeeAge)))
    
        {
    
            printf("获取第%d位员工的年数失败.\n", iInfoCount);
    
            return -1;

    
        }
    
     
    
        //逐条打印每个员工的信息
    
        printf("第%d位员工的信息为:工号=%s, 姓名=%s,年数=%d.\n", iInfoCount, t_EmployeeInfo.szEmployeeID, t_EmployeeInfo.szEmployeeName, t_EmployeeInfo.iEmployeeAge);
    
    }
    
     
    
    fclose(hFile);         //最后必然要封锁文件句柄
    
     
    
    return 0;
    
}

#p#副标题#e#

4.措施内容详解

#p#分页标题#e#

4.1员工信息布局体T_EmployeeInfo

typedef struct
    
{
    
    UINT8  szEmployeeID[1024];       //员工工号
    
    UINT8  szEmployeeName[1024];    //员工姓名
    
    UINT32 iEmployeeAge;            //员工年数
    
} T_EmployeeInfo;

说明:

(1)因为文件中每笔记录包罗了工号、姓名和年数,所以布局体中要界说三个成员变量,个中工号和姓名是字符串范例,年数为整型。

#p#分页标题#e#

(2)留意成员变量的定名法则,字符串范例以“sz”开头,整型以“i”开头,利便对变量举办识别。同时,为了防备每个字段的内容过长,界说字符数组的长度为1024(不要高出MAX_RET_BUF_LEN的巨细)。

4.2字段数据获取函数GetValueFromStr

该函数的事情道理为:按照输入的参数来从pSourceStr中获取第iSerialNum字段的内容,存放到pDstStr中,各个字段以cIsolater脱离开来。

留意,在执行函数的主要逻辑之前,要对指针举办掩护,即对指针的异常环境举办判定(判定其是否为空,详细见措施代码)。在实际的软件开拓项目中,这一点长短常重要的。

该函数的事情步调为:

第一步:获取每个字段的字段首和字段尾指针。strchr函数用于查询两个字段之间cIsolater的地点,字段的首位指针值相减就获得该字段的长度,并利用memcpy函数将该字段值拷贝到szRetBuf中。为了防备源串中字段值过长,还对理会出来的字段长度举办了异常掩护。该要领在实际的软件开拓项目中常常用到。

第二步:将理会出的字段值放到pDstStr中去。按照差异的数据范例(如字符串、整型等),将第一步得到的字段值存放到pDstStr中。由于第一步的szRetBuf为字符数组,而某些字段值要求为整数,因此在要求参数范例为整型的case分支中利用了atoi函数。留意,switch语句必然要有default分支。

4.3主函数中的文件操纵函数

在主函数(main)中,利用了文件操纵函数fopen、fgets和fclose。

(1) fopen函数

在利用文件之前,先要将其打开,本措施以只读的方法(该函数第二个参数为r)操纵文件,防备对文件的错误写入。

(2) fgets函数

该函数用于从文件中读取一个字符串,其描写如下:

函数界说:char *fgets(char *s, int size, FILE *stream);

函数说明:fgets()用来从参数stream所指的文件内读入字符并存到参数s所指的内存空间,直到呈现换行字符、读到文件尾或是已读了size-1个字符为止,最后会加上NULL作为字符串竣事。

返回值:若乐成则返回s指针,返回NULL则暗示有错误产生或内容读取完成。

在本措施中,将从文件中读取到的内容存放到szContentLine中。

(3) fclose函数

该函数用于在操纵完文件之后封锁文件指针,防备对该文件的错误操纵。fclose函数必然要与fopen函数配对。在利用完文件之后,必然要挪用fclose函数将文件封锁。

4.4 GetValueFromStr函数的挪用

以获取员工年数的挪用为例加以说明,挪用代码如下:

if (TRUE != GetValueFromStr(3, MML_INT32_TYPE, szContentLine, (UINT8 *)&(t_EmployeeInfo.iEmployeeAge), ‘|’, sizeof(t_EmployeeInfo.iEmployeeAge)))

{

printf("获取第%d位员工的年数失败.\n", iInfoCount);

return -1;

}

(1) GetValueFromStr函数的界说为:BOOL GetValueFromStr(UINT16 iSerialNum, UINT8 iContentType, UINT8 *pSourceStr, UINT8 *pDstStr, UINT8 cIsolater, UINT32 iDstStrSize),挪用的时候,实参3对应形参iSerialNum,实参MML_INT32_TYPE对应形参iContentType,实参szContentLine对应形参pSourceStr,实参&(t_EmployeeInfo.iEmployeeAge)对应形参pDstStr,实参’|’对应形参cIsolater,实参sizeof(t_EmployeeInfo.iEmployeeAge)对应形参iDstStrSize。

(2)在函数挪用的时候,实参和形参范例要完全匹配,如GetValueFromStr函数要求第3个参数为字符型指针,则传入参数szContentLine也要为同样范例的指针(因为字符数组名就代表该字符数组的首地点,即指针,所以满意要求)。对付第4个参数,因为年数为整型数据,而要求传入的实参为字符型指针,因此要在t_EmployeeInfo.iEmployeeAge前面添加&来暗示指针,同时还要在前面添加(UINT8 *)将该指针范例转换为字符范例。第5个参数要求为一个字符,因此实参为’|’,留意不要将单引号写成了双引号(双引号暗示字符串)。

(3)假如获取字段失败,那么直接返回-1,不再走下面的流程。这样可确保每条打印出的信息都是正确的。

4.5字段信息的输出打印

为了查察措施理会是否正确,需要在终端打印相关信息。直接利用布局体成员变量来输出对应字段的值。

#p#副标题#e#

5.措施测试

#p#分页标题#e#

在实际的软件开拓项目中,将测试分为正常测试和异常测试。正常测试是严格凭据措施的要求来设计测试流程,异常测试的目标是看在不满意措施要求时,获得的功效会是奈何的。

(1)正常测试

凭据图3的文件内容来编写EmployeeInfo.ini文件,并将之放到与“Pointer.c”同级目次下。运行措施,获得的功效如图4所示。

提前认识软件开辟(13) 指针及机关体的操作

图4正常测试的输出功效

从输出功效可以看出,措施对信息内容的理会是正确的,因此,指针和布局体变量的利用也是正确的。

(2)异常测试

在实际的软件开拓项目中,必然要举办大量的异常测试,以查抄措施的正确性。

1)未正确安排EmployeeInfo.ini文件

删除EmployeeInfo.ini文件,或将它放到其它目次下,则措施输出功效如图5所示。

提前认识软件开辟(13) 指针及机关体的操作

图5文件不存在时的输出功效

2) EmployeeInfo.ini文件中的记录内容不切合要求

将第二笔记录的字段脱离符“|”去掉,则措施输出功效如图6所示。

提前认识软件开辟(13) 指针及机关体的操作

图6第二笔记录的字段脱离符“|”去掉时的输出功效

3) EmployeeInfo.ini文件中无内容

将EmployeeInfo.ini文件中的内容全部删除去,则措施输出功效如图7所示。

提前认识软件开辟(13) 指针及机关体的操作

图7 EmployeeInfo.ini文件中无内容时的输出功效

尚有许多异常的环境,这里就纷歧一罗列了。

一般而言,在产物宣布之前,必然要颠末充实的测试。

6.总结

指针及布局体在软件开拓项目中是很常见的,把握它们的利用要领是软件开拓工程师的必修课。

本文用实例来描写了指针及布局体的详细用法。“冰冻三尺,非一日之寒”,要想纯熟把握它们的用法,还需要我们多多地实践,还需要我们不绝地操练和总结。

From:csdn博客 周兆熊

    关键字:

在线提交作业