甲公司承担了某市政府门户网站建设项目_与该市信息中心签订了合同app代理推广合作
C语言中用数组表示一组相同类型元素的集合,要表示不同类型元素的集合就要用到结构体。
结构体的声明
结构体的标准格式如下:
//struct是关键字,tag是结构体标签,member-list是结构体成员列表,variable-list是结构体变量列表,就是在结构体声明的同时创建的变量(也可以没有)
struct tag
{member-list;
}variable-list;//这里的分号不能省略
例如要描述一个学生类型:
struct student
{char name[20];//姓名int age;//年龄char id[20];//学号
};//分号不能省略
结构体变量的定义和初始化
结构体变量的定义有以下两种方式:
struct point
{int x;int y;
}p1;//在声明结构体类型的同时创建变量int main()
{struct point p2;//定义结构体变量p2,此时的struct point相当于一个数据类型,和int等数据类型类似,注意struct不能省略
}
在定义变量的同时为变量的成员赋值就是初始化:
struct point
{int x;int y;
};
struct Point p3 = {x, y};//创建变量同时赋值
struct Stu //类型声明
{char name[15];//名字int age; //年龄
};
struct Stu s = {"zhangsan", 20};//初始化
struct Node
{int data;struct Point p;struct Node* next;
}n1 = {10, {4,5}, NULL}; //结构体嵌套初始化
struct Node n2 = {20, {5, 6}, NULL};//结构体嵌套初始化
结构体成员访问
结构体变量访问成员使用.操作符,结构体指针访问成员使用->操作符:
struct Stu
{char name[20];int age;
};
void print(struct Stu* ps)
{//使用结构体指针访问指向对象的成员printf("name = %s age = %d\n", ps->name, ps->age);
}
int main()
{struct Stu s = {"zhangsan", 20};printf("name:%s age:%s\n",s.name,s.age);//结构体变量访问成员print(&s);//结构体地址传参return 0;
}
结构体传参
struct S
{int data[1000];int num;
};
struct S s = {{1,2,3,4}, 1000};
//结构体传参
void print1(struct S s)
{printf("%d\n", s.num);
}
//结构体地址传参
void print2(struct S* ps)
{printf("%d\n", ps->num);
}
int main()
{print1(s); //传结构体print2(&s); //传地址return 0;
}
上面两个print函数中print2更好,因为值传递需要临时拷贝一份结构体变量,如果结构体很大会加大内存开销,降低性能,所以结构体传参最好用地址传递。
匿名结构体
结构体类型声明时可以没有标签tag,称为匿名结构体,但是这种结构体只能用一次,下面举个例子:
//匿名结构体类型
struct
{int a;char b;float c;
}x;//只能在这里定义变量,因为没有标签,不能在其他地方定义变量struct
{int a;char b;float c;
}a[20], *p;p = &x;//这样写是不可以的,p是匿名结构体指针,x是匿名结构体变量,虽然两个结构体的成员相同,但是也视为是两种不同的结构体。
结构体的自引用
是否可以在结构体中包含一个该结构体类型的成员呢:
//代码1
struct Node
{int data;struct Node next;//结构体成员的类型和结构体相同
};
//上面的写法是不可行的,如果可以,sizeof(struct Node)求不出来//下面是正确的方法,链表的节点就是这样的结构体
//代码2
struct Node
{int data;struct Node* next;//这里存放指向下一个节点的地址
};
结构体内存对齐
结构体是不同类型的元素的集合,那么不同结构体的大小怎么计算呢,这就涉及到结构体的内存对齐,下面是结构体内存对齐的规则:
①第一个成员在与结构体变量偏移量为0的地址处,也就是结构体的起始位置。
②其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。
对齐数=编译器默认的一个对齐数 与 该成员大小的较小值。
vs中默认对齐数为8,Linux中没有默认对齐数,对齐数就是成员自身的大小
③结构体总大小为最大对齐数(每个成员变量都有一个对齐数)的整数倍。
④如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。
下面举个例子说明一下:
c1的大小是1,默认对齐数是8,所以根据规则2,c1的对齐数是1,对齐到0的地址处是可以的。
i的大小是4,默认对齐数是8,所以根据规则2,i的对齐数是4,因为c1只占了1个字节,i又要对其到4的整数倍,所以需要浪费三个字节,i从偏移量4的位置开始存储。
c2同c1,对齐数是1,i占四个字节,c2从偏移量8的位置开始是可以的。
又因为规则3,总大小应该是最大对齐数的整数倍,c1和c2对齐数是1,i的对齐数是4,所以最大对齐数是4,目前的总大小是9,因此总大小应该增加到4的整数倍,也就是12,c2后面要再浪费三个字节的空间。
根据上面的例子可以得出下面的结论:
在设计结构体类型时,尽量让占用空间小的成员集中在一起。
默认对齐数可以自行修改:
#pragma pack(8)//设置默认对齐数为8
struct S1
{char c1;int i;char c2;
};
#pragma pack()//取消设置的默认对齐数,还原为默认