嵌入式学习Day12 C语言 --- 指针

一、指针

1、指针概念

  a.指针 --- 地址 ---内存单元编号  //值 
  b.指针 --- 数据类型 ---指针类型  //类型 
  
  不同语境:
  
  定义一个指针?       //指针类型的变量 
  打印某个变量的指针? //指针 --地址   
     

2、指针变量的定义

  基类型 * 变量名
  
  a.基类型   --- > 表示 指针变量 指向的目标的 数据类型
    数据类型
    基本数据类型 
    数组类型 
    指针类型
  b. *   //定义时候的 * 表示定义的是一个 指针类型的变量 
  c. 变量名 

eg1:
  int a,b,*p,*q; //p和q都是指针类型的变量 *是修饰指针变量名的  
  
  int *p,q; //p是指针变量 q int型变量
  
eg2:
  int a = 10;
  float b = 1.23;
  int *p = &a; //指针变量 p 指向 了 int型变量a    

  p变量的数据类型 int * //指针类型 
  
  &b --地址值 ---类型? 
      float 
      b有一块内存空间 -- 放的是float类型的数据 
      &b ---得到了一块 存放着 float类型数据的 空间的地址 

      float *p1 = &b;'

float b = 10;

&b --- > 地址值的类型 -->float *      
          b的数据类型 -- 决定了这块空间放的的数据类型 
                   &b -- 获得了 这块空间 的地址编号 
            
            float *  //这种类型的地址数据 
            
            float *p1 =  &b;

  注意:
  1.指针变量的 大小 64位(8字节) 32位(4字节)
  2.指针类型 --- 存储的是 地址 这种特殊的数据 
  
  指针变量的给值:
  int *p;  //野指针 --- 随机值 ---被当做了地址   
             //避免野指针 -- 一般初始化为NULL --0号地址(不能访问NULL更不能给NULL赋值)
 

3、通过地址实现对指针变量的间接访问


* 指针运算符
* 单目运算 
  运算数必须是指针类型的值(地址)
 
 *p 过程
 1.首先拿出p指针变量中的值(地址) 到内存中定位 
 2.从定位处开始,偏移出sizeof(基类型)大小的一块空间 
 3.把这块空间当做一个 基类型的 变量来看  

  *p 整体就是一个基类型的变量  

eg:
    int a = 0;
    int *p = &a;  
     a = 20;        //直接访问给变量a赋值20
    *p = 20;        //间接访问给变量a赋值20
    
    int a = 0x12345678;
    char* p = &a;
    *p --> 此时只能访问到0x78; //因为从定位处开始,偏移出sizeof(基类型)大小的一块空间

4、核心用途(80%):  被调修改主调 

被调修改主调的过程:
1.想修改谁,就把谁的地址传过去 
2.必须要做*运算(间接访问),实现修改
  
  
扩展:
          指针 操作 一维整型数组 

  int a[10];

  int *p = a; 

  //1.数组本身的特点 (连续性,单一性,有序性)
  //2.p+1   --- 偏移了一个基类型 

  通过指针访问到数组元素:
  *(p+i) <=> int型的变量   <=> a[i] <=>p[i]<=> *(a + i) 

5、指针运算 

 &
 * 
 p+1
 p++ 
 p-1
 p-- 
 
 关系运算 
   > >= < <= == != 
   p>q
 
   p-q  
        前提: 同一类型的指针 
        表示之间差了几个基类型 

        
    p+q  //指针不能做加法运算 

#include<stdio.h>

#if 0
//逆序指针实现

void reverse(int *p1,int *p2)
{
	int t = 0;

	while(p1 < p2)
	{
	  t = *p1;
	  *p1 = *p2;
	  *p2 = t;
	  p1++;
	  p2--;
	}
}
#endif


#if 0
//二分查找指针形式

void binary_search(int *p1,int *p2,int n)
{
	int *mid = NULL;

	while(p1 <= p2)
	{
		mid = p1 + (p2 - p1 + 1)/2;

		if(*mid > n)
		{
			p2 = mid - 1;
		}
		else if(*mid < n)
		{
			p1 = mid + 1;
		}
		else
		{
			break;
		}
	}

	if(p1 <= p2)
	{
		printf("1\n");
	}
	else
		printf("0\n");
}

#endif

#if 0
//二分查找递归形式***********************************

int* binary_search(int *p1,int *p2,int n)
{
	int *mid = NULL;
	int *ret = NULL; 

		mid = p1 + (p2 - p1 + 1)/2;
	
		if(p1 > p2)
		{
			return	NULL;
		}
		
		if(*mid > n)
		{
			p2 = mid - 1;
		    ret = binary_search(p1,p2,n);
		}
		else if(*mid < n)
		{
			p1 = mid + 1;
			ret = binary_search(p1,p2,n);
		}
		else
		{
			ret = mid;
		}

		return ret;
		
}

#endif

#if 0
//选择排序指针形式

void line(int *begin,int *end)
{
	int t = 0;
	int* i = NULL;
	int* j = NULL;

	for(i = begin;i < end;i++)
	{
		for(j = i+1;j <= end;j++)
		{
			if(*i > *j)
			{
				t = *i;
				*i = *j;
				*j = t;
			}
		}
	}
}
#endif

#if 0
//冒泡排序指针形式

void line(int* begin,int *end)
{
	int i = 0;
	int len = end - begin;
	int* j = NULL;
	int t = 0;

	for(i = 0;i < len;i++)
	{
		for(j = begin;j < end;j++)
		{
			if(*j > *(j+1))
			{
				t = *j;
				*j = *(j+1);
				*(j+1) = t;
			}
		}

	}

}
#endif

#if 0
//插入排序,指针实现

void line(int *begin,int *end)
{

	int *i = NULL;
	int *j = NULL;
	int t = 0;

	for(i = begin;i <= end;i++ )
	{
		t = *i;
		j = i;

		while(j > begin && *(j-1) > t)
		{
			*j = *(j - 1);
			j--;
		}
		*j = t;

	}

}
#endif 

#if 0
int main()
{
	int s1[10] = {10,9,8,7,6,5,4,3,2,1};
	int i = 0;

	line(s1,s1+9);

//	int n = 0;

//	scanf("%d",&n);
  
	//	reverse(s1,s1+9);
	//	line(s1,s1+9);
	
	//  int t *ret = binary_search(s1,s1+9,n);
	
	//printf("%p\n",ret);  //printf("%d",NALL); -->  error
	
	for(i = 0;i < 10;i++)
	{
		printf("%d ",s1[i]);
	}

	return 0;
}

二、const前缀

int puts(const char *s); 
const char * s;
const int a; //只读变量 
int a = 10;
const int *p = &a; //表示 基类型 为只读 
p --- 指针变量 --本身的类型 int * 
a --- int型变量 --本身类型 int 
   
p = &a; 

const int *p = &a; 
int const *p = &a; //就近原则 -- 离谁近,就限定谁的  

int *const p = &a;  //限定p为只读 
const int * const p = &a;  //p不能被修改,指向的目标类型不能被修改 
                           //(是不能通过*p) 
   
   
   
    int puts(const char *s)
    {
    }//const char *s --在函数里面 不能通过*s修改到外面的数据 
    
    好处:
    1.可以接收 字符数组名   //char *
      也可以接收 字符串常量 //const char *
      提高了参数的适用性 
    2.避免了 可能出现的修改的操作 
      可以将 运行时的错误,提前到 编译时发现 
      
    const char * p 可以用来保存字符串常量的地址 

作业:

1.实现
  strlen
  strcat 
  strcpy
  strcmp

#include<stdio.h>

#if 0
int my_strlen(char* p)
{
	int count = 0;

	while(*p != '\0')
	{
		count++;
		p++;
	}

	return count - 1;
}
#endif

#if 0
void my_strcpy(char* p1,char* p2)
{
	while(*p2 != '\0')
	{
		*p1 = *p2;
		p1++;
		p2++;
	}
	*p1 = '\0';
}
#endif

#if 0
void my_strcat(char* p1,char* p2)
{
	int count = 0;
	while(*p1 != '\0')
	{
		p1++;
	}

	while(*p2 != '\0')
	{
		*p1 = *p2;
		p1++;
		p2++;
	}
	*p1 = '\0';

}
#endif

#if 0
int my_strcmp(char* p1,char* p2)
{		
	while(*p1 == *p2 && *p1 != '\0' && *p2 != '\0')
	{
		p1++;
		p2++;
	}
	return *p1-*p2;

}

#endif

#if 0
int main()
{

   //char s1[] = "hellloworld";
   //char s2[100];
   //my_strcpy(s2,s1);
   //puts(s2);
	
    //int ret = 0;
	//ret = my_strlen(s1);
    //printf("%d",ret);
	
	//char s1[6] = "hello";
	//char s2[6] = "world";
	//my_strcat(s1,s2);
	//puts(s1);
	
   //char s1[3] = "aaa";
   //char s2[3] = "bbb";
   //int i = 0;
   //i = my_strcmp(s1,s2);
   //printf("%d\n",i);
	

	return 0;
}


2、"编写程序实现单词的倒置  "how  are  you"  ->  "you  are  how"

    S1. 整体逆序
       "uoy era woh"
    S2. 逐个单词逆序
       "you are how" 

#include<stdio.h>

void reverse(char* begin,char* end)
{
	char t = 0; 

	while(begin < end)
	{
		t = *begin;
		*begin = *end;
		*end = t;

		begin++;
		end--;
	}

}
int main()
{
	char s1[] = "how are you";

	reverse(s1,s1+10);
	reverse(s1,s1+2);
	reverse(s1+4,s1+6);
	reverse(s1+8,s1+10);

	puts(s1);
	return 0;
}


4、编写程序实现将"12345"  转化为12345 (数值)

#include<stdio.h>

int char_int(char* p1)
{
	int i = 1;
	int j = 10000;
	int sum = 0;

	while(*p1 != '\0')
	{
		i = (*p1 - 48) * j;
		sum = sum + i;
		j = j/10;
		p1++;
	}
	return sum;
}
int main()
{
	char s1[10] = "12345";

	printf("%d\n",char_int(s1));


	return 0;
}

三、指针操作一维字符型数组(字符串)一般会用到的函数 

gets
puts 
strlen
strcpy /strncpy 
strcat /strncat 
strcmp /strncmp 

注意:
1.const 能加都加 
2.函数功能 尽可能写的全面 

  
  char *strncpy(char *dest, const char *src, size_t n)
{  
   正常拷贝 
   多了 一个n 
   n < strlen(src)  
     只拷贝前n个字符,最终dest中不会有'\0'
   n == strlen(src)
     正常拷贝 
   n > strlen(src)
   if (n) 拷贝够了次数 
   剩余拷贝 统统补0
   
   思路:
   
   // 结束条件 *src == '\0'
   // n次 拷贝完成没有
}
功能:拷贝指定长度的字符串


  char *strncat(char *dest, const char *src, size_t n)
{
   拼接的基础上 多 n控制条件 
   
   n < strlen(src) 拼n下就结束   n == 0
    
   n >= strlen(src) src拼完就结束  src=='\0'
   
   *dest = '\0' //?
}
功能:在dest所指定的字符串后边拼接指定个数的字符


  int Strncmp(const char *s1, const char *s2, size_t n)
{
     
}
功能:比较指定长度以内的字符串

  //hello
  //help  //3 

 四、回调函数与函数指针

 定义: 通过函数指针调用的函数 叫回调函数 
 技术上: 通过函数指针的实现 
 函数指针(指向基类型-为函数类型) 函数类型的指针 
 
eg:

写一个程序 实现加,减,乘,除
以回调函数的形式,打印对应的结果 

//用函数回调实现加减乘除

int func0(int a,int b)
{
	return a+b;
}
int func1(int a,int b)
{
	return a - b;
}
int func2(int a,int b)
{
	return a*b;
}
int func3(int a,int b)
{
	return a / b;
}

//int (*pfunc)(int,int) 是函数指针,类型是 int (int,int) 
//可以理解为是:   int (int,int) *pfunc
//C规定书写格式是:int (*pfunc)(int,int)

void processData(int a,int b,int (*pfunc)(int,int))  
{
	printf("%d",pfunc(a,b));
}

int main()
{
    int a = 0;
	int b = 0;
	char c;

	scanf("%d%c%d",&a,&c,&b);
//	printf("%d %c %d",a,b,c);  //检验scanf是否输入正确

	switch(c)
	{
	case '+':
		processData(a,b,func0); // 回调 func0
		break;
	case '-':
		processData(a,b,func1);
		break;
	case '*':
		processData(a,b,func2);
		break;
	case '/':
		processData(a,b,func3);
	break;
	}

    return 0;
}

五、万能指针 

void *  //万能指针  --可以接收任意类型的指针 
           //void类型 
注意: 
    如果通过该类型的地址进行数据访问
    一定要转换为 明确类型 

int compar(const void *a, const void *b)  //回调函数
  {
     *(const int *)a - *(const int *)b     
  }

  int a[10] = {1,2,3,4};
  int b[10] = {5,6,7,8};

六、数组指针 

1、本质:指向数组的指针

int a[3][4]; //本质还是一维数组 
int[4] a[3]; //理解角度 
             //a --数组名 --代表类型 int [3][4]
             //a --代表的值 -- 首元素的地址 -- a[0] 
             //a[0] 的数据类型 int[4]
             //&a[0]--对应的数据类型 int(*)[4] //数组类型 (一维整型数组类型)
             //数组类型的指针 --- 数组指针                 

2、格式:int (*p)[4] = a;

*p   //三步运算完成后
     *p 相当于 是 int[4]这种类型 //数组
     *p 就相当于 int[4]这个数组的 数组名

*(*(p+i) + j)<=>a[i][j]

p+1 //偏移到了 下一个 int[4],因为基类型是int[4],+1偏移一个基类型大小的空间
       //类型为int(*)[4]
*(p+1) //偏移到下一个int 
       //*(p+1) 代表的类型int[4] 此时相当于是 int[4]的数组名 
       //*(*(p+1) + 1)
       

练习:
  定义一个二维字符数组,找出数组最大值 

#include<stdio.h>
{

    void maxarr(char (*p)[4],int row)  //类型为 char[4]  * 代表指针  p 是变量名
    {                                  //理解记忆 char[4]* p 
    	int i = 0;
    	char max[4];
        strcpy(max,*p);  //注意要对p进行*操作

    	for(i = 0;i < row;i++)
    	{
    		if(strcmp(max,*(p+i)) < 0)
    		{
    			strcpy(max,*(p+i));
    		}
		
    	}
	
    			puts(max);
    }

    int main()
    {
        char s1[3][6] = {"aaa","bbb","ccc"};

        maxarr(s1,3);
    
        return 0;
    }
}

七、指针数组

指针数组:存放指针的数组

char** p

char* --> 基类型,表示指向的对象的类型是char *类型
    * --> 指针标志
    p --> 变量名   

**p 

*p  --> 找到指针数组中所存储的地址元素
**p --> 找到所存地址的所对应的变量



练习:

//指针数组进行排序和查找

#include<stdio.h>
#include<string.h>

void print(char** s1,int n)
{
	int i = 0;

	for(i = 0;i < n;i++)
	{
		printf("%s ",*(s1+i));
	}
}

void line(char** s1,int n)
//插入排序
{
	int i = 0,j = 0;
	char* p = NULL;

	for(i = 0;i < n;i++)
	{
		p = *(s1+i);
		j = i;

		while(j > 0 && strcmp(*(s1+j-1),p) > 0)
		{
		    *(s1+j) = *(s1+j-1);
			j--;
		}
		*(s1+j) = p;
		
	}
}

int binary_search(char** begin,char** end,char* ch1)
{
//二分查找
	char** mid = NULL;

	while(begin <= end)
	{
		mid = begin + (end - begin + 1)/2;
		
		if(strcmp(*mid,ch1) > 0)
		{
			end = mid - 1;
		}
		else if(strcmp(*mid,ch1) < 0)
		{
			begin = mid + 1;
		}
		else
			break;

	}
	if(begin<=end)
	{
		return 1;
	}
	else
		return 0;
}

int main()
{
	char* s1[3] = {"hello","world","china"};  //指针数组 --> char* s1[3]
	char ch1[] = "hello";                     //字符串常量本身就代表自身的地址
 	line(s1,3);
//	print(s1,3);

	printf("%d",binary_search(s1,s1+2,ch1));


	return 0;
}

八、函数指针数组

int func0(int a,int b)
{
	return a+b;
}
int func1(int a,int b)
{
	return a - b;
}
int func2(int a,int b)
{
	return a*b;
}
int func3(int a,int b)
{
	return a / b;
}

void processData(int a,int b,int (*pfunc)(int,int))
{
	printf("%d",pfunc(a,b));
}
#endif

#if 0
//函数指针的数组

int main()
{
	int (*P0)(int,int) = func0; //用函数指针来存放函数入口地址 
	int (*P1)(int,int) = func1; 
	int (*P2)(int,int) = func2; 
	int (*P3)(int,int) = func3;

	int (*p[4])(int,int) = {func0,func1,func2,func3}; //定义一个函数指针的数组,来存放函数的地址,函数名就代表函数的入口地址
    //理解记忆 int (int,int) * p[4]
    //           函数类型      
	int i = 0;
	int a = 10;
	int b = 3;

	for(i = 0;i < 4;i++)
	{
		printf("%d ",p[i](a,b));  //p[i] 就的等同于 func0,func1 ...;p[0](a,b)-->func0(a,b)
	}

	return 0;
}

九、类型大综合


       

  

  

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值