本章重点
1. 字符指针
2. 数组指针
3. 指针数组
4. 数组传参和指针传参
1. 字符指针
在我的前一章节,我们提到指针也有类型的区分,有整型指针,浮点型指针,下面我们讲讲字符指针
字符指针的用法通常是将一个字符变量的地址存放到该指针中去,通过对指针的解引用来对该值进行操作
int main()
{
char ch = 'w';
char *pc = &ch;
*pc = 'w';
return 0;
}
其实还有一种使用方式
int main()
{
const char* pstr = "hello bit.";//这里是把一个字符串放到pstr指针变量里了吗?
printf("%s\n", pstr);
return 0;
}
运行的结果是将整个字符串输出来,这里就会有小伙伴好奇了,字符指针也能存放字符串的地址吗?
其实结果与我们想的大不相同,本质是把字符串 的首地址存入到了指针中。
如果原理是这样的话,我们看看下面这段代码
#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;
}
运行结果
这是为什么呢?
这里str3和str4指向的是一个同一个常量字符串。C/C++会把常量字符串存储到单独的一个内存区域,当几个指针。指向同一个字符串的时候,他们实际会指向同一块内存。但是用相同的常量字符串去初始化不同的数组的时候就会开辟出不同的内存块。所以str1和str2不同,str3和str4不同。
2. 数组指针
数组指针,这是一个新的概念,那么它到底是数组还是指针呢?从名字上看,它是一个指针,既然指针是用来存放地址的,那么它存放的是谁的地址呢?
我们已经熟悉:
整形指针: int * pint; 能够指向整形数据的指针。
浮点型指针: float * pf; 能够指向浮点型数据的指针
那数组指针指向的应该就是数组的地址。
那数组指针该如何定义呢?
int *p1[10];
int (*p2)[10];
//p1, p2分别是什么?
很明显,p1应该是一个指针数组,这里要涉及到操作符的优先级问题,而[]的优先级要高于*号的
而p2加了一个括号,保证*与p2相结合为一个指针,定义为指针变量,然后指着指向的是一个大小为10个整型的数组。所以p是一个指针,指向一个数组,叫数组指针。
数组指针的使用
讲完了定义和原理,我们来看看它是如何使用的,既然数组指针指向的是数组,那数组指针中存放的应该是数组的地址。
#include <stdio.h>
int main()
{
int arr[10] = {1,2,3,4,5,6,7,8,9,0};
int (*p)[10] = &arr;//把数组arr的地址赋值给数组指针变量p
//但是我们一般很少这样写代码
return 0;
}
正常情况下
#include <stdio.h>
void print_arr1(int arr[3][5], int row, int col)
{
int i = 0;
for(i=0; i<row; i++)
{
for(j=0; j<col; j++)
{
printf("%d ", arr[i][j]);
}
printf("\n");
}
}
void print_arr2(int (*arr)[5], int row, int col)
{
int i = 0;
for(i=0; i<row; i++)
{
for(j=0; j<col; j++)
{
printf("%d ", arr[i][j]);
}
printf("\n");
}
}
int main()
{
int arr[3][5] = {1,2,3,4,5,6,7,8,9,10};
print_arr1(arr, 3, 5);
//数组名arr,表示首元素的地址
//但是二维数组的首元素是二维数组的第一行
//所以这里传递的arr,其实相当于第一行的地址,是一维数组的地址
//可以数组指针来接收
print_arr2(arr, 3, 5);
return 0;
}
3. 指针数组
上一章讲过,指针数组就是一个存放指针变量的数组,不同的类型可以存放不同的指针
int* arr1[10]; //整形指针的数组
char *arr2[4]; //一级字符指针的数组
char **arr3[5];//二级字符指针的数组
4. 数组传参和指针传参
接下来我们讲讲关于数组和指针在传参方面相关的使用。对于一般的类型,传参很容易,相应的类型定义相应的参数即可,但数组和指针也和它们是同一种方式吗?
1.一维数组传参
我们来看看下面的代码
#include <stdio.h>
void test(int arr[])//ok?
{}
void test(int arr[10])//ok?
{}
void test(int *arr)//ok?
{}
void test2(int *arr[20])//ok?
{}
void test2(int **arr)//ok?
{}
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);
}
//总结:二维数组传参,函数形参的设计只能省略第一个[]的数字。
//因为对一个二维数组,可以不知道有多少行,但是必须知道一行多少元素。
//这样才方便运算。
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]);
//一级指针p,传给函数
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;
}