本文是读完《c和指针》后记录的一些关于结构体和指针中值得注意的细节,如有错误,还望指正;你还可以阅读指针,指针与数组,指针与函数了解更多关于指针的内容
结构体中的标签
首先我们来看一下不使用标签的一个示例:
struct {
int a;
float b;
char c;
}x1;
struct {
int a;
float b;
char c;
}x2, *x3;
int main(void)
{
x3 = &x1;
printf("%c\n",x3->c);
return 0;
}
编译结果:显示指针类型赋值错误
将主函数修改为:
int main(void)
{
x3 = &x2;
printf("%c\n",x3->c);
}
编译结果:
- 原因 : 变量 x1 和 x2 被编译器当成了两个不同类型的结构体,即使成员列表完全相同
然后我们将上面的结构体加上标签:
struct test{
int a;
float b;
char c;
};
struct test x1={1,1.0,'c'};
struct test x2={2,2.0,'b'},*x3;
int main(void)
{
x3 = &x1;
printf("%c\n",x3->c);
}
编译结果:
以上说明标签字段允许为成员列表提供一个名字,他允许多个声明使用同一个成员列表,并创建同一种类型的结构
结构体类型定义
以上结构体的定义和变量的定义方法还有以下几种:
typedef struct test{
int a;
float b;
char c;
}tmp;
tmp x1={1,1.0,'c'};
tmp x2={2,2.0,'b'},*x3;
typedef struct {
int a;
float b;
char c;
}tmp;
tmp x1={1,1.0,'c'};
tmp x2={2,2.0,'b'},*x3;
这里要强调的一点是:在使用typedef为结构创建类型名时,该类型名在结构体成员变量之后创建;如下陷阱:
typedef struct{
int a;
tmp *b;
}tmp;
这里创建了一个成员变量b,指向结构体tmp,但是在声明b时,tmp还未定义,故表述非法
利用标签可解决上述问题:
typedef struct test{
int a;
struct test *b;
}tmp;
在一个结构体内部不能包含类型为该结构体本身的成员,如下:
struct test1{
int a;
struct test1 b;
struct test1 *c;
};
- 因 b 的类型与它所处结构体类型一致,故这种表述是错误的
- c为指针类型,故这种表述是合法的 b
在结构体类型定义前,可以先进行不完整定义,如下示例:
struct B;
struct A{
struct B *p;
};
struct B{
struct A *p;
};
优先级说明
在结构体中 ->符 享有优先权
(->操作符) > (点操作符 . ) > (间接访问操作符 * )
(->操作符) > (取地址符 & )
- 在
void func(struct test *p);
内你需要这个括号:(*p).a,这等价于 p->a - *p->a.b 相当于 *((p->a).b)
- &p->a 相当于 &(p->a)
结构体的指针运算
结构体作为函数参数
结构体传参时一般都使用指针传参
- 原因: 普通传参(传递结构)需要对数据进行拷贝,当结构体成员很多时,需要拷贝的量非常大,效率低,占用堆栈空间
- 改进:
- 为提高速度:可声明为 register
- 为防止修改:可声明为 const
结构体位段的使用
- 位段成员必须声明为int型 (int, unsigned int, signed int),最好显式声明为unsigned int 或 signed int
- 因在不同系统中位段的结果可能不同,故应尽量不免使用位段,提升代码可移植性
struct test{
int a:7;
int b:6;
int c:19;
}x;
int main(void)
{
printf("%d\n",sizeof(x));
}
结果为:
该结构可处理128个字符,64种字体,0-524287个单位长度
通过指针常量访问结构体
struct test{
int a:7;
int b:6;
int c:19;
};
#define A ((struct test *)0x0060fefc)
int main(void)
{
A->a = 42;
printf("%d\n",A->a);
}
输出为:
联合
- 使用情况:不同时刻,将不同的东西存放在同一内存位置
- 联合的所有成员引用内存种的相同位置
- 若联合体内各成员的长度不一致,分配给联合的内存数量取决于最长成员的长度
- 对于第三点会有一个问题:当成员大小相差悬殊,当存储较小成员时,则浪费空间,解决办法是存储指向不同成员的指针
- 联合变量被初始化时,初始化值必须是联合第一个成员的类型,位于花括号中
union{
float i;
int j;
}test={3.14};
int main(void)
{
float reference = 3.14f;
printf("%d\n",(int)reference);
printf("%d\n",test.j);
}
结果为: