首页 > 编程笔记

C语言结构体指针的定义、赋值和使用

指针变量非常灵活方便,可以指向任一类型的变量。

我们知道基本数据类型的指针,如整型指针指向一个整型变量,字符指针指向一个字符型的变量。同样,也可以定义一个结构体类型的指针,让它指向结构体类型的变量。相应地,该结构体变量的指针就是该变量所占内存空间的首地址。

本文将介绍利用结构体指针处理结构体数据的方法。

C语言结构体指针

当用一个指针变量指向一个结构体变量时,该指针被称为结构体指针。通过结构体指针可访问该结构体变量、初始化结构体成员变量。下面详细介绍C语言结构体指针的使用方法。

C语言结构体指针的定义

和其他的指针变量一样,结构体指针在使用前必须先定义,并且要初始化后才能指向一个具体的结构体数据。

定义结构体指针变量的一般形式如下。

struct 结构体名 *指针变量名;

例如:struct student*p,stu;。

其中,struct student 是一个已经定义过的结构体类型,这里定义的指针变量 p 是 struct student 结构体类型的指针变量,它可以指向一个 struct student 结构体类型的变量,例如 p=&stu。

定义结构体类型的指针也有 3 种方法,和定义结构体类型的变量和数组基本一致。

C语言结构体指针的初始化

结构体指针变量在使用前必须进行初始化,其初始化的方式与基本数据类型指针变量的初始化相同,在定义的同时赋予其一个结构体变量的首地址,即让结构体指针指向一个确定的地址值。例如:

struct student
{
   char name[10];
   char sex;
   struct date birthday;
   int age;
   float score;
}stu,*p=&stu;

这里定义了一个结构体类型的变量 stu 和一个结构体类型的指针变量 p,定义的时候编译系统会为 stu 分配该结构体类型所占字节数大小的存储空间,通过 “*p=&stu” 使指针变量 p 指向结构体变量 stu 存储区域的首地址。

这样,指针变量 p 就有了确定的值,即结构体变量 stu 的首地址,以后就可以通过 p 对 stu 进行操作。

使用C语言指针访问结构体成员

定义并初始化结构体指针变量后,通过指针变量可以访问它所指向的结构体变量的任何一个成员。例如下面的代码。

struct
{
   int a;
   char b;
}m, *p;
p=&m;

在这里,p 是指向结构体变量 m 的结构体指针,使用指针 p 访问变量 m 中的成员有以下 3 种方法。

注意:由于运算符 “。” 的优先级高于 “*”,因此必须使用圆括号把 *p 括起来,即把 (*p) 作为一个整体。

结构体指针在程序中使用得很频繁,为了简化引用形式,C语言提供了结构成员运算符 “->”,利用它可以简化用指针引用结构成员的形式。并且,结构成员运算符“->” 和 “。” 的优先级相同,在C语言中属于高级运算符。

【示例1】利用结构体指针访问结构体变量的成员。代码如下:
#include "stdio.h"
int main ()
{
    struct ucode  /*声明结构体类型*/
    {
        char u1;
        int u2; 
    }a={'c',89},*p=&a;   /*声明结构体类型指针变量p并初始化*/
    printf("%c %d\n",(*p).u1,(*p).u2); /*输出结构体成员变量a的值*/
}
运行结果:

c 89

本例中,在声明结构体指针变量 p 时对它进行了初始化,使其指向结构体类型的变量 a,初始化后,就可以通过结构体指针 p 来对变量 a 中的成员进行引用。

其中,(*p).u1 等价于 p->u1,也等价于 a.u1;(*p).u2 等价于 p->u2,也等价于 a.u2。 因此,对第 9 行代码也可以修改如下。

printf("%c %d\n",p->u1,p->u2);    或    printf("%c %d\n",a.u1,a.u2);

虽书写形式不同,但功能是完全一样的。

C语言给结构体指针赋值

我们借助下面的一段代码来了解结构体指针的赋值方式:
struct ucode
{
  char u1;
   int u2; 
};
void main ()
{
   struct ucode a,*p;
   p=&a;
   p->u1='c';
   p->u2=89;
   printf("%c %d\n",a.u1,a.u2);
}
上面代码的输出结果和【示例1】的结果一样。

【示例2】指针变量自身的运算。代码如下:
#include <stdio.h>
#include <string.h>
int main()
{
    struct student
    {
        long num;
        char name[20];
        float score;
    };
    struct student stu_1;
    struct student *p;
    p=&stu_1;
    stu_1.num=89101;
    strcpy(stu_1.name,"LiLin");
    stu_1.score=89.5;
    printf("No.:%ld\nname:%s\nscore:%.2f\n",stu_1.num,stu_1.name,stu_1.score);
    printf("No.:%ld\nname:%s\nscore:%.2f\n",( *p).num,( *p).name,( *p).score);
}
运行结果:

No.:89101
name:LiLin
score:89.50
No.:89101
name:LiLin
score:89.50

示例分析:

在主函数中声明了 struct student 类型,然后定义一个 struct student 类型的变量 stu_1。同时又定义了一个指针变量 p,它指向一个 struct student 类型的数据。

在函数的执行部分将结构体变量 stu_1 的起始地址赋给指针变量 p,也就是使 p 指向 stu_1,然后对 stu_1 的各成员赋值。

第 1 个 printf() 函数的功能是输出 stu_1 各成员的值。用 stu_1.num 表示 stu_1 中的成员 num,依此类推。第 2 个 printf() 函数也用来输出 stu_1 各成员的值,但使用的是 (*p).num 这样的形式。

(*p) 表示 p 指向的结构体变量,(*p).num 是 p 指向的结构体变量中的成员 num。

注意,*p 两侧的括号不可省略,因为成员运算符 “.” 优先于 “*” 运算符,*p.num 就等价于 *(p.num)。括号不可省略,因为成员运算符 “.” 优先于 “*” 运算符,*p.num 就等价于 *(p.num)。

C语言指向结构体数组的指针

结构体变量指针还可以用来指向一个结构体数组。此时,指向结构体数组的结构体指针变量加1的结果是指向结构体数组的下一个元素,那么结构体指针变量地址值的增量大小就是 “sizeof(结构体类型)” 的字节数。

例如,有以下代码:

struct ucode
{
    char u1;
    int u2;
} tt[4],*p=tt;

这里定义了一个结构体类型的指针 p,指向结构体数组 tt 的首地址,即初始时指向数组的第 1 个元素,那么 (*p).u1 等价于 tt[0].u1,(*p).u2 等价于 tt[0].u2。

如果对 p 进行加 1 运算,则指针变量 p 指向数组的第 2 个元素,即 tt[1],此时 (*p).u1 等价于 tt[1].u1,(*p).u2 等价于 tt[1].u2。

总之,指向结构体类型数组的结构体指针变量使用起来并不复杂,但要注意区分以下情况:

p->u1++      /*等价于 (p->u1)++,先取成员 u1 的值,再使 u1 自增1 */
++p->u1      /*等价于 ++(p->u1),先对成员 u1 进行自增 1,再取 u1 的值 */
(p++)->u1      /*等价于先取成员 u1 的值,用完后再使指针 p 加 1*/
(++p)->u1     /*等价于先使指针 p 加 1,然后再取成员 u1 的值 */


【示例3】指向结构体数组的指针的应用。代码如下:
#include "stdio.h"
int main()
{
    struct ucode
    {
        char u1;
        int u2;
    }tt[4]={{'a',97},{'b',98},{'c',99},{'d',100}}; /*声明结构体类型的数组并初始化*/
    struct ucode *p=tt;
    printf("%c %d\n",p->u1,p->u2);      /*输出语句*/
    printf("%c\n",(p++)->u1);            /*输出语句*/
    printf("%c %d\n",p->u1, p->u2++);       /*输出语句*/
    printf("%d\n",p->u2);               /*输出语句*/
    printf("%c %d\n",(++p)->u1,p->u2);  /*输出语句*/
    p++;                             /*结构体指针变量增1*/
    printf("%c %d\n",++p->u1,p->u2);      /*输出语句*/
}
运行结果:

a 97
a
b 98
99
c 99
e 100

示例分析:

C语言结构体指针作为函数参数

运用指向结构体类型的指针变量作为函数的参数,将主调函数的结构体变量的指针(实参)传递给被调函数的结构体指针(形参),利用作为形参的结构体指针来操作主调函数中的结构体变量及其成员,达到数据传递的目的。

【示例4】利用结构体指针变量作函数的参数的传址调用方式计算三角形的面积。代码如下:
#include "math.h"
#include "stdio.h"
struct triangle
{
    float a;
    float b;
    float c;
};
/*自定义函数,利用结构体指针作参数求三角形的面积*/
float area(struct triangle *p)
{
    float l,s;
    l=(p->a+p->b+p->c)/2;            /*计算三角形的半周长*/
    s=sqrt(l*(l-p->a)*(l-p->b)*(l-p->c));    /*计算三角形的面积*/
    return s;
}
/*程序入口*/
int main()
{
    float s;
    struct triangle side;
    printf("输入三角形的3条边长:\n");   /*提示信息*/
    scanf("%f %f %f",&side.a,&side.b,&side.c);   /*从键盘输入三角形的3条边长*/
    s=area(&side);                      /*调用自定义area()函数求三角形的面积*/
    printf("面积是:%f\n",s);
}
运行结果:

输入三角形的3条边长:
7 8 9
面积是:26.832815

本程序中,自定义函数的形参用的是结构体类型的指针变量。

函数调用时,在主调函数中,通过语句 “s=area(&side)” 把结构体变量 side 的地址值传递给形参 p,由指针变量 p 操作结构体变量 side 中的成员,在自定义函数中计算出三角形的面积,返回主调函数中输出。

本例中由结构体指针变量作为函数的形参来进行参数传递,实质是把实参的地址值传递给形参,这是一种传址的参数传递方式。

C语言用结构体指针作函数参数,这种方式比用结构体变量作函数参数的效率高,因为无须传递各个成员的值,只需传递一个地址,且函数中的结构体成员并不占据新的内存单元,与主调函数中的成员共享存储单元。这种方式还可通过修改形参所指的成员影响实参所对应的成员值。

高手点拨

指向结构体变量的指针是指向结构体变量的,即结构体变量的首地址。

结构体类型的指针在初始化时只能指向一个结构体类型的变量。

结构类型的数据往往由许多成员组成,结构体指针实际指向结构体变量中的第一个成员。

当指向成员时,用 (*p).name 或 p->name,不能用 p.name 表示,此方式是变量的成员表示。

优秀文章