目录
1指针的使用和传值调用
1.1strlen的模拟实现
strlen的功能是求字符串长度,统计的是字符串中\0之前字符的个数
函数原型如下:
size_t strlen(const char*str);
参数str接收一个字符串的起始地址,然后开始统计字符串中\0之前的字符个数,最终返回长度。如果要模拟实现只要开始从起始地址开始向后逐个字符的遍历,只要不是\0字符,计数器就加1,这样直到\0就停止。
#include<stdio.h>
int my_strlen(const char* str)
{
int count = 0;
while (*str)
{
count++;
str++;
}
return count;
}
int main()
{
char arr[] = "hello world";
int a = my_strlen(arr);
printf("%d",a);
}
1.2传值调用和传址调用
学习指针的目的是使用指针解决问题,那么什么问题,一定要用指针来解决呢?
例如:写一个函数,交换两个整形变量的值
如果不用地址我们会写出这样的代码:
#include<stdio.h>
void exchange(int x,int y)
{
int step = x;
x = y;
y = step;
}
int main()
{
int a = 1;
int b = 2;
printf("交换前:a=%d b=%d", a, b);
exchange(a, b);
printf("交换后:a=%d b=%d", a, b);
}
这里可以看到,并没有实现两个数字的交换 。原因是:在exchange函数里用形参接受了a和b的值,进行交换的也是形参x和y。并不会改变实参a和b的值。
这时候就要用到地址了。代码如下:
#include<stdio.h>
void exchange(int* x,int *y)
{
int step = *x;
*x =* y;
*y = step;
}
int main()
{
int a = 1;
int b = 2;
printf("交换前:a=%d b=%d", a, b);
exchange(&a, &b);
printf("交换后:a=%d b=%d", a, b);
}
所以,当我们未来用通过函数来修改主函数中的变量,就可以使用传值调用。
2数组名的理解
在上一个章节我们在使用指针访问数组的内容时,有这样的代码:
int data[10] = { 0,1,2,3,4,5,6,7,8,9 };
int* p = &data[0];
这里我们使用&data【0】的方式拿到了数组第一个元素的地址,但其实数组名本来就是地址,而且是数组首元素的地址,我们可以做个测试:
可以看到结果,确实是相等的,但有的同学会疑问,下面这段代码如何理解呢 ?
int main()
{
int data[10] = { 0,1,2,3,4,5,6,7,8,9 };
printf("%zd\n", sizeof(data));
return 0;
}
结果是:40,如果data是数组首元素的地址,那输出的应该是4\8,
其实数组名就是首元素的地址,但是有两个意外:
- sizeof(数组名),sizeof中单独放数组名,这里的数组名就表示整个数组,计算的是整个数组的大小,单位是字节
- &数组名,这里的数组名表示整个数组,取出的是整个数组的地址(整个数组的地址和数组首元素的地址是有区别的)
除此之外,任何地方使用数组名,数组名都表示首元素的地址.
接下来看看这段代码:
int main()
{
int data[10] = { 0,1,2,3,4,5,6,7,8,9 };
printf("&data[0] =%p\n", &data[0]);
printf("&data[0]+1 =%p\n", &data[0]+1);
printf("data =%p\n", data);
printf("data+1 =%p\n", data+1);
printf("&data =%p\n", &data);
printf("&data+1 =%p\n", &data+1);
return 0;
}
结果如下:
&data[0] =010FF8B8
&data[0]+1 =010FF8BC
data =010FF8B8
data+1 =010FF8BC
&data =010FF8B8
&data+1 =010FF8E0
可以看到&data[0]和&data[0]+1相差4个字节,data和data+1相差4个字节,&data[0]和data都是数组的首元素地址,+1就是跳过4个字节.
但是&data和&data+1相差40个字节,这就是因为&data是数组的地址,+1就是跳过整个数组.
3使用指针访问数组
有了前面知识的支持,再结合数组的特点,我们就可以很方便的使用指针访问数组了.
int main()
{
int arr[10] = { 0 };
int i = 0;
int se = sizeof(arr) / sizeof(arr[0]);
int* p = arr;
//输入
for (i = 0; i < se; i++)
{
scanf("%d", p+i);
}
//输出
for(i=0;i<se;i++)
{
printf("%d", p[i]);
}
return 0;
}
这是不使用地址的写法,那是不是也可以用地址来实现呢?代码如下:
int main()
{
int arr[10] = { 0 };
int i = 0;
int se = sizeof(arr) / sizeof(arr[0]);
int* p = arr;
//输入
for (i = 0; i < se; i++)
{
scanf("%d", p+i);
}
//输出
for(i=0;i<se;i++)
{
printf("%d", *(p+i));
}
return 0;
}
4一维数组传参的本质
数组我们学过了,之前也讲过了,数组是可以传给函数的,那我们来讨论一下数组传参的本质.
首先从一个问题出发,我们之前都是在主函数中那个计算数组的元素个数,那我们可以把素组传给一个函数后,函数内部求数组的元素个数吗?
void test(int arr[])
{
printf("%zd\n", sizeof(arr) / sizeof(arr[0]));
}
int main()
{
int arr[10] = { 0,1,2,3,4,5,6,7,8,9 };
printf("%zd\n", sizeof(arr) / sizeof(arr[0]));
test(arr);
}
结果如下:
10
1
我们发现函数内部没有正确获得数组的元素个数.
这个时候我们就要学习数组传参的本质了,上一小节我们学习了:数组名就是数组首元素的地址;那么在数组传参的时候,传递的就是数组名,也就是说本质上数组传参传递的就是数组首元素的地址.
所以函数形参的部分理论上应该使用指针变量来接受首元素的地址.那么在函数内部我们写sizeof(arr)计算的是一个地址的大小(单位字节)而不是数组的大小(单位字节).正是因为函数的参数部分的本质是指针,所以在函数内部是没办法求数组的元素个数的.
void test(int arr[])// 参数写成数组形式,本质上还是指针
{
printf("%zd\n", sizeof(arr) / sizeof(arr[0]));
}
void test1(int *arr)
{
printf("%zd\n", sizeof(arr) / sizeof(arr[0]));
}
int main()
{
int arr[10] = { 0,1,2,3,4,5,6,7,8,9 };
printf("%zd\n", sizeof(arr) / sizeof(arr[0]));
test(arr);
test1(arr);
}
总结:一维函数传参,形参的部分可以写成数组的形式,也可以写成指针的形式.