C语言之指针
(二)指针篇
一,内存编址与变量地址
变量的地址为所占连续内存中最低位的地址。 &a == 0xffff fffa
&b == 0x0000 ffd1
&c == 0x0000 0010
二,指针与指针变量
1,指针的本质
指针
实质就是一个有类型的地址
int a=0x12345678;
printf("&a=%p\n",&a);
//0x0060feac printf("*&a=%x\n",*&a);
//12345678 pritnf("*&a=%x\n",*((int *)0x0060feac));
//12345678
由此,可得:&a == (int *)0x0060feac
所以说,指针就是有类型的地址。
2,指针变量
内存的地址,为指针常量。而存放内存地址的变量为指针变量。
指针变量的3个条件:
- 大小为4B(32位机器)
- 有类型:决定了以p存放的地址为起始地址的寻址大小。
- 区别于其他变量
int a=0x12345678;
printf("%p\n",(char *)&a);
//78 printf("%p\n",(short *)&a);
//5678 printf("%p\n",(int *)&a);
//12345678
3,引用与解引用
int arr[5];
arr == &arr[0]
//p arr+1 == &arr[1]
//p+4B &arr
//p &arr+1
//p+20B *(&arr)
//arr[0] *(&arr+1)
//arr[6]
int a[10];
printf("&a[9]-&a[4]=%d\n",&a[9]-&a[4]);
//5 printf("(int)&a[9]-(int)&a[4]=%d\n",(int)&a[9]-(int)&a[4]);
//20
三,二级指针
二级指针是一种指向一级指针的指针。利用二级指针可以实现:
- 间接访问数据
- 改变一级指针的指向问题
1,改变一级指针的指向
int a=10,b=20;
int *ps=&a;
int **pps=&ps;
printf("*ps=%d\n",*ps);
//ps=&a,*ps=10 *pps=&b;
printf("*ps=%d\n",*ps);
//ps=&b,*ps=20
由此,可以推出N级指针
可以改变N-1级指针
的指向
。
2,初始化一级指针
#include<stdio.h>
enum
{
Success,NameErr,SexErr,NumErr,ScoreErr
};
struct Stu
{
char *name;
char *sex;
char *num;
float *score;
};
`/*这种初始化方式最大的好处就是能返回多种多样的返回值,我们`
可以掌握很多程序的执行返回信息.*/`
int init(struct Stu **pp)
{
*pp=(struct Stu *)malloc(sizeof(struct Stu));
if(*pp==NULL)
return -1;
(*pp)->name=(char *)malloc(100);
if((*pp)->name==NULL)
return NameErr;
(*pp)->Sex=(char *)malloc(1);
if((*pp)->Sex==NULL)
return SexErr;
(*pp)->Num=(char *)malloc(10);
if((*p)->Num==NULL)
return NumErr;
(*pp)=(float *)malloc(10);
if((*pp)->Score==NULL)
return ScoreErr;
return Success;
}
void main()
{
struct Stu *ps=NULL;
int ret=init(&ps);
if(ret!=Success)
{
printf("%d\n",ret);
return -1;
}
strcpy(ps->name,"Bori Liu");
*(ps->sex)='W';
strcpy(ps->num,"10001");
*(ps->score)=100;
printf("Name:%s\nSex:%c\nNum:%s\nScore:%.1f\n",*(ps->name),
*(ps->sex),*(ps->num),*(ps->score));
}
3,二级指针的步长
由于二级指针的实质为指针,其内容为地址,粟所以,二级指针的步长(大小)为4Bytes。
四,指针数组(字符指针数组)
1,定义
指针数组的本质是数组。数组中每一个成员都是指针。
2,使用
char *parray[5]={“China”,”Russia”,”America”,”British”,NULL};
其在内存中的位置如下图:
3,二级指针访问指针数组
char *parray[10];
parray == &parray[0]
parray[0]的类型为char *
,所以,&parray的类型为char **
。
因此,parray的类型是char **
。
char *pArray[10];//定义
char **p=pArray;//赋值
//利用数组下标形式访问成员
for(int i=0;i<sizeof(pArray)/sizeof(pArray[0]);++i)
{
printf("%s\n",pArray[i]);
}
//也可以用二级指针访问指针数组成员
while(*p)
{
printf("%s\n",*p++);
}
五,指针的输入与输出
指针做输入与输出,是一种常见的称谓。输入指的是“指针指向的内容,作为函数处理的原始数据的一部分”。输出指的是“指针指向的内容,作为函数处理的结果保存下来”。
六,堆上一维空间
1,返回一级指针
char *allocSpace(int n)
{
char *p=(char *)malloc(n);
return p;
}
2,返回二级指针
int allocSpace(char **p,int n)
{
*p=(char *)malloc(n);
return *p==NULL?-1:1;
}
七,堆上二维空间
1,一级指针作返回值输出
二维数组是一种二维空间。二维空间并不一定是二维数组,但是具有数组的访问形式。但已远远不是数组的定义了。
#include<stdio.h>
#include<stdlib.h>
void *alloc2dSpace(int typesize,int row,int line)
{
void *p=malloc(typesize*row*line);
return p;
}
int main()
{
int row=5;
const int line=6;
int (*p)[line]=
(int (*)[line])alloc2dSpace(sizeof(int),row,line);
for(int i=0;i<row;++i)
{
for(int j=0;j<line;++j)
{
p[i][j]=i+j;
}
}
for(int i=0;i<row;++i)
{
for(int j=0;j<line;++j)
{
printf("%d\t",p[i][j]);
}
putchar(10);
}
free(p);
return 0;
}
2,二级指针作返回值输出
#include<stdio.h>
#include<stdlib.h>
void **allocSpace(int typesize,int row,int line)
{
void **(void **)malloc(row*sizeof(void *));
for(int i=0;i<row;++i)
{
p[i]=malloc(typesize*line);
}
return p;
}
int main()
{
int row=5,line=6;
int **p=(int **)allocSpace(sizeof(int),row,line);
for(int i=0;i<row;++i)
{
for(int j=0;j<line;++j)
{
p[i][j]=i+j;
//*(*(p+i)+j)=i+j;
}
}
for(int i=0;i<row;++i)
{
for(int j=0;j<row;++j)
{
printf("%d\t",p[i][j]);
//printf("%d\t",*(*(p+i)+j));
}
putchar(10);
}
for(int i=0;i<row;++i)
{
free(p[i]);
}
free(p);
return 0;
}
八,三级指针作函数返回值输出
#include<stdio.h>
#inlcude<stdlib.h>
int allocSpace(int ***p,int typesize,int row,int line)
{
*p=(int **)malloc(row*sizeof(int *));
if(*p==NULL)
return -1;
for(int i=0;i<row;++i)
{
(*p)[i]=(int *)malloc(typesize*line);
if((*p)[i]==NULL)
return -1;
}
}
void freeSpace(int **p,int row)
{
for(int i=0;i<row;++i)
{
free(p[i]);
}
free(p);
int main()
{
int row=5,line=6;
int **p;
int ret=allocSpace(&p,sizeof(int),row,line);
if(ret<0)
return -1;
for(int i=0;i<row;++i)
{
for(int j=0;j<line;++j)
{
p[i][j]=i+j;
}
}
for(int i=0;i<row;++i)
{
for(int j=0;j<line;++j)
{
printf("%d\t",p[i][j]);
}
putchar(10);
}
freeSpace(p,row);
return 0;
}
九,数组指针
1,对一维数组的误解与纠正
一维数组名实质为一级指针。因此,二维数组名的类型很容易会被误解为二级指针。但事实上,二维数组名并不是二级指针,而是数组指针。 int array[10];
int (*p)[10]=array;
//正确 int **p=array;
//错误
2,定义
int (*pName)[N];
“( )”的优先级比“[ ]”高。“*”和“pName”构成了一个指针的定义。指针的类型为”int [] “(一维数组)。
3,别名
typedef int(*TYPE)[N];
TYPE称为数组指针类型
可以通过类型强转将一级指针赋值于数组指针(二维数组名)。
int a[10]={0,1,2,3,4,5,6,7,8,9};
int (*p)[5]=(int (*)[5])a;
//将一级指针(一维数组名)强转为数组指针
十,const
1,const修饰变量
const修饰的变量成为“常变量”。常变量有常量的属性,并且比用#define定义的常量多了“类型”的属性。
常变量类型必须要初始化。
const int a=400;
常变量不能直接赋值,但可以间接改变其值。
const int a=100;
int *p=&a;
*p=200;
- 常变量比宏定义的常量多了类型的属性。
#define A 200a
//正确const int A=200a;
//错误
//对于宏定义,编译时只是简单得进行宏替换,并不进行语法检查,但会对const修饰的常变量进行语法检查。
2,const修饰指针
- const位置位于“*”后(int *const p),修饰指针指向。也就是说,指针变量的内容(地址)不可被修改。
int a=100;
int b=200;
int *const p=&a;
p=&b;//这是不允许的
- const位于“*”前(const int *p或int const *p),修饰指针指向的内容。意思就是,指针变量的指向可以被改变,但其指向的内容(保存的地址所在空间里的内容)不可被修改。
int a=100,b=200;
int const *p=&a;
//const int *p=&a;
*p=300;//指向的内容不可被修改,这是不允许的
p=&b;//指向是可以被改变的
*p=400;//不允许
上一篇: TensorFlow的一些基本概念
下一篇: 详解Java编程中对象的序列化