欢迎来CILMY23的博客喔,本期系列为【C语言】指针的入门篇2,深入理解指针和数组的关系,图文讲解指针和数组关系的知识,带大家理解指针和数组的关系,以及指针+数组的用法,感谢观看,支持的可以给个赞哇。
前言
在上一篇博客中,我们了解了指针就是地址,并且把地址存放的变量叫做指针变量,以及指针用法,并且为了防止野指针,我们还学习了assert断言等等来防止野指针的产生,本期博客将用strlen函数的模拟实现来回顾先前知识,并学习指针和数组之间的关系。
目录
一、strlen的模拟实现
二、数组名和数组首元素
二、指针访问数组
三、一维数组的传参
四、冒泡排序
五、二级指针
六、指针数组
七、指针数组模拟二维数组
一、strlen的模拟实现
strlen函数,我们可以在cplusplus网站查询cplusplus.comhttps://cplusplus.com/
它的功能是获取字符串的长度,返回字符串的长度。那字符串的特点是在“”的末尾有一个\0,所以我们可以采取计数的方式来统计字符串的长度。所以我们可以写出以下代码:
int my_strlen(char* str)
{
int len = 0;
while (*str != '\0')
{
len++;
str++;
}
return len;
}
int main()
{
char str[] = "hello SILMY23";
printf("%d",my_strlen(str));
return 0;
}
我们可以看到结果:
但当我们在模拟实现的时候,往往可以发现一些问题,如果外界传入的是空指针呢?其次我们并不希望修改指针所指向的内容,因此可以加const修饰我们的形参str。 因为assert检测的时候,如果表达式为真,就不影响程序,如果为假,就停止程序并进行报错。最后,因为字符串长度是不可能有负数的,所以我们可以用size_t来代替int。
#include<stdio.h>
#include<assert.h>
size_t my_strlen(const char* str)
{
assert(str);
int len = 0;
while (*str != '\0')
{
len++;
str++;
}
return len;
}
int main()
{
char str[] = "hello SILMY23";
printf("%d",my_strlen(str));
return 0;
}
这样一个strlen字符函数的模拟实现就完成了,当然主体我们也可以不采取计数的方式,可以采取指针-指针的写法。
让一个指针找到尾巴,然后利用指针相减指针是元素的个数,返回指针减去指针就可以了。前提是仍然需要对str断言,如果str不为空我们再赋值给tail指针。
#include<stdio.h>
#include<assert.h>
size_t my_strlen(const char* str)
{
assert(str);
const char* tail = str;
while (*tail != '\0')
{
tail++;
}
return tail - str;
}
int main()
{
char str[] = "hello SILMY23";
printf("%d",my_strlen(str));
return 0;
}
二、数组名和数组首元素
在上一篇文章中,我们用了以下代码
#include<stdio.h>
int main()
{
int arr[] = { 1,2,3,4,5,6,7,8,9,10 };
int i = 0;
int sz = sizeof(arr) / sizeof(arr[0]);
//指针访问
int* p = arr;
for (i = 0; i < sz; i++)
{
printf("%d " , *(p + i));
}
return 0;
}
我们发现在用指针访问数组的时候,我把数组名赋值给了指针,那是否意味着所有情况下的数组名都是数组首元素的地址呢?
我们接下来看以下代码:
#include<stdio.h>
int main()
{
int arr[] = { 1,2,3,4,5,6,7,8,9,10 };
int sz = sizeof(arr) / sizeof(arr[0]);
printf("%d ", sizeof(arr));
printf("%d ", sizeof(arr[0]));
return 0;
}
我们发现结果是40 4,那说明并不是所有情况下的数组名都是数组首元素的地址。
数组名就是数组首元素的地址,但是有两个例外:
1.sizeof(),在sizeof的关键字后面单独放数组名,这里的数组名表示整个数组,计算的是整个数组的大小, 单位是字节。
2.&arr,这里表示的也是整个数组,取出的是整个数组的地址(整个数组的地址和数组首元素的地址是有区别的)
那arr,&arr,&arr[0]的联系和区别又在哪里呢?我们看以下代码:
#include<stdio.h>
int main()
{
int arr[] = { 1,2,3,4,5,6,7,8,9,10 };
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);
printf("&arr = %p\n ", &arr);
printf("&arr + 1 = %p\n ", &arr + 1);
return 0;
}
结果如下:
我们看指针走的步长是指针类型,前四个反应的是arr和&arr[0],几乎无差别,而最后一个&arr它所走的空间大小是整个数组的大小。
二、指针访问数组
在上一篇文章我们初步写了指针访问数组,并打印数组的值,那如果我们想要输入呢?
#include<stdio.h>
int main()
{
int arr[10] = {0};
int sz = sizeof(arr) / sizeof(arr[0]);
int* p = arr;
for (int i = 0; i < sz; i++)
{
scanf("%d", p + i);
}
for (int i = 0; i < sz; i++)
{
printf("%d ", *(p + i));
}
return 0;
}
当我们输入1 -10的时候,打印的也是如此。
当编译器处理的时候,打印的时候,其实arr[i] == *(arr+i), 它们之间是等价的。同时我们也可以写成p[i] == *(p+i)
甚至可以写成以下形式
arr[i] == *(arr+i) == *(i+arr) == i[arr],感兴趣的读者可以自己敲一下验证验证
三、一维数组的传参
我们看以下代码:
#include<stdio.h>
void print(int arr[])
{
int i = 0;
int sz = sizeof(arr) / sizeof(arr[0]);
for (i = 0; i < sz; i++)
{
printf("%d ", arr[i]);
}
}
int main()
{
int arr[10] = { 1,2,3,4,5,6,7,8,9,10};
print(arr);
return 0;
}
当我们运行之后发现,结果并没有像我们预期的那样打印出1到10,而是只打印了1.这就涉及一维数组传参的本质了,一维数组传参实际上传入的是数组首元素地址,所以在函数内部计算sz的时候,sizeof(arr)是计算了指针变量的大小,四个字节,而后面计算出的也是四个字节,最后sz只计算出了1。
所以要修改代码我们可以在没有传参之前把数组长度计算出来,然后去传参,arr[]它实际还是指针变量,所以我们可以写成int *p.
void print(int *p,int sz)
{
int i = 0;
for (i = 0; i < sz; i++)
{
printf("%d ", p[i] );
}
}
int main()
{
int arr[10] = { 1,2,3,4,5,6,7,8,9,10};
int sz = sizeof(arr) / sizeof(arr[0]);
print(arr,sz);
return 0;
}
四、冒泡排序
冒泡排序作为练习我也写过这篇啦,可以跳转细看,这里我粗略讲解练习。
http://t.csdnimg.cn/VCpbBhttp://t.csdnimg.cn/VCpbB冒泡排序,假设有一个数组,我们需要把一个有序数组排序为升序。其核心思想是两两相邻的元素进行交换。
五、二级指针
指针变量也是变量,是变量就有地址,那指针变量的地址存放在哪里呢?再存放在一个指针里,这就是二级指针。
#include<stdio.h>
int main()
{
int a = 10;
int* pa = &a;
int** ppa = &pa;
printf("%p\n", &a);
printf("%p\n", &pa);
printf("%p\n", &ppa);
return 0;
}
在以上代码中,a是整型变量,占据四个字节的空间,pa和ppa都是指针变量,占据4或者八个字节的空间,a,pa,ppa三个变量都有对应的地址。 我们把pa叫做一级指针,ppa叫做二级指针。
如果我们要通过ppa找到a,我们就需要两层的解引用,
六、指针数组
什么是指针数组呢?我们回想一下,整型数组是用来存放整型数据的,字符数组是用来存放字符的,所以指针数组就是用来存放指针的。
#include<stdio.h>
int main()
{
int a = 10;
int* arr[] = { &a };
printf("%d ", *arr[0]);
return 0;
}
我们开辟一个指针数组来存放a的地址,我们需要对数组元素解引用来获得a的值。
七、指针数组模拟二维数组
假设我们现在有三个数组,arr1,arr2,arr3,我们将它放到指针数组parr中,我们在用parr[]的时候,得到的是三个数组的地址,再对其解引用,我们就得到三个数组中具体的数值。
#include<stdio.h>
int main()
{
int arr1[5] = { 1,2,3,4,5 };
int arr2[5] = { 2,3,4,5,6 };
int arr3[5] = { 3,4,5,6,7 };
int* parr[3] = { arr1,arr2,arr3 };
int i = 0;
for (i = 0; i < 3; i++)
{
int j = 0;
for (j = 0; j < 5; j++)
{
printf("%d ", parr[i][j]);
}
printf("\n");
}
return 0;
}
结果如下:
这就是指针数组的用法。 上述代码的分布图如下:
感谢各位同伴的支持,本期指针入门篇2就讲解到这啦,如果你觉得写的不错的话,可以给个赞,若有不足,欢迎各位在评论区讨论。