C语言提供两种结合不同类型的对象来创建数据类型的机制:结构(structure),用关键字struct声明,将多个对象集合到一个单元中;联合(union),用关键字union声明,允许用几种不同的类型来引用一个对象。
(1)结构(struct)
结构就是一个或多个变量的集合,这些变量可以为不同的类型,为了处理方便而将这些变量组织在一个名字之下。关键字struct引入结构声明。结构声明由包含在花括号内的一系列声明组成。关键字struct后面的名字是可选的,称为结构标记(structure tag)。
(数组也可以看成是一个或多个类型相同的变量的集合)
结构的初始化,初始化式中的成员数可以少于它所初始化的结构,就像数组那样,任何“剩余的”成员都用0作为它的初始值。特别地,剩余的字符数组中的字节数为0,表示空字符串。结构的赋值操作——结构可以拷贝、赋值、传参或作为函数返回值。
结构在使用之前应进行初始化或赋值。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define BUF_LEN 32
struct person {
char name[BUF_LEN];
int age;
int gender;
char address[BUF_LEN];
};
void PrintPerson(struct person *p)
{
printf("name: %s\n", p->name);
printf("age: %d\n", p->age);
printf("gender: %d\n", p->gender);
printf("address: %s\n", p->address);
}
int main()
{
struct person a = { 0 };
struct person b;
struct person c;
struct person d;
printf("\nperson a\n");
PrintPerson(&a);
memcpy(b.name, "jinagxt", sizeof("jinagxt"));
b.age = 28;
b.gender = 1;
memcpy(b.address, "wuhan", sizeof("wuhan"));
printf("\nperson b\n");
PrintPerson(&b);
memset(&c, 0, sizeof(c));
printf("\nperson c\n");
PrintPerson(&c);
printf("\nperson d\n");
PrintPerson(&d);
d = b; // 赋值
printf("\nperson d\n");
PrintPerson(&d);
getchar();
return 0;
}
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// 数据对其,要求某种类型对象的地址必须是某个值K(通常是2、4、8)的倍数。
typedef struct st { // 24
char cVal;
short sVal;
int iVal;
long lVal;
double dVal;
} MyStruct;
typedef struct st2 { // 12
short sVal;
long lVal;
char cVal;
} MyStruct2;
typedef struct st3 { // 4
short sVal;
char cVal;
} MyStruct3;
void PrintTypeSize()
{
printf("sizeof(void *): %d\n", sizeof(void *));
printf("sizeof(int): %d\n", sizeof(int));
printf("sizeof(char): %d\n", sizeof(char));
printf("sizeof(short): %d\n", sizeof(short));
printf("sizeof(long): %d\n", sizeof(long));
printf("sizeof(float): %d\n", sizeof(float));
printf("sizeof(double): %d\n", sizeof(double));
}
int main()
{
MyStruct stVal = { 1, 2, 3.0, 'a' };
printf("sizeof(stVal): %d\n", sizeof(stVal)); // 24
printf("&stVal.cVal: 0x%x\n", &stVal.cVal);
printf("&stVal.sVal: 0x%x\n", &stVal.sVal);
printf("&stVal.iVal: 0x%x\n", &stVal.iVal);
printf("&stVal.lVal: 0x%x\n", &stVal.lVal);
printf("&stVal.dVal: 0x%x\n\n", &stVal.dVal);
printf("sizeof(MyStruct2): %d\n", sizeof(MyStruct2)); // 12
printf("sizeof(MyStruct3): %d\n", sizeof(MyStruct3)); // 12
printf("sizeof(flags): 0x%x\n", sizeof(flags)); // 4
PrintTypeSize();
getchar();
return 0;
}
长度为0的数组
1)长度为0的数组并不占有内存空间,而指针方式需要占用内存空间。
2)对于长度为0的数组,在申请内存空间时,采用一次性分配的原则进行;对于包含指针的结构体,在申请空间时需分别进行,释放时也需分别释放。
3)对于长度为0的数组的访问可采用数组方式进行。
typedef struct { // 4
unsigned int length;
int contents[0]; // 不占用内存
} Line;
int main()
{
Line *pstTest = NULL;
unsigned int length = 12;
printf("sizeof(MyStruct): %d\n", sizeof(Line));
pstTest = (Line *)malloc(sizeof(Line) + sizeof(int) * length);
pstTest->length = length;
for (int i = 0; i < length; i++) {
pstTest->contents[i] = i + 1;
printf("pstTest->contents[%d]: %d\n", i, pstTest->contents[i]);
}
getchar();
return 0;
}
(2)联合(union)
联合是可以(在不同时刻)保存不同类型和长度的对象的变量,编译器负责跟踪对象的长度和对齐要求。联合提供了一种方式,以在单块存储区中管理不同类型的数据,而不需要在程序中嵌入任何同机器有关的信息。
实际上,联合就是一个结构,它的所有成员相对与基地址的偏移量都为0,此结构空间要大到足够容纳最“宽”的成员,并且,其对齐方式要适合与联合中所有类型的成员。
联合只能用其第一个成员类型的值进行初始化。
union U {
unsigned int nVal;
unsigned char cArr[4];
};
int main()
{
union U uTest;
uTest.nVal = 0x12345678;
printf("union uTest\n", uTest.nVal);
printf("uTest.nVal: 0x%x\n", uTest.nVal);
for (int i = 0; i < 4; i++) {
printf("uTest.arr[%d]: 0x%x\n", i, uTest.cArr[i]);
}
getchar();
return 0;
}