C语言入门---第九章 C语言指针
没学指针就是没学C语言! 指针是C语言的精华,也是C语言的难点。
所谓指针,也就是内存的地址,所谓指针变量,也就是保存了内存地址的变量。不过人们往往不会区分两者的概念,而是混淆在一起使用。
=====指针的概念=======
计算机中的所有数据都必须放在内存中,不同类型的数据占用的字节数不一样,例如int占用4个字节,char 占用1个字节。
我们将内存中字节的编号称为地址或指针。
一切都是地址
C 语言用变量来存储数据,用函数来定义一段可以重复使用的代码,他们最终都要放到内存中才能供CPU使用。
数据和代码都以二进制的形式存储在内存中,计算机无法从格式上区分某块内存中存储的是数据还是代码。当程序被加载到内存后,操作系统会给不同的内存块指定不同的权限,拥有读取和执行权限的内存块就是代码,而拥有读取和写入权限,也可能只有读取权限的内存块就是数据。
CPU只能通过地址来取得内存中的代码和数据,程序在执行过程中会告知CPU要执行的代码以及要读写的数据的地址,如果程序不小心出错,在CPU要写入数据时给它一个代码区域的地址,就会发生内存访问错误。这种内存访问错误会被硬件和操作系统拦截,强制程序奔溃,程序员没有挽回的机会。
CPU访问内存时需要的是地址,而不是变量名和函数名,变量名和函数名只是地址的一种助记符,当源文件被编译和链接成可执行程序后,他们都会被替换成地址。编译和链接过程的一项重要任务就是找到这些名称所对应的地址。
需要注意的是,虽然变量名、函数名、字符串名和数组名在本质上是一样的,他们都是地址的助记符,但在编写代码的过程中,我们认为变量名表示的是数据本身,而函数名,字符串名和数组名则表示的是代码块或数据块的首地址。
=====C指针变量的定义和使用(精华)=====
1. 数据在内存中的地址也成为指针,如果一个变量存储了一份数据的指针,我们就称它为指针变量。
在C语言中,允许用一个变量来存放指针,这种变量称为指针变量。指针变量的值就是某份数据的地址,这样的一份数据可以是数组、字符串、函数,也可以是另外的一个普通变量或指针变量。
例如:
int a = 100;
int *p_a = &a;
在定义指针变量p_a 的同时对它进行初始化,并将变量a 的地址赋予它,此时p_a就指向了a.值得注意的是,p_a需要的一个地址,a 前面必须要加取地址符&,否则是不对
* 是一个特殊符号,表明一个变量是指针变量,定义p1、p2 时必须带*。而给p1、p2 赋值时,因为已经知道了它是一个指针变量,就没有必要再加上*。
也就是说,在定义指针变量时必须带*,给指针变量赋值时不能带*。
=====关于 * 和 & 的谜题
假设有一个int 类型的变量a ,pa 是指向它的指针,那么*&a 和&*pa 分别是什么意思呢?
&a : 取变量a 的地址(等价于pa),
*(&a): 表示取这个地址上的数据,等价于 *pa, 仍然等于a
=======指针变量的运算(加、减和比较运算)====
指针变量保存的是地址,地址本质上是一个整数,所以指针变量可以进行部分于是奶奶。例如:加法、减法、比较等。
===9.4 C语言数组指针 (指向数组的指针)====
数组(Array)是一系列具有相同类型的数据的集合,每一份数据叫做一个数组元素。数组中的所有元素在内存中是连续排列的,震哥哥数组占用的是一块内存。
定义数组时,要给出数组名和数组长度,数组名可以认为是一个指针,它指向数组的第0个元素。在C语言中,我们将第0个元素的地址称为数组的首地址。
===========
求数组长度: int len = sizeof(arr) / sizeof(int);
sizeof(arr) 会获得挣个数组所占用的字节数,sizeof(int) 会获得一个数组元素所占用的字节数,他们相除的结果就是数组包含的元素的个数,即数组长度。
============
如果一个指针指向了数组,我们就称它为数组指针。
数组指针指向的是数组中的一个具体元素,而不是整个数组,所以数组指针的类型和数组元素的类型有关。
======C语言字符串指针(指向字符串的指针)=====
C语言中没有特定的字符串类型,我们通常是将字符串放在一个字符数组中。
字符数组存储在全局数据区或栈区,第二种形式的字符串存储在常量区。全局数据区和栈区的字符串(也包括其他数据)有读取和写入的权限,而常量区的字符串只有读取权限,没有写入权限。
除了字符数组,C语言还支持另外一种表示字符串的方法,就是直接使用一个指针指向字符串,例如: char *str = “hhhhhhhhhhh”;
字符串中的所有字符在内存中是连续排列的,str 指向的是字符串的第0个字符,我们通常将第0个字符的地址称为字符串的首地址。字符串中的每个字符的类型都是char ,所以 str 的类型也必须是 char*
===到底使用字符数组还是字符串常量===
在编程过程中如果只涉及到对字符串的读取,那么字符数组和字符串常量都能够满足要求;如果有写入(修改)操作,那么只能使用字符数组,不能使用字符串常量。
获取用户输入的字符串就是一个典型的写入操作,只能使用字符数组,不能使用字符串常量。
#include<stdio.h>
int main() {
char str[30];
gets(str);
printf("%s\n", str);
return 0;
}
========== 指针变量作为函数参数====
在C语言中,函数的参数不仅可以是整数、小数、字符等具体的数据,还可以是指向他们的指针。用指针变量作函数参数可以将函数外部的地址传递到函数内部,使得在函数内部可以操作函数外部的数据,并且这些数据不会随着函数的结束而被销毁。
像数组、字符串、动态分配的内存等都是一系列数据的集合,没有办法通过一个参数全部传入函数内部,只能传它们的指针,在函数内部通过指针来影响这些数据集合。
====代码示例:
#include<stdio.h>
void swap(int *p1, int *p2){
int temp; //临时变量
temp = *p1;
*p1 = *p2;
*p2 = temp;
}
C语言为什么不允许直接传递数组的所有元素,而必须传递数组指针呢?
参数的传递本质上是一次赋值的过程,赋值就是对内存进行拷贝。所谓内存拷贝,是将一块内存上的数据赋值到另一块内存上。
=======指针作为函数返回值===
C语言允许函数的返回值是一个指针,我们将这样的函数称为指针函数。
用指针作为函数返回值时需要注意的一点是,函数运行结束后会销毁在它内部定义的所有局部数据,包括局部变量、局部数组和形式参数,函数返回的指针请尽量不要指向这些数据,C语言没有任何机制来保证这些数据会一直有效,它们会在后续使用过程中可能会引发运行时错误。
====二级指针(指向指针的指针)====
指针可以指向一份普通类型的数据,例如:int 、double 、char 等,也可以指向一份指针类型的数据,例如:int*、double* 、char*等。
如果一个指针指向的是另外一个指针,我们就称它为二级指针,或者指向指针的指针。
=====C语言指针数组(数组每个元素都是指针)=====
如果一个数组中的所有元素保存的都是指针,那么我们就称它为指针数组。指针数组的定义形式一般为:
int *arr[] = {&a,&b,&c};
===
#include<stdio.h>
int main() {
char *str[3] = {"111","222"."333"};
}
需要注意的是,字符数组str中存放的是字符串的首地址,不是字符串本身,字符串本身位于其他的内存区域,字符数组是分开的。
也只有当指针数组中每个元素的类型都是 char * 时,才能像上面那样给指针数组赋值,其他类型不同。
=========二维数组指针(指向二维数组的指针)====
二维数组在概念上是二维的,有行有列,但在内存中所有的数组元素都是连续排列的,它们之间没有“缝隙”。
为了更好的理解指针和二维数组的关系,我们先来定义一个指向a 的指针变量P:
int(*p)[4] = a;
括号中的*表明p 是一个指针,它指向一个数组,数组类型为int[4],这正是a 所包含的每个一维数组的类型。[]的优先级高于*,()是必须要加的,如果赤裸裸的写作int *p[4] ,那么应该理解为int *(p[4]),p就成了一个指针数组,并不是二维数组指针。
=====函数指针(指向函数的指针)====
一个函数总是占用一段连续的内存区域,函数名在表达式中有时会被转换为该函数所在内存区域的首地址,这和数组名非常相似,我们可以把函数的首地址(或称为入口地址)赋予一个指针变量,使指针变量指向函数所在的内存区域,然后通过指针变量就可以找到并调用该函数。这种指针就是函数指针。
函数指针的定义形式为:returnType (*pointerName)(param list)
returnType 为函数返回值类型。
====对C语言指针的总结====
指针就是内存地址,C预压允许用一个变量来存放指针,这种变量称为指针变量。指针变量可以存放基本类型数据的地址,也可以存放数组,函数以及其他指针变量的地址。
程序在运行过程中需要的是数据和指令的地址,变量名、函数名、字符串名和数组名在本质上是一样的,他们都是地址的助记符,在编写代码的过程中,我们认为变量名表示的是数据本身,而函数名、字符串名和数组名表示的是代码块或数据块的首地址,程序被编译和链接后,这些名字都会消失,取而代之的是他们对应的地址。
定义:int *p: p 可以指向int 类型的数据,也可以指向类似 int arr[n]的数组。
int **P : p为二级指针,指向 int * 类型的数据。
int *p[n] : p为指针数组。[]的优先级高于*,所以应该理解为int *(p[n]);
====
1.使用指针变量之前一定要初始化,否则就不能确定指针指向哪里,如果它指向的内存没有使用权限,程序就崩溃了。对于暂时没有指向的指针,建议赋值NULL。
2.两个指针变量可以相减。如果两个指针变量指向同一个数组中的某个元素,那么相减的结果就是两个指针之间相差的元素个数。
3.数组也是有类型的,数组名的本意是表示一组类型相同的数据。在定义数组时,或者和sizeof、&运算符一起使用时数组名才表示挣个数组,表达式中的数组名会被转换为一个指向数组的指针。
.
的。
C语言入门---第九章 C语言指针的更多相关文章
- 第九章 C语言在嵌入式中的应用
上章回顾 编码的规范和程序版式 版权管理和申明 头文件结构和作用 程序命名 程序注释和代码布局规范 assert断言函数的应用 与0或NULL值的比较 内存的分配和释放细节,避免内存泄露 常量特性 g ...
- C基础入门 - 第一章 - C语言绪言
第1章 C语言绪言 1.1 C语言概述 1.1.1 C语言世界 1.1.2 C语言学习, 能当饭吃吗 1.2 开发环境构建 1.2.1 visual studio安装使用 1.2.2 visual s ...
- 一.OC基础之:1,OC语言的前世今生 ,2,OC语言入门,3,OC语言与C的差异,4,面向对象,5,类和对象的抽象关系,6,类的代码创建,7,类的成员组成及访问
1,OC语言的前世今生 , 一, 在20世纪80年代早期,布莱德.麦克(Brad Cox)设计了OC语言,它在C语言的基础上增加了一层,这意味着对C进行了扩展,从而创造出一门新的程序设计语言,支持对象 ...
- 【Go语言入门系列】Go语言工作目录介绍及命令工具的使用
[Go语言入门系列]前面的文章: [保姆级教程]手把手教你进行Go语言环境安装及相关VSCode配置 [Go语言入门系列](八)Go语言是不是面向对象语言? [Go语言入门系列](九)写这些就是为了搞 ...
- C语言入门:01.C语言概述
一.计算机和软件常识 1.计算机运行原理 (1)硬件基本组成:硬盘.内存.CPU (2)个部件之间的运作协调(下图)
- 【C语言入门教程】4.5 指针变量的定义与引用
指针变量是包含内存地址的变量.一般的变量直接包含一个特定的值,而指针变量包含的是某一特定数据类型的内存地址.普通变量直接引用其中的值,指针变量则间接引用所指向内存地址中的值.指针变量在使用前需要声明与 ...
- 【C语言入门教程】4.6 指针 和 数组
数组在内存中以顺序的形式存放,数组的第一个存储单元的地址即数组的首地址.对一维数组来说,直接引用数组名就能获得该数组的首地址.指针变量可以存放于其内容相同的数组首地址,也可以指向某一具体的数组元素.通 ...
- C语言学习第九章
学习C语言的最后一节课了,原因嘛上一章的末尾说过了,其实写这篇博客的时候以后开始学习Java一个多月了,一直因为各种各样的原因没有坚持做到每天一篇学习记录,可能主要因为懒吧....也有点笨,Java的 ...
- 【C语言入门教程】4.4 指针 与 指针变量
在程序中声明变量后,编译器就会为该变量分配相应的内存单元.也就是说,每个变量在内存会有固定的位置,有具体的地址.由于变量的数据类型不同,它所占的内存单元数也不相同.如下列声明了一些变量和数组. int ...
随机推荐
- K8S集群搭建之软路由的安装
一.系统要求 ①镜像:win10 ②1C.4G.20G即可 ③仅主机模式(共享网卡上网) ④老毛桃PE ⑤ip为192.168.66网段(因为我设置的其他K8S节点也为该网段)---koolshare ...
- JS高级---实例对象使用属性和方法层层的搜索 (实例对象-->原型对象-->报错)
实例对象使用属性和方法层层的搜索: 实例对象使用的属性或者方法, 先在实例中查找, 找到了则直接使用: 找不到则, 再去实例对象的__proto__指向的原型对象prototype中找, 找到了则 ...
- 激活windows系统
1.下载KMS 2.如图所示,双击KMSpico看是否正常运行 3.双击KMSpico正常后出现以下界面 4.点击红色按钮 5.等自动退出就是激活成功,大概半年需要激活一次
- 连接(JOIN)
join 用于把来自两个或多个表的行结合起来,基于这些表之间的共同字段. INNER JOIN 从多个表中返回满足 JOIN 条件的所有行. INNER JOIN:如果表中有至少一个匹配,则返回行 L ...
- 交换机出现err-disable的原因及解决方法
转:https://www.2cto.com/net/201303/198724.html 交换机出现err-disable的原因及解决方法 LOG示例: 21w6d: %ETHCNTR-3-LOOP ...
- oracle sys可以登录,system权限不足,解决方法
今天在自己电脑上安装了oracle 11g,安装成功后发现 sys 可以正常登录.system 无法登录,显示 ORA-01031: insufficient privileges(权限不足) sel ...
- 【MySQL】存储引擎
" 目录 #. MySQL支持的存储引擎 1. InnoDB 2. MyISAM 3. NDB 4. Memory 5. Infobright 6. NTSE 7. BLACKHOLE My ...
- 计算机二级-C语言-程序填空题-190117记录-对文件的处理,复制两个文件,往新文件中写入数据。
//给定程序的功能是,调用函数fun将指定源文件中的内容赋值到指定目标文件中,复制成功时函数返回1,失败时返回0,把复制的内容输出到终端屏幕.主函数中源文件名放在变量sfname中,目标文件名放在变量 ...
- LeetCode练题——53. Maximum Subarray
1.题目 53. Maximum Subarray——Easy Given an integer array nums, find the contiguous subarray (containin ...
- 安装go1.11.2
1. 设置go环境变量 vim $HOME/.bashrc export GOROOT=$HOME/go export PATH=$PATH:$GOROOT/bin export GOPATH=$HO ...