指针:
内存单元的编号,就是地址。
指针就是地址,地址就是指针。
地址就是内存单元的编号。
指针变量就是存放地址的变量。
指针和指针变量是两个不同的概念
但是要注意:通常我们叙述时,会把指针变量简称为指针,实际它们含义并不一样
指针的重要性:
表示一些复杂的数据结构
快速地传递数据,减少了内存的耗用
使函数返回一个以上的值
能直接访问硬件
能够方便地处理字符串
是理解面向对象语言中引用的基础
总结:指针是C语言的灵魂。
指针的定义:
地址:内存单元的编号
从零开始的非负整数
范围:4G [0, 4G-1]
指针
指针就是地址,地址就是指针
指针变量就是存放内存单元编号的变量,或者说指针变量就是存放地址的变量。
指针的本质就是一个操作受限的非负整数
指针的分类:
1. 基本类型指针【重点】
int *p; //p是变量的名字, int * 表示p变量存放的是int类型变量的地址
// int * p; 不表示定义了一个名字叫做*p的变量
//int * p; 应该这样理解:p是变量名,p变量的数据类型是int * 类型,
// 所以int * 类型实际就是存放int变量地址的类型
int i = 3;
int j;
p = &i;
/*
1. p保存了i的地址,因此p指向i
2. p不是i,i也不是p,更准确地说:修改p的值不影响i的值,修改i的值也不改变p的值
3. 如果一个指针变量指向了某个普通变量,则
*指针变量就完全等同于普通变量。
例子:
如果p是个指针变量,并且p存在了普通变量i的地址,
则p指向了普通变量i
*p就完全等同于i
或者说:在所有出现*p的地方都可以替换成i
在所有出现i的地方都可以替换成*p
*p 就是以p的内容为地址的变量
*/
//p = i; //error,因为类型不一致,p只能存放int类型变量的地址,不能存放int类型变量
j = *p;
printf("i = %d, j = %d\n", i, j);
附注:
*号的含义:
1) 乘法
2)定义指针变量
举例:
int *p; //p是变量的名字, int * 表示p变量存放的是int类型变量的地址
3)指针运算符
该运算符放在已经定义好的指针变量前面
如果p是一个已经定义好的指针变量,则*p表示以p的内容为地址的变量。
如何通过被调函数修改主调函数普通变量的值
1. 实参必须为该普通变量的地址
2. 形参必须为指针变量
3. 在被调函数中,通过
*形参名 = ....
的方式就可以修改主调函数相关变量的值
2. 指针和数组
指针和一维数组
一维数组名是个指针常量
它存放的是一维数组第一个元素的地址
printf("%#x\n", &a[0]);
//以 十六进制输出
下标和指针的关系
如果p是个指针变量,则p[i]永远等价于*(p+i)
确定一个一维数组需要哪些信息?
如果一个函数要处理一个一维数组,则需要接受该数组的哪些信息
需要两个参数:
数组第一个元素的地址
数组的长度
指针变量运算
指针变量不能相加,不能相乘,不能相除
如果两个指针变量指向的是同一块连续空间中的不同的存储单元,则这两个指针变量才可以相减。
一个指针变量到底占几个字节?一个指针变量无论它指向的变量占几个字节,该指针变量本身都是占4个字节
一个变量的地址都是用该变量的首地址来表示。
指针和二维数组
3. 指针和函数
4. 指针和结构体
5. 多级指针
专题:
动态内存分配
传统数组的缺点:
1. 数组长度必须事先指定,且只能是常整数,不能是变量
例子:
int a[5]; //正确
int len = 5; int a[len]; //error
2. 传统形式定义的数组,该数组的内存,程序员无法手动释放
数组一旦定义,系统为该数组分配的存储空间就会一直存在,
直到该函数运行完毕时,数组的空间才会被系统释放
3. 数组的长度一旦定义,其长度就不能再更改
数组的长度不能在函数运行的过程中动态地扩充或者缩小。
4. A函数定义的数组,在A 函数运行期间可以被其它函数使用,但A函数运行完毕后,A函数中
的数组将无法再被其它函数使用。
传统方式定义的数组不能跨函数使用。
为什么额需要动态内存分配?
动态数组很好地解决了传统数组的这4个缺陷。
传统数组也叫静态数组。
动态内存分配举例——动态数组的构造
静态内存和动态内存的比较
静态内存由系统自动分配,由系统自动释放,静态内存是在栈中分配
动态内存是由程序员手动分配,手动释放。
动态内存是在堆分配的。
跨函数使用内存的问题