现在的位置: 首页 > 综合 > 正文

C++结构体非面向对象特性

2018年08月15日 ⁄ 综合 ⁄ 共 17196字 ⁄ 字号 评论关闭

8.1.1  结构体定义

C++中结构体不同于数组使用。结构体中可以存放不同类型的数据元素,它比数组使用更加灵活。一个结构体可以存放不同类型的数据,从而将不同类型数据放到一起组成一条记录。结构体部分的学习也是面向对象中类的基础。结构体定义将会在C++程序中形成一种类型的概念,用于开发者创建其对象实例使用。C++中结构体定义的一般语法形式:

struct struct_name                       //声明结构体名称为struct_name

{

         typedata_name1;              //结构体数据成员1

         typedata_name2;              //结构体数据成员2

         …

};

结构体声明由关键字struct标识,随后是结构体的名字struct_name。它由开发者自己定义符合要求的标识符。一对大括号中间包含着结构体成员的定义,结构体成员可以是C++基本数据类型变量也可以是结构体或结构体指针。最后结构体定义以分号为结尾,可以将结构体定义看成一条完整的语句。

结构体声明仅仅定义了结构体这样一种类型,此刻的结构体并不占用内存空间。真正编译器分配内存空间是在使用该结构体类型定义其对象时发生的。下面通过一个结构体声明实例来直观的了解结构体定义使用情况。

struct Record                                //定义结构体Record

{

         intid;                                     //结构体成员,整型变量id

         chardata[100];                    //结构体成员,字符型数组data

};

Record record;                             //结构体对象定义

如上实例声明名为Record的结构体,包含两个结构体成员变量:一个整型变量id和一个包含100个元素的字符数组。随后使用该结构体作为类型定义其变量,也可称为其对象。此时编译器为其结构体变量分配内存,该结构体变量可以在程序中用于存储数据等操作。结构体声明还可以在声明的同时定义其变量,使得结构体使用更加紧凑。这样在检查程序时有多少该结构体变量的应用一目了然。下面通过这种方式的结构体类型声明和变量定义来了解其使用情况。

struct Record                                //定义结构体Record

{

         intid;                                     //结构体成员,整型变量id

         chardata[100];                    //结构体成员,字符型数组data

}record1,record2;                        
//结构体声明同时对象定义

以上实例在声明结构体类型的同时定义其两个对象record1record2。结构体声明时可以根据需要定义多个变量,之间使用逗号隔开,最后末尾不要忘记使用分号为结尾。

8.1.2  结构体成员访问及初始化

C++中结构体声明之后就可以使用其定义变量,结构体变量的定义在C语言中必须结构体名前加上struct关键字。而C++中除了包含这种方式定义外,还可以简化其定义方式,允许定义结构体变量时直接使用结构体名作为类型来定义。结构体变量定义完毕后,结构体变量在使用前需要初始化其值。此时涉及结构体变量访问其成员的问题。C++中通过“.”符号紧跟在变量名之后来访问其某个成员。如下实例定义结构体变量,通过“.”符号方式访问结构体变量的成员,并为其初始化值。

struct Record                                //定义结构体Record

{

         intid;                                     //结构体成员,整型变量id

         chardata[10];                      //结构体成员,字符型数组data

};

Record record;                             //定义结构体Record对象record

record.id = 1;                                //为结构体id成员变量赋值

上述实例中定义包含两个成员的结构体类型Record。随后使用该结构体类型定义其变量,此刻结构体中成员被分配了内存空间,该变量使用“.”符号访问其数据成员id并且给其初始化值为1C++中结构体初始化通常有两种方式,一种是定义结构体变量的同时进行初始化。另一种是定义结构体变量后,针对其数据成员分别赋值。两个结构体变量初始化方式实例如下所示。

struct Record                                //定义结构体Record

{

         intid;                                     //结构体成员,整型变量id

         chardata[10];                      //结构体成员,字符型数组data

}record = {1,”jack”};                      //结构体对象record定义同时初始化

Record record1 = {1,”hello”};     //定义结构体对象record1并初始化

Record record2;                           //定义结构体对象record2

Record2.id = 2;                             //为结构体对象成员id赋值

Record2.data = “welcome”;       //为结构体对象成员data赋值

定义结构体变量同初始化与数组类似,依然采用一对大括号,其间的数据值依次给结构体中成员变量赋值。该种方式初始化又有两种形式,一种是在声明结构体的同时定义其变量并初始化。另一种是声明结构体后,使用结构体定义其变量再初始化。第二种初始化方式通过结构体变量使用“.”符号访问到结构体对应的数据成员,单个的使用赋值的方式来初始化。两种方式的结果同样是将结构体成员变量赋上初值供程序中处理使用。

8.1.3  结构体指针

结构体既然可以作为用户自定义类型,那么可以定义使用该类型的指针,称为结构体指针。结构体指针的定义使用与普通数据类型相同。当使用结构体类型定义变量时,为该结构体变量分配一块内存空间供处理使用,与数组类似结构体指针则指向了这块内存空间的首地址。

结构体指针访问结构体变量成员则使用“->”符号来操作,下面通过一个实例来了解结构体指针变量的定义以及其初始化方式。

struct bookInfo                                       //定义结构体bookInfo表示书本信息

{

         intid;                                              //定义结构体成员,整型变量id表示书本编号

         charbookname[100];                 //定义结构体成员,包含100个元素的字符数组表示书名

         charauthor[100];                         //定义结构体成员,包含100个元素的字符数组表示作者名

         charpublisher[100];                    //定义结构体成员,包含100个元素的字符数组表示出版社名

};

bookInfo book1 = {1,”Linux”,”jack”,”OxfordUniversity Press”};//定义该结构体类型变量,同时初始化

bookInfo *book2 = &book1;                                                     //定义该结构体指针,同时将book1地址用于其初始化

book2->id = 2;                                                                             //通过指针的方式访问并修改其指向结构体成员id的值

以上实例定义书的基本信息结构体为bookInfo。随后采用该结构体定义其变量book1同时初始化该结构体变量各个数据成员。定义结构体指针变量book2,将结构体变量book1的地址赋给book2指针变量,即此刻book2指针变量指向了结构体变量book1的首地址。与数组不同,结构体可以像C++中基本数据类型变量那样相互赋值。如再定义bookInfo结构体变量book3,可以执行book3
= book2
这样的语句用来实现结构体变量之间的相互赋值。

上述例子中,结构体指针变量book2指向book1变量地址后,可以通过“->”符号访问其结构体变量成员并修改其值。如上book2->id
=2
语句访问结构体变量book1的数据成员id并修改其值为2

结构体另一个灵活之处在于结构体的嵌套定义,即结构体中可以再以结构体变量或者结构体指针作为其成员。结构体嵌套定义其变量初始化方式与结构体变量初始化方式相同。下面通过在上述实例基础上增加结构体成员的实例来了解嵌套结构体定义及初始化使用方式。

struct authorInfo                                             //定义结构体authorInfo表示作者信息

{

         charname[50];                                      //定义结构体成员,包含50个元素的字符数组表示作者姓名

         char country[50];                                   //定义结构体成员,包含50个元素的字符数组表示作者国籍

         charcity[50];                                           //定义结构体成员,包含50个元素的字符数组表示作者所在城市

};

struct bookInfo                                                //定义结构体bookInfo表示书本信息

{

         intid;                                                        //定义结构体成员,整型变量id表示书本编号

         charbookname[100];                           //定义结构体成员,包含100个元素的字符数组表示书名

         authorInfoauthor;                                 //定义结构体成员,定义结构体authorInfo变量author表示作者信息

         charpublisher[100];                             //定义结构体成员,包含100个元素的字符数组表示出版社名

};

bookInfo book1 = {1,”Linux”,{“jack”,”American”,”NewYork”},”Oxford University Press”};//定义结构体变量并初始化

bookInfo *book2 = &book1;                                            //定义结构体指针,同时使用结构体变量book1地址初始化

strcpy(book2->author.name,”jonh”);                             //采用结构体指针的方式访问指向结构体变量的成员并修改值

以上实例中定义两个结构体,其中bookInfo结构体中使用到结构体authorInfo的变量作为其成员。随后定义结构体bookInfo变量book1以及其结构体指针book2,同时初始化book1并将其地址用于初始化结构体指针book2,即结构体指针book2指向了变量book1的地址。结构体指针通过符号“->”访问其指向的结构体变量book1的成员变量。通过strcpy拷贝函数修改了结构体变量author的成员变量name值,由于字符数组不能够直接互相赋值,所以这里需要使用strcpy采用拷贝赋值的方式实现该字符数组的赋值。

结构体指针在软件编程中有许多的应用,作为函数的参数传递、通过指针的方式方便的操纵结构体成员参与运算等。结构体指针使用恰当,在完成程序处理功能之外会提高程序的处理性能和效率。

8.1.4  结构体数组

C++中结构体作为一种自定义类型,可以定义该类型的数组供程序批量处理结构体记录。元素都为结构体类型的数组,称为结构体数组。结构体数组定义与其变量定义相似,同样有两种定义实现方式。下面通过两种方式的结构体数组定义实例,了解结构体数组的基本概念。

struct bookInfo                                                //定义结构体bookInfo表示书本信息

{

         intid;                                                        //定义结构体成员,整型变量id表示书本编号

         charbookname[100];                           //定义结构体成员,包含100个元素的字符数组表示书名

         charauthor[100];                                   //定义结构体成员,定义结构体authorInfo变量author表示作者信息

         charpublisher[100];                             //定义结构体成员,包含100个元素的字符数组表示出版社名

};

bookInfo book[3];                                           //定义拥有3个结构体变量数组

struct bookInfo                                                //定义结构体bookInfo表示书本信息

{

         intid;                                                        //定义结构体成员,整型变量id表示书本编号

         charbookname[100];                           //定义结构体成员,包含100个元素的字符数组表示书名

         charauthor[100];                                   //定义结构体成员,定义结构体authorInfo变量author表示作者信息

         charpublisher[100];                             //定义结构体成员,包含100个元素的字符数组表示出版社名

}book[3];                                                           //声明结构体同时定义结构体数组,该数组拥有3个结构体变量元素

如上实例中定义了两种方式的结构体数组。一种为结构体定义后以该类型定义结构体数组。另一种方式为定义结构体类型同时定义结构体类型数组。该结构体数组包含5个元素,每个元素都为一个结构体bookInfo类型的变量。结构体数组初始化、元素成员的访问也与结构体变量初始化及访问方式类似。

bookInfo book[3] = {{1,”Linux”,”jack”,” OxfordUniversity Press”},{2,”C++”,”jonh”,” Oxford University Press”},

                                     {3,”java”,”lining”,”Oxford University Press”}};

book[0].id = 4;

结构体数组初始化方式与单个结构体变量初始化方式类似。用一对大括号表示,其中每个元素都采用单个结构体变量初始化方式。不同结构体变量作为元素之间通过逗号隔开,依次初始化结构体数组中每个元素。由于结构体数组每个元素又是结构体变量,所以初始化每个元素意味着初始化单个结构体变量。

结构体数组访问其元素通过下标来进行,依然可以使用下标与“.”符号来组合访问具体结构体数组元素中的成员。上例中book[0].id = 4语句即通过下标与点符号来访问并修改该结构体数组第一个元素的成员id的值。结构体数组元素之间可以直接相互赋值,但是整个结构体数组之间不允许互相赋值。

C++程序中结构体数组多用来处理特定格式数据的记录,如下通过一个打印记录信息的实例来了解一下结构体数组在应用程序中的操作。

1.准备实例

打开UE工具,创建新的空文件并且另存为chapter0801.cpp。该代码文件随后会同makefile文件一起通过FTP工具传输至Linux服务器端,客户端通过scrt工具访问操作。程序代码文件编辑如下所示。

/**

* 实例chapter0801

* 源文件chapter0801.cpp

* 数组应用实例

*/

#include <iostream>

using namespace std;

struct record                                  //定义结构体record,包含两结构体成员

{

           int id;                                 //结构体成员整型变量id

           char data[20];                  //结构体成员字符数组data

};

/*

* 函数功能:打印传入结构体数组的记录信息

* 函数参数:传入结构体数组名及其数组长度

* 返回值:空

*/

void showReacord(record *array,int length);

int main()

{

           record record_array[2] ={{1,"lining"},{2,"jack"}};     //定义拥有2个元素的结构体数组,同时初始化

           showReacord(record_array,2);                                  //调用打印结构体数组记录函数

           return 0;

}

void showReacord(record *array,int length)                         //打印传入结构体数组的记录信息函数定义

{

           for(int i=0;i<length;i++)                                                //根据传入的数组长度作为循环条件打印数组记录

           {

              cout<<"The structarray"<<"["<<i<<"]:"<<endl;   //打印结构体数组元素提示信息

              cout<<array[i].id<<""<<array[i].data<<endl;      //打印结构体数组元素信息

           }

}

上述实例主要演示了结构体作为元素的数组应用情况。本程序由两个部分组成,比主函数入口新增加了打印结构体数组元素的方法。该方法主要用于遍历并向屏幕输出数组元素值。关于函数的概念,在后续章节会有专门的讲解,初学者在此处主要掌握数组赋值以及相互遍历的使用方法即可。

2.编辑makefile

Linux平台下需要编译源文件为chapter0801.cpp,相关makefile工程文件编译命令编辑如下所示。

OBJECTS=chapter0801.o

CC=g++

 

chapter0801: $(OBJECTS)

    $(CC)$(OBJECTS) -g -o chapter0801

clean:

    rm -fchapter0801 core $(OBJECTS)

submit:

    cp -f -rchapter0801 ../bin

    cp -f -r*.h ../include

上述makefile文件套用了上个实例模板。之所以其中采用变量定义替换的方式,目的就是为了方便编译程序的替换。从makefile工程文件中可以看出,布局是相同的。不同的地方仅仅是代码的文件名、生成可执行程序的名称等,大大方便了每次都要重新编写一遍编译命令的编辑方式。

3.编译运行程序

当前shell下执行make命令,生成可执行程序文件,随后通过make submit命令提交程序文件至本实例bin目录,通过cd命令定位至实例bin目录,执行该程序文件运行结果如下所示。

[developer@localhost src]$ make

g++ -c -o chapter0801.o chapter0801.cpp

g++ chapter0801.o -g -o chapter0801

[developer @localhost src]$ make submit

cp -f -r chapter0801 ../bin

cp -f -r *.h ../include

[developer @localhost src]$ cd ../bin

[developer @localhost bin]$ ./chapter0801

The struct array[0]:

1 lining

The struct array[1]:

2 jack

以上实例主要实现了结构体数组的定义、初始化、作为函数参数传递,通过数组下标访问结构体元素及其成员变量等使用方式。通过本小节的讲述主要应该掌握结构体数组的基本概念及其定义和相关的操作,结构体数组作为记录形式处理还会在后续章节中有所体现,尤其在Linux系统下的文件处理部分,结构体数组会有更多的应用。

8.1.5  结构体作为函数参数

结构体与C++中基本类型变量类似,同样可以作为应用程序中函数间的参数来传递使用。下面通过一个完整实例,就结构体作为参数的几种参数传递方式展开讲述。

1.准备实例

打开UE工具,创建新的空文件并且另存为chapter0802.cpp。该代码文件随后会同makefile文件一起通过FTP工具传输至Linux服务器端,客户端通过scrt工具访问操作。程序代码文件编辑如下所示。

/**

* 实例chapter0802

* 源文件chapter0802.cpp

* 结构体参数传递方式实例

*/

#include <iostream>

using namespace std;

 

struct bookInfo                                                //定义书本结构体bookInfo,表示书本基本信息

{

          int id;                                                      //定义结构体数据成员,整型变量id表示书本编号

          char bookname[50];                           //定义结构体数据成员,包含50个元素的字符型数组表示书名

          char author[50];                                   //定义结构体数据成员,包含50个元素的字符型数组表示作者名

          char publisher[50];                              //定义结构体数据成员,包含50个元素的字符型数组表示出版社名

};

/*

* 函数功能:结构体变量参数值传递

* 函数参数:传入结构体变量参数

* 返回值:空

*/

void dealValueParam(bookInfo book);

/*

* 函数功能:结构体变量参数引用传递

* 函数参数:传入结构体引用参数

* 返回值:空

*/

void dealQuoteParam(bookInfo &book);

/*

* 函数功能:结构体变量指针参数传递

* 函数参数:传入结构体指针参数

* 返回值:空

*/

void dealPointerParam(bookInfo *book);

/*

* 函数功能:结构体数组参数传递

* 函数参数:传入结构体数组参数

* 返回值:空

*/

void dealStructArrary(bookInfo *book);

/*主程序入口*/

int main()

{

          bookInfo book1 ={1,"Linux","jack","Oxford University Press"};                   //定义结构体变量book1并初始化

          bookInfo book2 = book1;                                     //定义结构体变量book2,使用变量book1直接为其初始化赋值

          bookInfo *book3 = &book2;                       //定义结构体指针变量,使用变量book2地址为其初始化赋值

          bookInfo book4[2] ={{10,"C++","john","Oxford University Press"},

                                    {11,"java","lining","Oxford UniversityPress"}};          //定义包含两个元素的结构体数组

          dealValueParam(book1);                                                                                  //调用结构体参数值传递处理函数

          cout<<"After dealValueParam =>book1 id:"<<book1.id<<endl;      //打印函数处理后其成员值的变化

          dealQuoteParam(book2);                                                                         //调用结构体参数引用传递处理函数

          cout<<"After dealQuoteParam =>book2 id:"<<book2.id<<endl;      //打印函数处理后其成员值的变换

          dealPointerParam(book3);                                                                       //调用结构体参数指针传递处理函数

          cout<<"After dealPointerParam =>book2id:"<<book2.id<<"or"<<book3->id<<endl;      

          dealStructArrary(book4);                                                                                    //调用结构体数组传递处理函数

          return 0;

}

/*结构体变量参数值传递处理函数定义*/

void dealValueParam(bookInfo book)

{

          book.id = 2;                                          //修改传入结构体参数中成员值

          cout<<"The bookinfo:"<<endl;                  //打印修改后传入结构体变量的成员值

          cout<<"bookid:"<<book.id<<" "<<"bookname:"<<book.bookname<<endl;

          cout<<"bookauthor:"<<book.author<<" "<<"bookpublisher:"<<book.publisher<<endl;

}

/*结构体变量参数引用传递处理函数定义*/

void dealQuoteParam(bookInfo &book)

{

          book.id = 3;                                          //修改传入结构体参数中成员值

          cout<<"The bookinfo:"<<endl;                  //打印修改后传入结构体变量的成员值

          cout<<"bookid:"<<book.id<<" "<<"bookname:"<<book.bookname<<endl;

          cout<<"bookauthor:"<<book.author<<" "<<"bookpublisher:"<<book.publisher<<endl;

}

/*结构体变量参数指针传递处理函数定义*/

void dealPointerParam(bookInfo *book)

{

          book->id = 4;                                        //修改传入结构体参数中成员值

          cout<<"The bookinfo:"<<endl;                  //打印修改后传入结构体变量的成员值

          cout<<"bookid:"<<book->id<<" "<<"bookname:"<<book->bookname<<endl;

          cout<<"bookauthor:"<<book->author<<" "<<"bookpublisher:"<<book->publisher<<endl;

}

/*结构体数组参数传递处理函数定义*/

void dealStructArrary(bookInfo *book)

{

          cout<<"The book struct arrayinfo:"<<endl;      //打印传入结构体数组元素信息

          for(int i = 0;i < 2;i++)

          {

                    cout<<"bookid:"<<book[i].id<<" "<<"bookname:"<<book[i].bookname<<endl;

                    cout<<"book author:"<<book[i].author<<""<<"book publisher:"<<book[i].publisher<<endl;

          }

}

本实例程序主要通过结构体方式演示函数传值、传引用、传指针以及数组参数功能。程序主要由主函数、传值打印信息、引用传递打印信息、指针参数传递打印信息以及数组参数传递信息打印函数组成。具体程序剖析见注释与后面程序讲解。

2.编辑makefile

Linux平台下需要编译源文件为chapter0802.cpp,相关makefile工程文件编译命令编辑如下所示。

OBJECTS=chapter0802.o

CC=g++

 

chapter0802: $(OBJECTS)

    $(CC)$(OBJECTS) -g -o chapter0802

clean:

    rm -fchapter0802 core $(OBJECTS)

submit:

    cp -f -rchapter0802 ../bin

    cp -f -r*.h ../include

上述makefile文件套用前面的模板格式,主要替换了代码文件、程序编译中间文件、可执行程序等。在编译命令部分-g选项的加入,表明程序编译同时加入了可调式信息。

3.编译运行程序

当前shell下执行make命令,生成可执行程序文件。随后通过make submit命令提交程序文件至本实例bin目录,通过cd命令定位至实例bin目录,执行该程序文件运行结果如下所示。

[ocs@vm-rh:~/users/wangfeng/Linux_c++/chapter08/chapter0802/src]$make

g++    -c -ochapter0802.o chapter0802.cpp

g++ chapter0802.o -g -o chapter0802

[ocs@vm-rh:~/users/wangfeng/Linux_c++/chapter08/chapter0802/src]$makesubmit

cp -f -r chapter0802 ../bin

cp -f -r *.h ../include

[ocs@vm-rh:~/users/wangfeng/Linux_c++/chapter08/chapter0802/src]$cd../bin

[ocs@vm-rh:~/users/wangfeng/Linux_c++/chapter08/chapter0802/bin]$./chapter0802

The book info:

book id:2 book name:Linux

book author:jack book publisher:Oxford UniversityPress

After dealValueParam => book1 id:1

The book info:

book id:3 book name:Linux

book author:jack book publisher:Oxford UniversityPress

After dealQuoteParam => book2 id:3

The book info:

book id:4 book name:Linux

book author:jack book publisher:Oxford UniversityPress

After dealPointerParam => book2 id:4or4

The book struct array info:

book id:10 book name:C++

book author:john book publisher:Oxford UniversityPress

book id:11 book name:java

book author:lining book publisher:Oxford UniversityPress

以上实例共演示了结构体变量三种参数传递方式以及结构体数组作为参数传递的处理。结构体变量作为函数参数在传递方式与C++中基本数据类型变量相同。该实例重温了函数参数传递章节的知识。该程序首先定义了书本信息结构体bookInfo,随后声明了4个功能函数。前三个函数处理结构体变量三种参数传递,后一个函数处理结构体数组参数传递。

主程序中首先定义了2bookInfo类型的变量,其中结构体变量book1定义同时初始化,变量book2定义同时使用book1变量赋值初始化。可见结构体不同于数组可以作为普通类型变量相互赋值。定义结构体指针book3并将变量book2的地址给其赋值初始化,即book3指向了结构体变量book2的地址。book4为包含两个结构体变量的数组,定义同时进行了初始化。

随后调用4个预先定义好的参数处理功能函数。第一个函数主要针对结构体变量作为参数传值方式处理,传入结构体变量并修改其成员值后在函数体内打印该结构体变量成员值。该函数调用后打印了被修改的成员id的值,按照值传递的约定函数调用时实参只是拷贝了副本给形参操作,因此形参在函数内的修改不会影响实参。

第二个、第三个函数分别以引用参数传递以及指针参数传递的方式处理传入结构体变量。由于引用传递和指针传递都是直接操作实参地址,所以函数调用时形参的修改会直接影响实参。

最后以结构体数组作为参数进行传递处理,结构体数组名作为参数在函数调用时会完全退化到指向该结构体数组首地址的指针上。在函数体内完全可以按照结构体指针的方式方便的操作结构体数组。

8.1.6  结构体内存分布

前面在结构体定义时已经讲述过,编译器是根据定义结构体变量时根据其成员变量的大小来分配内存空间。结构体内存空间的分配情况,这里需要重点说明一下。因为软件编程中这关乎程序使用时所占用空间以及执行效率。结构体在内存中分配情况有一点可以确定,即编译器会保证其各个成员变量在内存中出现的位置顺序按照定义时的顺序依次存放,并且结构体第一个成员变量通常为整个结构体对象的首地址。

计算机软件编程中需要关注内存对齐的概念,尤其是开发应用程序在不同平台移植情况下。计算机系统中为了保证处理器访问内存的高效,因此约定要求数据类型在内存空间中最好能够按照自然边界对齐。所谓的自然边界首先为偶数地址,并且可以被48整除的地址。由于计算机处理器在访问自然边界对齐的内存区域只需要一次处理,而处理未对齐的内存区域可能需要两次访问。因此在各个程序语言编译器上都针对这种情况作了优化,C++编译器也针对此类情况在程序编译期作了优化。

每个不同平台编译器都有其默认的对齐参数,供编译器在程序编译期间去优化所定义的数据类型的内存布局。C++编译同样也具有该默认对齐参数,经过查阅相关资料以及测试证实,32Windows平台下C++编译器默认采用8字节对齐方式。而32Linux台下C++译器默认采用4字节对齐处理方式。下面通过一个结构体内存分配实例验证两种不同平台下针对内存对齐的不同处理方式。

struct Record                                                   //定义记录结构体Record

{

         chardata1;                                             //结构体数据成员data1,字符型变量

         doubledata2;                                        //结构体数据成员data2double类型变量

         intdata3;                                                 //结构体数据成员data3,整型变量

};

Record record;                                                //记录结构体变量定义

sizeof(record);                                                //计算结构体变量大小

以上实例代码在32Windows操作系统VC++平台自带编译器下程序执行结果计算出该结构体长度为24个字节大小。而32LinuxC++编译器下程序执行结果该结构体大小为16字节,这种差异是由于两种不同平台下编译器默认设置差异引起的。Linux平台默认设置为4字节对齐,该结构体在内存中布局如下。

char data1;        //1字节大小,可以认为其偏移从0开始,data1数据在内存中位置为0

double data2;   //8字节大小,可以认为偏移从1~8

int data3;           //4字节大小,由于从9开始不能够被4整除,则后移至12位置,从12~16

32Linux平台下以上数据在内存中假设存放的偏移从位置0开始,单个字节表示逐个递增。由于该结构体变量首地址为第一个数据成员地址,该成员为char型数据大小为1个字节,所以可以认为其偏移量为0。第二个double类型数据大小为8个字节,偏移位置从1~8位置,其起始位置都可以被4整除。

因此到目前为止编译器应该并没有对数据存放位置作优化,第三个数据为整型大小为4个字节,偏移位置应从9开始。由于该起始偏移位置不能被4整除,故编译器需要作出填充使得该数据从12偏移位置开始到16为结束。最终计算出整个结构体数据大小为16字节。Windows平台下依然可以按照此方式理解。

C++编译器中也提供选项设置来修改内存对齐参数,通常可以使用如下程序命令来设置该参数。

#pragma  pack(n)            //n表示设置几字节对齐

以上设置方式修改了编译器默认对齐参数,其中n表示以几字节对其方式优化数据类型成员内存布局。同样上述实例如果在设置对齐字节为1时,最终计算该结构体大小为13字节,即每个成员都是以紧凑无空隙的方式被分配内存。C++中内存对齐完全理解清楚需要涉及非常底层且专业的深入研究,这里所讲述的布局方式也仅仅是建立在相关资料查阅以及实验验证的基础上作出的猜测。更多的测试总结规律还需要初学者随着不断深入学习,一种不断探寻真相的精神,继续下去。

由于篇幅限制,C++中内存对齐概念只作出初略肤浅的讲述。随着在实践中不断深入相信会遇到与此有关的问题。尤其笔者在不同的UNIX平台下从事软件开发过程中,发现不同平台的UNIX可能会对内存管理作出一定的优化修改。因此造成的差异在平台应用程序移植中经常会遇到一些意想不到的问题,需要我们不断的深入学习并总结经验和规律。

抱歉!评论已关闭.