欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

C语言之指针

程序员文章站 2024-03-08 08:31:15
...

(二)指针篇

一,内存编址与变量地址

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

三,二级指针

C语言之指针
二级指针是一种指向一级指针的指针。利用二级指针可以实现:

  • 间接访问数据
  • 改变一级指针的指向问题
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,定义

指针数组的本质是数组。数组中每一个成员都是指针。

C语言之指针

C语言之指针

2,使用

char *parray[5]={“China”,”Russia”,”America”,”British”,NULL};

其在内存中的位置如下图:

C语言之指针

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++);
}

五,指针的输入与输出

指针做输入与输出,是一种常见的称谓。输入指的是“指针指向的内容,作为函数处理的原始数据的一部分”。输出指的是“指针指向的内容,作为函数处理的结果保存下来”。

C语言之指针

六,堆上一维空间

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;//不允许