zoukankan      html  css  js  c++  java
  • C语言的指针

    1.取地址运算

    • scanf("%d",&i);里的&
    • 获得变量的地址,它的操作数必须是变量
      • int i; printf("%x",&i);

    注意:

    • 获取地址时,用%p来获取
      • int i; printf("%p",&i);
    • 地址的大小是否与int相同取决于编译器
    #include <stdio.h>
    
    int main()
    {
    	int a[10];
    	printf("%p\n",&a);	
    	printf("%p\n",a);
    	printf("%p\n",&a[0]);
    	printf("%p\n",&a[1]);		
    	return 0;
    }
    000000000062FDF0
    000000000062FDF0
    000000000062FDF0
    000000000062FDF4
    

    1.1 &不能取的地址

    &不能对没有的东西取地址

    比如:

    • &(a+b)
    • &(a++)
    • &(++a)

    2.指针

    定义:就是保存地址的变量

    int i;
    int* p=&i;//这个p指向的是个int,把i的地址交给了这个p,这个*p只会有别的变量的地址
    int* p,q;//q只是个普通的b	
    int *p,q; 
    
    • 变量的值是内存的地址
    • 普通变量的值是实际的值
    • 指针变量的值是具有实际值的变量的地址

    2.1 作为参数的指针

    #include <stdio.h> 
    void f(int *p);
    void g(int k);
    int main()
    {
    	int i = 6;
    	printf("&i=%p\n",&i);
    	f(&i);
    	g(i); 
    	return 0;
    } 
    
    void f(int *p)
    {
    	printf("p=%p\n",p);
    }
    
    void g(int k)
    {
    	printf("k=%d\n",k);//得到的只是i的值 
    }
    
    • void f(int *p);

    • 在被调用的时候得到了某个变量的地址;

    • int i=0; f(&i);

    • 在函数里面可以通过这个指针访问外面的这个i

    2.2 访问那个地址上的变量*

    是一个单目运算符,用来访问指针的值所表示的地址上的变量*

    可以做右值也可以做左值

    • int k= *p;
    • *p = k+1;
    #include <stdio.h> 
    void f(int *p);
    void g(int k);
    int main()
    {
    	int i = 6;
    	f(&i);
    	g(i); 
    	return 0;
    } 
    
    void f(int *p)
    {
    	printf("p=%p\n",p);
    	printf("*p=%d\n",*p); 
    	*p = 26;//可以通过这种方式访问外面的变量,可以修改,访问 
    }
    
    void g(int k)
    {
    	printf("k=%d\n",k);//得到的只是i的值 
    }
    

    2.3 左值之所以叫左值

    是因为出现在赋值好左边的不是变量,而是值,是表达式计算的结果(运算的结果):

    • a[0]=2;
    • *p=3
    • 是特殊的值,所以叫做左值

    2.4 指针的运算符&*

    • 互相反作用
      • *&yptr -> *(&yptr) -> *(yptr的地址) -> 得到那个地址上的变量 -> yptr
      • &*yptr -> &( *yptr) -> &(y) -> 得到y的地址,也就是yptr -> yptr

    3.指针的使用

    3.1 指针的应用场景一

    • 交换两个变量的值(函数)

      #include <stdio.h>
      void swap(int *pa,int *pb);
      int main()
      {
      	int a=5;
      	int b=6; 
      	printf("a=%d,b=%d \n",a,b);
      	swap(&a,&b);
      	printf("a=%d,b=%d",a,b);
      	return 0;
       } 
       
      void swap(int *pa,int *pb)
      {
       	int t=*pa;
       	*pa=*pb;
       	*pb=t;
       }
      //a=5,b=6
      //a=6,b=5
      

    3.2 指针的应用场景二

    • 函数返回多个值,某些值就只能通过指针返回
      • 传入的参数实际上是是需要保存带回结果的变量
    #include <stdio.h> 
    void minmax(int a[],int len,int *min,int *max);
    
    int main(void)
    {
    	int a[]={1,2,3,4,5,6,7,8,9,10};
    	int min,max;
    	minmax(a,sizeof(a)/sizeof(a[0]),&min,&max);
    	printf("min=%d,max=%d \n",min,max);
    	return 0;
    }
    
    void minmax(int a[],int len,int *min,int *max)
    {
    	int i;
    	*min =*max =a[0];
    	for(i=1;i<len;i++){
    		if(*min>a[i]){
    			*min=a[i];
    		}
    		if(*max<a[i]){
    			*max=a[i];
    		}
    	}
    }
    

    应用场景二b(运算出错)

    • 函数返回运算的状态,结果通过指针返回

    • 常用的套路是让函数返回特殊的不属于有效范围内的值来表示出错

      • -1或0(在文件操作会看到大量的例子)
    • 但是当任何数值都是有效的可能结果时,就得分开返回

      #include <stdio.h>
      
      /*如果除法成功,返回1;否者返回0 */
       
      int divide(int a,int b,int *result);
      
      int main()
      {
      	int a=5;
      	int b=2;
      	int c;//c是a/b的结果 
      	if( divide(a,b,&c)){
      		printf("%d/%d=%d",a,b,c); 
      	} 
      	return 0;
      } 
      
      int divide(int a,int b,int *result)
      {
      	int ret =1;
      	if( b==0)ret=0;//除数不能是0 
      	else{
      		*result=a/b;
      	}
      	
      	return ret;
      	
      }
      

    4.指针最常见的错误

    • 定义了指针变量,还没有指向任何变量,就开始使用

    5.传入函数的数组成了什么?

    函数参数表中的数组实际上是指针

    • sizeof(a) == sizeof(int*)
    • 但是可以用数组的运算符[]进行运算
    #include <stdio.h> 
    
    void minmax(int *a,int len,int *max,int *min);
    
    int main(void)
    {
    	int a[] ={1,2,3,4,5,6,7,8,9,10};
    	int min,max;
    	printf("minmax sizeof(a)=%lu\n",sizeof(a));
    	printf("main a=%p\n",a);
    	minmax(a,sizeof(a)/sizeof(a[0]),&min,&max); 
    	printf("a[0]=%d\n",a[0]);
    	printf("min=%d,max=%d\n",min,max);
    
    	return 0;
    }
    
    void minmax(int *a,int len,int *max,int *min)
    {
    	int i;
    	printf("minmax sizeof(a)=%lu\n",sizeof(a));//接收到的是首地址 
    	printf("main a=%p\n",a);
    	a[0]=1000; 
    	for ( i=1;i<len;i++){
    		if( a[i]< *min){
    			*min = a[i];
    		}
    		if( a[i]> *max){
    			*max = a[i];
    		}
    	}
    }
    minmax sizeof(a)=40
    main a=000000000062FDF0
    minmax sizeof(a)=8
    main a=000000000062FDF0
    a[0]=1000
    min=10,max=0
    

    5.1 数组的变量是特殊的指针

    • 数组变量本身表达地址,所以不需要加

      • int a[10];int *p=a;//无需用&取地址
      • 但是数组的单元表达的是变量,需要用&去地址
      • a == &a[0]
    • []运算符可以对数组做,也可以对指针做:

      • p[0] <==> a[0]
    • *运算符可以对指针做,(取出所指变量例的值),也可以对数组做

      • *a =25;
    • 数组变量是const的指针,所以不能被赋值

      • int a[] <==> int * const a=...

    6.指针与const

    6.1 如果q是const

    • 表示q的值(即i的地址)不能被改变,就是说不能再指向别人了
      • int *const q =&i; //q是const
      • *q=26;//ok
      • q++;//ERROR

    6.2 如果是const int

    • 表示
      • const int *p =&i;
      • p = 26;//ERROR! ( *P)是const
      • i = 26;
      • p =&j;

    image-20211007210438397

    在前面就代表int不能被修改,在后面表示指针不能被修改。

    6.3 转换

    • 总是可以把一个非const的值转换成const

      void f(const int* x);
      int a=15;
      f(&a);//ok
      const int b=1;
      
      f(&b);//ok
      b = a+1;//Error!
      
    • 当要传递的参数的类型比地址大的时候,这是常用的手段:既能用比较少的字节数传递值给参数,又能避免函数对外面的变量的修改

    6.4 const数组

    • const int a[]={1,2,3,4,5,6};
    • 数组变量已经全是const的指针了,这里的const表明数组的每个单元都是const
    • 所以必须通过初始化进行赋值

    保护数组值

    • 因为把数组传入函数时传递的是地址,所以那个函数内部可以修改数组的值
    • 为了保护数组不被函数破坏
      • int sum(const int a[],int length);

    7.指针运算

    这些算术运算可以对指针做:

    • 给指针加、减一个整数(+,+=,-,-=)
    • 递增递减(++/--)
    • 两个指针相减,可以得到两个地址差了几个单元(地址相减/sizeof(数据类型))

    7.1 *p++

    • 取出p所指的那个数据来,完事以后顺便把p移到下个位置去
    • *的优先级虽然高,但是没有++搞
    • 常用于数据类的连续空间操作
    • 在某些CPU上,这可以直接被翻译成一条汇编指令

    7.2 指针比较

    • <,<=,==,>,>=,!= 都可以对指针做
    • 比较它们在内存中的地址
    • 数组中的单元的地址肯定是线性递增

    7.3 0地址

    • 当然你的内存中有0地址,但是0地址通常是个不能随便碰的地址

    • 所以你的指针不应该具有0值

    • 因此可以用0地址来表示特殊的事情

      • 返回的指针是无效的
      • 指针没有被真正初始化(先初始化为0)
    • NULL是一个预定定义的符号

      • 有的编译器不愿意你来用0来表示0的地址

    8. 指针的类型

    1. 无论指向什么类型, 所有的指针的大小都是一样的,因为都是地址
    2. 但是指向不同类型的指针是不能直接互相赋值的
    3. 这是为了避免用错指针

    9. 指针的类型转换

    • void*表示不知道指向什么东西的指针
      • 计算时与char*相同(但不通)
    • 指针也可以转换类型
      • int *p = &i; void *q =(void *) p;
    • 这并没有改变p所指的变量的类型,而是让后人用不同眼光通过p看它所指的变量
    • 我不再当你是int啦,我认为你就是个void!

    10. 总结

    1. 需要传入较大的数据时用作参数
    2. 传入数组后对数组做操作
    3. 函数返回不止一个结果
    4. 需要用函数来修改不止一个变量
    5. 动态申请内存时
  • 相关阅读:
    为系统添加语言包
    除掉任务栏上的隐藏小按钮
    叠加多个无线网络
    在win7中通过手机投放媒体
    开关WI-Fi显示列表
    hibernate(二)对象的三种状态、一级缓存、多对一、inverse、cascade
    hibernate(一)helloworld、api、事务、配置文件、映射文件
    SQL Server2019安装
    Windows server2012搭建FTP服务器
    Gradle的使用教程
  • 原文地址:https://www.cnblogs.com/DL50/p/15380357.html
Copyright © 2011-2022 走看看