前言
C语言是指针最重要的一部分不要觉得很难其实十分有趣,一定要看下去!
重温指针
1. 指针就是个变量,用来存放地址,地址唯一标识一块内存空间。
2. 指针的大小是固定的4/8个字节(32位平台/64位平台)。
3. 指针是有类型,指针的类型决定了指针的+-整数的步长,指针解引用操作的时候的权限。
4. 指针的运算。
1:字符指针
概念:指向字符类型的指针叫做字符指针,字符指针 char* ;
一般使用:
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;
}
在这串代码中我们很容易把“hello bit”这串字符完全赋值给char*pstr其实不然我们复制的是首元素的地址也就是h
我们来看下面这段代码加深一下理解
#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:指针数组
概念:指向数组的指针(用来存放指针的数组)
那指针数组有什么实际用法呢?下面来看一个实际用法:
#include<stdio.h>
int main()
{
int arr[5] = { 1,2,3,4,5 };
int arr1[5] = { 2,3,4,5,6 };
int arr2[5] = { 5,3,4,5,6 };
int* parr[3] = { arr,arr1,arr2 };
for (int i = 0;i < 3;i++)
{
for (int j = 0;j < 5;j++) {
printf("%d",parr[i][j]);
}
printf("\n");
}
}
我们可以用指针来模仿二维数组
3:数组指针
3.1:含义
数组指针-指向数组的指针 int (*ptr)[2]{}
整形指针-指向整形的指针 int* arr;
字符指针-指向字符的指针 char* ch;
数组指针是指针?还是数组?
答案是:指针。
我们已经熟悉:
整形指针: int * pint; 能够指向整形数据的指针。
浮点型指针: float * pf; 能够指向浮点型数据的指针。
那数组指针应该是:能够指向数组的指针
我们来看一段代码
int main()
{
int *arr[5] = {};
int(*arr)[5] = {};
}
//这里要注意:[]的优先级要高于*号的,所以必须加上()来保证p先和*结合。
第一个是指针数组
第二个是数组指针 arr与*结合成为指针然后这个数组元素有5个类型是整行并且指向数组所以是数组指针
官方解释;:解释:p先和*结合,说明p是一个指针变量,然后指着指向的是一个大小为10个整型的数组。所以p是一个指针,指向一个数组,叫数组指针
我们再来看第二个问题:数组指针存放的数组的地址是什么数组的地址呢?答&arr
来看一段代码
思考一下:我们&arr的地址看似和我们arr的地址很相似但是我们对其同时加一arr跳动的是四个字节但是&arr跳动的是四十个字节恰好是&arr的长度
结论:&arr 表示的是数组的地址,而不是数组首元素的地址。(细细体会一下)
本例中 &arr 的类型是: int(*)[10] ,是一种数组指针类型
数组的地址+1,跳过整个数组的大小,所以 &arr+1 相对于 &arr 的差值是40
int main()
{
int *arr[5] = { 0 };
int*(*pc)[5] = &arr;//数组指针地址用指针数组指针来存放
char c = 'cc';
char *cc= &c;
char** ccc = &cc;
}
3.2:数组指针用法
先讲一个错误用法
在这里我们必须把10填进去这样才错误
那么现在来看我们用法这么长的代码不要怕仔细看写的是俩个打印函数我们来看看有哪些区别,分别是通过数组接受数据来打印,还有一个是通过数组指针来接受
#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;
}
//数组名arr,表示首元素的地址
//但是二维数组的首元素是二维数组的第一行
//所以这里传递的arr,其实相当于第一行的地址,是一维数组的地址
//这个时候我们就可以数组指针来接收
来看看这个分析
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);
}
我们来看这些传参是否可以首先前三个应该毋庸置疑都可以我们重点来看test2
首先第一个我们传参的数组是int*那么我们用int*指针来接受当然没问题
第二个我们写成二级指针的样子同样也可以因为我们数组传参本质上是二维数组的首地址所以用二级指针来接受当然没问题
4.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);
}
第二个不可以因为二维数组进行传参的时候可以省略咧不可以省略行
二维数组传参传的是二维数组的第一行是有五个整形的数很显然第四个接受不了
第五个也不行它首先与数组结合然后在和int*结合这是一个指针数组
最后一个当然不可以因为是二级指针
思考:
当一个函数的参数部分为一级指针的时候,函数能接收什么参数?
答:一级指针,整形地址,数组地址
5:函数指针(重点)
5.1:概念
首先看一段代码:
#include <stdio.h>
void test()
{
printf("hehe\n");
}
int main()
{
printf("%p\n", test);
printf("%p\n", &test);
return 0;
}
输出的是两个地址,这两个地址是 test 函数的地址。
那我们的函数的地址要想保存起来,怎么保存?
我们来调用一下
5.2用法
具体用法我们请看下章节
. 任何东西的价格都可能会跌,但人类智慧的价格会持续上涨。每个人的最佳投资策略
是投资于自己的身体和大脑,比如通过学习C语言让自己充满智慧