专栏首页ZackSockC语言指针(上)

C语言指针(上)

一、指针的概念

1.1变量和地址

  • 变量:直观来说,int a、char ch、float num这些都是声明变量,而a、ch、num就是变量
  • 地址:在计算机中,内存被分为一小块一小块的,而每一块都有一个编号,叫做地址。
  • 一般变量都存储在内存当中。而每块内存都有一个独一无二的地址,这个地址就是指针
  • 如果把内存比作一个宾馆,在声明一个变量时(int a),就相当于在宾馆前台办了入住手续。前台会给你一个门卡和门牌号,简单理解门牌号就是地址。

二、变量的指针与指针变量

指针为变量的地址,而专门用来存储另一个变量的地址的变量就是指针变量。

2.1、指针变量的定义及使用

(1)、指针变量的定义

定义指针变量的符合为*,如下定义了三个指针变量。它们的变量名为pi、pj、pf,而不是*pi、*pj、*pf。*号在此只用来声明。

//声明了两个整型变量和一个浮点型变量
int i, j;
float f;

//声明三个指针变量
int *pi, *pj;
float *pf;

(2)、指针变量的使用

  • 取地址符&:单目运算符“&”的功能是取操作对象的地址。常量、表达式和寄存器变量不能取地址,因为它们不是存放在内存某个存储单元中,而是放在寄存器中,寄存器无地址。
  • 指针运算符(间接寻址运算符)*:单目运算符“*”的功能是按照操作对象的地址值,访问对应存储单元。与“&”互为逆运算。
//声明一个变量i,初始化值为10
int i = 10;

//利用取地址符&获取i的地址
printf("%d", &i);

//定义一个指针变量pi,指向i的地址
int *pi = &i;

//利用指针运算符*获取pi指向的内存,即为i的值
printf("%d", *pi);

注:在C语言中,所有变量的声明都必须放在最前面,但是有些编译器你没放前面也可以通过,这里注意一下

(3)、&和*运算符的结合方向

“&”和“*”两个运算符优先级相同,但按从右至左方向结合。可理解为从右开始运算

//声明一个变量i
int i = 10;
//声明一个指针变量pi,指向i
int *pi = &i;
//输出i的地址
printf("%d", &*pi);

上面的代码定义了一个指向i的指针变量pi,而输出i的地址使用了“&*pi”。首先,pi是一个指针变量,pi的内容为i的地址。因为运算符是右结合,则先是运算*pi。即为pi地址中的内容,就是10。然后再取地址,&*pi即为i的地址。

2.2、指针变量的初始化

void main(){
    int a = 10;
    //利用取地址符&,获取变量a的地址,给指针变量pa赋值
    int *pa = &a;
}

2.3、指针运算

(1)赋值运算

void main(){
    int *px, *py, *pz, x;
    //1、指向某个地址
    px = &x;
    
    //2、赋予空指针
    py = NULL;
    
    //3、赋予指定地址
    pz = 4000;

}

(2)指针与整数的加减运算

  • 指针变量自增或自减,即指针向前或者向后移动一个存储单元
  • 指针比那里加上一个整型数,即指针向前或者向后移动指定的存储单元

(3)关系运算

  • px < py,判断px指向的地址是否小于py指向的地址
  • px == py,判断px和py是否指向同一个地址
  • px == 0和px != 0表示px是否为空指针

接下来来一个小练习:

//声明函数
void invert(int *a, int start, int end);

/**
*    采用递归法对a数组的元素进行逆序
*/
void main(){
    
}

/**
*    实现函数
*    a为数组首地址
*    i位起始逆序元素
*    j为逆序结尾元素
*/
void invert(int *a, int start, int end){
    //临时变量,用于交换
    int temp;        

    //当起始逆序元素小于逆序结尾元素时,说明还没有逆序到中间元素    
    if(start < end){
        //将起始元素和结尾元素交换
        temp = a[start];
        a[start] = a[end];
        a[end] = temp;

        //交换后再次调用invert,将其余元素逆序,此时start和end要同时向中间移动
        invert(a, start + 1, end - 1);
    }
}

三、指针与数组

3.1、指向数组的指针

  • 数组名即为该数组的首地址,a为一个数组,a = &a[0]。
  • 可以通过指针对数组元素进行访问,*a = a[0]、*(a + 1) = a[1]。
  • 数组名不能进行指针的操作,像指针p++是合法的,但是数组a++是非法的。

3.2、字符指针和字符数组

在C语言中,系统本身没有提供字符串数据类型,但可以使用两种方式存储一个字符串:字符数组方式和字符指针方式。

(1)字符数组方式

也就是我们比较常用的方式

void main(){
    //定义一个字符数组
    char sentence[] = "Do not go gengle into that good night!";
    printf("%s", sentence);
}

来个小练习:

/**
*    用数组将字符串sentence复制到字符串copy
*/
void mian(){
    char *sentence = "Do not go gentle into that good night!", copy[50];
    int i;
    //当没有遇到结束符时,一直循环
    for(i = 0; sentence[i] != '\0'; i++){
        //将数据复制到copy中
        copy[i] = sentence[i];
    }
    printf("复制后的copy是:%s", copy);
}

3.3、多级指针及指针数组

(1)多级指针

简单来说就是指针的指针,指针变量作为一个变量,也有自己的存储空间。而这个存储空间也有一个地址:

void main(){
    //定义一个普通变量
    int a = 10;

    //定义一个指针变量,指向a
    int *p = &a;
    
    //定义另一个指针变量,指向指针变量p,此时pp就是二级指针
    int **pp = &p;

    //输出两个指针
    printf("一级指针pa为:%d\n", p);
    printf("二级指针ppa为:%d", pp);

    //指针的指针和普通指针操作一样,可以用*pp获取pp指向地址中的内容,即p存储的内容
    printf("p存储的内容为:%d", *pp);
}

注:因为一级指针和二级指针性质不一样,所以一级指针和二级指针之间不能赋值,如p = pp在编译时会报错(这是书中写的,但是在我实际测试当中,可以赋值,可能是编译器的问题)。

(2)指针数组

即一个元素为指针的数组,定义如下:

int *a[10];

用一个练习熟悉指针数组,解释全在注释当中:

void main(){
    //定义并初始化一个int数组
    int a[5] = {1, 3, 5, 6, 8}, i;

    //定义一个指针数组,与a数组中元素对应
    int *p[5];
    for(i = 0; i < 5; i++){
        p[i] = &a[i];
    }

    //定义一个二级指针,存放指针数组的首地址。指针数组和普通数组一样,数组名为数组首地址
    int **pp = p;

    //利用指针数组首地址输出数据
    for(i = 0; i < 5; i++){
            
        //数组a中第零个元素地址为p,而p的的地址为pp,所以**pp = a[0]
        //数组a中第一个元素地址为p + 1,而p + 1的地址为pp + 1,所有**(pp + 1) = a[1]
        //以此类推,**(pp + n) = a[n]
        printf("%d\t", **(pp + i));
    }
}

3.4、指针与多维数组

(1)多维数组的地址

假设有个二维数组a[4][2],那么可以分两个维度来理解这个数组。

  • 先去掉[2],只看“a[4]”一个维度。此时a只是个普通的一维数组,而后面的[2]也只是决定了数组a元素的性质
  • 在数组a中,有四个元素,我们取一个来分析第二个维度。其第一个元素为a[0],我们将a[0]看做一个整体,不作为数组元素,只作为一个名称X。那么第二个维度就可以看做X[2],即一个有两个元素的数组。
  • 由上面可知,X数组的首地址为数组名,即X。X实际上是a[0],类推的话X1、X2等就是a[1]、a[2]。可以间接理解为数组的第一个维度装的全是地址,每个元素X的地址。

(2)多维数组的指针

举个例子方便理解

void mian(){
    //创建一个普通二维数组
  int num[5][5] = {
    {1, 3, 4, 5, 6},
    {4, 5, 7, 8, 8},
    {6, 8, 9, 0, 1},
    {3, 4, 2, 1, 2},
    {4, 5, 6, 3, 2}
  };

    //声明一个指针数组
  int *p_num[5];
  int i, j;

    //初始化指针数组,每个元素分别指向num[0][0]、num[1][0]、、、
  for(i = 0; i < 5; i++){
    p_num[i] = &num[i][0];
  }
    
    //利用指针数组p_num输出num数组中的元素
  for(i = 0; i < 5; i++){

    printf("这是第%d轮数组\n", i+1); 
    for(j = 0; j < 5; j++){
            
            //将p_num[i]作为一个数组首地址,数组存储的内容为*(p_num+j)
      printf("%d\t", *(p_num[i] + j));
    } 
    printf("\n");
  }


}

本文分享自微信公众号 - ZackSock(AndrewRubin),作者:ZackSock

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2019-09-27

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 结构体与指针(一)

    在学习数据结构的时候,会经常使用到结构体。今天分享的内容是结构体与指针,因为结构体和指针本身的内容并不是太多,所以今天的内容还包括了链表的实现。希望可以通过这篇...

    ZackSock
  • Python实现简易爬图

    Python网络请求的类在urllib中,我们这次只需要用request。发出请求,获取响应:

    ZackSock
  • Python用10行代码爬取大批美女图片

    说到美女,第一个想到的就是美女云集的相亲网站了。所以今天也是选取某个相亲网站作为素材,爬取美女图片。

    ZackSock
  • 【编程基础】数组和指针为什么不等价?

    好多初学C语言的人都认为数组和指针是相等的,在C 语言中对数组和指针的困惑多数都来自这句话。说数组和指针“等价”不表示它们相同, 甚至也不能互换。它的意思是说数...

    程序员互动联盟
  • C语言中指针数组和数组指针的区别

    指针数组:首先它是一个数组,数组的元素都是指针,数组占多少个字节由数组本身决定。它是“储存指针的数组”的简称。 数组指针:首先它是一个指针,它指向一个数组。在...

    用户1215536
  • web框架

    小小咸鱼YwY
  • 关于搭建HTTPS服务...

    关于 HTTPS 的基本原理大家都已经不再陌生,今天和大家说说如何搭建一个支持 HTTPS 的服务端。

    Jean
  • 《挑战30天C++入门极限》新手入门:C/C++中数组和指针类型的关系

    landv
  • 如何一步一步用DDD设计一个电商网站(十一)—— 最后的准备

    最近实在太忙,上周停更了一周。按流程一步一步走到现在,到达了整个下单流程的最后一公里——结算页的处理。从整个流程来看,这里需要用户填写的信息是最多的,那么在后...

    Zachary_ZF
  • golang url 链接地址解析包

    copy_left

扫码关注云+社区

领取腾讯云代金券