要接受的观点
1.操作系统管理内存分配(有人来了,要分房间给它住)、内存回收(人走了,房间要收回来)的方式就是给内存编号。这个编号是二进制的编号,而且与操作系统位数相关。
2. 所有变量运行时都要保存在内存中,程序有两种方式来访问变量:
- 直接访问方式:根据变量对变量赋值或读取变量。
- 间接访问方式:每个变量都需要保存在内存中,因此它所在的首内存就有一个内存编号,此时也可通过该内存编号来访问变量。
3.内存编号 = 内存地址 = 指针(pointer)
指针变量及其基本用法:
定义指针变量:
类型 * 变量名;
定义了一个变量,该变量用于装内存编号(地址、指针),而且该变量只能装指定类型的变量所在内存的内存地址。
两个运算符:
&变量:取地址运算符,获取给定变量所在内存的首地址。
*指针变量:取变量运算符。获取指定的内存编号(地址、指针)中所装的变量。
指针作为函数的参数
当使用指针作为函数参数,程序传入函数的不再是普通的变量、而是地址的副本。
使用指针作为函数参数时,有两点注意:
1. 程序传入函数的,依然是指针(地址)的副本。
2. 函数可以改变指针所指变量的值。但函数对传入参数(指针变量本身)依然不会有任何影响。
指针与数组
数组变量,本身就是指针。数组变量指向数组的首地址。
数组变量,里面装的是第一个元素所在内存的编号(首地址)。
数组变量与普通指针变量的区别为:普通指针变量可以被赋值(改变),但数组变量不允许被赋值。
指针变量的赋值方式:
int * p = 0x14344 ; ×
int * p = &int型变量; √
int * p = q; 其中q是另一个int*型的变量 √
int * p = arr; 其中arr是int数组 √
int * p = arr[i]; 其中arr是int数组 ×
int * p = &arr[i]; 其中arr是int数组 √
指针的运算:
指针的加法、减法并非以字节为单位,而是size(类型)为单位。
指针 + n: 将内存编号 + n*sizeof(类型)
通常而言,只有对指向数组元素的指针进行加N才有意义,相当得到该元素后面第N个元素的地址
指针 - n: 将内存编号 - n*sizeof(类型)
通常而言,只有对指向数组元素的指针进行减N才有意义,相当得到该元素钱面第N个元素的地址
指针 - 指针 => 编号差/sizeof(类型)
通常而言,只有两个指向数组元素的指针相减才有意义,得到结果就是两个元素之间相差几个元素。
指针与指针可以比较大小。通常而言,只有对指向同一个数组元素的两个指针比较大小,指针越大,位于数组的越后面。
数组本身作为函数的参数
当程序把数组本身作为函数的参数时,实际上就是把指针作为函数的参数。
1. 程序只是将数组变量的副本传入函数。
2. 函数不能改变数组变量本身(肯定不能改变),但是可以改变数组元素的值
记住:如果函数需要改变数组元素的值,那么应该向函数传入数组元素的地址,或者数组变量本身(第一个数组元素的地址)。
arr[i]在底层的访问方式,本身就是2步:①、先计算arr+i的地址;②、根据地址去取得对应元素。
如果你写成&arr[i]——这就会导致系统先计算地址,再根据地址得到数组元素,最后又根据元素获取地址。
指向多维数组的指针:
对于intarr[3][4]这样一个数组。
arr+n与 arr[n]、*(arr+n)都是指针,它们装的编号是相同的,都是指向整个数组的首地址。
*(arr+n)与*arr+n是否相同?不同,其实计算公式如下:
arr+n => arr编号 + n* 第二维长度 * sizeof(类型)
*arr+n => arr编号 + n*sizeof(类型)
arr[m] + n 、*(arr+m)+n 、&arr[m][n]是一样的。
*(arr[m] + n) 、*(*(arr+m)+n)、 arr[m][n]是一样的。
字符串与字符指针
C本身并没有字符串,C采用了字符数组来保存字符串。
程序可以直接用字符串对字符数组进行赋值,程序会自动在所有字符之后添加结束符(\0)。
【虽然程序可以用字符数组来装多个字符,从而代码字符串,但由于数组不能被重复赋值,因此用起来不方便】。
由于数组的本质,就是指针,因此实际上往往会使用字符指针来代表字符串。
使用字符指针声明字符串之后,有如下2个特征:
-- 由于指针是可以被多次赋值的,因此char*可以被多次存入不同的字符串。
每次传入字符串时,实际上是先将字符串转换为字符数组,再将数组的首地址赋值给字符指针变量。
-- 使用char*声明的字符串,是不可变的字符串,它的字符序列是不允许改变的。
函数和指针
C语言允许定义函数指针来指向函数, 函数指针可以在不同时间指向不同的函数。
函数指针
定义函数指针的语法:
函数返回值类型 (* 函数指针变量)();
【注意】:对函数指针类型的变量赋值时,要用函数本身进行赋值,而不是调用函数!!
int (* fnPt)();
fnPt = jiechen; //jiecheng为自定义的一个函数
(*fnPt)(5); //利用函数指针类型调用函数的方法
返回指针的函数
对于返回指针的函数,该指针指向的东西,不能是函数中的局部变量。函数返回的指针,通常有如下做法:
1. 如果指针指向的该函数中的局部变量,应该将局部变量用static修饰。
2. 让函数返回的指针指向全局变量
3. 让函数返回的指针指向main函数中的变量——这是因为main结束时,你的程序大致上也就结束了。