抱歉,您的浏览器无法访问本站

本页面需要浏览器支持(启用)JavaScript


了解详情 >

计算机内存区域中的每字节都对应一个编号,这个编号就是“地址”。如果在程序中定义了一个变量,那么在对程序进行编译时,系统就会给这个变量分配内存单元。如果我们在程序中需要将某个整型、浮点型或字符型数据变量的地址保存下来,需要用到 C 语言为我们提供的指针。在 C 语言中,指针变量是一种特殊的变量,它用来存放变量地址。掌握指针,对于学习操作系统、理解操作系统原理会有非常大的帮助。以下为我在学习和实战练习过程中所做的笔记,可供参考。

一、取地址操作符和取值操作符

取地址操作符为 &,也称引用,通过该操作符我们可以获取一个变量的地址值;取值操作符为 *,也称解引用,通过该操作符我们可以得到一个地址对应的数据。取地址操作符和取值操作符优先级相同,按照自右向左的顺序相结合。

1
2
3
4
5
6
7
8
int main()
{
int i = 10;
int *p; //p就是一个指针变量,可以用来存储地址,类型是整型指针
p = &i; //初始化赋值
printf("i = %d\n", i); //直接访问
printf("*p = %d\n", *p); //间接访问
}

指针变量前面的表示该变量为指针型变量,例如 float **pointer_1; 的指针变量名是 pointer_1。在定义指针变量时必须指定其类型,只有整型变量的地址才能放到指向整型变量的指针变量中。

二、指针的传递和偏移

C 语言中的函数调用均为值传递。指针的传递将变量 i 的地址传递给 change 函数时,实际效果是 j=&i,这时 j 是一个指针变量,依然是值传递, 只是 j 内部存储的是变量 i 的地址,所以通过 *j 就间接访问到了与变量 i 相同的区域,通过 *j=5 就实现了对变量 i 的值的改变。

指针提供随意访问内存的能力:

1
2
3
4
5
6
7
8
9
10
11
void change(int* j)
{
*j = 5; //间接访问得到变量 i
}
int main() //指针的传递
{
int i = 10;
printf("before change i=%d\n", i);
change(&i); //传递变量 i 的地址
printf("after change i=%d\n", i);
}

我们把对指针的加减称为指针的偏移,加就是向后偏移,减就是向前偏移,但对指针进行乘除是没有意义的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//指针的偏移
int main()
{
int a[5]={1,2,3,4,5};
int *p;
int i;
p = a; //保证等号两边的数值类型一致
for(i=0; i<5; i++) //正序输出
{
printf("%3d", *(p+i));
}
p = &a[4]; //让 p 指向最后一个元素
for(i=0; i<5; i++) //逆序输出
{
printf("%3d",*(p-i));
}
}

三、指针与一维数组

数组在传递时弱化为指针。这是由于一维数组名中存储的是数组的首地址,因此一维数组在函数调用进行传递时,它的长度子函数无法知道。定义一个指针变量时,指针变量的类型要和数组的数据类型保持一致,通过取值操作, 就可获取数组元素, 这种方法称为指针法。也可以通过取下标的方式来获取数组元素并进行修改,这种方法称为下标法。

1
2
3
4
5
6
7
8
9
10
11
12
void change(char *d)  //*d 为形参
{
*d = 'H'; //指针法
d[1] = 'E'; //下标法
*(d+2) = 'L'; //指针的偏移
}
int main()
{
char c[10] = 'hello';
change(c); //c 为实参
printf("%c\n", c);
}

四、指针与动态内存申请

C 语言的数组长度固定是因为其定义的整型、浮点型、字符型变量、数组变量都在栈空间中,而栈空间的大小在编译时是确定的。如果使用的空间大小不确定,那么就要使用堆空间申请动态内存申请。栈空间由系统自动管理,而堆空间的申请和释放需要自行管理,所以在具体例子中需要通过 free 函数释放堆空间。

栈是计算机系统提供的数据结构,计算机会在底层对栈提供支持:分配专门的寄存器存放栈的地址,压栈操作、出栈操作都有专门的指令执行,这就决定了栈的效率比较高;堆则是 C/C++ 函数库提供的数据结构,它的机制很复杂,例如为了分配一块内存,库函数会按照一定的算法在堆内存中搜索可用的足够大小的空间,如果没有足够大小的空间(可能由于内存碎片太多)那么就有可能调用系统功能去增加程序数据段的内存空间,这样就有机会分到足够大小的内存,然后返回。显然,堆的效率要比栈低得多。

1
2
3
4
5
6
7
8
9
10
11
int main()
{
int i;
char *p;
scanf("%d", &i); //输入要申请的空间大小
p = (char*)malloc(i);//使用 malloc 动态申请堆空间,(char *)是强制类型转换
strcpy(p, "malloc success");
puts(p);
free(p);//free 时必须使用 malloc 申请时返回的指针值,不能进行任何偏移
p = NULL; //消除野指针
}

C 初始动态分配 vs C++ 初始动态分配

1
2
3
4
5
// C 初始动态分配
L.data = (ElemType *)malloc(sizeof(Elemtype) *InitSize);
int *p = (int *)malloc(sizeof(int) *10);
// C++ 初始动态分配
L.data = new Elemtype[InitSize];

评论



Copyright © 2020 - 2022 Zhihao Zhuang. All rights reserved

本站访客数: 人,
总访问量: