
数据结构基础--指针,结构体,typedef,形参实参详解、区分
在学习数据结构线性表的时候,遇到了一些问题,像指针,结构体,typedef,形参实参等较为抽象的概念,以及它们之间的联系,在此整理记录一下。目录一、实参与形参(一)定义(二)二者的区别与联系:(三)易混点:形参和实参的三种传递方式:1-值传递2-址传递3-引用(重)(四)形参、实参的传递方式的区别:二、结构体(一)、定义结构体1.基础知识2.结构体的嵌套3.结构体变量使用的注意事项(二)、结构体的
在学习数据结构线性表的时候,遇到了一些问题,像指针,结构体,typedef,形参实参等较为抽象的概念,以及它们之间的联系,在此整理记录一下。
目录
一、实参与形参
(一)定义
形参(形式参数)
在函数定义中出现的参数可以看做是一个占位符,它没有数据,只能等到函数被调用时接收传递进来的数据,所以称为形式参数,简称形参。
实参(实际参数)
函数被调用时给出的参数包含了实实在在的数据,会被函数内部的代码使用,所以称为实际参数,简称实参。
形参和实参的功能是传递数据,发生函数调用时,实参的值会传递给形参。
(二)二者的区别与联系:
1) 形参变量只有在函数被调用时才会分配内存,调用结束后,立刻释放内存,所以形参变量只有在函数内部有效,不能在函数外部使用。
2) 实参可以是常量、变量、表达式、函数等,无论实参是何种类型的数据,在进行函数调用时,它们都必须有确定的值,以便把这些值传送给形参,所以应该提前用赋值、输入等办法使实参获得确定值。
3) 实参和形参在数量上、类型上、顺序上必须严格一致,否则会发生“类型不匹配”的错误。当然,如果能够进行自动类型转换,或者进行了强制类型转换,那么实参类型也可以不同于形参类型。
4) 函数调用中发生的数据传递是单向的,只能把实参的值传递给形参,而不能把形参的值反向地传递给实参;换句话说,一旦完成数据的传递,实参和形参就再也没有瓜葛了,所以,在函数调用过程中,形参的值发生改变并不会影响实参。
(三)易混点:形参和实参的三种传递方式:
1-值传递
只是传递了变量的值,但是不能对变量的值做更改。
#include <stdio.h>
int i = 3;
void test(int j)
{
j = 20;
//形参变量只有在函数被调用时才会分配内存,调用结束后,立刻释放内存,
//所以形参变量只有在函数内部有效,不能在函数外部使用。
printf("j = %d\n",j);
printf("数值传递之后,i = %d\n",i);
}
void main()
{
printf("i = %d\n",i);
test(i);
}
运行结果如下:
因为在int j=i 时,这是一个开辟一个新内存,然后将i中的值复制到j中的过程。所以,j中的值改变不会引起i中的值的改变。也就是说 形参的值发生改变并不会影响实参。
2-址传递
传递了变量的地址,可以对变量的值做修改。
#include <stdio.h>
int i = 3;
void test(int *j)
{
*j = 10;
printf("j = %d\n",*j);
printf("数值传递之后,i = %d\n",i);
}
void main()
{
printf("i = %d\n",i);
test(&i);
}
运行结果如下:
主函数内传来的是I的地址,所以形参也要是个地址, int *j,j就是int型的指针,第一句话中, *j代表的是指针指向的整型量的值 。在 int *j=&i 时,这是一个开辟一个新内存,然后将i的地址的值传入到j中的过程。所以,*j =10就是访问j中地址(就是i的地址),将10赋值于其中,改变值(等于i=10)。
3-引用(重)
这是一种C++里面的使用方法,在数据结构中使用的还非常普遍。这对于我们刚刚学完C语言就学数据结构的小白来说不太友好,而这也是困扰我好几天的地方。用C语言的知识怎么想也想不通。
定义引用的表示方法与定义C语言的指针相似,只是用&代替了*。
int a=2,&ra=a; |
a为目标原名称,ra为目标引用名。给ra赋值:ra=1; 等价于 a=1;
int a = 0;
int &b = a;
b就是a的引用,a和b指向的内存是同一个地址,b可以修改变量的值。
引用必须在创建时被初始化,一旦引用被初始化为一个对象,就不能被指向到另一个对象。
int a = 10;
int &b;
b = a. 这样是不对的
int *c;
c = &a; 这样是可以的
引用在参数传递时的使用(重):
与址传递一样,也可以对变量的值做修改。
主函数: int a = 100,b = 200;
swap(a, b); 把两个变量a,b传递过去
子函数头:void swap(int& x, int& y)
表示 定义两个int型引用x和y,接收的是a与b的地址。
x是a的引用,y是b的引用;
a和x指向的内存是同一个地址,b和y指向的内存是同一个地址;
a可以修改变量x的值,b可以修改变量y的值。
#include <stdio.h>
void swap(int& x, int& y);
int main ()
{
int a = 100,b = 200;
printf("交换前,a的值:%d\n交换前,b的值:%d\n\n",a,b);
swap(a, b);
printf("交换后,a的值:%d\n交换后,b的值:%d\n",a,b);
}
void swap(int& x, int& y)
{
int temp;
temp = x; /* 保存地址 x 的值 */
x = y; /* 把 y 赋值给 x */
y = temp; /* 把 x 赋值给 y */
}
输出结果如下:
(四)形参、实参的传递方式的区别:
对于函数要改变变量的值的情况下,函数进行值传递是不能改变的,只是创建了一个临时变量,拷贝了原函数的变量值,并不能改变原函数变量的值(但是如果要传入的数据类型比较大的话,是很浪费内存的,因为内存要创建一个临时变量进行对原变量进行拷贝)。可以使用指针和引用进行改变。
二、结构体
概述:需要将不同类型的数据组合成一个整体,以便于引用。
(一)、定义结构体
1.基础知识
定义结构的一般形式:
struct 结构名
{
成员列表
}
成员列表由若干个成员组成,每个成员都是该结构的一个组成部分。对每个成员也必须做类型说明,其形式为:类型说明符 成员名;例如:
struct student
{
Int num; char name[20]; char sex; int age; float score;
}
定义结构体类型变量的方法:先声明结构体,再定义变量名
例如:
2.结构体的嵌套
首先定义一个结构date,由month、day、year三个成员组成。在定义并说明变量boy1和boy2时,其中的成员birthday被说明为date结构类型。成员名可与程序中其他变量同名,互不干扰。
struct date
{
int month;
int day;
int year;
};
struct
{
int num;
char name[20];
char sex;
struct date birthday;
float score;
}boy1,boy2;
3.结构体变量使用的注意事项
(1)不能将结构体变量作为一个整体进行输入和输出。其正确应用方式为:结构体变量名.成员名 student 1.num表示student1变量中的num成员,即student1的num(学号)项。可以对变量的成员赋值,例如:student1.num=100; 注意:“.”是成员运算符,优先级最高,所以可以把student1.num可以看成一个整体。
(2)两个结构体变量之间的赋值可以把一个结构体变量的所有值都赋给另一个变量。
#include <stdio.h>
void main()
{
struct student
{
int num;
char *name;
char sex;
float score;
}boy1,boy2;
boy1.num= 007;
boy1.name="Jane";
printf("Please input boy1's sex and score\n");
scanf("%c %f",&boy1.sex,&boy1.score);
boy2=boy1;//赋值:将boy1的全体成员给了boy2
printf("boy2's num=%d\nname=%s\n",boy2.num,boy2.name);
printf("sex=%c\nscore=%f\n",boy2.sex,boy2.score);
}
运行结果如下:
(3)如果成员本身又属一个结构体类型,则要用若干个成员运算符,一级一级地找到最低的一级的成员。只能对最低级的成员进行赋值或存取以及运算。
对上面定义的结构体变量student1,可以这样访问各成员:
student1. num
student1.birthday.month
(4)对结构体变量的成员可以像普通变量一样进行各种运算(根据其类型决定可以进行的运算)。
例如 :
student2.score = student1.score;
sum = student1.score + student2. score;
student1.age++;
++student2.age;
(5)可以引用结构体变量成员的地址,也可以引用结构体变量的地址。
#include <stdio.h>
void main()
{
struct student
{
int num;
char *name;
char sex;
float score;
}boy1,boy2;
boy1.num= 007;
boy1.name="Jane";
printf("the adress of struct is %o:\n",&boy1);
printf("the adress of num is %o:\n",&boy1.num);
}
运行结果如下:
(二)、结构体的初始化
最简单的初始化方法如下:
struct 结构名
{
成员列表
} 结构体变量名 = {成员列表的各个值,逗号隔开};
或者
struct 结构名
{
成员列表
} 结构体变量名
结构体变量名. 成员 = 初始值;
(三)、结构体数组
一个结构体变量中可以存放一组数据(如一个学生的学号、姓名、成绩等数据)。如果有10个学生的数据需要参加运算,显然应该用数组,这就是结构体数组。
结构体数组与以前介绍过的数值型数组不同之处在于每个数组元素都是一个结构体类型的数据,它们都分别包括各个成员(分量)项。
如何定义:与定义结构体变量的方法相仿,只需说明其为数组即可。例如:
struct student
{
int num;
char name [20];
char sex;
int age;
float score;
char addr[30];
};
struct student student[3];
赋初值:直接跟在后面赋值即可。如下:定义了两个结构体数组,用两个大括号分别给它们赋值,中间用逗号隔开。并且每一个都要包含结构体里面的每一个成员。
struct student
{
int num;
char name[20];
char sex;
int age;
float score;
char addr[30];
}stu[2]={
{101,"LiLin”,'M',18,87.5,"Beijing"} ,
{102,"Zhang",'F',19,99,"Shanghai"}
}
(四)、结构体的指针
1.基础知识
一个结构体变量的指针就是该结构体变量所占据的内存段的起始地址。
可以设一个指针变量,用来指向一个结构体变量,此时该指针变量的值是结构体变量的起始地址。指针变量也可以用来指向结构体数组中的元素。
结构指针变量说明的一般形式为: struct结构名*结构指针变量名
注意:赋值是把结构变量的首地址赋予该指针变量,不能把结构名赋予该指针变量。
例如: struct stu
{ … … } boy;
pstu=&boy是正确的,pstu=&stu是错误的。
有了结构指针变量,就能更方便地访问结构变量的各个成员。
2.结构体变量成员的访问的一般形式为:
这里是我们一开始学结构体最容易绕迷糊,混淆的地方,下面我们就好好的梳理一下。
(*结构指针变量).成员名
或为:
结构指针变量->成员名
或为:
结构体变量. 成员名
例如:(*pstu).num 或者: pstu->num 或者:boy1.num
#include <stdio.h>
struct stu
{
int num;
char *name;
char sex;
float score;
}boy1 = {102,"Fishc",'M',78.5};
void main()
{
struct stu *pstu;//定义了一个结构变量指针
pstu = &boy1; //将 boy1 的初始地址给了pstu
printf("num=%d\nname=%s\n",boy1.num,boy1.name);
printf("sex=%c\nscore=%f\n\n",boy1.sex,boy1.score);
printf("num=%d\nname=%s\n",(*pstu).num,(*pstu).name);
printf("sex=%c\nscore=%f\n\n",(*pstu).sex,(*pstu).score);
printf("num=%d\nname=%s\n",pstu->num,pstu->name);
printf("sex=%c\nscore=%f\n",pstu->sex,pstu->score);
}
结合指针那一章的知识点,我们可以知道,pstu是一个地址,里面存放了boy1的地址;(*pstu)是值,存放着boy1所有成员的值。我们可以简单理解:值.结构体成员等价于地址->结构体成员,等价于结构体变量.结构体成员都代表着该地址下结构体变量的某个成员的值。
3.结构指针变量做参数函数(重)
这里也比较重要,在后面线性表中经常用到
将一个结构体变量的值传递给另一个函数,有3个方法:
(1)用结构体变量的成员作参数
(2)用结构体变量(结构体变量的名称)作实参
#include <stdio.h>
#include <string.h>
struct student
{
int num;
char name[20];
float score[3];
};
void print(struct student stuu);//结构体做形参
//函数声明:由函数返回类型、函数名和形参列表组成。
//形参列表必须包括形参类型,但是不必对形参命名,当然加上参数名(stu也行)
void main()
{
struct student stu;
stu.num = 8;
strcpy(stu.name,"Fishc.com!");
stu.score[0] = 98.5;
stu.score[1] = 99.0;
stu.score[2] = 99.5;
print(stu);//将结构体变量作为实参传递过去
}
void print(struct student stuu)
//函数的定义:开头要把参数(stu)写上
{
printf("\tnum :%d\n",stuu.num);
printf("\tname :%s\n",stuu.name);
printf("\tscore_1 :%5.2f\n",stuu.score[0]);
printf("\tscore_2 :%5.2f\n",stuu.score[1]);
printf("\tscore_3 :%5.2f\n",stuu.score[2]);
printf("\n");
}
(3)用指向结构体变量(或数组)的指针作实参,将结构体变量(或数组)的地址传给形参。
//指向结构体变量的指针作为实参
#include <stdio.h>
#include <string.h>
struct student
{
int num;
char name[20];
float score[3];
};
void print(struct student *p);//结构体指针做形参
void main()
{
struct student stu;
stu.num = 8;
strcpy(stu.name,"Fishc.com!");
stu.score[0] = 98.5;
stu.score[1] = 99.0;
stu.score[2] = 99.5;
print(&stu);//将结构体的地址作为实参传递过去
}
void print(struct student *p)//创建了一个P来接收,P指向的还是上面的结构体
{
printf("\tnum :%d\n",p -> num);
printf("\tname :%s\n",p -> name);
printf("\tscore_1 :%5.2f\n",p -> score[0]);
printf("\tscore_2 :%5.2f\n",p -> score[1]);
printf("\tscore_3 :%5.2f\n",p -> score[2]);
printf("\n");
}
输出结果都如下:
(4)使用C++引用的用法,实参传递的是结构体变量,形参定义了一个引用变量。
#include <stdio.h>
#include <stdlib.h>
#define LIST_INIT_SIZE 100
typedef int T;
typedef struct
{
T *elem; //存储空间基址
int length; //当前长度
int listsize; //当前分配的存储容量
}SqList;
T InitList_Sq(SqList &L)//构造一个空的线性表L
{
int i, j;
L.elem = (T*)malloc(LIST_INIT_SIZE*sizeof(T));
L.length = 0; //空表的长度为0
L.listsize = LIST_INIT_SIZE; //初始化存储容量
scanf("%d",&j);
for (i=0; i<j; i++)
scanf("%d",&L.elem[i]);
L.length = j; // 当前表的长度
}
int main()
{
SqList La;
InitList_Sq(La); //初始化线性表La
printf("%d ",La.elem[2]);
}
运行结果如下:
L就是La的引用,L和La指向的内存是同一个地址,L可以修改变量的值,所以才有了创建线性表下面的操作。
(3)和(4)两种方法均可以改变结构体变量的值,在线性表中常常用到。
三、共用体 Typedef
用typedef声明新的类型名来代替已有的类型名。
例如:声明INTEGER为整型
typedef int INTEGER;
给 int 起个别名叫做 INTEGER,程序中见到INTEGER就代表 int
声明结构类型:
Typedef struct
{
int month;
int day;
int year ;
}DATE;
声明NUM为整型数组类型
关于typedef的一些说明:
用typedef 可以声明各种类型名,但不能用来定义变量。
用typedef 只是对已经存在的类型增加一个类型名,而没有创造新的类型。
当不同源文件中用到同一类型数据时,常用typedef 声明一些数据类型,把它们单独放在一个文件中,然后在需要用到它们的文件中用#include命令把它们包含进来。
使用typedef有利于程序的通用与移植。
typedef与#define区分:
typedef与#define有相以之处,例如:typedef int COUNT;#define COUNT int的作用都是用COUNT代表int。但是,它们二者是不同的#define是在预编译时处理的,它只作简单的字符串替换,而typedef是在编译时处理的。实际上它并不是作简单的字符串替换,而是采用如同定义变量的方法那样来声明一个类型。
四、struct与typedef的结合
这里在线性表用的非常的多,作为初学者,常常因为分不清混淆它们各自的作用, 存在着各种各样的疑惑,我选取了一个典型的例子分析一下。
typedef struct LNode
{
ElemType data;
struct LNode *next;//链表结点指向下一个结点的指针,用来存储下一个结点的指针域。
} LNode,*LinkList;
为什么要用struct LNode * next定义?而不用LNode*next或LinkList next?
定义指针域的时候,LNode和LinkList还不是一种“类型”。
LNode:用typedef给声明的结构体struct LNode起了一个别名:LNode
*LinkList:用typedef给struct LNode{…}*起了一个别名:LinkList(这只是个指针类型,而不是变量),也就是说,为结构体指针定义了一个别名:LinkList
LNode a :声明了一个struct LNode型变量a,相当于struct LNode a;
LinkList p:声明了一个struct LNode*型变量p,相当于struct LNode* p;
如果没有typedef,LNode是struct LNode型变量,LinkList是struct LNode*型指针。
更多推荐
所有评论(0)