目录
一. 字符指针
二. 指针数组
三. 数组指针
四. 数组参数、指针参数
1.一维数组传参
2.二维数组传参
3.一级指针传参
4.二级指针传参
一. 字符指针
在之前,我们就了解到过字符指针
int main()
{
char a='W';//字符变量
char* pa=&a;//字符指针
*pa='w';
return 0;
}
而除此之外,字符指针还有一种使用方法
char* pstr="abcdef";
我们可以看到,我们似乎是将一串字符串赋给了字符指针pstr
而事实上,我们是将abcdef这个常量字符串的首字符的地址放在了pstr中
而abcdef作为一个常量字符串,本身应该不能被改变,因此我们可以使用const来修饰pstr
const char* pstr="abcdef";
实例
#include <stdio.h>
int main()
{
char str1[] = "hello bit.";
char str2[] = "hello bit.";
const char *str3 = "hello bit.";
const char *str4 = "hello bit.";
if(str1 ==str2)
printf("str1 and str2 are same\n");
else
printf("str1 and str2 are not same\n");
if(str3 ==str4)
printf("str3 and str4 are same\n");
else
printf("str3 and str4 are not same\n");
return 0;
}
上述代码中,由于str1,str2两个数组分别开辟了两个空间分别来存储字符串,所以str1和str2的地址固然不相同。
而常量字符串是存储在静态存储区的,str3和str4都是取静态区中abcdef的首元素地址,所以地址相同。
二. 指针数组
无非就是存储指针的数组。
而在上面的字符指针中,我们可以赋予字符串的首地址,因此,指针数组也可以这样操作
char* arr[3]={"abcd","bcde","cdef"};
同样,数组名是该数组首元素的地址,因此,我们也可以将数组名作为指针存放在指针数组中
int nums1[]={1,2,3,4};
int nums2[]={2,3,4,5};
int nums3[]={3,4,5,6};
int nums4[]={4,5,6,7};
int* nums[]={nums1,nums2,nums3,nums4};
三. 数组指针
同样,数组指针也是存放数组地址的指针
在考虑数组指针前,我们可以先来了解一下数组名和&数组名的区别
首先,我们知道,数组名是该数组首元素的地址
那么,将数组名取地址得到的也必然是一个地址,那么这个地址代表的是什么呢?
在我们直接打印两个地址时, 可以发现是相同的。但指针还存在步长,我们可以从步长来了解两者之间的差别
可以看到,这时两者出现了差别,nums与nums+1之间差距4个字节, 也就是int类型的大小,而&nums与&nums+1之间差距16个字节,也就是4个int类型所占的大小,当然也就是整个数组所占内存大小。
因此我们可以得知,不同于数组名所代表的的首元素地址,&数组名所代表的整个数组的地址
在我们了解到数组名与&数组名的差别后,我们也就可以来实现数组指针
数组指针理所应当存储整个数组的地址,因此我们要选择&数组名,同时(以整形数组为例)数组指针的类型应该为int (*) [10],其中int 指的是数组中的元素类型,而*表示这是一个指针类型,而[10]则代表数组的大小为10;因此我们就可以实现数组指针
int nums[4]={1,2,3,4};
int(*p)[4]=&nums;
printf("%d", (*p)[0]);
printf("%d",nums[0]);//效果相同
而在使用中,我们很少将一维数组的地址存储为数组指针,因为比较的鸡肋,我们可以在二维数组中使用到它
void print_arr1(int arr[3][5], int row, int col)
{
for (int i = 0; i < row; i++)
{
for (int j = 0; j < col; j++)
{
printf("%d ", arr[i][j]);
}
printf("\n");
}
}
void print_arr2(int(*arr)[5], int row, int col)
{
for (int i = 0; i < row; i++)
{
for (int j = 0; j < col; j++)
{
printf("%d ", (*(arr + i))[j]);
}
printf("\n");
}
}
int main()
{
int arr[2][5] = { 1,2,3,4,5,6,7,8,9,10 };
print_arr1(arr, 2, 5);
print_arr2(arr, 2, 5);
return 0;
}
二维数组在传参时,数组名同样是首元素的地址,而二维数组首元素的地址指的是第一行的地址
,而第一行其实是一个一维数组,因此可以使用数组指针来接受
四. 数组参数、指针参数
1.一维数组传参
#include <stdio.h>
void test(int arr[])
{}
void test(int arr[10])
{}
void test(int* arr)
{}
void test2(int* arr[20])
{}
void test2(int** arr)
{}
int main()
{
int arr[10] = { 0 };
int* arr2[20] = { 0 };
test(arr);
test2(arr2);
}
一维数组传参其实都比较简单,我们可以知道,上面几种传参方法都是正确的
2.二维数组传参
void test(int arr[3][5])//ok?
{}
void test(int arr[][])//ok?
{}
void test(int arr[][5])//ok?
{}
void test(int* arr)//ok?
{}
void test(int* arr[5])//ok?
{}
void test(int(*arr)[5])//ok?
{}
void test(int** arr)//ok?
{}
int main()
{
int arr[3][5] = { 0 };
test(arr);
}
首先我们先看一下前三种传参方式,由于二维数组的行可以忽略,而列不能忽略,我们可以知道第一种和第三种是正确的,而第二种是违规的
而后面四种则和指针相关。首先我们在上面提到过,二维数组的数组名代表二维数组第一行的地址,而第一种写法中使用整形指针显然不行。 而第二种的参数为一个指针数组,也不可以。 第三种写法在我们上面提到过类似的,是一个数组指针,因此可以。 最后一种写法作为一个二级整形指针,固然也不可以
而经常刷题的老铁会发现一个问题
类似于这样的题目,在二维数组传参时使用的是就是char** board,而这种方法我们在前面已经被否定了,这又是为什么呢?
这是因为,在leetcode中,所传入的“二维数组”其实是由一维数组模拟而来的
char** board = (char**)malloc(sizeof(char*) * m);
for (int i = 0; i < m; i++)
{
board[i] = (char*)malloc(sizeof(char) * n);
}
而malloc我们会在后面的动态内存管理中学到,这里就不过度展开
3.一级指针传参
#include <stdio.h>
void print(int* p, int sz)
{
int i = 0;
for (i = 0; i < sz; i++)
{
printf("%d\n", *(p + i));
}
}
int main()
{
int arr[10] = { 1,2,3,4,5,6,7,8,9 };
int* p = arr;
int sz = sizeof(arr) / sizeof(arr[0]);
print(p, sz);
return 0;
}
指针的传参实现是比较简单的,我们主要考虑函数参数部分为指针时能接受什么参数
一级指针所能接受的参数我们目前常见的有数组名、&变量、指针变量
4.二级指针传参
#include <stdio.h>
void test(int** ptr)
{
printf("num = %d\n", **ptr);
}
int main()
{
int n = 10;
int* p = &n;
int** pp = &p;
test(pp);
test(&p);
return 0;
}
同样,二级指针所能接受的参数有二级指针变量、&一级指针变量、指针数组的数组名等。