大家好啊,我是小象٩(๑òωó๑)۶
我的博客:Xiao Xiangζั͡ޓއއ
很高兴见到大家,希望能够和大家一起交流学习,共同进步。
这一节是对之前内容的修整
目录
- 一、传值调用和传址调用
- 二、数组名的理解
- 三、指针访问数组
- 四、结尾
一、传值调用和传址调用
学习指针的目的是使用指针解决问题,那什么问题,非指针不可呢?
例如:写一个函数,交换两个整型变量的值
我们可能写出这样的代码:
#include <stdio.h>
void Swap1(int x, int y)
{int tmp = x;x = y;y = tmp;
}
int main()
{int a = 0;int b = 0;scanf("%d %d", &a, &b);printf("交换前:a=%d b=%d\n", a, b);Swap1(a, b);printf("交换后:a=%d b=%d\n", a, b);return 0;
}
我们发现其实没产生交换的效果,这是为什么呢?
在这个代码里,Swap1 函数采用的是传值调用。传值调用的特点是,函数接收的是实参的副本,而不是实参本身。具体来说:
在 main 函数中调用 Swap1(a, b) 时,a 和 b 的值会被复制一份,分别传递给 Swap1 函数的形参 x 和 y。
在 Swap1 函数内部,虽然 x 和 y 的值进行了交换,但这只是对副本的操作,并不会影响到 main 函数中原始的 a 和 b 的值这是因为 实参传递给形参的时候,形参只是实参的一份临时拷贝,对形参的修改不会影响实参,像图中的调试一样,并没有对a和b进行本质的交换,只是在函数中对xy表面上的交换
这也是我们所说的传值调用
结论:实参传递给形参的时候,形参会单独创建一份临时空间来接收实参,对形参的修改不影响实参。
所以Swap1是失败的了。
那怎么办呢?
我们现在要解决的就是当调用Swap函数的时候,Swap函数内部操作的就是main函数中的a和b,直接将a和b的值交换了。那么就可以使用指针了,在main函数中将a和b的地址传递给Swap函数,Swap函数里边通过地址间接的操作main函数中的a和b,并达到交换的效果就好了。
像这样:
#include <stdio.h>
void Swap2(int* px, int* py)
{int tmp = 0;tmp = *px;*px = *py;*py = tmp;
}
int main()
{int a = 0;int b = 0;scanf("%d %d", &a, &b);printf("交换前:a=%d b=%d\n", a, b);Swap2(&a, &b);printf("交换后:a=%d b=%d\n", a, b);return 0;
}
这里调用Swap2函数的时候是将变量的地址传递给了函数,通过地址对变量的本质进行修改,这种函数调用方式叫:传址调用。
传址调用,可以让函数和主调函数之间建立真正的联系,在函数内部可以修改主调函数中的变量;所以未来函数中只是需要主调函数中的变量值来实现计算,就可以采用传值调用。如果函数内部要修改主调函数中的变量的值,就需要传址调用。
二、数组名的理解
在上一个章节我们在使用指针访问数组的内容时,有这样的代码:
int arr[10] = {1,2,3,4,5,6,7,8,9,10};
int *p = &arr[0];
这里我们使用 &arr[0] 的方式拿到了数组第一个元素的地址,但是其实数组名本来就是地址,而且是数组首元素的地址,我们来做个测试。
#include <stdio.h>
int main()
{int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };printf("&arr[0] = %p\n", &arr[0]);printf("arr = %p\n", arr);return 0;
}
通过结果我们发现数组名和数组首元素的地址打印出的结果一模一样,数组名就是数组首元素(第一个元素)的地址。
然后,我们再来看另一段代码:
#include <stdio.h>
int main()
{int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };printf("%d\n", sizeof(arr));return 0;
}
输出的结果是:40,如果arr是数组首元素的地址,那输出应该的应该是4/8才对。
其实数组名就是数组首元素(第⼀个元素)的地址是对的,但是有两个例外:
• sizeof(数组名),sizeof中单独放数组名,这⾥的数组名表示整个数组,计算的是整个数组的大小,单位是字节
• &数组名,这里的数组名表示整个数组,取出的是整个数组的地址(整个数组的地址和数组首元素的地址是有区别的)
除此之外,任何地方使用数组名,数组名都表示首元素的地址。
我们来看看这三个:
#include <stdio.h>
int main()
{int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };printf("&arr[0] = %p\n", &arr[0]);printf("arr = %p\n", arr);printf("&arr = %p\n", &arr);return 0;
}
我们发现打印结果一样
#include <stdio.h>
int main()
{int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };printf("&arr[0] = %p\n", &arr[0]);printf("&arr[0]+1 = %p\n", &arr[0]+1);printf("arr = %p\n", arr);printf("arr+1 = %p\n", arr+1);printf("&arr = %p\n", &arr);printf("&arr+1 = %p\n", &arr+1);return 0;
}
看看结果:
&arr[0] = 0077F820
&arr[0]+1 = 0077F824
arr = 0077F820
arr+1 = 0077F824
&arr = 0077F820
&arr+1 = 0077F848
这里我们发现&arr[0]和&arr[0]+1相差4个字节,arr和arr+1 相差4个字节,是因为&arr[0] 和 arr 都是首元素的地址,+1就是跳过⼀个元素。
但是&arr 和 &arr+1相差40个字节,这就是因为&arr是数组的地址,+1 操作是跳过整个数组的。
到这里大家应该搞清楚数组名的意义了吧。
数组名是数组首元素的地址,但是有2个例外。
三、指针访问数组
有了前面知识的支持,再结合数组的特点,我们就可以很方便的使用指针访问数组了。
在 C 语言里,指针和数组有着紧密的联系,指针可以非常灵活地用于访问数组元素。
#include <stdio.h>
int main()
{int arr[10] = { 0 };//输⼊int i = 0;int sz = sizeof(arr) / sizeof(arr[0]);//输⼊int* p = arr;for (i = 0; i < sz; i++){scanf("%d", p + i);//scanf("%d", arr+i);//也可以这样写}//输出for (i = 0; i < sz; i++){printf("%d ", *(p + i));}return 0;
}
这段 C 语言代码的主要目的是创建一个包含 10个元素的整型数组,实现用户输入元素值并将这些元素输出显示。代码首先包含了标准输入输出库的头文件stdio.h,以使用scanf和printf函数。在main函数里,定义并初始化了一个有10 个元素且初始值都为 0 的整型数组arr,同时定义变量i用于循环计数,通过sizeof(arr) /sizeof(arr[0])计算出数组元素个数并存储在sz中。接着定义一个整型指针p,使其指向数组arr的首地址。之后使用for循环和scanf函数,通过指针p加上偏移量i来获取数组每个元素的地址,将用户输入的整数依次存储到数组元素对应的内存位置,也可使用arr+ i替代p + i。最后,再次使用for循环,利用*(p + i)解引用获取数组各元素的值,并用printf函数将元素值输出,每个元素后有一个空格,最后mai n函数返回 0 表示程序正常结束。
数组名arr是数
组首元素的地址,可以赋值给p,其实数组名arr和p在这里是等价的。那我们可以使用arr[i]可以访问数组的元素,那p[i]是否也可以访问数组呢?
#include <stdio.h>
int main()
{int arr[10] = { 0 };//输⼊int i = 0;int sz = sizeof(arr) / sizeof(arr[0]);//输⼊int* p = arr;for (i = 0; i < sz; i++){scanf("%d", p + i);//scanf("%d", arr+i);//也可以这样写}//输出for (i = 0; i < sz; i++){printf("%d ", p[i]);}return 0;
}
这段 C 语言代码旨在创建一个包含 10个元素的整型数组,接收用户输入为数组元素赋值,再将数组元素输出。代码首先包含标准输入输出库头文件stdio.h以使用 scanf 和printf 函数。在main函数里,定义并初始化了含 10 个元素且初始值全为 0的整型数组arr,定义变量i作为循环计数器,通过sizeof(arr)/sizeof(arr[0])计算数组元素个数存于sz。接着定义整型指针p指向数组arr首地址,利用for循环和scanf函数,借助p + i获取数组元素地址,将用户输入存入对应内存位置,也可使用arr + i替代。在输出部分,同样使用for循环,通过p[i]访问数组元素并输出,每个元素后有一个空格,最后main函数返回 0 表示程序正常结束。与上一段代码相比,不同之处在于输出部分。上一段代码使用*(p + i)来解引用获取数组元素值进行输出,而这段代码采用p[i]的形式来访问数组元素并输出。虽然p[i]和*(p + i)在功能上是等价的,但代码表现形式不同,p[i]这种写法更符合数组下标的常规使用习惯 。
在第18行的地方,将*(p+i)换成p[i]也是能够正常打印的,所以本质上p[i] 是等价于 *(p+i)。
同理arr[i] 应该等价于 *(arr+i),数组元素的访问在编译器处理的时候,也是转换成首元素的地址+偏移量求出元素的地址,然后解引用来访问的。
四、结尾
这一课的内容就到这里了,下节课继续学习指针的其他一些知识
如果内容有什么问题的话欢迎指正,有什么问题也可以问我!