示例结构体:
1 2 3 4
| struct student{ int id; char name[50]; };
|
一、正常定义,正常访问
1.1 示例代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| #include <stdio.h> #include <string.h> #include <stdlib.h>
typedef struct student{ int id; char name[50]; }stu;
int main() { stu normal; normal.id = 114514; strcpy_s(normal.name,strlen("homo") + 1,"homo"); printf("%d\n",normal.id); puts(normal.name); return 0; }
|
1.2 原理分析
正常定义结构体,系统自动在内存中开辟一块内存空间,访问结构体名称即访问该内存空间。访问该结构体时,将结构体与该结构体内的成员用 . 连接,例如
表示的是访问normal结构体里的id成员。这是结构体变量的正常访问方法。
二、正常定义,指向访问
2.1 示例代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| #include <stdio.h> #include <string.h> #include <stdlib.h>
typedef struct student{ int id; char name[50]; }stu;
int main() { stu normal; stu * point = &normal; point -> id = 114514; strcpy_s(point -> name,strlen("homo") + 1,"homo"); printf("%d\n",point -> id); puts(point -> name); return 0; }
|
2.2 原理分析
指向符号 -> 和成员符号 . 的区别在于,指向符号指向的是指向该成员内存地址的指针,成员符号直接指向该成员。因此,采用指向型访问前一定要创建一个指针类型的结构体变量指向原结构体的地址。
这样,结构体指针本身及其成员就成为一个指向原结构体及其成员的指针变量。之后就是符号替换即可。
三、正常定义,指针访问
3.1 示例代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| #include <stdio.h> #include <stdlib.h> #include <string.h>
typedef struct student{ int id; char name[50]; }stu;
int main() { stu normal; stu * point = &normal; *((int*)point) = 114514; strcpy_s((char*)point + sizeof(int),strlen("homo") + 1,"homo"); printf("%d %s\n",*((int*)point), (char*)point + sizeof(int)); return 0; }
|
3.2 原理分析
在正常定义,指针访问的这个例子中,同样需要创建一个结构体指针。但是,与上面采用指向符号访问不同,这里是与普通变量一样的指针访问。采用纯指针访问结构体需要知道基本变量类型在定义结构体之后内存的存储方式。
基本变量类型 |
内存占用(windows) |
(unsigned char) char |
1 |
void |
1 |
(unsigned short) short |
2 |
(unsigned int) int |
4 |
(unsigned long) long |
4 |
float |
4 |
long long |
8 |
double |
8 |
long double |
8 |
指针变量(int*,char*,long*……) |
8 |
在程序中使用sizeof而不是用数字能避免平台间变量占用内存不一致的问题。
在结构体中,变量占用的内存空间是以其中占用内存最大的变量的内存占用确定的。假设一个结构体内有三个变量,定义顺序是int,char,double,那么它所占的内存空间大小为16,这个16是怎么分配的呢?正常来说,int占用4,char占用1,double占用8,分开定义占用内存大小为13,而在结构体中,不是这样。看看下表就明白了。
变量类型 |
占用内存大小 |
int |
4 |
char |
1 |
空 |
1 |
空 |
1 |
空 |
1 |
double |
8 |
可以看到,有三个内存空间被浪费了。为了占满这废弃的三个内存空间,定义char的时候可以采用char a[4],将char的占用内存空间达到4个。这就是结构体中数据的对齐原则。结构体总占用空间一定是占用内存空间最大的数据类型大小的n倍,其中的空隙根据定义顺序填充。例如,同样是四个变量的结构体,成员定义顺序不同,占用的内存空间不同:
1 2 3 4 5 6 7 8 9 10 11 12 13
| struct a{ int s; char d[2]; short l; double x; };
struct b{ int s; char d[2]; double x; short l; };
|
知道结构体在内存中的存储方式后,通过*struct访问到结构体头指针,即指向结构体内第一个元素的指针,此时即可直接访问到结构体中第一个元素的值。如果要访问下一个元素,需要对结构体指针的类型进行强制类型转换,否则进行运算后,该指针指向的内存空间便会越过该结构体。进行强制类型转换的规则是,要取什么类型的成员值就转成什么类型,然后根据该类型值在结构体中的位置以及前面所占的内存空间进行加减即可。如我们要取结构体a中l的值,示例代码如下:
1 2 3 4 5 6 7 8 9 10
| typedef struct a{ int s; char d[2]; short l; double x; }sa;
sa a1; sa * a2 = &a1; short alv = *((short*)a2 + 3);
|
四、指针定义,手动分配内存,指向访问
4.1 示例代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| #include <stdio.h> #include <string.h> #include <stdlib.h>
typedef struct student{ int id; char name[50]; }stu;
int main() { stu * point = malloc(sizeof(stu)); point -> id = 114514; strcpy_s(point -> name,strlen("homo") + 1,"homo"); printf("%d\n",point -> id); puts(point -> name); free(point); return 0; }
|
4.2 原理分析
同2.2
五、指针定义,手动分配内存,正常访问
5.1 示例代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| #include <stdio.h> #include <string.h> #include <stdlib.h>
typedef struct student{ int id; char name[50]; }stu;
int main() { stu * point = malloc(sizeof(stu)); (*point).id = 114514; strcpy_s((*point).name,strlen("homo") + 1,"homo"); printf("%d\n",(*point).id); puts((*point).name); free(point); return 0; }
|
5.2 原理分析
同1.2
六、指针定义,手动分配内存,指针访问
6.1 示例代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| #include <stdio.h> #include <stdlib.h> #include <string.h>
typedef struct student{ int id; char name[50]; }stu;
int main() { stu * point = malloc(sizeof(stu)); *((int*)point) = 114514; strcpy_s((char*)point + sizeof(int),strlen("homo") + 1,"homo"); printf("%d %s\n",*((int*)point), (char*)point + sizeof(int)); free(point); return 0; }
|
6.2 原理分析
同3.2