文章目录

  • 1. 地址和变量
  • 2. 指针
    • 2.1 基本声明
    • 2.2 指针的目标
  • 3. 指针的运算
    • 3.1 指针的赋值运算
    • 3.2 指针的算术运算
      • 3.2.1 指针与常量的加法与减法
      • 3.2.2 两指针相减运算
    • 3.3 指针的关系运算
  • 4. 指针与数组
    • 4.1 一维数组
    • 4.2 二维数组
    • 4.3 行指针
  • 5. 指针数组
  • 6. 多级指针
    • 6.1 基本用法
    • 6.2 多级指针运算
    • 6.3 多级指针和指针数组
  • 7. void指针和const修饰符
    • 7.1 void指针
    • 7.2 const修饰符

1. 地址和变量

  • 指针的作用

    • 让程序简介、紧凑、高效、
    • 有效地表示复杂的数据结构
    • 动态分配内存
    • 得到多于一个的函数返回值
  • 地址:在计算机内存中,以字节为单元,每个字节都有一个编号,称为地址

  • 如果我们定义int i,然后用&i查看地址,得到的是i的存储地址

  • 变量:对程序中数据存储空间的抽象

2. 指针

2.1 基本声明

  • 指针是内存单位的地址,专门用来存放地址的变量。
  • 用法:存储类型 数据类型 *指针变量名=地址
  • 举例:
#include <stdio.h>
#include <string.h>int main(int argc, char *argv[])
{int a = 10;int *p=&a;printf("&p: %p, sizeof: %ld\n", &p, sizeof(p));printf("%p: %p\n", p, &a);return 0;
}

Return,返回值告诉我,我的电脑是8个字节的,但是我发现我返回只有12位16进制,也就是只有48位而不是64位,产生了困惑。经查询得到解答。地址数值只有48位是表像,实际上它是64位的地址,这是当前的x86_64处理器硬件限制所致。因为目前面世的x86_64处理器的地址线只有48条,硬件要求传入的地址的48到63位必须与47位相同。因此有两段合法的地址空间,最直观的是0 - 0x00007fff ffffffff,另一段是0xffff8000 00000000 - 0xffffffff ffffffff。两段加在一起一共2^48 = 256TB,这就是当前处理器的寻址能力。但一般我们是见不到第二段地址的,因为操作系统一般使用低段地址,高段这部分需要你的机器至少有128TB以上的内存。

&p: 0x7ffc78ef9b90, sizeof: 8 \\8*8=64->64位的计算机
0x7ffc78ef9b8c: 0x7ffc78ef9b8c

2.2 指针的目标

  • 指针指向的变量称为目标变量或指针的目标

  • 假设px=0x0012fe80x=-126的指针,那么有以下三种方法可以拿到x的值:*px=*(&x)=x,在这里*不在是之前声明是指针的作用,而是做取值的作用

  • 例1:假设px是一个指针,则:

    • px:指针变量,内容是地址
    • *px:指针所指向的对象,内容是数据
    • &px:存放这个指针的地址,是个长亮
  • 例2

#include <stdio.h>
#include <string.h>int main(int argc, char *argv[])
{int a = 10;int *p=&a;printf("%d %d\n", a, *p); //10 10return 0;
}

3. 指针的运算

3.1 指针的赋值运算

  • 通过赋值运算符向指针变量送一个地址
  • 赋的值的要求:必须是地址常量
double x=15, *px, *py;
px = &x;
py = px;

3.2 指针的算术运算

  • 指针运算是对地址进行运算
  • 指针运算的种类有限,它只能进行赋值运算、算术运算和关系运算
运算符 计算形式 意义
+ px+n 向地址大的方向移动n个数据
- px-n 向地址小的方向移动n个数据
++ px++或++px 向地址大的方向移动1个数据
px–或–px 向地址小的方向移动1个数据
- px-py 两个指针之间相隔数据元素的个数

3.2.1 指针与常量的加法与减法

  • 举例
int a = 10, *p;
double b = 3, *q;p = &a;
q = &b;printf("p: %p p+2: %p\n", p, p+2);
printf("q: %p q+2: %p\n", q, q+2);

Return

p: 0x7fff26799fec p+2: 0x7fff26799ff4 \\在这里是查了八个字节,也就是两个int,所以+2是移动2个数据
q: 0x7fff26799ff0 q+2: 0x7fff2679a000 \\移动了16个字节,两个double的大小
  • 注意

    • 不同数据类型的两个指针实行佳见证书运算是没有意义的,因为加两个int是加四个字节,而加1个double就是八个字节。
    • px+n表示的实际位置地址量是:(px)+sizeof(px的类型)*n
    • px-n表示的实际位置地址量是:(px)-sizeof(px的类型)*n

3.2.2 两指针相减运算

  • 两指针相减的结果值不是地址量,而是一个整数值,表示两指针之间相隔数据的个数,比如数组中&a[3]-&a[0]=3
  • 举例
int a[5] = {4, 8, 1, 2, 3};
int *p, *q;
p = a; //&a[0]
q = &a[3];
printf("q-p: %ld\n", q-p);

Return:3

3.3 指针的关系运算

  • 两指针之间的关系运算表示它们指向的地址位置之间的关系。指向地址大的指针大于指向地址小的指针
  • 指针与一般整数变量之间的关系运算没有意义,但可以和进行等于或不等于的关系运算,判断指针是否为空。下面举个例子
int *p=NULL;
printf("%d %p\n", p, p); //return 0 (nil)
运算符 说明
> 大于
< 小于
>= 大于等于
<= 小于等于
!= 不等于
== 等于
  • 例子:通过指针给数组赋值
int a[5];
int *p = a, i;for (i=0; i>N; i++)scanf("%d", p++);p=a
for (i=0; i<N; i++)printf("%d ", *p++);
puts("");

4. 指针与数组

4.1 一维数组

  • 数组的指针是指数组在内存中的起始地址,数组元素的指针是指数组元素在内存中的起始位置

  • 访问第i+1个数组元素的方法:

    • x[i]
    • *(px+i)
    • *(x+i)
    • px[i]
    • 可以通过px++px--来找第i+1个数组元素,但是不可以用a++来找,会报错,因为a是常量并不是地址变量
  • 举例

int a[6] = {1,2,3,4,5,6};
int *p=a, *q=a+5; // *q=&a[5];

4.2 二维数组

  • 首先通过2x2的二维数组了解二维数组在内存空间中的存储方式:
    我们直观的中的二维数组是这样的
第1列 第2列
第一行 a[0][0] a[1][1]
第二行 a[1][0[] a[1][1]

在内存中的二维数组是下面这样的。a[0]代表第一行,a[1]代表第二行,因此二维数组常被称为行地址

内存
a[0] ⇔\Leftrightarrow⇔*a a[0][0]
a[0][1]
a[1]⇔\Leftrightarrow⇔*(a+1) a[1][0]
a[1][1]
  • 练习1:用一个指针遍历二维数组
#include <stdio.h>
#include <string.h>int main(int argc, char *argv[])
{int a[2][2] = {{1,2},{3,4}};int *p=a[0], i, n;n = sizeof(a)/sizeof(int);for (i=0; i<n; i++)printf("%d ", *p++);puts("");return 0;
}
  • 练习2:二维数组a, a+1与a[0], a[0]+1 的区别
#include <stdio.h>
#include <string.h>int main(int argc, char *argv[])
{int a[2][2] = {{1,2},{3,4}};printf("%p %p\n", a, a+1); // 0x7ffcad435160 0x7ffcad435168 移动了八个字节printf("%p %p\n", *a, *(a+1)); // 0x7ffcad435160 0x7ffcad435168 移动了八个字节printf("%p %p\n", a[0], a[0]+1); // 0x7ffcad435160 0x7ffcad435164 移动了四个字节printf("%p %p\n", *a, *a+1); // 0x7ffcad435160 0x7ffcad435164 移动了四个字节return 0;
}

4.3 行指针

  • 用法:存储类型 数据类型 (*指针变量名)[n];
  • 原来指针+1都是前进1个数据,在这里因为有n(列数),又是行指针,所以+1是前进n个数据,二维数组就是很好的一个例子
  • 练习1:+1为前进三个数据
int a[2][3];
int(*p)[3];
  • 练习2:访问取值的方法p[row][col]*(*(p+row)+col)
#include <stdio.h>
#include <string.h>int main(int argc, char *argv[])
{int a[2][3] = {{1,2,3}, {4,5, 6}};int (*p)[3];p = a;printf("%d %p %p\n", a[1][1], a, a+1); //5 0x7ffefe3406a0 0x7ffefe3406acprintf("%d %p %p\n", p[1][1], p, p+1); //5 0x7ffefe3406a0 0x7ffefe3406ac, 和a一样的用法printf("%d %d %d\n", a[1][2], p[1][2], *(*(a+1)+2)); //6 6 6return 0;
}

5. 指针数组

  • 指针数组指若干个具有相同存储类型数据类型的指针变量构成的集合
  • 用法:存储类型 数据类型 *指针数组名[大小]
  • 思考:
    • 指针数组占用的内存空间大小:sizeof(p)
    • 指针数组
  • 指针数组名表示的是数组的起始地址
  • 举例
#include <stdio.h>
#include <string.h>int main(int argc, char *argv[])
{int *p[3];int a[] = {1,2,3,4,5,6};p[0] = a;p[1] = a + 1;p[2] = a + 3;printf("%d %d %d\n", a[0], a[1], a[3]);printf("%d %d %d\n", *p[0], *p[1], *(p[2]));return 0;
}

返回

1 2 4
1 2 4
  • 例2:指针数组经常和二维数组结合在一起
#include <stdio.h>
#include <string.h>int main(int argc, char *argv[])
{int a[2][3] = {{1, 4, 6}, {12, 2, 3}};int *p[2];p[0] = a[0]; //&a[0][0]p[1] = a[1]; //&a[1][0]printf("%d\n", a[0][1]);printf("%d\n", *(a[0]+1));printf("%d\n", *(p[0]+1));return 0;
}
  • 例3:用六种方式遍历二维数组(*(p[row]+col)代表二维数组的第row行第col列)
#include <stdio.h>
#include <string.h>int main(int argc, char *argv[])
{int a[2][3] = {{1, 4, 6}, {12, 2, 3}};int *p[2] = {a[0], a[1]};int i, j;printf("第一种写法: a[i][j]\n");for (i=0; i<2; i++){for (j=0; j<3; j++)printf("%d ", a[i][j]);puts("");}printf("第二种写法: *(a[i]+j)\n");for (i=0; i<2; i++){for (j=0; j<3; j++)printf("%d ", *(a[i]+j));puts("");}printf("第三种写法: *(*(a+i)+j)\n");for (i=0; i<2; i++){for (j=0; j<3; j++)printf("%d ", *(*(a+i)+j));puts("");}printf("第四种写法: p[i][j]\n");for (i=0; i<2; i++){for (j=0; j<3; j++)printf("%d ", p[i][j]);puts("");}printf("第五种写法: *(p[i]+j)\n");for (i=0; i<2; i++){for (j=0; j<3; j++)printf("%d ", *(p[i]+j));puts("");}printf("第六种写法: *(*(p+i)+j)\n");for (i=0; i<2; i++){for (j=0; j<3; j++)printf("%d ", *(*(p+i)+j));puts("");}return 0;
}

return,省略了遍历内容

第一种写法: a[i][j]
第二种写法: *(a[i]+j)
第三种写法: *(*(a+i)+j)
第四种写法: p[i][j]
第五种写法: *(p[i]+j)
第六种写法: *(*(p+i)+j)
1 4 6
12 2 3

6. 多级指针

6.1 基本用法

  • 指向指针变量的指针,称为多级指针
  • 用法:存储类型 数据类型 ** 指针名
  • 举例
#include <stdio.h>
#include <string.h>int main(int argc, char *argv[])
{int m = 10;int *p;int **q; //二级指针int ***qq; //三级指针p = &m;q = &p;qq = &q;printf("p: %p, &p: %p\n", p, &p); //p: 0x7ffcda64d38c, &p: 0x7ffcda64d390printf("q: %p, &q: %p\n", q, &q); //q: 0x7ffcda64d390, &q: 0x7ffcda64d398printf("%d %d %d %d\n", m, *p, **q, ***qq); //10 10 10 10return -1;
}

6.2 多级指针运算

  • int **p; p+1;移动一个int *变量所占用的内存空间。再比如int ***p, p+1就是移动一个int **所占用的内存空间

6.3 多级指针和指针数组

#include <stdio.h>
#include <string.h>int main(int argc, char *argv[])
{int a[] = {1, 2, 3};int *p[2] = {&a[0], &a[1]};int **q;q = p; //&p[0]printf("%d %d\n", a[0], a[1]);printf("%d %d\n", *p[0], *p[1]);printf("%d %d\n", **q, **(p+1)); //*q是p **q=*p=a[0]return -1;
}

7. void指针和const修饰符

7.1 void指针

  • 一般形式:void *变量名称
  • 一种不确定数据类型的指针变量,他可以通过强制类型转换让该变量指向任何数据类型的变量
  • void在没有强制转换前不能进行数值运算
  • 常用在设计的时候告诉用户可以转换成用户任意想要用的变量
  • 举例
#include <stdio.h>
#include <string.h>int main(int argc, char *argv[])
{int m = 10;double n = 3.14;void *p, *q;p = &m; //隐式转换不易理解p = (void *)&m; //最好强制转换q = &n;printf("%d %d\n", m, *(int *)p); printf("%d %d\n", m, *p); // 这句会报错 error: invalid use of void expressionq = (void *)&n;printf("%.2lf %.2lf\n", n, *(double *)q); return -1;
}
  • 用指针遍历一维数组
#include <stdio.h>
#include <string.h>int main(int argc, char *argv[])
{int a[] = {1, 2, 3, 4};int i, n;void *p;p = a;i = 0;n = sizeof(a)/sizeof(int);for (i=0; i<n; i++)printf("%d ", *((int *)p+i));puts("");return -1;
}

7.2 const修饰符

  • 用法:

    • 变量: const 数据类型 变量名=[表达式];
    • 指针:const 数据类型 *变量名=[表达式],限制通过指针改变目标的数值,但指针变量存储的地址值可以修改
    • 指针:数据类型 * const 变量名=[表达式],限制存储的地址值不能修改,但可以通过指针变量修改对应数值
  • 常量化变量使变量的值不能修改
  • 变量const修饰时,若想用指针间接访问变量,指针也要有const修饰
  • 常量化的作用是限制通过指针改变目标的数值,但指针变量存储的地址值可以修改,下面这个例子就是不可取
#include <stdio.h>
#include <string.h>int main(int argc, char *argv[])
{int m = 10;const int *p;int * const q;//int * const q=&m; 正确用法const int * const r=&m; //这个值之后地址不能更改,地址对应的值也不能更改,只能给个初始值p = &m; //正确用法(*p)++; //会报错error: increment of read-only location ‘*p’q = &m; //会报错error: assignment of read-only variable ‘q’return -1;
}

C语言-指针-基础用法、运算、指针与数组、指针数组、多级指针、void指针和const修饰符-学习笔记08相关推荐

  1. C++ const修饰符和指针

    C++ const修饰符和指针 开发工具与关键技术:C++.VisualStudio 作者:何任贤 撰写时间:2019年04月10日 const修饰符,是用来修饰变量,被const修饰符,修饰过的变量 ...

  2. php this指针的用法,C#_C#中this指针的用法示例,本文实例展示了C#中this指针的 - phpStudy...

    C#中this指针的用法示例 本文实例展示了C#中this指针的用法,对于初学者进一步牢固掌握C#有很大帮助,具体内容如下: 一.this指针是什么: 这里有一些面向对象编程的概念需要说明:类(Cla ...

  3. 《数据结构(C语言版)》学习笔记08 查找

    目录 写在前面 一.顺序查找 1.1 普通的无序查找方法 1.2 改进的无序查找--哨兵 1.3 有序的顺序查找 二.折半查找/二分查找 三.分块查找 四.二叉搜索树 五.平衡二叉搜索树 六.B树 七 ...

  4. const修饰符的用法总结

    一.const常量 在 C++ 中,有两种简单的定义常量的方式: 1.使用 #define 预处理器. #define LENGTH 10 #define WIDTH 5 #define NEWLIN ...

  5. Spark基础学习笔记08:Scala简介与安装

    文章目录 零.本讲学习目标 一.Scala简介 (一)Scala概述 (二)函数式编程 (三)Scala特性 1.一切都是对象 2.一切都是函数 3.一切都是表达式 (四)在线运行Scala 二.选择 ...

  6. Spring Boot基础学习笔记08:Spring Boot整合Redis

    文章目录 零.学习目标 1.熟悉Redis相关概念 2.掌握使用Spring Boot整合Redis 一.Redis概述 1.Redis简介 2.Redis优点 (1)存取速度快 (2)数据类型丰富 ...

  7. C语言 unsigned signed void 关键字和 类型修饰符

    有无符号位置 signed 和unsigned 其实就是很简单的逻辑:存储数据的空间的最高位 是符号(+ -号什么的)还是数据 signed 表示数据的最高位是符号位 一般用于保存数字类信息 一般我们 ...

  8. c语言中void指针,C 语言 void指针

    C 语言 void指针 到目前为止,我们已经研究了分配给指针的地址应该与指针声明中指定的类型相同. 例如,如果我们声明了int指针,则此int指针不能指向float变量或某种其他类型的变量,即它只能指 ...

  9. Python语言学习:python语言的特点、入门、基础用法之详细攻略

    Python语言学习:python语言的特点.入门.基础用法之详细攻略 相关内容 Python 基础教程 目录 python语言的特点 python语言的入门 python语言的基础用法 python ...

最新文章

  1. eclipse中如何将java项目转为java Web项目
  2. 独家 | 如何用XGBoost做时间序列预测?
  3. R语言:再谈REmap包
  4. android动态化ui框架,动态化高性能的 UI 框架 Virtualview-Android
  5. 服务器系统盘单独硬盘,我的服务器今天加了个硬盘,可以实现双系统吗?
  6. 在MongoDB中实现聚合函数
  7. easyclick联众打码
  8. 《WEB全栈工程师的自我修养》--索引笔记
  9. 我的精神家园——陈皓(@左耳朵耗子)专访
  10. MySQL--- 有哪些“饮鸩止渴”提高性能的方法?
  11. Ubuntu下搭建SVN与Apache权限控制
  12. java大马后门_【猥琐流】制作一个隐藏在黑页下的大马并且添加后门
  13. 如何修改Win10的Windows Terminal的背景(带图文)
  14. POSTGIS路径规划的简单配置(数据库配置)
  15. java实现txt文件/文本转语音(全网最详细易懂)
  16. 美术鉴赏课的体会和深入理解计算机系统,中外美术鉴赏学习心得体会(选修课)-20210612092854.pdf-原创力文档...
  17. C语言的字符串输入函数gets_s()
  18. java半碳前叉结构,山地车避震前叉功能及原理分类详解(图文)
  19. 如何使用烙铁:初学者指南
  20. wps带阴影的边框怎么设置_WPS图片阴影效果怎么添加?WPS表格阴影边框如何设置?...

热门文章

  1. 【PySimpleGUI】Python用户交互图形界面开发(3)
  2. 大学生创业交流会计算机二级,我校举行第七届中国国际“互联网+”大学生创新创业大赛交流研讨会...
  3. 【愚公系列】2021年12月 攻防世界-进阶题-MISC-063(saleae)
  4. java compareto 返回值_Java comparable接口及compareTo返回值所决定的升序降序问题
  5. 天津大学电工技术实验
  6. Oracle删除索引 判断,Oracle索引(index)的分类、建立与删除
  7. 服务器推送之HTTP Mime multipart/x-mixed-replace
  8. pyhon 安装第一个模块 xlrd
  9. OpenStack之Neutron理论部分
  10. 算法学习笔记 - 哈希(Hash)