文章目录
一、指针数组
我们可以先来回顾一下整形数组和字符数组。
- 整形数组:
是存放整形的数组
- 字符数组:
是指存放字符的数组
类比上面两个例子之后,我们可以知道指针数组就是存放指针的数组,指针数组的每一个元素都是用来存放地址(指针)的。
二、指针数组来模拟二维数组
首先我们需要知道数组名是指数组首元素的地址
#include<stdio.h>
int main()
{
int str[] = { 1,2,3,4,5 };
int str1[] = { 3,4,5,6,7 };
int str2[] = { 5,6,7,8,9 };
int* pa[3] = { str,str1,str2 };
for (int i = 0; i < 3; i++)
{
for (int j = 0; j < 5; j++)
{
printf("%d ", *(*(pa + i)) + j);
}
printf("\n");
}
return 0;
}
我们可以得到如下的结果:
第一次的for循环是循环的行,第二次的dor循环也就是列,数组名就是数组的首元素,我们这里的pa[i]找到数组元素指向的是整形一维数组,pa[i][j]就是指向整形一维数组的元素。
模拟出⼆维数组的效果,实际上并非完全是⼆维数组,因为每⼀行并非是连续的
画个图方便大家理解:
三、字符指针变量
我们知道字符指针是char*
常如下使用:
#include<stdio.h>
int main()
{
char p = 'e';
char* s = &p;
printf("%c\n", *s);
*s = 't';
printf("%c\n", *s);
return 0;
}
结果会分别打印出:e 和 t 。
当然还有其他适用方式:
#include<stdio.h>
int main()
{
char str[] = "hello world!!!";
char* pa = str;
printf("%s\n", pa);
return 0;
}
注意:
这里的hello world!!!
并没有放在指针变量pa
中,而是把这个字符串的首字符的地址放在了pa
中。
pa
也就是调用字符串中首字符的地址,而字符串的地址也是首字符元素的地址。
字符串的例子(曾经的一道笔试题)
#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 and str2 are not same
和str3 and str4 are same
我们大致来分析一下:
str1
和str2
都是存的相同的字符串内容,在内存中str1
和str2
是开辟了不同的空间存放相同的内容的,实质上完全没有必要存相同的内容开辟两个空间,两个空间的地址完全不一样,所以会打印出str1 and str2 are not same
;而str3
和str4
指向的是⼀个同⼀个常量字符串,C/C++会把常量字符串存储到单独的⼀个内存区域,指针指向同⼀个字符串的时候,他们实际会指向同⼀块内存,所以这里会打印出str3 and str4 are same
。
四、数组指针变量
1、什么是数组指针变量
首先我们可以来回顾一下:整形指针变量和浮点型指针变量
-
整形指针变量:
存放的是整型变量的指针,能够指向整形数据的指针。
形如:int* pa
-
浮点型指针变量:
存放的是浮点型变量的指针,能够指向浮点型数据的指针。
形如:float* pa
那么数组指针变量就是存放的是数组的地址,指向数组数据的指针。
形如:int(*p)[10]
这里的p是自己命名,10 也是根据自己写的代码来写。
p
先和*结合,所以p
是一个指针变量,指向的是一个大小为10个整形的数组。
综上:p是一个指针,指向一个数组,这就是数组指针。
2、数组指针怎么初始化
数组指针变量是⽤来存放数组地址的
int arr[5]={0};
&arr得到的就是数组的地址
int (*p)[5]=&arr//这个数组的地址存放在数组指针变量中
五、二维数组传参的本质
(二维数组我们可以看成许多一维数组的组成)
首先我们知道数组名是数组首元素的地址,那么⼆维数组的数组名表示的就是第⼀行的地址,也就是一维数组的地址,⼆维数组传参本质上也是传递了地址,传递的是第一行这个⼀维数组的地址
我们形参用指针写的形式如下:
#include<stdio.h>
void Shuzu(int(* pa)[5], int a, int b)
{
for (int i = 0; i < a; i++)
{
for (int j = 0; j < b; j++)
{
printf("%d ", *(*(pa + i) + j));
}
printf("\n");
}
}
int main()
{
int arr[3][5] = { {1,2,3,4,5},{6,7,8,9,10},{11,12,13,14,15} };
Shuzu(arr, 3, 5);
return 0;
}
当然形参我们也可以数组的形式写:
#include<stdio.h>
void Shuzu(int p[][5], int s, int c)
{
for (int i = 0; i < s; i++)
{
for (int j = 0; j < c; j++)
{
printf("%d ", p[i][j]);
}
printf("\n");
}
}
int main()
{
int arr[3][5] = { {1,2,3,4,5},{6,7,8,9,10},{11,12,13,14,15} };
Shuzu(arr, 3, 5);
return 0;
}
六、函数指针
1、什么是函数指针变量
通过上面的种种类比我们知道了什么是数组指针,整形指针,那么我们可以知道函数指针变量就是用来存放函数地址的。
2、函数的地址
首先我们可以简单的写一个代码,看看函数是否有地址:
#include<stdio.h>
void Ad()
{
printf("abder\n");
}
int main()
{
Ad();
printf("&Ad=%p\n", &Ad);
printf("Ad=%p\n", Ad);
return 0;
}
由结果我们可以知道函数有地址,函数名就是我们的地址
如果我们要将函数的地址存放起来,就得创建函数指针变量:
#include<stdio.h>
int Add(int x, int y)
{
return x + y;
}
int(*pa)(int , int ) = Add;//&Add
其中==int(*)(int,int)==就是pa函数指针变量的类型。
3、使用
#include<stdio.h>
int Add(int x, int y)
{
return x + y;
}
int main()
{
int (*pa1)(int ,int) = Add;
printf("%d\n", (*pa1)(2,4));
printf("%d\n", pa1(5,6));
return 0;
}
可以得出答案是6和11。
为什么第二个打印pa1可以不解引用?
因为pa1这个指针变量存放的是Add的地址,&符号与*相互直接抵消了,所以可以不使用解引用符号。
4、 typedef
关键字
typedef是用来类型重命名的
比如:
singed int 可能你觉得每次书写比较麻烦,你就可以直接有时候typedef来重命名
也就是:typedef singed int ty_t,这个时候,想要再使用singed int 就可以直接用ty_t替换,
本质上是一样的
1.但是函数指针的重命名有些许区别:
比如,我们想要把int(*pa)(int )
重命名为pa_t
:
typedef int(*pa_t)(int); //新类型的名字必须在*号的右边
2.数组指针重命名也是如此:
比如:我们需要把int(*pc)[10]
重命名为py_y
typedef int(*py_y)[10]; //新类型的名字也需要在*号的右边
结束语
这次的内容可能有些多,但是比较好理解,希望大家可以拿到自己想要的offer,最后,如果文章有什么打错字的地方,希望大家不要介意,也希望大家可以给我提出来。
最后,祝大家每天都开开心心,我们下次再见