传值与传址
《C和指针》函数的参数一节(7.3节)指出,C函数的所有参数均以“传值调用”方式进行传递,这意味着函数将获得参数值的一份拷贝。这样函数可以放心地修改这个拷贝值,而不必担心会修改调用程序实际传递给它的参数。
如果被传递的参数是一个数组名,由于数组名的值是一个指向数组第一个元素的指针,因此实际传递给函数的是指向数组起始位置的指针的一份拷贝,该指针同样指向数组起始位置。在函数内部对指针形参进行间接访问操作,实际访问的是原数组的元素。
对于传值和传址:
1. 传递给函数的标量参数是传值调用的。
2. 传递给函数的数组参数在行为上就像它们是通过传址调用的那样。
如果想把一个数组名参数传递给函数,正确的函数形参应该是怎样的?它是应该声明为一个指针还是一个数组?
调用函数时实际传递的是一个指针,所以函数的形参实际上是一个指针,但为了使程序员新手更容易上手一些,编译器也接受数组形式的函数形参。因此,下面这两个函数原型是相等的:
void InitStudent2(int *arrayStu, int Num)
void InitStudent2(int arrayStu[], int Num)
由于数组名作为参数传递给函数时,函数实际接收到的是一个指针,因此第一种声明是更为准确的。在函数内部sizeof(str)的值将会是数组指针的长度,而不是数组的长度。
编译器同样接受第二种声明形式。数组形参无需写明它的元素数目,是因为函数并不为数组参数分配内存空间,形参只是一个指针。因此数组形参可以与任何长度的数组匹配。如果函数需要知道数组的长度,它必须作为一个显式的参数传递给函数。
struct student
{
string name; //姓名
int age; //年龄
string hobby; //爱好
};
有typedef关键字的 typedef struct student Stu;
使用typedef定义结构体。typedef用来定义新的数据类型,通常typedef与结构体的定义配合使用。使用typedef的目的使结构体的表达更加简练(所以说typedef语句并不是必须使用的)。
定义一个名字为 student 的结构体类型(现在并没有定义结构体变量,并不占用内存空间)
为结构体起一个别名 Stu,这时 Stu 等价于 struct student
下面两种定义方式效果相同
struct student
{
string name; //姓名
int age; //年龄
string hobby; //爱好
};
typedef struct student Stu;
typedef struct student
{
string name; //姓名
int age; //年龄
string hobby; //爱好
}Stu;
在这里我们要区别一下结构体名和变量名,结构体名是一种类型,不能被当成变量名来使用
变量名声明方法:
1)struct +结构体名+结构体变量名;
2)直接在定义结构体的时候添加变量名的声明
函数中的参数列表本质上都是原来实参的副本,由于是指针,所以副本和实参指向的都是同一个内存单元,如若只是改变副本所指内存单元的数据,那么实参内存所指的数据也同样被改变;
但是由于值传递(2.1)在子函数内部,改变了副本的内存单元,实参的内存单元并没有发生改变,所以实参的数据并没有跟着副本一起改变。
如果想改变实参的数据,那么应该传递结构体指针变量的指针(2.3)或者其引用(2.2)
1.第一种方式,结构体指针变量的指针本质上就是这个变量的地址,通过改变这个指针指向的内存单元的内容从而达到修改结构体指针的内容;
2.第二种方式,结构体指针变量的引用其本质就是这个结构体变量的别名,对其引用所进行的操作本质上都是对其自身的操作。
在主函数中单独调用 printStudent1(stu1, stu2);
void printStudent1(Stu stu1, Stu stu2) //值传递
{
cout << "在printStudent1子函数中" << endl;
// 打印前修改年龄
stu2.age = 1000;
cout << stu1.name << "\t" << stu1.age <<"\t" << stu1.hobby << endl;
cout << stu2.name << "\t" << stu2.age <<"\t" << stu2.hobby << endl;
}
在主函数中单独调用 printStudent1_1(stu1, stu2);
void printStudent1_1(Stu &stu1, Stu &stu2) //引用传递
{
cout << "在printStudent1_1子函数中" << endl;
// 打印前修改年龄
stu1.age = 1000;
cout << stu1.name << "\t" << stu1.age << "\t" << stu1.hobby << endl;
cout << stu2.name << "\t" << stu2.age << "\t" << stu2.hobby << endl;
}
在主函数中单独调用 printStudent1_2(&stu1, &stu2);
结构体指针的访问变量方法
1)p->结构体成员;
2)(*p).结构体成员;
void printStudent1_2(Stu *stu1, Stu *stu2) //结构体指针传递函数形参
{
cout << "在printStudent1_2子函数中" << endl;
// 打印前修改年龄
stu1->age = 2000;
//cout << (*stu1).name << "\t" << (*stu1).age << "\t" << (*stu1).hobby << endl;
//cout << (*stu2).name << "\t" << (*stu2).age << "\t" << (*stu2).hobby << endl;
cout << stu1->name << "\t" << stu1->age << "\t" << stu1->hobby << endl;
cout << stu2->name << "\t" << stu2->age << "\t" << stu2->hobby << endl;
}
int main()
{
cout << "姓名" << "\t" << "年龄" << " " <<" 爱好" << endl;
cout << endl;
Stu stu1 = { "张三" ,18 ,"打篮球" };
Stu stu2 = { "李四" ,28 ,"rap" };
InitStudent1(stu1, stu2);
//printStudent1(stu1, stu2);
printStudent1_1(stu1, stu2);
//printStudent1_2(&stu1, &stu2);
cout << endl;
cout << "在主函数中" << endl;
cout << stu1.name << "\t" << stu1.age << "\t" << stu1.hobby << endl;
cout << stu2.name << "\t" << stu2.age << "\t" << stu2.hobby << endl;
return 0;
}
//方式一 结构体数组作为函数参数 下标引用1
void InitStudent2(Stu arrayStu[], int Num)
{
// 第一种赋值方式
//Stu arrayStu[4];//结构体数组
//arrayStu[0].name = "大头";
//arrayStu[0].age = 20;
//arrayStu[0].hobby = "唱歌";
//arrayStu[1].name = "王五";
//arrayStu[1].age = 28;
//arrayStu[1].hobby = "跳舞";
// 第二种赋值方式
arrayStu[0] = { "大头" ,20,"唱歌" };
arrayStu[1] = { "王五" ,28,"跳舞" };
}
//方式一 结构体数组作为函数参数 下标引用2
void InitStudent2(Stu *arrayStu, int Num)
{
//第一种赋值方式
//Stu arrayStu[4];//结构体数组
//arrayStu[0].name = "大头";
//arrayStu[0].age = 20;
//arrayStu[0].hobby = "唱歌";
//arrayStu[1].name = "王五";
//arrayStu[1].age = 28;
//arrayStu[1].hobby = "跳舞";
//第二种赋值方式
arrayStu[0] = { "大头" ,20,"唱歌" };
arrayStu[1] = { "王五" ,28,"跳舞" };
}
我们知道数组作为函数参数的使用方法,那结构体数组本质上也是数组,所以结构体数组作为函数参数的使用方法应该与数组作为函数参数的方法一样。同时我们也知道,下标引用与间接访问完全相同 。下图中函数访问结构体数组是使用下标引用的方式。
//方式一 结构体数组作为函数参数 下标引用2
void printStudent2(Stu *arrayStu, int Num)
{
for (int i = 0; i < Num; i++)
{
cout << arrayStu[i].name << "\t" << arrayStu[i].age << "\t" << arrayStu[i].hobby << endl;
}
}
即然下标引用与间接访问相同,那间接访问应该如何实现?见下图:
//方式二 结构体数组作为函数参数 间接访问
void printStudent2_1(Stu *arrayStu, int Num)
{
for (int i = 0; i < Num; i++)
{
// 方法一
//cout << (arrayStu + i)->name << "\t" << (arrayStu + i)->age << "\t" << (arrayStu + i)->hobby << endl;
// 方法二
// 由于 " * "取值运算符优先级小于 " . "成员选择运算符,所以需要将 *arrayStu 用括号括起来。
cout << (*(arrayStu + i)).name << "\t" << (*(arrayStu + i)).age << "\t" << (*(arrayStu + i)).hobby << endl;
}
}
打印函数部分代码
//方式一 结构体数组作为函数参数 下标引用2
void printStudent2(Stu *arrayStu, int Num)
{
for (int i = 0; i < Num; i++)
{
cout << arrayStu[i].name << "\t" << arrayStu[i].age << "\t" << arrayStu[i].hobby << endl;
}
}
//方式二 结构体数组作为函数参数 间接访问
void printStudent2_1(Stu *arrayStu, int Num)
{
for (int i = 0; i < Num; i++)
{
//cout << (arrayStu + i)->name << "\t" << (arrayStu + i)->age << "\t" << (arrayStu + i)->hobby << endl;
// 方法二
// 由于 " * "取值运算符优先级小于 " . "成员选择运算符,所以需要将 *arrayStu 用括号括起来。
cout << (*(arrayStu + i)).name << "\t" << (*(arrayStu + i)).age << "\t" << (*(arrayStu + i)).hobby << endl;
}
}
// 这种写法实际处理函数只针对 一个结构体 (传进来的那个结构体)操作
void printStudent2_2(Stu *arrayStu)
{
cout << arrayStu->name << "\t" << arrayStu->age << "\t" << arrayStu->hobby << endl;
}
int main()
{
cout << "姓名" << "\t" << "年龄" << " " <<" 爱好" << endl;
cout << endl;
//结构体数组作为函数参数
Stu stu[4];
InitStudent2(stu, 2);
printStudent2(stu, 2);
printStudent2_1(stu, 2);
printStudent2_2(&stu[0]);
printStudent2_2(&stu[1]);
return 0;
}
/* 2022 08 02 */
#include
#include
using namespace std;
//struct student
//{
// string name; //姓名
// int age; //年龄
// string hobby; //爱好
//};
typedef struct student
{
string name; //姓名
int age; //年龄
string hobby; //爱好
}Stu;
//struct student
//{
// string name; //姓名
// int age; //年龄
// string hobby; //爱好
//
//};
//typedef struct student Stu;
// 结构体作为函数参数
void InitStudent1(Stu &stu1, Stu &stu2)
{
// 第一种赋值方式
//stu1.name = "张三";
//stu1.age = 18;
//stu1.hobby = "打篮球";
//stu2.name = "李四";
//stu2.age = 25;
//stu2.hobby = "rap";
// 第二种赋值方式
stu1 = { "张三" ,18 ,"打篮球" };
stu2 = { "李四" ,28 ,"rap" };
}
void printStudent1(Stu stu1, Stu stu2) //值传递
{
cout << "在printStudent1子函数中" << endl;
// 打印前修改年龄
stu2.age = 1000;
cout << stu1.name << "\t" << stu1.age <<"\t" << stu1.hobby << endl;
cout << stu2.name << "\t" << stu2.age <<"\t" << stu2.hobby << endl;
}
void printStudent1_1(Stu &stu1, Stu &stu2) //引用传递
{
cout << "在printStudent1_1子函数中" << endl;
// 打印前修改年龄
stu1.age = 1000;
cout << stu1.name << "\t" << stu1.age << "\t" << stu1.hobby << endl;
cout << stu2.name << "\t" << stu2.age << "\t" << stu2.hobby << endl;
}
void printStudent1_2(Stu *stu1, Stu *stu2) //结构体指针传递函数形参
{
cout << "在printStudent1_2子函数中" << endl;
// 打印前修改年龄
stu1->age = 2000;
cout << (*stu1).name << "\t" << (*stu1).age << "\t" << (*stu1).hobby << endl;
cout << (*stu2).name << "\t" << (*stu2).age << "\t" << (*stu2).hobby << endl;
}
//结构体数组作为函数参数
方式一 结构体数组作为函数参数 下标引用1
//void InitStudent2(Stu arrayStu[], int Num)
//{
// // 第一种赋值方式
// //Stu arrayStu[4];//结构体数组
//
// //arrayStu[0].name = "大头";
// //arrayStu[0].age = 20;
// //arrayStu[0].hobby = "唱歌";
//
// //arrayStu[1].name = "王五";
// //arrayStu[1].age = 28;
// //arrayStu[1].hobby = "跳舞";
//
//
// // 第二种赋值方式
// arrayStu[0] = { "大头" ,20,"唱歌" };
// arrayStu[1] = { "王五" ,28,"跳舞" };
//}
//方式一 结构体数组作为函数参数 下标引用2
void InitStudent2(Stu *arrayStu, int Num)
{
//第一种赋值方式
//Stu arrayStu[4];//结构体数组
//arrayStu[0].name = "大头";
//arrayStu[0].age = 20;
//arrayStu[0].hobby = "唱歌";
//arrayStu[1].name = "王五";
//arrayStu[1].age = 28;
//arrayStu[1].hobby = "跳舞";
//第二种赋值方式
arrayStu[0] = { "大头" ,20,"唱歌" };
arrayStu[1] = { "王五" ,28,"跳舞" };
}
//方式一 结构体数组作为函数参数 下标引用2
void printStudent2(Stu *arrayStu, int Num)
{
for (int i = 0; i < Num; i++)
{
cout << arrayStu[i].name << "\t" << arrayStu[i].age << "\t" << arrayStu[i].hobby << endl;
}
}
//方式二 结构体数组作为函数参数 间接访问
void printStudent2_1(Stu *arrayStu, int Num)
{
for (int i = 0; i < Num; i++)
{
//cout << (arrayStu + i)->name << "\t" << (arrayStu + i)->age << "\t" << (arrayStu + i)->hobby << endl;
// 方法二
// 由于 " * "取值运算符优先级小于 " . "成员选择运算符,所以需要将 *arrayStu 用括号括起来。
cout << (*(arrayStu + i)).name << "\t" << (*(arrayStu + i)).age << "\t" << (*(arrayStu + i)).hobby << endl;
}
}
// 这种写法实际处理函数只针对 一个结构体 (传进来的那个结构体)操作
void printStudent2_2(Stu *arrayStu)
{
cout << arrayStu->name << "\t" << arrayStu->age << "\t" << arrayStu->hobby << endl;
}
int main()
{
cout << "姓名" << "\t" << "年龄" << " " <<" 爱好" << endl;
cout << endl;
Stu stu1 = { "张三" ,18 ,"打篮球" };
Stu stu2 = { "李四" ,28 ,"rap" };
InitStudent1(stu1, stu2);
//printStudent1(stu1, stu2);
printStudent1_1(stu1, stu2);
//printStudent1_2(&stu1, &stu2);
cout << endl;
cout << "在主函数中" << endl;
cout << stu1.name << "\t" << stu1.age << "\t" << stu1.hobby << endl;
cout << stu2.name << "\t" << stu2.age << "\t" << stu2.hobby << endl;
//结构体数组作为函数参数
//Stu stu[4];
//InitStudent2(stu, 2);
//printStudent2(stu, 2);
//printStudent2_1(stu, 2);
//printStudent2_2(&stu[0]);
//printStudent2_2(&stu[1]);
return 0;
}
附:C语言常见运算符优先级
优先级 | 运算符 | 含义 | 结合方向 |
---|---|---|---|
1 | [] | 数组下标 | 从左向右 |
() | 圆括号 | ||
. | 成员选择(对象) | ||
-> | 成员选择(指针) | ||
2 | - | 负号 | 从右向左 |
~ | 按位取反 | ||
++ | 自增 | ||
-- | 自减 | ||
* | 取值 | ||
& | 取地址 | ||
! | 逻辑非 | ||
sizeof | 数据类型长度 | ||
(类型)强制类型转换 | 强制类型转换 | ||
3 | / | 除 | 从左向右 |
* | 乘 | ||
% | 取模 | ||
4 | + | 加 | 从左向右 |
- | 减 | ||
5 | << | 左移 | 从左向右 |
>> | 右移 | ||
6 | > | 大于 | 从左向右 |
>= | 大于等于 | ||
< | 小于 | ||
<= | 小于等于 | ||
7 | == | 等于 | 从左向右 |
!= | 不等于 |