C 语言 指针

发布于 2021-07-21 12:50:15 字数 7316 浏览 1767 评论 0

一、指针

1.几个概念

  • 指针:指针就是一个地址
  • 指针变量:指针变量是用来存放指针(一个地址)的变量
  • 指针变量与普通变量的区别:普通变量存放的是数据,指针变量存放的是地址
  • 指针变量的类型:存放的地址指向的数据的数据类型

2.取地址运算符和取值运算符(取值与取址)

如果需要获取某个变量的地址,可以使用取地址运算符 (&)

如果需要访问指针变量指向的数据,可以使用取值运算符 (*)

3.指针的声明和赋值

类型名 *指针变量名

eg: char *pa;
eg: int *pb;

申明并赋值

#include<stdio.h>

int main(){
  char a = 'F';//这是声明一个普通变量,a存放的就是字符F

  char *pa = &a;//这是声明一个指针变量pa,并将a的地址存放在指针变量pa中
  //注意赋值时等号(=)的右边必须为一个地址
  //char *pa = a;//由于a不是一个地址,所以这种赋值是错误的

  printf("a的地址为:%p\n",&a);
  printf("pa的值为:%p\n",pa);
  return 0;
}

申明后赋值

#include<stdio.h>

int main(){
  char a = 'F';//这是声明一个普通变量,a存放的就是字符F

  char *pa;//这里只声明了一个指针变量,还没赋值

  //错误初始化:*pa = &a;//因为指针变量pa已经声明,所以此处不该有*号,有*号意味着修改指针的值,要避免访问未初始化的指针
  //错误初始化:*pa = a;//因为指针变量pa已经声明,所以此处不该有*号,有*号意味着修改指针的值,要避免访问未初始化的指针

  pa = &a;//正确初始化,将a的地址赋值给指针变量pa

  return 0;
}

避免访问未初始化的指针

int main(){
  int *a;//此处的指针没有被初始化
  *a = 123;//间接访问很危险
  return 0;
}

4.修改指针的值

#include <stdio.h>

int main(){
  //定义两个普通变量
  char a = 'F';
  int f = 123;

  //定义两个指针变量
  char *pa = &a;
  int *pb = &f;

  //打印指针变量指向的值
  printf("a = %c\n",*pa);//F
  printf("f = %d\n",*pb);//123

  //修改指针变量指向的值
  *pa = 'C';
  *pb += 1;

  //打印修改后的指针变量指向的值
  printf("now,a = %c\n",*pa);//C
  printf("now,f = %d\n",*pb);//124
  //打印修改后的变量的值
  printf("now,a = %c\n",a);//C
  printf("now,f = %d\n",f);//124

  //打印指针变量占用的内存空间
  printf("sizeof pa = %d\n",sizeof(pa));//8
  printf("sizeof pb = %d\n",sizeof(pb));//8

  //打印指针变量指向的地址
  printf("the addr of a = %p\n",pa);//0x7ffee6cb19ab(每次都不一样)
  printf("the addr of f = %p\n",pb);//0x7ffee6cb19a4(每次都不一样)
  return 0;
}

二、指针和数组

1.数组名其实是数组第一个元素的地址

#include <stdio.h>
int main(){
  char str[128];//定义一个字符数组
  printf("请输入域名:");//shuidi.cn

  scanf("%s",str);//数组名其实是数组第一个元素的地址,所以这里不用&符号
  printf("域名为:%s\n",str);//shuidi.cn
  //这个例子说明:
  //1、数组名其实是数组第一个元素的地址
  printf("str的地址是:%p\n",str);//0x7ffee3bfe920(每次都不一样)
  printf("str第一个元素的地址是:%p\n",&str[0]);/0x7ffee3bfe920(每次都不一样)

  return 0;
}

2.指针的运算

当指针指向数组元素的时候,我们可以对指针变量进行加减运算,这样做的意义相当于指向距离指针所在位置向前或向后第n个元素 对比标准的下表法访问数组元素,这种使用指针进行间接访问的方法叫做指针法

3.指针和数组的区别

数组名只是一个地址,而指针是一个左值

#include<stdio.h>
int main(){
  char str[] = "shuidi.cn";
  char *pa = str;
  int count = 0;
  //++的优先级高于*,所以此处是先将地址+1再取值
  while(*pa++ != '\0'){//此处不能直接使用str++,因为str是数组名字,不是左值;
    count++;
  }
  printf("一共有%d字符\n",count);
  return 0;
}

三、指针数组和数组指针

1.数组指针(指针)

数组指针是一个指针,它指向的是一个数组

定义:int (\*pa)[5];//[] 的优先级大于 *,但是 () 的优先级与 [] 一样,但是结合是从左到右

#include<stdio.h>
int main(){
  int temp[5] = {1,2,3,4,5};
  int (*p1)[5] = &temp;
  int i;
  for(i=0;i<5;i++){
    printf("%d\n",*(*p1+i));
  }
  return 0;
}

2.指针数组(数组)

指针数组是一个数组,每个数组元素存放一个指针变量

定义:int *pa[5];//[] 的优先级大于 *

#include<stdio.h>
int main(){
  int a = 1;
  int b = 2;
  int c = 3;
  int d = 4;
  int e = 5;

  int *p1[5] = {&a,&b,&c,&d,&e};

  int i;
  for(i=0;i<5;i++){
    printf("%d\n",*p1[i]);
  }
  return 0;
}

int main(){
  char *p1[5] = {
    "aaaa",
    "bbbb",
    "cccc",
    "dddd",
    "eeee"
  };
  int i;
  for(i=0;i<5;i++){
    printf("%s\n",p1[i]);
  }
}

3.指向数组的指针

#include <stdio.h>
int main(){
  int temp[5] = {1,2,3,4,5};
  int *p = temp;

  int i;

  for(i=0;i<5;i++){
    printf("%d\n",*(p+i));
  }
}

四、指针和二维数组

c 语言中并没有正真的二维数组,二维数组是通过线性扩展的方式实现

# include <stdio.h>
int main(){
  int a[2][3] = { {1,2,3},{4,5,6} };
  int i;
  int j;
  printf("二维数组a的地址:%p\n",a);
  printf("二维数组a[0]的地址:%p\n",a[0]);
  printf("二维数组a[0][0]的地址:%p\n",&a[0][0]);
  printf("*(a+1)=%d\n",**(a+1));//语法糖:*(a+1) = a[1]
  for(i=0;i<2;i++){
    for(j=0;j<3;j++){
      printf("%d ",a[i][j]);
    }
    printf("\n");
  }
  return 0;
}

五、void 指针和 NULL 指针

1.void 指针

void 指针我们把它称之为通用指针,就是可以指向任意数据类型的。也就是说,任何类型的指针都可以赋值给 void 指针

#include<stdio.h>
/**
将void类型的指针指向为其他类型的指针就需要强制转换
*/
int main(){
  int num = 1024;
  int *pa = #
  char *pb = "shuidi";
  void *pc;

  pc = pa;
  printf("pa:%p,pc:%p\n",pa,pc);
  printf("pa=%d,pc=%d\n",*pa,*(int *)pc);//注意强制转换

  pc = pb;
  printf("pb:%p,pc:%p\n",pb,pc);
  printf("pb=%s,pc=%s\n",pb,(char *)pc);

  return 0;
}

2.NULL 指针

NULL 代表该指针不指向任何东西

  • 当你还不清楚要将指针初始化为什么地址时,请将它初始化为NULL;
  • 在对指针进行解引用时,先检查该指针是否为NULL。
  • 这种策略可以为你今后编写大型程序节省大量的调试时间。
#include <stdio.h>
int main(){
  int *pa;
  int *pb = NULL;

  printf("%d\n",*pa);
  printf("%d\n",*pb);
  return 0;
}

/**
NULL 不是 NUL(表示空字符'\0')
**/

六、指向指针的指针

#include <stdio.h>
int main(){
  int num = 520;
  int *p = #
  int **pp = &p;//pp就是指向指针的指针(pp里面存放的是指针的地址)

  printf("num:%d\n",num);
  printf("*p:%d\n",*p);
  printf("**pp:%d\n",**pp);

  printf("&p:%p,pp:%p\n",&p,pp);
  printf("&num:%p,p:%p,*pp:%p\n",&num,p,*pp);
  return 0;
}

int main(){
  //定义一个数组指针
  char *cBooks[] = {
    "《C程序设计语言》",
    "《C专家编程》",
    "《C和指针》",
    "《C陷阱与缺陷》",
    "《C Primer Plus》",
    "《带你学C带你飞》"
  };

  //定义一个指向指针的指针
  char **byFishC;

  //定义一个
  char **jiayuLoves[4];
  int i;

  byFishC = &cBooks[5];
  jiayuLoves[0] = &cBooks[0];
  jiayuLoves[1] = &cBooks[1];
  jiayuLoves[2] = &cBooks[2];
  jiayuLoves[3] = &cBooks[3];

  printf("FishC出版的图书有:%s\n",*byFishC);
  printf("小甲鱼喜欢的图书有:\n");

  for(i=0;i<4;i++){
    printf("%s\n",*jiayuLoves[i]);
  }
  return 0;
}

int main(){
  int array[10] = {0,1,2,3,4,5,6,7,8,9};
  int *p = array;
  int i;

  for(i=0;i<10;i++){
    printf("%d\n",*(p+i));
  }

  printf("-----------\n");
  int a[3][4] = {
    {1,2,3,4},
    {5,6,7,8},
    {9,10,11,12}
  };
  int m,n;
  for(m=0;m<3;m++){
    for(n=0;n<4;n++){
      printf("%d ",*(*(a+m)+n));
    }
    printf("\n");
  }
  int (*q)[4] = a;
  for(m=0;m<3;m++){
    for(n=0;n<4;n++){
      printf("%d ",*(*(q+m)+n));
    }
    printf("\n");
  }

  return 0;
}

七、指向常量的指针

#include <stdio.h>
//定义常量
#define PI 3.14


int main(){
  //还可以使用const关键字将一个变量变为常量
  int a = 1;
  const int b = 2;

  a = 2;//此时可以修改变量a
  printf("%d\n",a);

  //b = 1;//此时不能修改用const关键字定义的变量
  printf("%d\n",b);




  //指向常量的指针
  int num = 520;
  const int cnum = 880;
  const int *pc = &cnum;

  printf("cnum:%d,&cnum:%p\n",cnum,&cnum);
  printf("*pc:%d,pc:%p\n",*pc,pc);

  //*pc = 1024;//不允许
  pc = #//这个是可以的

  return 0;
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据

关于作者

JSmiles

生命进入颠沛而奔忙的本质状态,并将以不断告别和相遇的陈旧方式继续下去。

0 文章
0 评论
84961 人气
更多

推荐作者

醉城メ夜风

文章 0 评论 0

远昼

文章 0 评论 0

平生欢

文章 0 评论 0

微凉

文章 0 评论 0

Honwey

文章 0 评论 0

qq_ikhFfg

文章 0 评论 0

    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文