前篇引要
- 指针就是个变量,用来存放地址,地址唯一标识一块内存空间
- 指针的大小就是固定的4/8个字节(32位平台/64位平台)
- 指针是有类型,指针的类型决定了指针加减整数的步长。
字符指针
char *
int main()
{
char ch='w';
char *pc=&ch;
char *pstr="hello bit";//字符串hello bit的首地址放到pstr中
return 0;
}
面试题
int main()
{
char str1[]="hello bit";
char str2[]=""hello bit";
char *str3="hello bit";
char *str4="hello bit";
if(str1==str2)
{
printf("str1和str2相同);
}else{
printf("str1和str2不相同");
}
if(str3==str4)
{
printf("str3和str4相同");
}else{
printf("str3和str4不相同");
}
return 0;
}
C/C++会把字符串常量存储到单独的一个内存区域。
几个指针指向同一字符串的时候,实际会指向同一内存,同时这几个指针变量里的值都是字符串的首地址,因此str3==str4
相同的常量字符串取初始化不同的数组的时候就会开辟出不同的内存栈,所以str1!=str2
指针数组
指针数组是一个存放指针的数组
int *arr1[10];//征信指针数组
char *arr2[4];//一级字符指针数组
char **arr3[5];//二级字符指针数组
数组指针
数组指针是指向数组的指针
&arr和arr在数值上一样但意义不一样,&arr表示的死数组的地址arr是数组首元素的地址
arr+1加的是数组元素类型的大小&arr+1加的整个数组的大小
#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);
pirntf("&arr+1=%p\n",&arr+1);
}
运行例图:不一定相同用于分析地址的变化
arr+1加元素类型的大小故变化4个字节
&arr+1加整个数组的大小故变化40个字节
数组传参
一维数组传参,函数参数可以省略中括号的数组
二维数组及之上的多维数组,函数参数的设计只能省略第一个[]的数字因为
对一个二维数组而言,可以不知道有多少行,但必须知道一行有多少元素这样才方便运算。
当一个函数的参数部分为一级指针时,函数能接受一级指针,数组
当函数的参数为二级指针时,可以接受一级指针数组,二级指针,一级指针变量取地址(二级指针)
一级指针传参
#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, 10 };
int *p = arr;
int sz = sizeof(arr) / sizeof(arr[0]);
print(p, sz);
print(arr, sz);
}
二级指针传参
#include<stdio.h>
void test(int **ptr){
printf("num=%d\n", **ptr);
}
int main()
{
int n = 10;
int *p = &n;
int **pp = &p;
int *arr[10] = {0};
test(pp);
test(&p);
test(arr);//这个参数传不了,实参arr为一级指针和形参二级指针类型不匹配
return 0;
}
数组名是变量吗
答:变量的定义是一段命名的内存空间。但数组名是不能统一地说它是不是变量的,因为数组名有些情况下代表数组对象本身,这时候它是一个变量,例如数组对象的定义、sizeof的操作数、&运算符的操作数,这些情况下数组名是一个变量;但除上述情况外,表达式中的数组名都不是变量,因为此时数组名被转换为一个符号地址,不代表任何地址空间,并不是对象,因此不是变量
函数指针
- 函数名和函数名取地址都表示函数的地址
int main()
{
printf("%p\n",test);
printf("%p\n",&test)
}
趣味实例
//代码1
(*void(*)()0)();
//代码2
void (*signal)(int,void(*)(int)))(int);
//代码3
typedef void(*pfun_t)(int);//重定义后pfun_t就是类型名不是变量名
pfun_t signal(int,pfun_t);
指向函数指针数组的指针
指向函数指针数组的指针是一个指针,指针指向一个数组,数组的元素是函数指针。
void test(const char *str)
{
printf("%s\n",str);
}
int main()
{
//函数指针pfun
void (*pfun)(const char *)=test;
//函数指针的数组pfunArr
void (*pfunArr[5])(const char *str);
//指向函数指针数组pfunArr的指针ppfunArr
void (*(ppfunArr)[10])(const char *)=&pfunArr;
return 0;
}
回调函数
回调函数就是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。回调函数不是由该函数的实现方法直接调用,而是在特定的事件或条件发生时由另一方调用的,用于对该事件或条件进行响应。
C语言中有一个快速排序的标准库函数 qsort ,在stdlib.h 中声明。
定义如下:
void qsort(void *base, int nelem, unsigned int width, int ( * pfCompare)( const void *, const void *));
参数解读:
- base是待排序数组的起始地址
- nelem是待排序数组的元素个数
- width是待排序数组的每个元素的大小单位是字节
- pfCompare是一个函数指针,指向一个比较函数
比较函数
“比较函数”的原型是:int 函数名(const void * elem1, const void * elem2);该函数的两个参数,elem1 和elem2,指向待比较的两个元素。也就是说, * elem1 和* elem2 就是待比较的两个元素。
比较方法如下:
- 如果 * elem1 应该排在 * elem2 前面,则函数返回值是负整数(任何负整数都行)。
- 如果 * elem1 和* elem2 哪个排在前面都行,那么函数返回0。
- 如果 * elem1 应该排在 * elem2 后面,则函数返回值是正整数(任何正整数都行)。
qsort 函数执行期间,需要比较两个元素哪个应在前面时,就以两个元素的地址作为参数,调用 pfCompare 函数。根据返回值大小,默认从小到大排序。如果返回值等于0,则哪个在前都行。
qsort函数实现一个比较函数
#include<stdio.h>
int int_cmp(const void *p1,const void *p2)
{
return (*(int *)p1-*(int *)p2);
}
int main()
{
int arr[]={1,3,5,7,9,2,4,6,8,0};
int i=0;
qsort(arr,sizeof(arr)/sizeof(arr[0]),sizeof(int),int_cmp);
for(i=0;i<sizeof(arr)/sizeof(arr[0]);i++){
printf("%d",arr[i]);
}
printf("\n");
return 0;
}