C语言学习笔记——指针

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_38908061/article/details/72801985

1.简单指针  *p:

(1)用于数组

int *p 定义指针

该指针表示指向某个变量的地址;

当指针与自增符号结合时,简单举例如下:

int m[5]={1,3,5,7,9};p=m;

A.*p++;     B.*++p;    C.++*p;    D.x=*p++;   E.x=*++p;    F.x=(*p)++;   G.x=*(p++) ; 

首先,看个简单的例子,int m=0,n;   n=m++; n=++m;  n=(m++);中第一个式子得到m=1,n=0第二个式子得到m=1,n=1.

其中m++和++m的区别在于是 先赋值还是先计算。易搞混的是第三个式子n=(m++)得到的结果仍然是m=1,n=0 此处须说明,虽然括号将m++扩起优先计算,但是在C中,m++的含义是在计算其他赋值等式子之后再单独进行++,可以理解为单独一句m=m+1,因此优先级考虑就与扩号无关了。

由此再看ABC:先来比较*与++的优先级:*做乘号,比++优先级低,但是与此处无关;*做取内容符号时,优先级同++p中的++,但是低于p++中的++。

在AB中,二者意思相同,均为将指针后移一个单位,A中容易混淆。须记住它不是 *p = *p + 1; 它却是 *p = *(p+1); 而且是后加加;由于++和*优先级相同,C选项中先计算*p,取到p指向的内容,再对内容做自增,与前二者不同,代码如下:

#include<stdio.h>
main() 
{
	int m[5]={1,3,5,7,9},*a,*b,*c;
	a=m;
	b=m;
	c=m;
	*a++;
	*++b;
	++*c;
	printf("this is *a: %d \n",*a);
	printf("this is *b: %d \n",*b);
	printf("this is *c: %d \n",*c);
}
输出结果见下



对于DEFG,结合上面两个方面也就不难理解:

D先将p指针内容给x,随后指针自增,得到*p为3,x为1;

E中先指向下一位再赋值,x=*p=3;

F中x仍为指针原本指向的值x=1;此处先取到p的值,再对值自增,得到*p最后为2;同时m[0]的值同步变为2;

G中由上方分析可知,()的存在对式子和++的先后顺序无影响,结果同D。


(2)用于字符串

字符串中,指针在用法上与数组有很大区别:

a.指针指向字符串须与字符串同数据类型;

b.讲指针指向字符串后,指针即可代替该字符串进行使用,表示整串输出字符串内容,而不是地址,如用puts(指针名)直接输出整个字符串;

c.二者用取内容运算符*,均表示相应数组在对应位置的内容,字符串指针p的类型是string,*p则是char(具体应用在输出格式上),注意,字符串指针*p指单个char变量,判断是否结束,用*p==‘\0’;

d.一个易错点!:

如下写法会正常编译,但是在运行时出现卡死:

int main() {
	char *s1="abc",*s2="def";
	s1[2]=s2[1] ;//这里出错

	return 0;
}:
如果把字符串指针当做数组来用,必须先定义已有的数组,再令字符串指针指向该数组,否则会出错。正确写法:
int main() {
	char s1[]="abc",s2[]="def";
        char *p1=s1,*p2=s2
	p1[2]=p2[1] ;//这个时候才能把字符串数组当指针用

	printf("%c",s1[2]);
	return 0;
}


2.指针数组*p[],数组指针(*q)[]:

     二者被我多次搞混,出现语法等错误,故放在一起讨论

     *p[n]表示的是一个指针数组,从符号优先级可以看出来,其和*(p[n])等价。即一个指针的集合,包含n个指针,他们相互独立。这种用法与二级指针**p相似

其常见应用:表示二维字符数组:

int main( )
2.{  char  *p[ ]={"PROGRAM","BASIC","C","JAVA"};
3.   int    i;
4.   for (i=3;i>=0;i--)  printf("%c",*p[i]);
5.   printf("\n"); 
6.return 0;
7.}
结果:JAVACBASICPROGRAM

其结果为倒叙输出四个单词,char *p[n]表示一串指针时 等价于 n个char *p 等价于char  **p(二级指针详细部分见下)



另外 用二维整型数组表示*p[n]容易出现的一种错误,刚好与(*q)[n]做简单对比:

int main(int argc, char *argv[]) {
	int a[3][4]={1,2,3,4,
				5,6,7,8,
				9,10,11,12},i,j;  
	int *p[3];        //这里的角标是3,因为这是一个含有3个指针的数组,每个指针对应上面二维数组的一行 
	*p=a;             //这里用的*p,区别于下面。因为这里的p相当于二级指针,对应的*p才是一级指针,指向数组
	for(i=0;i<3;i++)  
	    for(j=0;j<4;j++)  
	        printf("%3d",a[i][j]) ;  
	printf("\n");
	for(i=0;i<3;i++)  
	for(j=0;j<4;j++)  
	    printf("%3d",*(*(p+i)+j)) ;
	
}

此处在输入12个数之后  得到的结果只有前四个与上吻合,原因是指针数组内部变量相互独立。输出如下:




下面再来看 (*p)[n]

如int(*P)[10],p先和*结合,意味着p是一个指针,他指向int [10],即p是一个指向一个数组的指针

int main(int argc, char *argv[]) {
	int a[3][4]={1,2,3,4,
				5,6,7,8,
				9,0,1,2},i,j;  
	int (*p)[4];     //注意这里角标是4.原因是这个指针指向int [4]的数组
	p=a;             //注意这里直接p=a,区别上面的*p=a;因为这里的p是一级的指针,指向数组
	for(i=0;i<3;i++)  
	    for(j=0;j<4;j++)  
	        printf("%3d",a[i][j]) ;  
	printf("\n");
	for(i=0;i<3;i++)  
	for(j=0;j<4;j++)  
	    printf("%3d",*(*(p+i)+j)) ;
	
}

注意二者在表示上略有不同 此处n=4;等于二维数组的列宽;另外在赋予地址时,此处不需要间接引用符*,只需将q指针指向数组第一个内存位置即可。
输出结果如下:




3.二级指针**p

二级指针表示指向指针的指针,可以理解为指针数组*p[]


假如如此定义:int **p,(*q)[5];

这时的*(p+i)和q[i]是等价的;

int  main( )
{  char *a[6]= {"ABCD","EFGH","IJKL","MNOP","QRST","UVWX"};
   char      **p;
   int       i;
   p=a;
   for( i=0; i<6; i++ )    printf("%s",*(p+i));
   printf("\n");
   return 0;
}
如上,其中的*(p+i)就等价于a[i],等价于int *q="...."

二级指针一个很重要的用法就是在调用函数过程中使用,使在函数内部可以改变原先指针的指向:

void change_ptr(void **ptr, void *dest)

{

     *ptr = dest;

}
change_ptr(&ptr,dest)
注意此处调用时  要取二级指针对应项的地址,才能在函数内部使二级指针指向指针,通过二级指针改变指针本身的内容。
可以这样改变指针内容是因为,C中进入一个子函数的时候,会默认把原函数中需带入的变量拷贝一份进入子函数;这时拷贝一个指针的地址,等于在函数中可以改变这个指针本身

关于二维数组和二级指针,指针数组相关,可以参考下面这个文章:

点击打开链接



4.指向结构的指针

指向结构的指针用p->xxx来表示,其中xxx是结构内部定义的一个项;

如定义struct list{}a此时 p->xxx等价于 a.xxx等价于*(p).xxx





补充部分:

1.关于free(q): free和malloc要成对出现

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

int main()
{
  char *str = (char *)malloc(100);
  strcpy(str, "hello");
  free(str);
  if(str != NULL)
    {
      strcpy(str, "world");
      printf("%s\n", str);
    }
      return 0;
}

free后的指针,要及时进行NULL,否则会成为野指针,影响稳定性;

另外free()不能用在这种情况下:

int *p=0;
p=malloc(100*1024);
p++;
free(p);
free()必须释放指定分配的内存,不可以是进行移动、赋值后的指针。


2.关于两字符串相减

C中把字符串认为是指针,字符串相减就是指针相加减,如

char s1="abcdef",s2="def";

int n=s2-s1;

得到的值n是很大的数,因为两字符串地址不相连


第二种情况

char *s1="abcdefg",*s2;

s2=s1;

s2+=5;

int n=s2-s1;

这里得到的n就是二者地址差,因为二者的字符串内容在同一段地址上,s2指针在后移之后是"fg",二者地址差是5.


第三种情况:两个指针相减:

int a[]={2,4,6,8,10,12};
int *p1 = a;
int *p2 = &a[3];
printf("p2-p1=%d",p2-p1)
这里可能有如下两种结果:

  I.得到的是二者地址差,二者差3个int型的地址,也就是3*4=12或3*8=24;

 II.得到的就是二者指向位置之差,即3.

经过编译运算,这里是第II种情况。指针相减不能得到地址差,而是得到“地址差÷sizeof(数据类型)”。



3.指针用于多个返回值

 指针可用于函数中来返回多个值,具体用法:

设置swap函数,交换两个变量的值:

void swap(int *x1,int *x2);
int main(int argc, char *argv[]) {
	int a,b;
	scanf("%d",&a);
	scanf("%d",&b);
	swap(&a,&b);
	printf("a=%d,b=%d",a,b);
	return 0;
}
void swap(int *x1,int *x2){
	int temp;
	temp=*x1;
	*x1=*x2;
	*x2=temp;
}
 函数部分曾经错写为:

int *temp;temp=x1;x1=x2;x2=temp;

这里的交换是交换过程操纵两个指针变量,修改对应地址的内容,而不是对指针进行交换,函数内部的交换在返回main时失效。



4.不要这样写指针

int *p=5;
 这种写法容易使程序崩溃。道理很简单:当我们定义一个指针int * p 的时候,这个指针会随机指向某个位置,如果我们这时修改指针指向的内存,使之变为“5”这个数字,那么该地址中原本的内容会被覆盖掉,如果是一些重要数据,则会导致崩溃。因此一般情况下,定义指针后要用已经定义的变量去“引导”指针指向固定好的位置。
int a = 5;
in *p = a;

此外,在C中,数组p[]和指针*p在C中是同一个东西,即数组是一个常量指针,同时定义数组p[]和指针*p就会出现冲突错误。

易错点:

当对一个指针进行自增操作(q++)时,不是地址值+1,而是地址值+n。如:

 int *p ,则p++表示地址+4/+8;

 char *q,则q++表示地址值+1。


另外,注意区分:*(p+1)和 *p + 1。二者区别一个是地址++,一个是内容++。


 5.const 与 指针

有如下三种写法:

int a;
const int *p1=&a;  //写法1
int const *p2=&a ;     //写法2
int *const p3=&a ;     //写法3
这里三者区别,只看 const 与 * 谁前谁后:

const 在 * 之前,p可以改变地址,但是不能利用p去改变指向处的变量值,如1,2;

const 在 * 之后,不可变的是指针p,因此指针p指向的地址不能变,如3;

另外,三者对变量a无影响,仍然可以直接通过a修改内容。



以上个人笔记,如有错误请指出,会持续进行补充

   

猜你喜欢

转载自blog.csdn.net/qq_38908061/article/details/72801985