前言

嗨嗨,这里是渡枫,欢迎阅读第五期的C语言浮游塔。

在正式学习指针之前,我们先提出几个问题。

  1. 什么是指针?
  2. 什么决定了数据的长度?
  3. 指针是否有类型?
  4. &a是指针吗?

一:指针就是一个变量的内存地址,指针变量是用来存放变量地址的变量。

内存的地址:

即内存单元的地址,为了便于管理内存,每个存储单元(可以存放一个字节的存储空间)都有一个唯一的编号,且编号从0开始。

变量的地址:

根据变量类型的不同,系统会为其分配一定字节数的存储空间,所分配存储空间的第一个存储单元的地址即为该变量的地址。

二:C语言中所有的变量都是要占据内存的,并且其占据的内存大小是由变量类型所决定的。

       所有的指针变量都占据相同大小的内存由操作系统(编译器)的位数决定,更根本上来说是由硬件体系架构决定的。

       例如:在32位的电脑上,指针变量占4个字节;在64位的电脑上,指针变量占8个字节。

三:有,并且与指针指向的数据结构有关。

四:是指针,只要是地址就是指针。

好,那么至此,我们对指针有了一个大致的了解,接下来就让我们正式的开始学习指针吧。

Link Start!

目录

浮游塔第41层:一重指针变量的定义及初始化

浮游塔第42层:一重指针变量的运算

浮游塔第43层:二重指针变量

浮游塔第44层:几种特殊的指针及其运算

浮游塔第45层:指针作为参数函数

浮游塔第46层:指针函数

浮游塔第47层:函数指针

浮游塔第48层:指针与数组

浮游塔第49层:指针与字符串

浮游塔第50层:动态内存分配与指向它的指针

后记



浮游塔第41层:一重指针变量的定义及初始化

一重指针变量的定义

和数组的一维,二维类似,指针也有着一重、二重、多重的分类,但比起数组,指针的分类更加好理解,下面介绍以下一重指针变量的定义语法:

其定义语法:

数据类型    *变量名;  int *p;

这个的数据类型,代表指针的目标变量的数据类型,在此处就可以理解位p的数据类型位整型。

而*在指针变量得定义时只是个标志,并没有什么实际意义,但在语句中是间接寻址符,而&则是取地址符)

上文中提到过,指针就是一个变量的内存地址。

指针变量,则是存放变量地址的变量。(简称,套娃)

在进行指针的定义时,需要注意以下几点:

①:在同时定义多个指针变量时,每个变量之间要用逗号隔开,且*不可以省略。

int *p1,*p2;int *p1,p2;//二者的内在含义是不同的。

②:*是定义指针变量的标志,p1,p2为变量名。

③:指针变量智能指向定义时所规定类型的变量。

④:指针变量在定义后,值是不确定的,所以在引用它之前,需要先进行初始化(赋值)。

一重指针变量的初始化

其初始化语法为:

数据类型 *指针名 =初始地址值int a;
int *p=&a;

此处的初始地址值是赋给指针变量的,而不是给目标变量

且该变量必须事先定义过,且数据类型相同。

在指针变量的初始化过程中,也容易出现以下四种情况:

①:类型不一致

int a;
double *b=&a;

②:赋值时,在指针变量前加*

int a;
int *p;
*p=&a;

③:把常数赋值给指针变量

int *p;
p=2000;

④:直接用指针存储数据。

int *p;
*p=10;

浮游塔第42层:一重指针变量的运算

指针运算是以指针变量所存放的地址量作为运算量而进行的运算

指针运算的实质就是地址的计算

指针运算的种类是有限的,它只能进行赋值运算、算术运算、关系运算。

算符 计算形式 意义
+ p+n 指针向地址大的方向移动n个数据
- p-n 指针向地址小的方向移动n个数据
++ p或p 指针向地址大的方向移动1个数据
-- p--或--p 指针向地址小的方向移动1个数据
- p-px 两个指针之间相隔元素的个数

指针的基本用法、指针的运算_扇贝丫丫的博客-CSDN博客_指针的指向运算符


浮游塔第43层:二重指针变量

指针变量也是占内存的,每个指针变量都是保存的地址,所以指针变量所占内存都是4个字节。那么指针变量的所占内存空间的地址该用什么数据类型变量来保存呢?

如果需要保存一级指针变量的地址,我们就需要使用二级指针。

二级指针的定义如下:

数据类型 **指针变量名;
int **p;
#include<stdio.h>
int main( )
{  int i = 10; int *p = &i; int **q = &p;int ***r = &q; printf("i = %d\n", ***r);return 0;
}

如果需要保存一个指针变量的地址,那么我们需要定义二级指针的变量保存。


浮游塔第44层:几种特殊的指针及其运算

①:野指针

作用:指向一个非法的或已销毁的内存的指针

危害:对系统造成不可预知的错误

野指针的出现

指针变量没有被初始化,它缺省值是随机的,它会乱指一气

例如:

char *p;     //野指针,p的值是随机的
int *pd;
printf("%p\n", pd);

在printf中,打印指针的值用%p

指针p被free或者delete之后,只是把指针所指的内存释放掉了,没有改变指针的值,此时,p沦落为野指针

为了避免此类野指针的出现,指针变量在创建的同时应该被初始化,要么将它设置为NULL,要么让它指向合法的内存

char *p = NULL  //#define NULL 0
char *p = (char*) malloc(100);

②:const指针

作用:限定变量的内存为只读(即内存不可被改写)

【案例】:

const int k = 0;//声明常量
k =10; //错误 ,常量不能修改

在指针声明符中,若使用了const关键字,则该指针称为const指针

一重变量的const指针:

const修饰一重指针变量,表明该指针变量的值初始化后不可改变

【案例】:

设有 int x=3;int y=4;请分析下面语句。

int* const p = &x;
p = &y; //错误,p的值不能被改写
*p=10;  //正确,*p的值可以被改写

const int x与int const x完全等价。

int* const p与const int *p之间存在天壤之别,

前者const修饰变量p,第二种const修饰变量*p。

③:零指针(空指针)

概念: 指针变量值为零

其定义形式如下:

 int  *p=0;

P并非指向地址为0的单元,它表示指针变量值没有意义,即不指向任何内存单元。


浮游塔第45层:指针作为参数函数

在此前我们提到过参数传递的两种方式,今天再结合指针给大家回忆一下。

函数调用时,根据参数传递的方式不同可以分为:数值传递和“地址”传递

①:数值传递(单向数据传递方式)

当实参和形参之间传递的是数据时,被调函数体内只能利用这一传递来的值,而无法通过这一“数值”影响到函数体外的变量。

将实参中存放的“数值”传递给形参,实参与形参完成“传递接力”后,两者再无干系。

void num(int a)
{a=5;
}int main()
{int b=0;num(b);printf("b=%d\n",b);
}

运行结果:b=0。

②:地址传递(双向数据传递方式)

当实参与形参之间传递的是“地址”这种特殊的值时,被调函数体内就能通过“地址”影响到函数体外变量的值。

实参中存放的“地址”传递给了形参,完成传递接力后,两者也再无干系。

void num(int *a)
{*a=5;
}int main()
{int b=0;num(&b);printf("b=%d\n",b);
}

运行结果:b=5。

地址传递有以下三种情况:

1.实参是数组,形参是整型变量

int max(int a[],int n)
{int i,m=a[0];for(i=1;i<n;i++)if(a[i]>m)m=a[i];return m;
}int main( )
{int b[5]={23,4,5,6,33};int res=max(b,5);printf("最大值为:%d\n",res);return 0;
}

2.实参是数组,形参是指针变量

int max(int *p,int n)
{int i,m=*p;for(i=1;i<n;i++)if(*(p+i)>m)m=*(p+i);return m;
}int main( )
{int b[5]={23,4,5,6,33};int res=max(b,5);printf("最大值为:%d\n",res);return 0;
}

3.实参是指针变量,形参是数组

int max(int a[],int n)
{int i,m=a[0];for(i=1;i<n;i++)if(a[i]>m)m=a[i];return m;
}int main( )
{int b[5]={23,4,5,6,33};int *p=b;int res=max(p,5);printf("最大值为:%d\n",res);return 0;
}

浮游塔第46层:指针函数

所谓指针函数,即返回值是指针的函数,其本质上是一个函数。

定义格式如下:

函数类型  *函数名(形参列表)
{函数体
}
int *fun(int a,int b)
{......
}

指针函数本身也没什么特别之处,与普通函数相比只是它返回了一个指针(即地址值)而已。

用以下代码为例:取出两个数中的最大值。

#include<stdio.h>
int *max(int *p1, int *p2)
{if(*p1 > *p2)return p1;elsereturn p2;
}
int main(int argc, char *argv[])
{int *p, a, b;a = 1; b = 2;p = max(&a, &b);printf("%d\n", *p);return 0;
}


浮游塔第47层:函数指针

如果在程序中定义了一个函数,那么在编译时系统就会为这个函数代码分配一段存储空间,这段存储空间的首地址称为这个函数的地址。

而且函数名表示的就是这个地址。

既然是地址我们就可以定义一个指针变量来存放,这个指针变量就叫作函数指针

与指针函数不同,函数指针 的本质是一个指针,该指针的地址指向了一个函数,所以它是指向函数的指针。
我们知道,函数的定义是存在于代码段。

因此,每个函数在代码段中,也有着自己的入口地址,函数指针就是指向代码段中函数入口地址的指针。

那么这个指针变量怎么定义呢?

虽然同样是指向一个地址,但指向函数的指针变量同我们之前讲的指向变量的指针变量的定义方式是不同的。

函数返回值类型 (* 指针变量名) (函数参数列表);

“函数返回值类型”表示该指针变量可以指向具有什么返回值类型的函数;“函数参数列表”表示该指针变量可以指向具有什么参数列表的函数。这个参数列表中只需要写函数的参数类型即可。

int fun(int x);  //声明一个函数
int (*p)(int x);  //定义一个函数指针
p=fun;           //将fun函数的首地址赋给指针变量p

浮游塔第48层:指针与数组

一、数组元素的引用

①:通过数组的首地址来应用数组元素

在主函数中定义了一个数组,数组名代表数组元素的首地址。

int arr[5] = {1,2,3,4};

说明:arr是arr数组元素的首地址,arr(即 arr +0)的值表示&arr[0],

则:arr+1 的值等于&arr[1]、arr+2的值等于&arr[2]、....arr+ 9 的值为&arr[9]。

arr[0]的值可以通过 *(arr + 0)来得到。

以此类推:arr[i]的值可以通过*(arr+ i)来得到。

总结:通过数组的首地址来引用数组元素有两种方法:

数组名[下标]      ②*(数组名 + 下标)

#include<stdio.h>
int main( )
{int arr[5]={1,2,3,4,5};int i=0;  //保存数组下标printf("①:数组名[下标]\n");for(i=0;i<5;i++) //遍历数组{printf("arr[%d]=%d\n",i,arr[i]);}printf("\n");printf("②:*(数组名+下标)\n");for(i=0;i<5;i++) //遍历数组{    printf("arr[%d]=%d\n",i,*(arr+i));  }return 0;
}

②:通过指针变量引用数组元素

1、定义一个指针,指向数组的首地址

int arr[5] = {1,2,3,4,5};
int *p ;
p = arr;//数组名代表数组的首地址

2、用指针访问数组元素

int arr[5] = {1,2,3,4,5};
int *p ;
p = arr;//数组名代表数组的首地址//用指针访问数组的0号元素
printf("%d\n",*p);//用指针访问数组的1号元素
p++;
printf("%d\n",*p);

③:用带下标的指针变量引用数组元素

若有以下定义和语句:

int *p,s[10],i;
p = s;

且0<= i && i<= 9,我们已经知道可以用三种方法去表示s[i]的地址:&s[i],s+i,p+i。

很明显:s[i]可以用表达式*(s + i)来表示

同理:*(p + i)也可以用p[i]来表示

因此当p指向s数组的数组的首地址是,表示数组元素s[i]的表达式应当有四种:

①:s[i]       ②:*(s + i)    ③:*(p +i)   ④:p[i]


浮游塔第49层:指针与字符串

首先,我们需要明白一件事情,字符串其实是存储在字符数组当中的。

不能通过指针直接修改常量区中的字符串常量

引用字符串有两种方法。

①:通过数组名和下标

#include <stdio.h>
int main()
{char string[]="Hello World!";printf("%s\n",string);printf("%c\n",string[7]);return 0;
}


②:通过字符指针变量引用一个字符串常量

#include <stdio.h>
int main()
{char *string="Hello World!";   printf("%s\n",string);return 0;
}


浮游塔第50层:动态内存分配与指向它的指针

首先,什么是内存的动态分配呢?

之前我们提到过全局变量和局部变量两个概念,其中,全局变量是分配在内存中的静态存储区的,非静态的局部变量(包括形参)是分配在内存中的动态存储区的,这个存储区是一个称为栈的区域。

除此之外,C语言还允许建立内存动态分配区域,以存放一些临时用的数据,这些数据不必在程序的声明部分定义,也不必等到函数结束时才释放,而是需要时随时开辟,不需要时随时释放。

这些数据时临时存放在一个特别的自由存储区,称为堆区。

可以根据需要,向系统申请所需大小的空间。由于未在声明部分定义它们为变量或数组,因此不能通过变量名或数组名去引用这些数据,只能通过指针来引用。

————大部分内容来自《C程序设计(第四版)谭浩强》

那么,又该怎样建立内存的动态分配呢?

对内存的动态分配是通过系统提供的库函数来实现的,主要有4各函数:

malloc、calloc、free、realloc

#include<stdio.h>
#include<stdlib.h>
int main()
{void *d=malloc(10);void *r=realloc(d,50);int a=100;int *m=&a;int *p=(int *)d;//指针类型可以强制转换int *q=(int *)calloc(10,4);free(d);free(m);printf("%d\n",m);  //此时再使用m的话,会报错,因为m的空间已经消失。printf("%d\n",sizeof(d));return 0;
}

1.使用malloc函数(分配长度为size的空间)

函数原型为:

void *malloc(unsigned int size);

malloc函数的作用就是开辟一个长度为size的连续的空间。

由于他是一个指针型函数,故它返回的指针指向分配空间的开头位置。

需要注意的是,由于指针的基类型为void,所以即不指向任何类型的数据,只提供一个地址。

如果此函数未能成功地执行,则返回空指针。

2.使用calloc函数(分配n个长度为size的空间)

函数原型为:

void *calloc(unsigned n,unsigned size);

calloc函数的作用与malloc函数类似,不过它可以分配n个长度为size的连续的空间。

它的返回值指向所分配域的起始位置的指针。

如果此函数未能成功地执行,则返回空指针。

3.使用free函数(释放空间)

函数原型为:

void free(void *p);

其作用是释放指针变量p所指向的动态空间,使这 部分空间能重新被其他变量使用,p应是最近依次调用calloc或malloc函数时得到的函数返回值。

如:

free(p);   //释放指针变量p所指向的已分配的动态空间

free函数无返回值。

4.使用realloc函数(重新分配长度为size的空间)

函数原型为:

void *realloc(void *p,unsigned int size);

如果已经通过malloc函数或者calloc函数获得了动态空间,想改变其大小,可以用realloc函数重新分配。

用realloc函数将p所指向的动态空间的大小改变为size。p的值不变。如果重新分配不成功,则返回NULL。

如:

realloc(p,50);   //将p所指向的已分配的动态空间改为50字节。


后记

着手写第五期内容的时候是七月五号,但没想到发出来的时候已经是二十六号了。

先跟大家伙道个歉,这二十多天由于工作的原因没有碰到电脑,

所以进度有些缓慢,内容质量也没有前几期的高,不过相信下一期会很快和大家见面的!

[编程神域 C语言浮游塔 第⑤期]内存地址——指针相关推荐

  1. [编程神域 C语言浮游塔 第③期]数组

    前言 嗨嗨,这里是枫枫. 没想到我们的专栏能迎来第三期,先来庆祝一下. 本期的内容主要是设计到一维数组以及字符串数组,其中会夹杂些其他的有关内容,例如一维数组作为函数参数等等. 下面,先给大家讲讲什么 ...

  2. [编程神域 C语言浮游塔 第②期] 程序框架的搭建及14种基本运算符

    前言 都不知道有没有人在看.(悲) 经过之前的学习,我们知道了C语言的起源,C语言的基本结构以及一些基础知识. 本期我们会在此基础上向大家讲解14种运算符(主流使用).格式以及字符的输入输出.数据类型 ...

  3. [编程神域 C语言浮游塔 第①期] Hello C language world

    建议新人收藏使用! 前言 个人介绍: 大家好,欢迎来到C语言浮游塔,这里是设计人茅场晶彦(误)专栏作者渡枫,人称枫子哥. 初衷: 因为自己很多软件相关的朋友在大一初学C语言的就直呼"难上了天 ...

  4. [编程神域 C语言浮游塔 第④期] 函数的基本知识

     前言 通过上一期的学习,我们知晓了数组及其不同的使用方法. 本期学习的是C语言中的函数,可类比为java中的方法. 函数是构成C程序的基本单元,可以说C程序通常由若干函数组成,它包括函数头和函数体. ...

  5. linux c语言变量地址类型,C语言基础知识:访问内存地址的方法

    汇编语言寄存器间接寻址方法 #define GPJ0CON0xE0200240 ldr r0, =GPJ0CON //把地址值赋给寄存器r0,从后面的=可以看出用的是ldr伪指令,因为需要编译器来判断 ...

  6. C语言调用 free 函数释放内存后指针指向及内存中的值是否改变的问题

    文章目录 1. 前言 2. 正文 2.1. "分配" 与 "释放" 2.2. 运行测试 2.2.1. VSCode 下使用 gcc 编译 2.2.2. VS20 ...

  7. C语言学习第008课——内存和指针

    内存含义 存储器:计算机的组成中,用来存储程序和数据,辅助CPU进行运算处理的重要部分 内存:内部存储器,暂存程序/数据--掉电丢失,SRAM DRAM DDR DDR1 DDR2 DDR3 外存:外 ...

  8. CUDA C 编程权威指南 Grossman 第4章 全局内存

    4.1 CUDA内存模型概述 内存访问和管理是所有编程语言的重要部分. 因为多数工作负载被加载和存储数据的速度所限制,所以有大量低延迟.高带宽的内存对性能是十分有利的. 大容量.低延迟的内存造价高且不 ...

  9. 【C语言】深入浅出理解指针及内存与指针的关系(详细讲解+代码展示)

    目录 概述 内存 内存含义 内存作用: 物理存储器和存储地址空间 物理存储器:实际存在的具体存储器芯片. 存储地址空间:对存储器编码的范围. 内存地址 指针和指针变量 指针基础知识 指针变量的定义和使 ...

最新文章

  1. C++中一个class类对象占用多少内字节(7个例子,很清楚)
  2. 一致性哈希算法----- 解决memecache 服务器扩容后的数据丢失。
  3. C语言结构体-大小,对齐,填充,使用及其他
  4. Quest 公司的Shareplex 与 GoldenGate比较
  5. Android之网络请求通过协程+okhttp的没有做网络异常处理导致程序奔溃问题
  6. 断路器操作机构分合闸线圈电流特征曲线特点
  7. 笔记本(华硕UL80VT)软件超频setFSB
  8. Android Zip文件解压缩代码
  9. emacs python_Emacs 下使用 lsp-mode 对 Python 进行补全
  10. 如何在 Simulink 中使用 PID Tuner 进行 PID 调参?
  11. 使用 Preload/Prefetch 优化
  12. 9.6 shell脚本应用3
  13. Promise--优雅的异步回调解决方案
  14. 打开小米随身wifi的无线网卡功能
  15. select 默认选中问题
  16. 图的广度优先搜索和深度优先搜索
  17. 示波器电流探头工作原理-Pintech品致
  18. 【java图形化用户界面】猜数游戏
  19. 基于BCNN的汽车车型识别研究
  20. 高可靠环境 FileNet 系统介绍和应用实例

热门文章

  1. 亡命摄影师为拍日落纵身越峡谷
  2. 使用PDH性能计数器(Windows)获取CPU使用率、可用物理内存、上传/下载速率、磁盘读写速率
  3. BurpSuite2021 -- Intruder模块
  4. linux lamp的配置
  5. 网上商城项目(加入购物车)
  6. 数字转为拼音的读法 8010 ba qian ling yi shi
  7. python字符串的内置函数_python内置函数字符串
  8. 计算机考试准考证压缩包打不开
  9. 聊城大学计算机学院格创,聊城大学计算机学院第九届科技学术节活动方案(9页)-原创力文档...
  10. 六年级下册计算机电子板报教案,《第1课制作电子小报教案》小学信息技术浙摄影社课标版六年级下册教案28605.docx...