指针(进阶)

指针详解

  • 二级指针
  • 字符指针
  • 数组指针
  • 指针数组
    • &数组名VS数组名
  • 函数指针
  • 函数指针数组
  • 指向函数指针数组的指针
  • 回调函数

指针的相关基础知识:
1. 指针就是个变量,用来存放地址,地址唯一标识一块内存空间
2. 指针的大小是固定的4/8个字节(32位平台/64位平台)
3. 指针是有类型,指针的类型决定了指针的+-整数的步长,指针解引用操作的时候的权限
4. 指针的运算

二级指针

指针变量也是变量,是变量就有地址,那指针变量的地址存放在哪里?
指针(进阶)_第1张图片
对于二级指针的运算有:
*ppa 通过对ppa中的地址进行解引用,这样找到的是 pa , *ppa 其实访问的就是 pa
在这里插入图片描述
**ppa 先通过 *ppa 找到 pa ,然后对 pa 进行解引用操作: *pa ,那找到的是 a .
指针(进阶)_第2张图片

字符指针

字符指针一般有两种应用场景:

int main()
{
  char ch = 'w';
  char *pc = &ch;
  *pc = 'w';
  return 0;
}
int main()
{
  const char* pstr = "hello bit.";//把一个常量字符串的首字符 h 的地址存放到指针变量 pstr 中
  printf("%s\n", pstr);
  return 0;
}

指针(进阶)_第3张图片
看看下面这个代码输出结果是什么:

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;
}

指针(进阶)_第4张图片
这里str3和str4指向的是一个同一个常量字符串,C/C++会把常量字符串存储到单独的一个内存区域,当几个指针指向同一个字符串的时候,他们实际会指向同一块内存,但是用相同的常量字符串去初始化,不同的数组的时候就会开辟出不同的内存块,所以str1和str2不同,str3和str4不同

数组指针

指针数组是一个存放指针的数组

int* arr1[10]; //整形指针的数组
char *arr2[4]; //一级字符指针的数组
char **arr3[5];//二级字符指针的数组

指针数组

整形指针: int * pint; 能够指向整形数据的指针
浮点型指针: float * pf; 能够指向浮点型数据的指针
那数组指针应该是:能够指向数组的指针
int ( * p)[10];
p先和结合,说明p是一个指针变量,然后指着指向的是一个大小为10个整型的数组,所以p是一个指针,指向一个数组,叫数组指针
这里要注意:[]的优先级要高于
号的,所以必须加上()来保证p先和*结合

&数组名VS数组名

数组名是数组首元素地址,那&数组名是什么呢?

#include 
int main()
{
  int arr[10] = {0};
  printf("%p\n", arr);
  printf("%p\n", &arr);
  return 0;
}

指针(进阶)_第5张图片
可见数组名和&数组名打印的地址是一样的
难道两个是一样的吗?
我们再看一段代码:

#include 
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;
}

指针(进阶)_第6张图片
根据上面的代码我们发现,其实&arr和arr,虽然值是一样的,但是意义应该不一样的,实际上: &arr 表示的是数组的地址,而不是数组首元素的地址,本例中 &arr 的类型是: int(*)[10] ,是一种数组指针类型,数组的地址+1,跳过整个数组的大小,所以 &arr+1 相对于 &arr 的差值是40

函数指针

#include 
void test()
{
	printf("hehe\n");
}
int main()
{
	printf("%p\n", test);
	printf("%p\n", &test);
return 0;
}

指针(进阶)_第7张图片
输出的是两个地址,这两个地址是 test 函数的地址
那我们的函数的地址要想保存起来,怎么保存?
下面我们看代码:

void test()
{
	printf("hehe\n");
}
void (*pfun1)();

pfun1先和*结合,说明pfun1是指针,指针指向的是一个函数,指向的函数无参数,返回值类型为void

函数指针数组

数组是一个存放相同类型数据的存储空间,那我们已经学习了指针数组,
比如:

int *arr[10];
//数组的每个元素是int*

那要把函数的地址存到一个数组中,那这个数组就叫函数指针数组,那函数指针的数组如何定义呢?

int (*parr1[10])();

parr1 先和 [] 结合,说明 parr1是数组,数组的内容是int (*)() 类型的函数指针
使用函数指针数组的实现计算器:

#include 
int add(int a, int b)
{
     return a + b;
}
int sub(int a, int b)
{
     return a - b;
}
int mul(int a, int b)
{
     return a*b;
}
int div(int a, int b)
{
     return a / b;
}
int main()
{
  int x, y;
  int input = 1;
  int ret = 0;
  int(*p[5])(int x, int y) = { 0, add, sub, mul, div }; //转移表
  while (input)
  {
     printf( "*************************\n" );
     printf( " 1:add      2:sub \n" );
     printf( " 3:mul      4:div \n" );
     printf( "*************************\n" );
     printf( "请选择:" );
   	 scanf( "%d", &input);
     if ((input <= 4 && input >= 1))
     {
        printf( "输入操作数:" );
        scanf( "%d %d", &x, &y);
        ret = (*p[input])(x, y);
     }
     else
       printf( "输入有误\n" );
       printf( "ret = %d\n", ret);
  }
   return 0;
}

指向函数指针数组的指针

指向函数指针数组的指针是一个 指针
指针指向一个 数组 ,数组的元素都是 函数指针

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[0] = test;
	//指向函数指针数组pfunArr的指针ppfunArr
	void (*(*ppfunArr)[5])(const char*) = &pfunArr;
	return 0;
}

回调函数

回调函数就是一个通过函数指针调用的函数,如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数,回调函数不是由该函数的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进行响应
使用回调函数,模拟实现qsort,因为不确定类型,所以使用void*

#include 
int int_cmp(const void * p1, const void * p2)
{
 	return (*( int *)p1 - *(int *) p2);
}
void _swap(void *p1, void * p2, int size)
{
  	int i = 0;
  	for (i = 0; i< size; i++)
 	{
    	char tmp = *((char *)p1 + i);
   		*(( char *)p1 + i) = *((char *) p2 + i);
   		*(( char *)p2 + i) = tmp;
 	}
}
void bubble(void *base, int count , int size, int(*cmp )(void *, void *))
{
  	int i = 0;
  	int j = 0;
  	for (i = 0; i< count - 1; i++)
 	{
   		for (j = 0; j<count-i-1; j++)
   		{
      		if (cmp ((char *) base + j*size , (char *)base + (j + 1)*size) > 0)
     		{
       			_swap(( char *)base + j*size, (char *)base + (j + 1)*size, size);
     		}
   		}
 	}
}
int main()
{
  	int arr[] = { 1, 3, 5, 7, 9, 2, 4, 6, 8, 0 };
  	//char *arr[] = {"aaaa","dddd","cccc","bbbb"};
  	int i = 0;
  	bubble(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;
}

总结:
数组名的意义:

  1. sizeof(数组名),这里的数组名表示整个数组,计算的是整个数组的大小
  2. &数组名,这里的数组名表示整个数组,取出的是整个数组的地址
  3. 除此之外所有的数组名都表示首元素的地址

你可能感兴趣的:(c语言)