前言
C语言中指针的重要性是:通过指针不仅可以对数据本身,还可以对存储数据的变量地址进行操作。指针能够帮助我们快速地传递数据,减少内存的耗用,还能够方便我们处理字符串。
一、指针是什么?
指针理解的2个要点:
- 指针是内存中一个最小单元的编号,也就是地址
- 平时口语中说的指针,通常指的是指针变量,是用来存放内存地址的变量
- 总结:指针就是地址,口语中说的指针通常指的是指针变量
二、指针的进阶
我们已经知道了指针的概念:
指针就是个变量,用来存放地址,地址唯一标识一块内存空间。
指针的大小是固定的4/8个字节(32位平台/64位平台)。
指针是有类型,指针的类型决定了指针的+-整数的步长,指针解引用操作的时候的权限。
1. 字符指针
在指针的类型中有一种指针类型为字符指针 char* ;
char ch = 'w';
char *pc = &ch;
*pc = 'w';
const char* pstr = "hello bit.";//这里是把一个字符串放到pstr指针变量里了吗?
printf("%s\n", pstr);
本质是把字符串 hello bit. 首字符的地址放到了pstr中,即是把一个常量字符串的首字符 h 的地址存放到指针变量 pstr 中。
看下面代码:
#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* arr1[10]; //整形指针的数组
char *arr2[4]; //一级字符指针的数组
char **arr3[5];//二级字符指针的数组
3.数组指针
3.1数组指针定义
数组指针是:能够指向数组的指针,还是一个指针变量
注意区分:
int *p1[10];
int (*p2)[10];
//这里p1, p2分别是什么?
对于p2来说:p先和*结合,说明p是一个指针变量,然后指着指向的是一个大小为10个整型的数组。所以p是一个 指针,指向一个数组,叫数组指针
注意::[ ]的优先级要高于号的,所以必须加上()来保证p先和结合。 即p1为指针数组
3.2 &数组名 和 数组名
int arr[10];
//arr 和 &arr 分别是啥?
我们知道arr是数组名,数组名表示数组首元素的地址。那&arr表示什么?看下面代码
#include <stdio.h>
int main()
{
int arr[10] = { 0 };
printf("arr = %p\n", arr);
printf("&arr= %p\n", &arr);
printf("arr+1 = %p\n", arr+1);
printf("&arr+1= %p\n", &arr+1);
return 0;
}
可见:&arr 表示的是数组的地址,而不是数组首元素的地址。
中&arr 的类型是: int(*)[10] ,是一种数组指针类型;数组的地址+1,跳过整个数组的大小,所以 &arr+1 相对于 &arr 的差值是40
3.3 数组指针的使用
既然数组指针指向的是数组,那数组指针中存放的应该是数组的地址。
我们一般这样使用:
#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;
}
**4. ****数组参数、指针参数 **
**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);
}
以上传参方式均可,即一维数组传参时,形参既可以是数组形式,也可以是指针形式。
**4.2 **二维数组传参
注:二维数组首元素的地址为第一行的地址,即整个一维数组的地址。
int main()
{
int arr[3][5] = { {1,2,3,4,5},{6,7,8,9,10},{10,11,12,13,14} }
test(arr);
}
//方式1
void test(int arr[3][5])
{
}
//方式2
void test(int arr[][5])
{
}
//方式3
void test(int (*p)[5])
{
}
总结:二维数组传参,函数形参的设计只能省略第一个[]的数字。
arr是二维数组的数组名,数组名代表首元素的地址。二维数组的首元素,就是二维数组的第一行。即arr就代表了二维数组中第一行的地址
**4.3 ****一级指针传参 **
void GetMemory(char** p, int num)
{
*p = (char*)malloc(num);
}
void test(void)
{
char* str = NULL;
GetMemory(&str, 100);
strcpy(str, "hello");
printf(str);
}
总结:如果是一个一级指针,那么我们就可以传一个相同类型的一级指针,或者传一个类型相同的地址过来;
**4.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;
}
void test(char **p)
{
}
int main()
{
char c = 'b';
char*pc = &c;
char**ppc = &pc;
char* arr[10];
test(&pc);
test(ppc);
test(arr);//Ok?
return 0;
}
总结:对一个形参为二级指针的函数,我们传参的时候可以传一个二级指针,或者传一个一级指针的地址,或传一个指针数组的数组名(指针数组的数组名就表示数组首元素地址,也就是指针数组里面的指针地址)
三、感谢与预告
- 以上就是指针的进阶上半部分,如果本文对你有所帮助,请帮忙点赞+收藏,如果有任何建议请评论留言,你的支持就是我更新的最大动力!🤞
- 预估下一篇文章《关于函数指针和函数指针数组的辨析》;😘
版权归原作者 Sid冲冲冲 所有, 如有侵权,请联系我们删除。