指针的一些用法及题目(未完待续)

  1. 字符指针

  2. 数组指针

  3. 指针数组

  4. 函数指针

  5. 函数指针数组

  6. 容易混淆的一些指针习题

  7. 字符指针

char* a="abcde";     //指针a保存的是字符串首元素的地址,而不是字符串的地址; 
                                  //注意这里是不能对 
                               //  指针指向的内容做改变的,因为指针指向的是一个常量,只读不能写
char  a[]="abcde"    
printf("%p",a+1);     //直接取数组名是数组首元素的地址,加1==a[1]的地址
printf("%p",&a+1)     //注意,这里是取数组地址加1,是跳过整个数组
  1. 数组指针和指针数组

数组指针不是数组而是指针,列如

int  (*p)[]        //(*p)加了括号之后*先和p结合,因此是指针,指向一个数组

指针数组
指针数组是指针
int *a[10]; //整形的指针数组,数组里保存的是指针

数组指针代码举例

#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");
	}
} 
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"); } }
  int main() {
	int arr[3][5] = { 1,2,3,4,5,6,7,8,9,10 };
	print_arr1(arr, 3, 5);
	//数组名arr,表示首元素的地址    
	//但是二维数组的首元素是二维数组的第一行    
	//所以这里传递的arr,其实相当于第一行的地址,是一维数组的地址    
	//可以数组指针来接收    
	print_arr2(arr, 3, 5);
	return 0;
  }

这两个函数的输出结果是一样的都是
1 2 3 4 5
6 7 8 9 10
0 0 0 0 0

  1. 函数指针
    如果想要保存一个函数的地址就需要创建一个函数指针
    以下哪个是函数指针?
 1   int  (*a)();
 2   int  *a();
  答案是1,1是 * 先和a结合所以为指针,能保存一个返回值为整形的,无参数的函数的地址,而第二种只是定义函数的返回值为int*类型,是函数.

函数指针的用途:回调函数
回调函数就是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个 函数,当这 个指针被用来调用其所指向的函数时,我们就说这是回调函数。
例如:

//冒泡排序
//比较函数
#include <stdio.h> 
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;     }
                     } 
 //bubble最后一个参数定义了一个函数指针,而主函数已将函数int_cmp的地址传给了它,
 //注意只能接受相同类型的函数的地址
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++)        { 
             //调用cmp函数,将要比较的元素的地址传给cmp函数,如果j号元素大于j+1号元素则
             //调用swap函数交换
            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 };         
       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. 函数指针数组
    函数指针数组是一个数组,数组里保存的是函数的地址
    它的定义 int (*p[10]])();
    *是先和p[10]结合的而指针数组里能保存地址,在和函数结合就可在数组里保存相同类型函数的地址
    函数指针数组的用途:转移表
    用例:
//简单计算器利用转移表(函数指针数组)实现
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
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来实现加减乘除的选择相对要麻烦许多
		//而利用转移表只需要做合法性判断即可,用户输入选项后即可根据数组保存的地址直接调用到相应的函数
		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;
}
  1. 容易混淆的一些指针习题 (//后注明答案,默认在32位系统下)
//一维数组
int a[] = {1,2,3,4}; 
printf("%d\n",sizeof(a));     //16  直接取数组名指的是整个数组的字节数
 printf("%d\n",sizeof(a+0));   //4  转换为指针
 printf("%d\n",sizeof(*a));     // 1     下标为0的元素
 printf("%d\n",sizeof(a+1));   //4  
 printf("%d\n",sizeof(a[1]));    //4
 printf("%d\n",sizeof(&a));       //  4/8    32位/64位系统    指针是固定的字节数
 printf("%d\n",sizeof(*&a));       //   16   
 printf("%d\n",sizeof(&a+1));   // 4  指针
  printf("%d\n",sizeof(&a[0]));   //4   指针
  printf("%d\n",sizeof(&a[0]+1));   // 4     指针加1,往后移动了一个元素,还是指针

//字符数组 
char arr[] = {'a','b','c','d','e','f'};
 printf("%d\n", sizeof(arr));    //6    char类型的字符占一个字节,6个字符即6字节
 printf("%d\n", sizeof(arr+0));  //4   指针
  printf("%d\n", sizeof(*arr));   //1
  printf("%d\n", sizeof(arr[1]));   //1
  printf("%d\n", sizeof(&arr));     //4指针
  printf("%d\n", sizeof(&arr+1));  //4
  printf("%d\n", sizeof(&arr[0]+1));   //4
  printf("%d\n",sizeof(arr[0]+1);        //4    隐式转换为int
 
printf("%d\n", strlen(arr));          //6
printf("%d\n", strlen(arr+0));       //6
printf("%d\n", strlen(*arr));          //未定义行为
printf("%d\n", strlen(arr[1]));        //未定义行为
printf("%d\n", strlen(&arr));           //6
printf("%d\n", strlen(&arr+1));       //未定义行为,访问非法内存,跳过了整个数组
printf("%d\n", strlen(&arr[0]+1));    //5   跳过一个元素
 
char arr[] = "abcdef"; 
printf("%d\n", sizeof(arr));          //7   字符串以'\0'结尾
printf("%d\n", sizeof(arr+0));       //4     
printf("%d\n", sizeof(*arr));          //1
printf("%d\n", sizeof(arr[1]));         //1
printf("%d\n", sizeof(&arr));          //4    
printf("%d\n", sizeof(&arr+1));     //4
printf("%d\n", sizeof(&arr[0]+1));   //4
 
printf("%d\n", strlen(arr));          //6    不包括'\0'
printf("%d\n", strlen(arr+0));       //6
printf("%d\n", strlen(*arr));        //未定义行为
printf("%d\n", strlen(arr[1]));      //未定义行为
printf("%d\n", strlen(&arr));        //6
printf("%d\n", strlen(&arr+1));     //未定义行为,跳过了整个数组  
printf("%d\n", strlen(&arr[0]+1));   //5
 
char *p = "abcdef"; 
printf("%d\n", sizeof(p));     //4   指针
printf("%d\n", sizeof(p+1));  //4
printf("%d\n", sizeof(*p));    //1
printf("%d\n", sizeof(p[0]));   //1
printf("%d\n", sizeof(&p));     //4
printf("%d\n", sizeof(&p+1));    //4
printf("%d\n", sizeof(&p[0]+1));    //4   
 
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));         //未定义行为,p原本就为指针,再取地址就变成了二级指针
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       12个int类元素 
printf("%d\n",sizeof(a[0][0]));   //4
printf("%d\n",sizeof(a[0]));      //16      第一行4个int元素
printf("%d\n",sizeof(a[0]+1));   //4   a[0]为int的0加1等于1,还是int类型
printf("%d\n",sizeof(*(a[0]+1)));  //4
printf("%d\n",sizeof(a+1));          //4
printf("%d\n",sizeof(*(a+1)));       //16     第二行
printf("%d\n",sizeof(&a[0]+1));    //4        
printf("%d\n",sizeof(*(&a[0]+1)));    //16
printf("%d\n",sizeof(*a));                 //16       第一行
printf("%d\n",sizeof(a[3]));                //数组越界未定义行为
//题目一
int main() {     
int a[5] = { 1, 2, 3, 4, 5 };     
int *ptr = (int *)(&a + 1);           //注意这里将数组指针强转为了int*,所以减1只跳过一个元素     
printf( "%d,%d", *(a + 1), *(ptr - 1));     
return 0; } 
//程序的结果是什么? 
 //2,5

题2
//此结构体的大小是20个字节 
struct Test {  
int Num;  
char *pcName;  
short sDate;  
char cha[2];  
short sBa[4]; 
}*p; 
//假设p 的值为0x100000。 
如下表表达式的值分别为多少? 
int main() {  
printf("%p\n", p + 0x1);  
printf("%p\n", (unsigned long)p + 0x1); 
printf("%p\n", (unsigned int*)p + 0x1);  
return 0; 
} 
//结果
//100014          //跳过整个结构体20字节
//100001           //强转为unsigned long形所以直接加1
//100004            //强转为了unsigned int*指针,所以加4
 
题3
int main() {
     int a[4] = { 1, 2, 3, 4 };
     int *ptr1 = (int *)(&a + 1);     
     int *ptr2 = (int *)((int)a + 1);    //注意:这里先将指针转为了int整形加一,再转为了int*,即以该地址往下的四个
                                    //字节,包含数组第一个元素的三个字节,和数组第二个元素的一个字节,所以输出结果与
                                    //电脑保存数字的方式有关,所以大端序和小端序结果不同    
     printf( "%x,%x", ptr1[-1], *ptr2);     
     return 0; 
     }
     //答案
     //4, 200000
笔试题4
#include <stdio.h> 
int main(int argc, char * argv[]) {     
int a[3][2] = { (0, 1), (2, 3), (4, 5) };     
int *p;     
p = a[0];     
printf( "%d", p[0]); 
} 
//结果
//0               //p保存的是数组第一行元素的地址,再对第一行元素的0号元素进行解引用,结果为0
笔试题5
int main() {     
int a[5][5];     //
int(*p)[4];     
p = a;     
printf( "%p,%d\n", &p[4][2] - &a[4][2], &p[4][2] - &a[4][2]);     
return 0; 
} 
//结果
//FFFFFFFC,-4
笔试题6
int main() {     
int a[2][5] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };     
int *ptr1 = (int *)(&a + 1);              
int *ptr2 = (int *)(*(a + 1));     
printf( "%d,%d", *(ptr1 - 1), *(ptr2 - 1));     
return 0; 
} 
//结果
//10,5
笔试题7
#include <stdio.h> 
 int main() {  
 char *a[] = {"work","at","alibaba"};          //指针数组,保存了三个字符串的首地址
 char**pa = a;                                          //二级指针
 pa++;                                                      
 printf("%s\n", *pa);                                  //解引用一次,还是指针,指向了元素a
 return 0; 
 } 
 //结果
 //at
笔试题8
int main() {  
char *c[] = {"ENTER","NEW","POINT","FIRST"};       //指针数组保存了4个字符串的地址
char**cp[] = {c+3,c+2,c+1,c};  char***cpp = cp;                  //二级指针数组,保存着c+3,c+2,c+1,c,即
                                                                                   //逆序地址
printf("%s\n", **++cpp);                    //运算顺序   ++  *    *      
printf("%s\n", *--*++cpp+3);               //++    *        --     *        +3
printf("%s\n", *cpp[-2]+3);                 //[-2]        *     +3
printf("%s\n", cpp[-1][-1]+1);            //[-1][-1]         +1
return 0; 
} 
//结果
//point
//ER
//ST
//EW
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值