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 ...
随机推荐
- Python3.6打开EAIDK-610开发板(计算机通用)摄像头拍照并保存
环境:python3.6 代码: import cv2 import os output_dir ='/home/openailab/Desktop/huahui/came/' i = cap = c ...
- python进阶(十七)正则&json(上)
1. 一个列表中所有的数字都是重复2次,但是有一个数字只重复了一次. 请找出重复一次的数字,不可以使用内置函数. [2,2,1,1,0,4,3,4,3] 方法1:通过字典计数,找到value等于1的k ...
- web应用程序上传文件 超过了最大请求长度
具体问题如下图 具体问题描述:在web应用程序中,上传了200M的文件,出现了如上图的问题,上传较小文件的时候,没有任何的问题.但是,测试的能力,不容小觑,真真的会测试的很全面.测试到了这个问题,好吧 ...
- Python实现一个桌面版的翻译工具【新手必学】
Python 用了好长一段时间了,起初是基于对爬虫的兴趣而接触到的.随着不断的深入,慢慢的转了其它语言,毕竟工作机会真的太少了.很多技能长时间不去用,就会出现遗忘,也就有了整理一下,供初学者学习和 ...
- Windows上搭建hexo博客
1.windows上下载git(官网太慢),建议去其他地方下载啊(右键出现 Git Bash Here 的标志就安装完成) 2.安装npm:http://nodejs.cn/download/ 3.安 ...
- Spring Boot框架 - application.properties配置
阅读此文章之前,需要参考 https://www.cnblogs.com/mysummary/p/12238242.html 创建Spring Boot项目 建成后目录如下 一.在com.demo.s ...
- ubuntu安装与设置
为学习Linux,在虚拟机中安装类ubuntu18.04,刚装完系统时间是不对的,系统中也没有gcc,g++. 关于安装软件无非就是: sudo apt-get install gcc sudo ap ...
- vs2013编译soui并创建一个项目
1.soui.10.sln改为soui.13.sln 2.用nodepad++打开soui.13.sln,作如下修改 注:第一行我是改为13.00,编译后似乎自己变为12.00了 编译结果: 注:这是 ...
- 计算机网络 --- IP 地址
概述 这一节主要了解网络中有关ip的知识,包括子网掩码等等,还有 ipv6的生成规则等等. 基本的ip结构 分类寻址 主要分为 ABCDE ,由两部分组成,网络号 + 主机号 . 可以看到 A 和 C ...
- oracle 密码过期问题
密码过期问题: ALTER PROFILE DEFAULT LIMIT PASSWORD_LIFE_TIME UNLIMITED;