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.}
其结果为倒叙输出四个单词,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修改内容。
以上个人笔记,如有错误请指出,会持续进行补充