【知识点1】指针的概述
1 指针的理解
1.1 虚拟内存的认识
程序中的数据,都会在内存中存储。在Linux 系统中程序运行构造一个虚拟内存空间;
1) 在32位系统中虚拟内存空间的大小为4G
在32位系统中,寄存器所占位数为32位,最大所能表示的数据为4G,也就说最大能够存储的地址编号为4G内存编号。
2) 在64位系统中,虚拟内存空间大小大于4G。
1.2 指针的认识
对于数据存储在虚拟内存中,而虚拟内存以字节为单位实现数据的存储,每一个字节都有对应序号,整虚拟内存空间的的地址编号是连续编号。在虚拟内存空间的数据可以通过地址编号实现数据的访问,也就是通过指针访问。
总结:
对于一个指针,需要关注两个点:
1) 指针的值:指针所指针空间的起始地址;
2) 指针的类型:指针解引用空间的大小和数据形式。
1.3 指针的定义
存储类型 数据类型 *指针变量名;
数据类型:表示指针指向空间(目标)数据类型;
存储类型:表示指针变量本身存储空间的存储属性;
*:类型修饰符,表示所定义的变量为指针变量;
在定义指针变量的同时设置初始值,需要保证初始值的数据类型和指针本身的数据类型保持一致。
#include <stdio.h>
int main() { int a = 34; /* 定义一个int类型变量 */
int *p = &a; /* 定义一个int类型指针,并初始化为变量a的地址 */
*p = a; /* 对指针p解引用访问修改为变量a的值 */
printf("a = %d, *p = %d\n", a, *p); }
1.4 指针的运算
对于指针的运算,只相同类型且连续空间的指针之间的运算才有意义。
- 指针的算术运算:
- 指针 + 常量 向高地址方向偏移常量个元素,偏移字节数 = 常量*元素空间的大小;
- 指针 - 常量 向低地址方向偏移常量个元素,偏移字节数 = 常量*元素空间的大小;
- 指针 - 指针 相差元素的个数;
- 指针的++和--
- 指针关系运算
指针的关系运算,用来判断两个指针之间的位置关系。
- 指针的逻辑运算
- 数据的大小存储
- 对于多字节数据,由于数据是以字节为单位存储,需要连续的多字节存储空间存储,并且连续内存空间有高低地址之分,而数据以字节有高低位之分,数据在内存中有两种存储方式:
- 小端存储:数据的低位存储在内存的低地址端,数据的高位存储在内存的高地址端;
- 大端存储:数据的低位存储在内存的高地址端,数据的高位存储在内存的低地址端。
#include <stdio.h> int main() {
int i;
int a = 1; //数据由高到低位数据分别为:0x00、0x00、0x00、0x01
char *p = &a; //使用char类型指针,可以取存储空间中低地址空间数据
/* 利用指针判断数据的大小端存储 */
if (*p != 0) {
/* 所取的数据为0x01(低地址数据为数据的低位)说明为小端存储 */
printf("lit\n");
} else {
printf("big\n");
}
a = 0x12345678;
for (i = 0; i < sizeof(int); i++) {
printf("%x ", *(p++));
//*p;
p++;
}
printf("\n");
}
【知识点2】 数组和指针
1 一维数组和指针
#include <stdio.h> int main() {
int i;
int arr[5] = {1,2,3,4,5}; /* 定义了一个一维数组:开辟连续内存空间存储多个相同类型的数据元素 */
int *q = arr; /* 定义指针存储首元素的地址 */
int (*p)[5] = &arr; /* 定义数组指针存储整个数组存储空间的地址 */
printf("%p-%p\n", p, p+1); /* 地址值的差值为整个数组空间的大小 */
printf("%p-%p\n", q, q+1); /* 地址值的差值为数组元素空间的大小 */ for (i = 0; i < 5; i++) { printf("%d ", arr[i]); /* 数组名[下标],其实质就是通过基地址+偏移量解引用访问 */
}
printf("\n");
for (i = 0; i < 5; i++) {
printf("%d ", *(arr+i));
}
printf("\n");
for (i = 0; i < 5; i++)
{
printf("%d ", q[i]);
} printf("\n");
for (i = 0; i < 5; i++) {
printf{
"%d ", *(q+i));
} printf("\n");
for (i = 0; i < 5; i++) { printf("%d ", *q++); }
printf("\n");
}
2 二维数组和指针
2.1 行指针
在二维数组中,其数组名表示的是首行元素的地址
2.2 列指针
数组元素在内存中是线性顺序存储,所有的元素可以看成一列元素。此时可以使用首元素的地址实现整个数组元素的访问。
访问:
1) 可以通过列指针的自加减实现元素的访问;
2) 可以指针+常量方式实现元素的访问;
#include <stdio.h> int main() { int arr[3][4] = {1,2,3,4,5,6,7,8,9,10,11,12}; int (*p)[4] = arr; int i; int j; printf("%p - %p\n", &arr, &arr+1); //48; printf("%p - %p\n", arr, arr+1); //16; printf("%d\n", arr[2][3]); printf("%d\n", *(*(arr+2)+3)); printf("%d\n", *(arr[2]+3)); printf("%d\n", (*(arr+2))[3]); printf("%d\n", p[2][3]); printf("%d\n", *(*(p+2)+3)); printf("%d\n", *(p[2]+3)); printf("%d\n", (*(p+2))[3]); for (i = 0; i < 3; i++) { for (j = 0; j < 4; j++) { printf("%d ", *((*p)+j)); } p++; printf("\n"); } int *q = &arr[0][0]; for (i = 0; i < 3; i++) { for (j = 0; j < 4; j++) { printf("%d ", *(q+i*4+j)); } printf("\n"); } for (i = 0; i < 3; i++) { for (j = 0; j < 4; j++) { printf("%d ", *q++); } printf("\n"); } }
3 指针数组
所谓的指针数组,其实质是数组,数组中的每一个元素是相同类型的指针。具有多个相同类型的指针所构成的集合。
语法:
存储类型 数据类型 *数组名[常量表达式];
存储类型:表示整个数组的存储属性。
数据类型:表示数组元素所指向的数据类型;
*:特殊类型说明符,表示所定义的数据为指针数据。
数组名:数组集合名称;
常量表达式:数组元素的个数
注意:
由于数组元素为指针,只能表示连续存储空间中起始元素的地址;
1) 所指向的连续存储空间中元素个数相同的时候,可以采用相同的逻辑实现数据元素的访问;
2) 所指向的连续存储空间中元素个数不同的时候,需要避免连续存储空间的访问越界和少访问的问题。
一般用于存储多个字符串数据,可以通过字符串结束表示'\0'判断;还可以在传递连续存储空间起始地址的时候传递元素的个数。
【知识点3】多级指针
所谓的多级指针,指的是指针的指针。
int main() { int a = 3; /* 定义一个普通数据变量 */ int *p = &a; /* 定义一级指针存储普通数据变量存储空间的地址 */ int **q = &p; /* 定义二级指针存储一级指针变量存储空间的地址 */ printf("a = %d, *(&a) = %d\n", a, *(&a)); printf("%d:a = %d, *p = %d\n", __LINE__, a, *p); *p = 4; /* 一级指针的解引用访问 */ printf("%d:a = %d, *p = %d\n", __LINE__, a, *p); printf("%d:a = %d, *p = %d, **q = %d\n", __LINE__, a, *p, **q); **q = 5; /* 二级指针的解引用访问 */ printf("%d:a = %d, *p = %d, **q = %d\n", __LINE__, a, *p, **q); }
【知识点4】 特殊指针
1 void 类型指针
void 是特殊的数据类型关键字,不能修饰数据类型变量;可以用于定义指针变量。
void * 指针变量名; /* 用于修饰指针变量 */
此时的指针变量,可以实现任意数据类型指针的存储。从而实现不同数据类型的数据的运算。
对于void 类型指针不能直接解引用访问,需要强制转换为实际数据类型的指针才能解引用访问。
#include <stdio.h> int main() { int a = 3; float f = 3.14; void *p; /* 定义void 类型指针 */ p = &a; /* 使用void类型指针存储int类型指针变量 */ printf("%d\n", *(int *)p); /* 对于void类型指针的解引用不能直接解引用访问,需要转换为实际数据类型指针解引用访问 */ p = &f; /* 使用void类型指针存储float类型指针变量 */ printf("%f\n", *(float *)p); }
2 NULL指针
NULL 是标识符常量,表示指针的零值。 具体值表示:((void *)0)。常用表示指针没有具体的指向。
3 野指针
所谓的野指针,指的是指针所指向的内存空间未做开辟,在对指针解引用访问会出现未知异常(程序正常运行且数据正常、程序正常运算数据异常、段错误异常)。
野指针的产生:
1) 只做定义,未作初始化的指针为野指针;
2) 强制修改指针变量的值,可能产生野指针;
3) 对于指针变量所指向的动态开辟的内存在做内存动态释放后,此时的指针也称为野指针。
避免野指针的产生:
1) 在定义的时候对指针指向初始化设置,没有指向设置为NULL;
2) 在指针修改的时候,可以保证新的指针指向有开辟,如果没有开辟设置为NULL
3) 在动态释放,将其值设置为NULL。
在对指针访问的时候,需要先做判断在做访问。
【知识点5】 const指针
1 const修饰变量
此时的变量为只读变量,在C语言中,不能直接修改,可以通过指针间接修改。
#include <stdio.h> int main() { const int a = 3; int *p = &a; printf("%d: a = %d\n", __LINE__, a); // a = 4; /* 语法问题:const修饰的只读变量,不能直接修改 */ *p = 5; /* const修饰的只读变量,可以通过指针实现间接修改 */ printf("%d: a = %d\n", __LINE__, a); }
2 const修饰指针
2.1 修饰指针的目标
也就是修饰指针所指向目标内存空间的内容,
int main() { const int a = 3; const int b = 4; const int *p = &a; /*const修饰指针的目标, 指针指向空间的内容不能被修改(在定义的时候需要初始化) 指针的指向可以修改(在定义的时候可以不做初始化) */ printf("%d: a = %d, *p = %d\n", __LINE__, a, *p); // *p = 5; /* 语法问题:指针的目标不可以修改 */ p = &b; /* 指针的指向可以修改 */ printf("%d: a = %d, *p = %d\n", __LINE__, a, *p); }
2.2 修饰指针的指向
也就是修饰指针的值
int main() { int a = 3; int b = 4; int * const p = &a; /* const 修饰的指针的指向,也就是指针的值; 1) 指针指向空间的内容可以修改, 2) 指针的值(指针指向的空间)不能修改,在定义指针的时候需要初始值设置。 */ printf("%d: a = %d, *p = %d\n", __LINE__, a, *p); *p = 5; // p = &b; printf("%d: a = %d, *p = %d\n", __LINE__, a, *p); }
2.3 修饰指针的目标和指向
int main() { int a = 3; int b = 4; const int * const p = &a; /* 1) 第1个const修饰的指针的目标,指针指向空间的内容不能被修改,需要定义同时初始值设置 2) 第2个const修饰的指针的指向,指针的值不能被修改,需要定义同时初始值设置。 */ printf("%d: a = %d, *p = %d\n", __LINE__, a, *p); // *p = 5; /* 语法问题 */ // p = &b; /* 语法问题 */ printf("%d: a = %d, *p = %d\n", __LINE__, a, *p); }
【知识点6】零值比较
bool数据类型变量、char数据变量、整型数据变量、float数据类型变量和指针类型变量和零值的比较
/* bool数据类型和零值比较 */ bool mybool; if (mybool == false) { printf("mybool is 0\n"); } else { printf(" mybool != 0"); } if (!mybool) { printf("mybool is 0\n"); } else { printf(" mybool != 0"); } int a; if (a == 0) { printf("a == 0\n"); } if (!a) {printf("a == 0\n") } float f; if ( f > -0.000001 && f < 0.000001) printf("f is 0\n"); char *p; if (p == NULL) printf("p is NULL\n"); if (!p) printf("p is NULL\n");
图解: