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 ...
随机推荐
- Error: Cannot find module 'webpack' 问题解决办法
这句话的意思是:没有找到webpack模块. 就算之前你装了webpack,那肯定是非全局安装 所以要全局安装 npm install --save-dev webpack 问题解决
- DOMException: Failed to execute 'open' on 'XMLHttpRequest': Invalid URL 未能在“xmlhttpRequest”上执行“open”:无效的URL。
出现这个报错主要是baseurl:http://192.168.*.*/后面的(/)或是请求里面的url:/user/login中前面的(/)有一个漏掉了,导致合成的路径不完整,所以报错:无效的URL
- jmeter实现服务器端后台接口性能测试
实现目的 在进行服务器端后台接口性能测试时,需要连接到Linux服务器端,然后通过命令调用socket接口,这个过程就需要用到jmeter的SSH Command取样器实现了. 脚本实现 设置CSV ...
- Java经典面试笔试题及答案
1.什么是对象序列化,为什么要使用? 所谓对象序列化就是把一个对象以二进制流的方式保存到硬盘上.好处:方便远程调用. 2.值传递与引用传递的区别? 所谓值传递就是把一个对象的值传给一个新的变量,但是系 ...
- Vue - 实现双击显示编辑框;自动聚焦点击的显示框;点击编辑框外的地方,隐藏编辑框
实现这三个功能的踩坑记录. 1. 需求 在Vue中,有一个input, 双击时编辑文本,点击该input节点外的其他地方,则取消编辑. 那么这里有三个要实现的地方 第一是双击显示编辑框. 第二是自动聚 ...
- C++ explicit的作用
explicit作用: 在C++中,explicit关键字用来修饰类的构造函数,被修饰的构造函数的类,不能发生相应的隐式类型转换,只能以显示的方式进行类型转换. explicit使用注意事项: * e ...
- 创业学习---《调研黑客上:锁定调研目标》--D-2.调研模块---HHR计划---以太一堂
第一,开始学习: 思考题: (1)你的项目有哪些值得关注的竞争对手?为什么是这些,你是如何分类的? (2)拿出其中一个产品,你会怎么分析他? 第一,<明确调研目标>(补充) 1,调研4大类 ...
- UVa 400 Unix Is命令
简单题 #include <bits/stdc++.h> using namespace std; const int maxn=110; string s[maxn]; int main ...
- 企业级Docker镜像仓库Harbor部署与使用
yum install docker 官网地址:https://docs.docker.com/compose/install/ 运行此命令以下载Docker Compose的当前稳定版本 1 sud ...
- 【C语言】创建一个函数,并调用比较两个数的大小
#include <stdio.h> int max(int x,int y) { if(x>=y) return x; else return y; } main() { int ...