C/C++中结构体类型,就这?
Posted 君子黎
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C/C++中结构体类型,就这?相关的知识,希望对你有一定的参考价值。
1. 概述
2. 结构体定义
结构体的定义语法如下所示:
struct tag-name(可选)① {
member-listt②
}variable-list(可选)③;
其中struct是定义结构体类型的关键字,tag-name是结构体类型的标志(标签名),该部分可选;member-list是任意数量的变量声明,即结构体的成员列表,此部分为定义结构体数据类型时候必填的数据,值得注意的是此部分不允许"不完整类型"的成员和"函数类型"的成员(除了柔性数组成员);variable-list是为该结构体声明的变量列表,该部分可选。
仍以书籍作为示例,现定义一个用以描述书籍的结构体类型 struct book.如下所示
struct book{
char book_name[20]; //书名
char isbn[50]; //ISBN
char author[30]; //作者
float price; //价格
};
此时,我们就已经定义好了类型为struct book的数据类型。和C中的其他基本数据类型一样,可以使用struct book数据类型来定义普通变量和指向struct book类型的指针变量。如:struct book englishBooks
、struct book *pBooks;
在该struct book数据类型中,book_name、isbn、author和price
等称为成员(member)。在数组中,访问元素通过下标进行访问,是因为数组中存储的相同数据类型的固定长度的元素。但是结构体类型中访问其成员是不能通过下标进行,因为其中每个成员的数据类型可能不同、长度也不同。因此需要通过成员名字来访问。
-
示例1
/*************************************************************************
* File Name: book.c
* Author: The answer
* Function: Other
* Mail: 2412799512@qq.com
* Created Time: 2020年02月20日 星期四 19时07分54秒
************************************************************************/
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
struct book{
char book_name[20]; //书名
char isbn[50]; //ISBN
char author[30]; //作者
float price; //价格
};
int main(int argc,char **argv)
{
//声明struct book数据类型的结构体变量englistBooks和指针变量*pBooks
struct book englishBooks, *pBooks = NULL;
memset(&englishBooks, 0x00, sizeof(struct book));
strncpy(englishBooks.book_name, "雅思口语", sizeof(englishBooks.book_name));
strncpy(englishBooks.isbn, "978-7-5605-3118-2", sizeof(englishBooks.isbn));
strncpy(englishBooks.author, "雅思研究院", sizeof(englishBooks.author));
englishBooks.price = 10.03;
printf("book_name: %s\nisbn: %s\nauthor: %s\nprice: %0.2f\n",
englishBooks.book_name, englishBooks.isbn, englishBooks.author, englishBooks.price);
pBooks = &englishBooks;
printf("\n使用指针访问:\nbook_name: %s\nisbn: %s\nauthor: %s\nprice: %0.2f\n",
pBooks->book_name, pBooks->isbn, pBooks->author, pBooks->price);
return 0;
}
运行结果
2.1 tag-nam、member-listt和variable-list必须同时存在两个
在结构体struct的定义中,可选部分不能够进行全部省略,即“2. 结构体定义”章节中的①、②、③这三部分必须同时出现两个。
2.2 结构体嵌套
结构体的成员可以是基本数据类型(int、float、double等),复杂数据类型如数组、指针,甚至是其他结构体数据类型。
struct work{
char company[20];
char address[20];
};
struct people{
char name[30];
char sex;
int age;
struct work works; /嵌套结构体 struct work/
};
也可以在一个结构体的成员列表中同时定义另外一种数据类型的结构体并声明该结构体类型的变量。如:
struct Student{
char name[20];
char sex;
int age;
struct Teacher{
int num;
char subject[20];
char address[30];
}teacher;
};
若有需要,在定义一个结构体数据类型时候,成员列表中可以定义若干个其他数据类型的结构体,并同时声明该类型的结构体变量。有点类似于嵌套。如下:
struct Student{
char name[20];
char sex;
int age;
struct Teacher{
int num;
char subject[20];
char address[30];
struct unit{
char company[30];
char company_addr[30];
}units;
}teacher;
};
2.3 初始化结构体成员列表
创建任何一种数据类型,都是为了将其应用在实际项目中去。因此,会创建该数据类型的变量,并且根据实际需要进行初始化操作。结构体变量的初始化通常有以下两种方式,分别是使用类似初始化数组的方式和使用C99/C11的“指定初始化器”方式进行初始化。
2.3.1 大括号“{}”的初始化列表
用花括号括起来的初始化列表去初始化一个数组变量。如:
int arr[] = {1,2,3,4,5,6};
结构体变量也可以采用此方式去进行初始化。各成员列表之间使用逗号分隔,花括号括起来的成员列表中,各成员一一对应结构体数据类型变量中的各成员。如:
struct Student{
char name[20];
char sex;
int age;
char number[20];
};
struct Student student = {“lixiaogang5”, ‘M’, 26, “6666”};
上面是在花括号括起来的成员列表数量刚好和结构体变量student中成员数量一一对其的情况下的初始化情况。若花括号中的成员列表数量小于变量中成员数量的情况下,前面的成员一一和变量中的对应成员对应;剩余的成员则将使用缺省值进行初始化。如:
struct Student student = {“lixiaogang5”, ‘M’};
printf(“name: %s\nsex: %c\nage: %d\nnumber: %s\n”, student.name, student.sex, student.age, student.number);
在Windows Qt和Linux上的打印结果相同:
name: lixiaogang5
sex: M
age: 0
number:
当花括号括起来的成员列表后面多一个逗号(",")号时候,并不会报错;编译器会认为这是合法的。如:
struct Student student = {“lixiaogang5”, ‘M’,/*此处多了一个\逗号,编译通过*/};
2.3.2 指定初始化器
从C99开始,C支持使用“指定初始化器(Designated Initializers)”的方式来初始化数组和结构体变量。当用指定初始化器去初始化结构体变量时候,则用点(".")运算符加成员名去标识特定成员。如声明一个数据类型struct Dog 。
struct Dog{
char name[20]; //狗的名字
int color; //身体颜色 1.白 2.黑 3.灰
int weight; //体重
float length; //身高
};
当用“指定初始化器”来初始化时候,方式如下:
struct Dog Husky = {.weight = 20, .length = 78.2, .name = “Husky”, .color = 2};
用printf来输出Husky变量的各成员,可的如下结果:
printf(“name: %s\ncolor: %d\nweight: %d\nlength: %0.1f\n”, Husky.name, Husky.color, Husky.weight, Husky.length);
name: Husky
color: 2
weight: 20
length: 78.2
指定初始化器对花括号列表里的字段顺序没有任何要求,换言之,可按照任意顺序。所谓指定初始化器,即手动指定结构体变量中某成员进行初始化。
-
若仅指定并初始化结构体变量Husky中某一位成员时候(即没有对全部成员进程初始化操作),未初始化的成员值为多少?
这里仅初始化了成员weight,其他成员如:color、name、length都未初始化,现打印该结构体变量Husky中各成员。
printf(“name: %s\ncolor: %d\nweight: %d\nlength: %0.1f\n”, Husky.name, Husky.color, Husky.weight, Husky.length);
name:
color: 0
weight: 20
length: 0.0
其结论是,未明确指定初始化的结构体成员,将会被执行默认的初始化操作。 整型默认初始化0,浮点数/双精度浮点数默认初始化0.0,数组默认初始化为空。
-
若想要初始化结构体全部成员为0,而又不想一一指定初始化为0,是否有其他方式?
可以使用以下两种方式达到效果。
struct Dog Husky = {0} ①;
struct Dog Husky = {} ②;
不过需要注意的是,①和②原理不等同。其中①是将结构体变量Husky中的第一个成员name初始化为空(也是0),剩余3个成员则是使用默认初始化,尽管效果上也是0。②是采用默认初始化,将结构体中全部成员初始化为0。
struct Rectangle
{
int x;
int y;
int width;
int height;
};
struct Rectangle Rect = {6};
printf(“x: %d\ny: %d\nwidth: %d\nheight: %d\n”, Rect.x, Rect.y, Rect.width, Rect.height);
x: 6
y: 0
width: 0
height: 0
2.3.3 初始化嵌套结构体
在上一节中说明了如何初始化一个结构体变量,但都是些简单的结构体数据类型,当在结构体中嵌套其他数据类型的结构体,且内部成员有数组时候,初始化过程稍微复杂些。
struct Book{
char name[20];
char isbn[20];
float price;
struct Author{
int Hobby[5];
char address[20];
int length;
}author;
};
struct Book book = {“PostgreSQL”, “968-67-5405-3218-2”, 20.60, {
{1,2,3,4,5}, “SomeWhere”, 178}};
打印结果:
printf(“name: %s\nisbn: %s\nprice: %0.2f\nHobby: [%d, %d, %d, %d, %d]\naddress: %s\nlength: %d\n”,
book.name, book.isbn, book.price, book.author.Hobby[0],
book.author.Hobby[1], book.author.Hobby[2], book.author.Hobby[3], book.author.Hobby[4],
book.author.address, book.author.length);
name: PostgreSQL
isbn: 968-67-5405-3218-2
price: 20.60
Hobby: [1, 2, 3, 4, 5]
address: SomeWhere
length: 178
2.3.4 结构体变量赋值结构体变量
C语言中结构体变量直接赋值(“=”)给另一个结构体变量是可以的。如示例2所示,结构体数据类型 struct Book中有一个字符数组成员。使用已初始化的结构体变量bookA直接赋值给变量bookB。
-
示例2
/*************************************************************************
* File Name: book.c
* Author: The answer
* Function: Other
* Mail: 2412799512@qq.com
* Created Time: 2020年02月21日 星期五 15时32分27秒
************************************************************************/
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
struct Book{
char name[20];
char isbn[30];
float price;
};
int main(int argc,char **argv)
{
struct Book bookA = {"Book1", "9785-2-234-2662", 66.5};
struct Book bookB;
memset(&bookB, 0x00, sizeof(bookB));
bookB = bookA;
printf("name: %s\nisbn: %s\nprice: %0.2f\n", bookA.name, bookA.isbn, bookA.price);
puts("---------------------");
printf("name: %s\nisbn: %s\nprice: %0.2f\n", bookB.name, bookB.isbn, bookB.price);
return 0;
}
打印结果:
当数据类型为 struct Book的结构体内部成员包含指针变量时候,是否也能够满足直接赋值呢?因为直接定义为数组,会浪费掉太多的内存空间,因为事先无法去确定一本书的书名有多长;一般为了让数组能够容纳下书名,都是一开始就定义一个较大的容量,但是这样对内存空间的利用实在是太低效。现在示例3中将字符数组的name改为使用指针的形式。
-
示例3
/*************************************************************************
* File Name: book.c
* Author: The answer
* Function: Other
* Mail: 2412799512@qq.com
* Created Time: 2020年02月21日 星期五 15时32分27秒
************************************************************************/
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
struct Book{
char *name;
char isbn[30];
float price;
};
int main(int argc,char **argv)
{
struct Book bookA, bookB;
bookA.name = (char*)malloc(sizeof(char) * 6);
memset(bookA.name, 0x00, sizeof(char) * 6);
strncpy(bookA.name, "bookA", strlen("bookA"));
strncpy(bookA.isbn, "isbn", sizeof(bookA.isbn));
bookA.price = 66.5;
memset(&bookB, 0x00, sizeof(bookB));
bookB = bookA;
printf("name_address: %p\nname: %s\nisbn: %s\nprice: %0.2f\n",
&(*(bookA.name)), bookA.name, bookA.isbn, bookA.price);
puts("---------------------");
printf("name_address: %p\nname: %s\nisbn: %s\nprice: %0.2f\n",
&(*(bookB.name)), bookB.name, bookB.isbn, bookB.price);
/*
* 释放bookA.name申请的内存空间,且置为NULL, 然后重新向这个内存空间赋值
*/
free(bookA.name);
bookA.name = NULL;
sleep(1);
printf("After freeing the memory of bookA.name, bookA.name address: %p, bookA.name: %s, bookB.name: %p\n",
&(*bookA.name), bookA.name, &(*(bookB.name)));
return 0;
}
strncpy(bookA.name, “Other”, strlen(“Other”));
运行结果:
2.4 访问结构体成员
在上章集的“结构体定义”中提到过,结构体的成员和数组元素不一样,不能通过下标来访问。对此,C/C++提供有专门的方法来访问结构体变量成员。
2.4.1 成员运算符“.”
结构体变量通过使用成员运算符(".")来访问各成员,用逗号表示。如englishBooks.book_name。成员运算符的结合性为自左向右。
2.4.2 箭头运算符“->”
对于结构体指针变量所指向的同类型的变量,需要使用“->”来访问各成员列表,“->”又称呼为“箭头运算符”。如:
struct book books;
struct book *pBs = &books;
pBs->book_name; //访问结构体变量books中的成员book_name.
该方法等同于(*pBs).book_name。
3. 结构体数组
声明一个结构体数组和声明一个其他数据类型的数组的格式一样。都是数据类型加上方括号,然后方括号中的数字是表示该数组的大小,即数组中有多少个同类型的元素。如下面示例4所示一样,声明了一个struct Book类型的数组books,该结构体数组books中共有两个元素,分别是books[0]和books[1], 其中books[0]和books[1]的数据类型都是struct Book数据类型。结构体数组同样可以使用下标的方式来访问数组中的每个元素,然后再打印每个元素内部的结构体成员。
-
示例4
/*************************************************************************
* File Name: array.c
* Author: The answer
* Function: Other
* Mail: 2412799512@qq.com
* Created Time: 2020年02月21日 星期五 17时27分24秒
************************************************************************/
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
struct Book{
char name[20];
char isbn[30];
float price;
};
int main(int argc,char **argv)
{
int i = 0;
struct Book books[2] = {
{"bookA", "isbn_1", 66.5},
{"bookB", "isbn_2", 88.5}
};
for(; i < sizeof(books)/sizeof(books[0]); ++i)
{
printf("name: %s, isbn: %s, price: %0.2f\n",
books[i].name, books[i].isbn, books[i].price);
}
return 0;
}
原理图如下图1所示。
4. 结构体作为函数参数
结构体变量是属于完整类型,它同样能够应用在其他变量能够应用的地方。比如作为函数参数,作为函数返回值。当作为函数参数时候,有两种方式,传值和传址。
4.1 传值方式
传值是指在函数调用参数时候,不是对原参数(实际参数)进行操作,而是去创建一份实参变量的拷贝放在被调函数内部的堆栈上,当该函数调用结束时候,再释放掉此拷贝。如下示例5所示。OrderPrice函数的功能仅是想知道englishBook变量的订单总额度,即 quantity * price ,本来仅需两个变量,但是现在却拷贝了整份结构体。
示例5
/*************************************************************************
* File Name: param.c
* Author: The answer
* Function: Other
* Mail: 2412799512@qq.com
* Created Time: 2020年02月22日 星期六 14时38分55秒
************************************************************************/
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<stddef.h>
struct Book{
char name[20];
char isbn[30];
float price; //书籍价格
int quantity; //书籍数量
};
int OrderPrice(struct Book books)
{
int iOrderAmount = 0;
iOrderAmount = books.price * books.quantity;
printf("iOrderAmount: %d\n", iOrderAmount);
return 0;
}
int main(int argc,char **argv)
{
struct Book englishBook;
memset(&englishBook, 0x00, sizeof(englishBook));
strncpy(englishBook.name, "englistBook", sizeof(englishBook.name));
strncpy(englishBook.isbn, "isbn_000", sizeof(englishBook.isbn));
englishBook.price = 66.5;
englishBook.quantity = 20;
if(!OrderPrice(englishBook))
{
puts("OrderPrice succeed!");
}
return 0;
}
打印结果:
iOrderAmount: 1330
OrderPrice succeed!
4.2 传址方式
-
示例6
/*************************************************************************
* File Name: paramPoint.c
* Author: The answer
* Function: Other
* Mail: 2412799512@qq.com
* Created Time: 2020年02月22日 星期六 15时13分18秒
************************************************************************/
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<stddef.h>
struct Book{
char name[20]; //20Byte
char isbn[30]; //32Byte
float price; //4Byte
int quantity; //4Byte
};
int OrderPrice(struct Book *books)
{
int iOrderAmount = 0;
iOrderAmount = books->price * books->quantity;
printf("iOrderAmount: %d books Address: %p\n", iOrderAmount, &(*books));
return 0;
}
int main(int argc,char **argv)
{
struct Book englishBook;
memset(&englishBook, 0x00, sizeof(englishBook));
strncpy(englishBook.name, "englistBook", sizeof(englishBook.name));
strncpy(englishBook.isbn, "isbn_000", sizeof(englishBook.isbn));
englishBook.price = 66.5;
englishBook.quantity = 20;
if(!OrderPrice(&englishBook))
{
printf("OrderPrice succeed! englishBook address: %p\n", &englishBook);
}
return 0;
}
以上是关于C/C++中结构体类型,就这?的主要内容,如果未能解决你的问题,请参考以下文章
结构体状态复位函数/使用完后将结构体恢复成初始值的怎么写呢?