文章目录
1. 字符指针
在指针的类型中我们知道有一种指针类型为字符指针 char* ;用来存放字符变量的地址。
(1)一般使用:
int main()
{
char ch = 'w';
char *pc = &ch;
*pc = 'w';
return 0;
}
(2)还有一种使用方式如下:
int main()
{
const char* pstr = "hello word";
printf("%s\n", pstr);
return 0;
}
代码 const char* pstr = “hello word”,并不是把字符串都放进到指针变量里,而是把字符串的首元素地址放到了指针变量里。
(3)那就有可这样的面试题:
#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;
}
运行程序我们可以发现:
到底为什么结果是这样呢?
2. 指针数组——是数组
字符数组——存放字符的数组
整型数组——存放整型的数组
指针数组——存放指针的数组
int* arr1[5]//存放整型指针
char* arr2[5]//存放字符指针
//模拟使用一个二维数组
int main()
{
int arr1[] = { 1,2,3,4,5 };
int arr2[] = { 2,3,4,5,6 };
int arr3[] = { 3,4,5,6,7 };
//int* int* int*
//指针数组
int* arr[] = { arr1, arr2, arr3 };
int i = 0;
for (i = 0; i < 3; i++)
{
int j = 0;
for (j = 0; j < 5; j++)
{
printf("%d ", arr[i][j]);
}
printf("\n");
}
return 0;
}
3. 数组指针——是指针
3.1 数组指针的定义
整形指针: int * pint; 能够指向整形数据的指针。
浮点型指针: float * pf; 能够指向浮点型数据的指针。
那数组指针应该是:能够指向数组的指针。
下面代码哪个是数组指针?
int *p1[10];
int (*p2)[10];
//p1, p2分别是什么?
int (*p)[10];
//解释:p先和*结合,说明p是一个指针变量,然后指着指向的是一个大小为10个整型的数组。
所以p是一个指针,指向一个数组,叫数组指针。
//这里要注意:[]的优先级要高于*号的,所以必须加上()来保证p先和*结合
//p1为指针数组,里面放了10个指针变量
3.2 &数组名VS数组名
先看这样一段代码
#include <stdio.h>
int main()
{
int arr[10] = {0};
printf("%p\n", arr);
printf("%p\n", &arr);
return 0;
}
这里我们分别打印两者的地址,你会发现这两个地址是一样的,难道真的就没有区别吗?稍安勿躁,我们继续再看下面的代码
#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是整个数组的地址,因此arr+1是跳过4个字节,而&arr+1是跳过40个字节。
数组名的理解
数组名是数组首元素的地址
但是存在2个例外:
1. sizeof(数组名),这里的数组名表示整个数组,sizeof(数组名)计算的是整个数组的大小,
单位是字节
2/ &数组名,这里的数组名表示整个数组,取出的是数组的地址
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 (int j = 0; j < col; j++)
{
printf("%d ", arr[i][j]);
}
printf("\n");
}
int main()
{
int arr[3][5] = { {1,2,3,4,5},{2,3,4,5,6 }, {3,4,5,6,7} };
print_arr1(arr, 3, 5);
return 0;
}
这是我们平时输出二维数组的一个函数,这在我们之前的扫雷游戏中就有体现,传参print_arr1(arr, 3, 5);,形式参数·用(int arr[3][5], int row, int col)来接收。但实际上这里只是为了我们初学者方便理解,才进行用二维数组来接收的,实际上是数组指针的形式
我们再来看另一段代码:
void print_arr2(int(*arr)[5], int row, int col)
{
int i = 0;
for (i = 0; i < row; i++)
{
for (int j = 0; j < col; j++)
{
printf("%d ", arr[i][j]);
}
printf("\n");
}
}
//数组名arr,表示首元素的地址
//但是二维数组的首元素是二维数组的第一行
//所以这里传递的arr,其实相当于第一行的地址,是一维数组的地址,
可以数组指针来接收
int main()
{
int arr[3][5] = { {1,2,3,4,5},{2,3,4,5,6 }, {3,4,5,6,7} };
print_arr2(arr, 3, 5);
return 0;
}
学了指针数组和数组指针我们来一起回顾并看看下面代码的意思:
int arr[5];
//这个就是数组,存储了5个整型元素的整型数组,类型为int
int *parr1[10];
//这个为数组指针,存储了10个整型指针类型的数组,类型为int*
int (*parr2)[10];
//这个为指针数组,,指向10个元素的数组
int (*parr3[10])[5];
//这个为数组,里面放的是指向数组指针的地址,数组指针指向的数组有10个元素,而这个数组有5个元素
//类型有5个元素
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*,所以为int* *
{}
int main()
{
int arr[10] = { 0 };
int* arr2[20] = { 0 };
test(arr);
test2(arr2);
}
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);
}
4.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.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;
}
同理 ,当函数的参数为二级指针的时候,可以接收什么参数?
5. 函数指针
函数指针中&函数名和函数名没有什么区别,这个与数组名不一样,如下图
一方面从地址方面进行了论述,另一方面从使用上也说明了两者没有区别。
(上面例子均有一个Add函数,就是实现a+b的)
这个函数指针的定义类似数组指针。
int Add(int x, int y)
{
return x + y;
}
int main()
{
int(*pf2)(int,int)=Add;//定义,先是指针(*pf2),然后是参数(int,int),
//最后是函数的返回类型int
int ret=(*pf2)(3,5)//使用,理解来看,pf2是函数的地址,(*pf2) 就是使用pf2的地址
//但是上面例子显示,没有*也是可以的
int ret=pf2(3,5);
//这样也是可以的,这也就是函数使用的本质Add(2,3)
}
再看下面这一段代码
//代码1
(*(void (*)())0)();
这个代码就是在调用0地址处的函数,函数没有参数。
首先从0这个地方入手,0的类型是整型,而(viod(*)())将0强制类型转化为函数指针类型,说明0地址处有函数,用这个指针变量解引用,去访问0这个函数。然后没有参数
//代码2
void (*signal(int , void(*)(int)))(int);
这个代码是一次函数声明,声明的是signal函数
还是从signal这儿入手,这个函数的参数有2个:
第一个是int类型
第二个是函数指针类型,该类型是void(*)(int), 该函数指针指向的函数,参数是int,返回类型是viod
signal函数的返回类型也是函数指针类型,该类型是void(*)(int)
,该函数指针指向的函数,参数是int,返回类型是void
我们再来思考一个问题代码二这么复杂,我们该如何去简化这个代码
typedef void (*pfun_t)(int);
pfun_t signal(int, pfun_t);
宏定义一个函数,注意的是函数指针类型,需要把名字写到()里面