C语言之深入指针
文章目录
- C语言之深入指针
- 1. 传值调用和传址调用
- 2. 数组名的理解
- 3. 使用指针访问数组
- 3. ⼀维数组传参的本质
1. 传值调用和传址调用
写一个函数用来交换a b的值
传值调用:
#include <stdio.h>
void Swap1(int x, int y)
{
int tmp = 0;
tmp = x;
x = y;
y = tmp;
}
int main()
{
int a = 10;
int b = 20;
printf("交换前:%d %d\n", a, b);
Swap1(a, b);
printf("交换后:%d %d\n", a, b);
return 0;
}
代码运行结果如下:
从运行结果可以看到,a和b的值并没有进行交换,因为传给Swap1函数的只是a和b的值,在Swap1函数内部创建了a和b的临时备份,也就是将实参传给了形参,形参有自己的内存空间,对形参的修改并不会影响实参
这种函数调用方式是:传值调用
结论:实参传递给形参的时候,形参会单独创建⼀份临时空间来接收实参,对形参的修改不影响实
参
传址调用:
#include <stdio.h>
void Swap2(int* x, int* y)
{
int tmp = 0;
tmp = *x;
*x = *y;
*y = tmp;
}
int main()
{
int a = 10;
int b = 20;
printf("交换前:%d %d\n", a, b);
Swap2(&a, &b);
printf("交换后:%d %d\n", a, b);
return 0;
}
从运行结果可以看出,a和b的值有进行交换,这是因为传给Swap2函数的是a和b的地址,然后Swap2函数用两个指针变量来储存a和b的地址,所以在函数中改变x和y的值,由于是相同地址,所以a和b的值也会改变,着这种函数调用方式是:传址调用
2. 数组名的理解
先来看段代码:
#include <stdio.h>
int main()
{
int arr[10] = { 0 };
printf(" arr = %p\n", arr);
printf("&arr[0] = %p\n", &arr[0]);
return 0;
}
代码运行结果:
从运行结果来看,arr和&arr[0]的打印出来的地址都是相同的,可以得知道,数组名就是数组⾸元素(第⼀个元素)的地址
但是有两个情况是例外
- • sizeof(数组名),sizeof中单独放数组名,这⾥的数组名表⽰整个数组,计算的是整个数组的⼤⼩,
单位是字节- • &数组名,这⾥的数组名表⽰整个数组,取出的是整个数组的地址(整个数组的地址和数组⾸元素
的地址是有区别的)
//情况1
#include <stdio.h>
int main()
{
int arr[10] = { 0 };
int sz = sizeof(arr) / sizeof(arr[0]);
printf("%d", sz);
return 0;
}
在以上情况下,sizeof中的数组名是表示整个数组,计算的是整个数组的大小
#include <stdio.h>
int main()
{
int arr[10] = { 0 };
/*int sz = sizeof(arr) / sizeof(arr[0]);
printf("%d", sz);*/
printf(" arr = %p\n", arr);
printf("&arr = %p\n", &arr);
printf("&arr[0] = %p\n", &arr[0]);
return 0;
}
从运行结果看出,arr,&arr和&arr[0]打印的地址是一样的,都是首元素地址,arr和&arr[0]都是表示首元素的地址,那么arr和&arr有什么区别嘛
再来看一段代码:
#include <stdio.h>
int main()
{
int arr[10] = { 0 };
/*int sz = sizeof(arr) / sizeof(arr[0]);
printf("%d", sz);*/
printf(" arr = %p\n", arr);
printf(" arr + 1 = %p\n", arr+1);
printf("&arr = %p\n", &arr);
printf("&arr + 1 = %p\n", &arr+1);
printf("&arr[0] = %p\n", &arr[0]);
printf("&arr[0]+1 = %p\n", &arr[0]+1);
return 0;
}
运行结果如下:
从结果可以看出
arr + 1 从~B78 变成了 ~B7C 加上了4个字节的空间
&arr + 1从~B78 变成了 ~BA0 由于是16进制,转换为二进制就是从120变成了160 加上了40个字节的空间
&arr[0] + 1和arr + 1的结果一样,都是加上了4个字节
• 结论 arr + 1 表示跳过4个字节也就是一个元素,而&arr + 1 表示跳过40个字节,也就是一整个数组
3. 使用指针访问数组
#include <stdio.h>
int main()
{
int arr[10] = { 0 };
int sz = sizeof(arr) / sizeof(arr[0]); //求数组中元素的个数
int* p = arr;
int i = 0;
for(i=0;i<sz;i++)
{
//输入10个数 以下方法都可以
scanf("%d", p + i);
/*scanf("%d", arr+i);*/
}
for (i = 0; i < sz; i++)
{
//输出10个数 以下方法都可以
printf("%d ", *(p + i));
/*printf("%d ", p[i]);*/
/*printf("%d ", *(arr + i));*/
/*printf("%d ", arr[i]); */
/*printf("%d ", i[arr]);*/
}
return 0;
}
p + i 等价于 arr + i
p [ i ] 等价于 *( p + i )
arr[ i ] 等价于 *( arr + i )
都是通过首元素的地址 + 偏移量,然后解引用来访问数组的元素
arr[ i ] 编译器会转换成 *( arr + i )来处理的,例如加法的交换律a+b可以写成b+a,那么也可以写成 *(i + arr),所以也就可以写成 i [arr],但是不建议这样写,代码的可读性极低,只是做个了解
3. ⼀维数组传参的本质
给定一共整形数组,求数组的的元素个数
#include <stdio.h>
void test1(int arr[])//参数写成数组形式,本质上是指针
{
int sz2 = sizeof(arr) / sizeof(arr[0]);
printf("sz2 = %d\n", sz2);
}
void test2(int* arr) //参数写成指针形式
{
int sz3 = sizeof(arr) / sizeof(arr[0]);
printf("sz3 = %d\n", sz3);
}
int main()
{
int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
int sz1 = sizeof(arr) / sizeof(arr[0]);
printf("sz1 = %d\n", sz1);
test1(arr);
test2(arr);
return 0;
}
代码运行结果如下:
1.数组名是数组⾸元素的地址;那么在数组传参的时候,传递的是数组名,也就是说本质上数组传参本质上传递的是数组⾸元素的地址
所以在函数中是无法计算数组的元素个数的
2. 其次,一维数组的传参,函数的参数可以写成数组的形式,也可以写成指针形式