1.字符指针
char arr[] = "hello bit."
char * p = arr
这里p指向的是数组的首元素,arr数组是可以修改的。
(const)char * pstr = "hello bit."
这里的字符串是常量字符串,不能修改。
这里有一个面试题:
#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("str1 and str2 are same\n");
else
printf("str1 and str2 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.数组指针
数组指针就是一个指向数组的指针。
int (*p1) [10]; //这是一个数组指针,指针指向一个整型数组, 这个数组有10个元素。
注意:这里的数组指针存放的不是数组的首元素地址,而是一整个数组的地址。
相当于
int arr[10];
int (*p1)[10] = &arr; //注意这里是对数组名取地址,而不是数组名。
4.数组参数、指针参数
4.1一维数组传参
#include <stdio.h>
//一维数组传参时,形参可以是数组也可以是一级指针。
void test(int arr[]) {} √
void test(int arr[10]){} √
void test(int * arr){} √
//指针数组传参时,形参可以是指针数组,也可以是二级指针。
void test2(int * arr[20]){} √
void test2(int ** arr){} √
4.2二维数组传参
#include <stdio.h>
//二维数组传参时,形参可以是数组指针,也可以是二维数组。
void test(int arr[3][5]){} √
void test(int arr[][]) {} ×
void test(int arr[][5]) {} √
void test(int (*arr)[5]){} √
思考:当函数的参数为二级指针的时候,可以接收什么参数?
二级指针变量
一级指针变量的地址
数组指针
5.函数指针
先看一段代码:
#include <stdio.h>
void test()
{
printf("hehe\n");
}
int main()
{
printf("%p\n",test);
printf("%p\n",&test);
}
这里的输出结果是:
所以我们得出结论:
函数名和&函数名都代表函数地址,没有区别。
函数指针的使用:
#include <stdio.h>
int Add(int x, int y)
{
return x+y;
}
int main()
{
int (*pf)(int ,int) = Add; //可以直接将函数名赋值过去
int ret = (*pf)(3,5); //可以将*省略,直接写成pf(3,5)。
printf("%d\n",ret);
}
结果:
6.函数指针数组
要把函数的地址存到一个数组中,那这个数组就叫做函数指针数组,那函数指针的数组应该怎么定义呢?
int (*parr1 [10]) ( )
拆解一下:这里(小黄)parr1[10]表示它是一个数组,数组的每个元素都是什么类型呢?
一般一个数组声明除了数组名和元素个数之外就是数组的元素类型了,所以这里的小绿(函数指针)就是类型。
函数指针数组的用途:转移表
7.指向函数指针数组的指针
指向函数指针数组的指针是一个指针,指向一个数组,数组的每个元素都是一个函数指针。
int (*pf) (int, int) = Add; //函数指针
int (*pf [4]) (int, int) = {Add, sub}; //指针数组,数组的每个元素都是一个函数指针
int (* (*pf) [4] ) (int ,int); //数组指针,指向的是一个函数指针数组
8.回调函数
回调函数就是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为函数传递给另一个函数,当这个指针被用来调用其指向的函数时,我们就说这是回调函数。回调函数不是由该函数的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进行相应。
指针和数组笔试题解析:
注意:strlen计算的是'\0'之前的字符,并且只能接收地址参数,而sizeof计算的有效字符,可以传地址,也可以传值,'\0'算在其中。
一维数组:
int a [ ] = {1,2,3,4};
printf ("%d\n",sizeof(a)); //16
printf("%d\n",sizeof(a+0)); //4
printf("%d\n",sizeof(*a)); //4(*a=a[0])
printf("%d\n",sizeof(a+1)); //4
printf("%d\n",sizeof(a[1])); //4
printf("%d\n",sizeof(&a)); // 4/8 取出的是数组的地址,只要是地址,都是4/8
printf("%d\n",sizeof(*&a)); //16( sizeof(a) )
printf("%d\n",sizeof(&a+1)); // 4/8
printf("%d\n",sizeof(&a[0])); //4/8
printf("%d\n",sizeof(&a[0]+1)); //4/8
字符数组:
char arr[] = {'a', 'b', 'c' , 'd', 'e', 'f'};
printf("%d\n",sizef(arr)); //6
printf("%d\n",sizef(arr+0)); //4/8
printf("%d\n",sizef(*arr)); //1
printf("%d\n",sizef(arr[1])); //1
printf("%d\n",sizef(&arr)); //4/8
printf("%d\n",sizef(&arr+1)); //4/8
printf("%d\n",sizef(&arr[0]+1)); //4/8
printf("%d\n",strlen(arr)); //不确定(>=6),strlen统计的是'\0'之前的字符个数
printf("%d\n",strlen(arr+0)); //不确定(>= 5)
printf("%d\n",strlen(*arr)); //将arr[0]传进来,字符ASCII码被强制当作地址,造成非法访问。
printf("%d\n",strlen(arr[1])); //同上
printf("%d\n",strlen(&arr)); //不确定
printf("%d\n",strlen(&arr+1)); //跳过字符串,也是不确定
printf("%d\n",strlen(&arr[0]+1)); //不确定
char arr[] = "abcdef"
printf("%d\n",sizeof(arr)); //7(加上'\0')
printf("%d\n",sizeof(arr+0)); //4/8
printf("%d\n",sizeof(*arr)); //1
printf("%d\n",sizeof(arr[1])); //1
printf("%d\n",sizeof (&arr)); //4/8
printf("%d\n",strlen(&arr+1)); //4/8
printf("%d\n",sizeof(&arr[0]+1)); //4/8
printf("%d\n",strlen(arr)); //6
printf("%d\n",strlen(arr+0)); //6
printf("%d\n",strlen(*arr)); //将arr[0]强制转换成地址变量,造成地址的非法访问。
printf("%d\n",strlen(arr[1])); //非法访问
printf("%d\n",strlen(&arr)); //6
printf("%d\n",strlen(&arr+1)); //跳过整个数组,包括'\0',所以是随机值。
printf("%d\n",strlen(&arr[0]+1)); //5
(const) char *p = "abcdef";
printf("%d\n",sizeof(p)); //4/8
printf("%d\n",sizeof(p+1)); //4/8
printf("%d\n",sizeof(*p)); //1
printf("%d\n",sizeof(p[0])); //1
printf("%d\n",sizeof(&p)); //4/8
printf("%d\n",sizeof(&p+1)); //4/8
printf("%d\n",sizeof(&p[0])); //4/8
printf("%d\n",strlen(p)); //6
printf("%d\n",strlen(p+1)); //5
printf("%d\n",strlen(*p)); //非法访问
printf("%d\n",strlen(p[0])); //非法访问
printf("%d\n",strlen(&p); //随机值 (无法确定'\0'的位置)
printf("%d\n",strlen(&p+1)); //随机值
printf("%d\n",strlen(&p[0]+1)); //5
二维数组:
int a[3][4] = {0};
printf("%d\n",sizeof(a)); //48
printf("%d\n",sizeof(a[0][0])); //4
printf("%d\n",sizeof(a[0])); //16
printf("%d\n",sizeof(a[0]+1)); //4/8
printf("%d\n",sizeof(*(a[0]+1))); //4
printf("%d\n",sizeof(a+1)); //4/8
printf("%d\n",sizeof(*(a+1))); //4
printf("%d\n",sizeof(&a[0]+1)); //4/8
printf("%d\n",sizeof(*(&a[0]+1))); //16
printf("%d\n",sizeof(*a)); //16
printf("%d\n",sizeof(a[3])); //16 (sizeof内部的表达式是不会计算的,不会越界。)