住房和城乡规划建设局官方网站,餐饮小程序开发,网页快照,苏州网站建设完整⾃定义类型#xff1a;结构体 结构体的概念
结构是⼀些值的集合#xff0c;这些值称为成员变量。结构的每个成员可以是不同类型的变量。
结构体是一个种自定义的数据类型#xff0c;它可以由很多个默认数据类型组成。它主要用于描述复杂场景下的变量。 例如#xff0c;想…⾃定义类型结构体 结构体的概念
结构是⼀些值的集合这些值称为成员变量。结构的每个成员可以是不同类型的变量。
结构体是一个种自定义的数据类型它可以由很多个默认数据类型组成。它主要用于描述复杂场景下的变量。 例如想通过结构体描述一个学生
struct Stu
{char name[20];//名字int age;//年龄char sex[5];//性别char id[20];//学号
}; //分号不能丢
结构体的形式:
struct 结构体名
{数据类型1 成员变量1数据类型2 成员变量2..........
}; 特殊的声明
在声明结构的时候可以不完全的声明。
//匿名结构体类型
struct
{int a;char b;float c;
}x;
struct
{int a;char b;float c;
}a[20], *p;
上⾯的两个结构在声明的时候省略掉了结构体标签tag。
那么问题来了 //在上⾯代码的基础上下⾯的代码合法吗 p x;
警告 编译器会把上⾯的两个声明当成完全不同的两个类型所以是⾮法的。
匿名的结构体类型如果没有对结构体类型重命名的话基本上只能使⽤⼀次
结构的⾃引⽤ 在结构中包含⼀个类型为该结构本⾝的成员是否可以呢 ⽐如定义⼀个链表的节点
struct Node
{int data;struct Node next;
}; 上述代码正确吗
如果正确那 sizeof(struct Node) 是多少
仔细分析其实是不⾏的因为⼀个结构体中再包含⼀个同类型的结构体变量这样结构体变量的⼤ ⼩就会⽆穷的⼤是不合理的。 正确的⾃引⽤⽅式
struct Node
{int data;struct Node* next;
};在结构体⾃引⽤使⽤的过程中夹杂了typedef对匿名结构体类型重命名也容易引⼊问题看看下⾯ 的代码可⾏吗
typedef struct
{int data;Node* next;
}Node;答案是不⾏的
因为Node是对前⾯的匿名结构体类型的重命名产⽣的但是在匿名结构体内部提前使 ⽤Node类型来创建成员变量这是不⾏的。 解决⽅案如下定义结构体不要使⽤匿名结构体了
typedef struct Node
{int data;struct Node* next;
}Node;
结构体变量的创建和初始化
创建的两种方式
struct Stu
{char name[20];//名字int age;//年龄float score;//成绩
}s1,s2,s3;
struct Stu
{char name[20];//名字int age;//年龄float score;//成绩
};
int main()
{struct Str s1;return 0;
} 初始化
struct Stu
{char name[20];//名字int age;//年龄float score;//成绩
};
int main()
{struct Stu s1 { zhangsan,20, 98.5f };struct Stu s2 { lisi,33, 68.5f};struct Stu s3 { wangwu,24, 98.0f };struct Stu s4 { .age 22,.name cuihua, .score 55.5f };printf(%s %d %f\n, s1.name, s1.age, s1.score);printf(%s %d %f\n, s4.name, s4.age, s4.score);return 0;
}结构成员访问操作符
结构成员访问操作符有两个
⼀个是 . ⼀个是 - . 形式如下
结构体变量.成员变量名
结构体指针—成员变量名
例如
#include stdio.h
#include string.h
struct Stu
{char name[15];//名字int age; //年龄
};
void print_stu(struct Stu s)
{printf(%s %d\n, s.name, s.age);
}
void set_stu(struct Stu* ps)
{strcpy(ps-name, 李四);ps-age 28;
}
int main()
{struct Stu s { 张三, 20 };print_stu(s);set_stu(s);print_stu(s);return 0;
}结构体内存对⻬
⾸先得掌握结构体的对⻬规则
1. 结构体的第⼀个成员对⻬到相对结构体变量起始位置偏移量为0的地址处
2. 其他成员变量要对⻬到某个数字对⻬数的整数倍的地址处。 对⻬数 编译器默认的⼀个对⻬数 与 该成员变量⼤⼩的较⼩值。 - VS中默认的值为8 - Linux中没有默认对⻬数对⻬数就是成员⾃⾝的⼤⼩
3. 结构体总⼤⼩为最⼤对⻬数结构体中每个成员变量都有⼀个对⻬数所有对⻬数中最⼤的的 整数倍。
4. 如果嵌套了结构体的情况嵌套的结构体成员对⻬到⾃⼰的成员中最⼤对⻬数的整数倍处结构 体的整体⼤⼩就是所有最⼤对⻬数含嵌套结构体中成员的对⻬数的整数倍。
//练习1
struct S1
{char c1;int i;char c2;
};
printf(%d\n, sizeof(struct S1)); 分析一下S1所占大小。 第一个成员变量大小是 1默认对齐数是4 那么它的对齐数就是1。但依据上面的规则c1是起始位置偏差为0的地方开始那么c1占据的就是起始位置占据1个字节 第二个成员变量大小是1默认对齐数是4 那么它的对齐数也是1。依据上面的规则c2是起始位置偏差为1的整倍数的位置但任何数字都是1的整倍数所以c2占据的是偏差值1的地址处占据1个字节 第三个成员变量大小是4默认的整倍数的位置就也是偏差值0、4、8、12… 这些。所以a的起始位置是偏差值为4的位置它占据了4个字节。对齐数是4 那么它的对齐数也是4。依据上面的规则a的起始位置必须是偏差值为4 注意这里偏差值为2和偏差值为3的位置是空的所以S1占了8个字节。下图中黄色为c1蓝色c2红色c3。旁边的数字为当前地址与起始地址的偏差值。 //练习3
struct S3
{double d;char c;int i;
};
printf(%d\n, sizeof(struct S3)); 得知S3的大小是16个字节。最大对齐数是8。那么S4在内存里就是这样的
struct S4
{char c1;struct S3 s3;double d;
};
printf(%d\n, sizeof(struct S4)); S4大小是32个字节。
为什么存在内存对⻬?
1. 平台原因(移植原因) 不是所有的硬件平台都能访问任意地址上的任意数据的某些硬件平台只能在某些地址处取某些特定 类型的数据否则抛出硬件异常。
2. 性能原因 数据结构(尤其是栈)应该尽可能地在⾃然边界上对⻬。原因在于为了访问未对⻬的内存处理器需要 作两次内存访问⽽对⻬的内存访问仅需要⼀次访问。假设⼀个处理器总是从内存中取8个字节则地 址必须是8的倍数。如果我们能保证将所有的double类型的数据的地址都对⻬成8的倍数那么就可以 ⽤⼀个内存操作来读或者写值了。否则我们可能需要执⾏两次内存访问因为对象可能被分放在两 个8字节内存块中。
总体来说结构体的内存对⻬是拿空间来换取时间的做法。
让占⽤空间⼩的成员尽量集中在⼀起
修改默认对⻬数修改后可以不考虑内存对齐
#include stdio.h
#pragma pack(1)//设置默认对⻬数为1
struct S
{char c1;int i;char c2;
};
#pragma pack()//取消设置的默认对⻬数还原为默认
int main()
{//输出的结果是什么printf(%d\n, sizeof(struct 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;
}上⾯的 print1 和 print2 函数哪个好些
答案是⾸选print2函数。
原因 函数传参的时候参数是需要压栈会有时间和空间上的系统开销。 如果传递⼀个结构体对象的时候结构体过⼤参数压栈的的系统开销⽐较⼤所以会导致性能的下 降。 结论 结构体传参的时候要传结构体的地址
结构体类型变量需要访问其成员 B应该为(*p).a