# **1. 准备知识** **1.1 内存地址** - 字节:字节是内存的容量单位,英文称为 Byte,一个字节有8比特位,即 1Byte = 8bits 1B = 8b - 地址:系统为了便于区分每一个字节而对它们逐一进行的编号,称为内存地址,简称地址。 ![](images/WEBRESOURCE73f0b54259e8460e97f84fd70e8aaef4stickPicture.png) ## **1.2 基地址** - 单字节数据:对于单字节数据而言,其地址就是其字节编号。 - 多字节数据:对于多字节数据而言,其地址是其所有字节地址中编号最小的那个,称为基地址。 ![](images/WEBRESOURCE06e4a4aac18e468d89b657886f10a5b3stickPicture.png) ### **1.3 取址符** - 每个变量都是一块内存,都可以通过取址符 & 获取其地址 - 注意:&符号的左边有操作数则表示位与运算符,若左边没有操作数则表示取地址符 ```c int main(int argc, char *argv[]) {     int a=110;     printf("整型变量a的地址是:%p\n", &a);     char b='a';     printf("字符型变量b的地址是:%p\n", &b);     float c=3.14;     printf("浮点型变量c的地址是:%p\n", &c);     printf("整型变量a的地址长度是:  %ld\n", sizeof(&a));     printf("字符型变量b的地址长度是:%ld\n", sizeof(&b));     printf("浮点型变量c的地址长度是:%ld\n", sizeof(&c));     return 0; } ``` - 总结 - 虽然不同类型的变量的内存尺寸不同,但是他们的地址尺寸(地址编号的位数)却是相同的(地址的尺寸与系统的字长相关,32位系统地址尺寸为32位,64位系统地址尺寸为64位) - 不同的地址虽然表示形式一样,但他们所代表的内存尺寸(在内存中所占内存大小)和类型都不相同,因此相同形式的地址编号在逻辑上要严格进行区分 ![](images/WEBRESOURCEc93ef0073e59a9197d1df93e15a42da9image.png) # 2.指针入门 ## 2.1指针概念 由于翻译问题及口语表达的习惯,在日常表述中,指针会有以下两种含义 - 指地址 ```c int a; &a; // 我们可以说&a指向a的地址 ``` - 指指针变量 ```c int *p; // 可以用于指向int类型数据的地址 ``` - *号左右两边都有操作数,表示两数相乘(a*b) - *号左右两边都有操作数,且左操作数数一个数据类型表示定义一个指针变量(int *p) - *号左边没有操作数,表示取值操作(*p) ## 2.2指针的定义 - 用于存放数据类型变量的地址的变量称为指针 ```c int *p1; // 用于存储int类型数据的地址,p1被称为int型指针或整型指针 char *p2; // 用于存储char类型数据的地址,p2被称为char型指针或字符型指针 double *p3; // 用于存储double类型数据的地址,p3被称为double型指针或浮点型指针 ``` 对于int *p;在32位系统下的理解如下: ![](images/WEBRESOURCE2f532ae1a2635b8f6d6eb26f2b49be3eimage.png) ![](images/WEBRESOURCE970015c2b46c95da2d92fad50ded8cadimage.png) - 注意:指针用于存放地址的变量,由于在相同的系统位数下地址的尺寸相同,故指针的尺寸相同 ![](images/WEBRESOURCEe6d0d1a5088db204eab4343b8bff4ef4image.png) - 指针变量的内存尺寸只于系统字长相关,与指针的类型无关。 ## 2.3指针的赋值 - 可将一个地址类型于指针类型相同的地址赋值这个指针 ```c int a=100; char b='A'; double c=3.14; p1 = &a; // 将a的地址赋值给指针p1,它们类型必须相同 p2 = &b; // 将b的地址赋值给指针p2,它们类型必须相同 p3 = &c; // 将c的地址赋值给指针p4,它们类型必须相同 int *p4 = p1; // 使用整型指针变量p1值初始化赋值整型指针p4 int *p5; p5 = p1; // 整型指针变量p1值赋值给整型指针p5 int *p6 = &a; // 使用a的地址初始化赋值整型指针变量p6 ``` ## 2.4指针的索引 - 所谓索引,指的是通过指针变量取得其指向的目标(访问指针变量中所存储的地址中的数据) ```c *p1 = 200; *p2 = 'B'; *p3 = 6.62; a = 10; a = 10; ``` ![](images/WEBRESOURCEe2db6c7a73c7bc9d780f9639545ccf3eimage.png) ## 练习 编写一个函数实现两个数交换。 # 3.特殊指针 ## 3.1野指针 - 指向一块未知区域的指针,称为野指针。**野指针是危险的** ![](images/WEBRESOURCE6a63aa7276f21b9af08cd146f5959cbbimage.png) - 危害 - 引用野指针,相当于访问了非法内存,常常会导致段错误(segmentatio fault) - 引用野指针,可能会破坏系统关键数据,导致系统崩溃等严重后果。 - 产生原因 - 指针定义式未进行初始化 - 指针指向内存被系统回收 - 指针越界 - 如何防止 - 定义指针时进行及时初始化(定义时无法明确指向则赋值为NULL) - 绝不引用已被回收的内存(回收内存后令指针指向NULL) - 确认所申请的内存边界,谨防越界 ## 3.2空指针 ```c NULL // (void *)0; ``` - 很多情况下,我们不可避免的会遇到野指针,如刚刚定义指针无法立即为其分配一块内存时,又或者指针所指向的内存被释放了等等。 - 对于一个暂时无法让其指向一块合法内存的指针而言, 我们最好将其初始化为“空指针” , 即给他赋一个空值(零) , 让他指向零地址 ![](images/WEBRESOURCE698f0b5e2b11e6c3a965743dd01df4caimage.png) ```c // 刚刚定义指针无法明确其指向,让其指向零地址以保证安全 int *p = NULL; char *q = NULL; //指针指向的内存被释放,令指针立即指向零地址保证安全 char *k = malloc(2); // 给指针分配内存 free(k); // 释放内存 k = NULL; ``` ## 3.3void指针 - 概念:所谓void指针也称为万能指针(泛型指针),无法明确指针所指向的目标的数据类型则可将这种指针定义为void类型 - 要点: - void型指针无法索引目标,必须将其转换为一个具体的类型指针方可索引目标。 - void型指针不支持加减运算。 > void只用于三种情况 > 定义指针 > 定义函数类型 > 表示函数的参数列表 ```c void swap(void *a, void *b) {     // int *p = a;     // int *q = b;     // *p^=*q;     // *q^=*p;     // *p^=*q;     *((int *)a) ^= *((int *)b);     *((int *)b) ^= *((int *)a);     *((int *)a) ^= *((int *)b); } ``` ## 3.4char型指针 - char型指针实质上与别的类型指针并无本质区别,但由于C语言中字符串以字符数组的方式进行存储,而数组在大多数场合又表现为指针,因此字符串在绝大多数场合下表现为char型指针 ```c char *p = "abcd"; // p指向的目标是字符串常量的地址 *p = 'x'; // 不可以修改常量数据 /* 上面的写法语法上无错,但逻辑不允许,推荐下面的写法,编译器会帮助我们检查语法错误 */ const char *q = "abcd"; // q指向的目标是字符串常量的地址 *q = 'x'; ``` ## 3.5const型指针 - const(只读)型指针有两种形式:①常指针(指针只读)②常目标指针(目标只读) - 常指针:const修饰指针本身,表示指针变量本身无法修改(指针常量) ```c char * const p; ``` ![](images/WEBRESOURCE55871207d1eef11e0fd010e335e4cc0f576913ed193ece92e9ad7fc8a81dc9cf.png) ```c char a,b; char  * const p = &a; // p = &b; // 错误,常指针不能修改其值 *p = 'S'; // 可通过指针对目标进行读写 printf("%c\n", *p); ``` - 常目标指针:const修饰指针的目标,表示无法通过该指针修改其目标(常量指针)。 ![](images/WEBRESOURCE51fc80bb52e549d39a1753ac77f656e5stickPicture.png) ```c const char  *q = &a; char const  *k = &a; // k和q等价 q = &b; // 指针指向的目标可以修改 k = &b; *q = 'a'; // 错误,常目标指针无法写访问目标 printf("%c\n", *k); // 正确。常目标指针读可访问目标 ``` - 常指针在实际应用中不常见。 - 常目标指针在实际应用中广泛可见,用来限制指针的读写权限 ## 晚上作业 [指针与数组作业.doc](attachments/WEBRESOURCEd71090e9d6659f19f9b36c93099793f4指针与数组作业.doc) ## 3.6函数指针 - 指向函数的指针称为函数指针 - 函数指针与普通指针本质上并无区别,只是在取址和索引时取址符&与索引符*均可省略 ```c  double fun(double a[], int len) {    double max = a[0];    for(int i=0; ia[i]?max:a[i];    }    double min = a[0];    for(int i=0; i