目录
1、为什么需要指针?
2、指针是什么?
3、指针与变量的关系
4、指针的分类
5、指针的用法
6、指针的运算
7、野指针
8、指针使用时的注意事项
同C语言中其他变量一样,把指针也可以看成是一种变量。不过,这种变量专门存储地址值。
1、为什么需要指针?
指针解决了一些编程中基本的问题。
第一,指针的使用使得不同区域的代码可以轻易的共享内存数据。当然你也可以通过数据的复制达到相同的效果,但是这样往往效率不太好,因为诸如结构体等大型数据,占用的字节数多,复制很消耗性能。但使用指针就可以很好的避免这个问题,因为任何类型的指针占用的字节数都是一样的(根据平台不同,有4字节或者8字节或者其他可能)。
第二,指针使得一些复杂的链接性的数据结构的构建成为可能,比如链表,链式二叉树等等。
第三,有些操作必须使用指针。如操作申请的堆内存。还有:C语言中的一切函数调用中,值传递都是“按值传递”的,如果我们要在函数中修改被传递过来的对象,就必须通过这个对象的指针来完成。
2、指针是什么?
指针是程序数据在内存中的地址,而指针变量是用来保存这些地址的变量。
由于内存中的每一个字节都有一个唯一的编号,因此,在程序中使用的变量,常量,甚至数函数等数据,当他们被载入到内存中后,都有自己唯一的一个编号,这个编号就是这个数据的地址。指针就是这样形成的。
char ch = 'a';
int num = 97;
我们可以大致画出变量ch和num在内存模型中的存储。(假设 char占1个字节,int占4字节)
3、指针与变量的关系
用来保存 指针 的变量,就是指针变量。如果指针变量p1保存了变量 num的地址,则就说:p1指向了变量num,也可以说p1指向了num所在的内存块 ,这种指向关系,在图中一般用 箭头表示。
int main(void)
{
int num = 97;
int *p1 = #
char* p2 = (char*)(&num);
printf("%d\n",*p1); //输出 97
putchar(*p2); //输出 a
return 0;
}
指针的值:很好理解,如上面的num 变量 ,其地址的值就是0028FF40 ,因此 p1的值就是0028FF40。数据的地址用于在内存中定位和标识这个数据,因为任何2个内存不重叠的不同数据的地址都是不同的。
指针的类型:指针的类型决定了这个指针指向的内存的字节数并如何解释这些字节信息。一般指针变量的类型要和它指向的数据的类型匹配。
由于num的地址是0028FF40,因此p1 和 p2的值都是0028FF40
*p1 : 将从地址0028FF40 开始解析,因为p1是int类型指针,int占4字节,因此向后连续取4个字节,并将这4个字节的二进制数据解析为一个整数 97。
*p2 : 将从地址0028FF40 开始解析,因为p2是char类型指针,char占1字节,因此向后连续取1个字节,并将这1个字节的二进制数据解析为一个字符,即’a’。
同样的地址,因为指针的类型不同,对它指向的内存的解释就不同,得到的就是不同的数据。
4、指针的分类
指针大致可以分为两类:
1、普通指针:指向普通变量的指针,如指向int类型变量的指针。
int *p;
2、函数指针:指向函数的指针,可以用来调用该函数。函数指针的定义方式如下:
返回类型 (*指针变量名)(参数列表)
例如,定义一个指向返回值为int类型,参数为两个int类型变量的函数的指针可以如下:
int (*p)(int, int);
5、指针的用法
指针可以用来访问和修改其他变量的值。例如,定义一个指向int类型变量的指针,可以通过以下方式访问和修改该变量的值:
int a = 10;
int *p = &a;
// 将指针p指向变量a的地址
*p = 20;
// 修改变量a的值为20
printf("%d\n", a);
// 输出20
指针还可以用来实现动态内存分配,即在程序运行过程中根据需要分配和释放内存空间。
在C语言中,我们可以使用指针来动态地分配和释放内存。动态内存分配可以在程序运行时根据需要分配内存,而不是在编译时分配固定大小的内存。
动态内存分配的函数有两个:malloc和calloc。它们都使用一个无符号整数(size_t)的参数来指定需要分配的字节数。如果分配成功,这些函数将返回一个指向新分配内存的指针。如果分配失败,它们将返回一个空指针(NULL)。
malloc函数在堆上分配指定数量的字节,但不会初始化内存。calloc函数在堆上分配指定数量的字节,并将内存初始化为0。
例如,以下代码分配了一个大小为10的整型数组,并将其初始化为0:
int *p = calloc(10, sizeof(int));
为了释放动态分配的内存,我们使用free函数。它需要一个指向需要释放的内存块的指针作为参数。注意,只有通过malloc或calloc函数分配的内存才能使用free函数释放。使用未分配的或已经释放的内存块的指针作为参数会导致未定义的行为。
例如,以下代码释放了先前分配的内存:
free(p);
需要注意的是,动态内存分配很容易出错,因此我们应该在使用完分配的内存后立即释放它,以避免内存泄漏或其他问题。建议使用动态内存分配时,要小心谨慎,确保没有内存泄漏和越界访问等问题。
6、指针的运算
指针是一个存储变量内存地址的变量。在C语言中,指针运算包括以下几种类型:
-
指针加法:指针可以加上一个整数值,这个整数值会被乘以指针指向类型的大小,然后加到指针的地址上。例如:
ptr + n
,其中ptr
是一个指向类型为int
的指针,n
是一个整数值。 -
指针减法:指针也可以减去一个整数值,这个整数值也会被乘以指针指向类型的大小,然后从指针的地址中减去。例如:
ptr - n
。 -
指针比较:指针可以进行比较操作,包括等于、不等于、大于、小于、大于等于、小于等于等。指针之间的比较操作的结果是通常是一个整数值。
-
指针赋值:指针还可以进行赋值操作,例如:
int *ptr1, *ptr2; ptr1 = ptr2;
。
需要注意的是,指针运算的结果可能会超出指针指向的内存区域,这会导致程序出现异常行为,如崩溃或数据损坏等。因此,在使用指针时,需要特别注意指针指向的内存区域是否被正确地分配和释放。
7、野指针
在 C 语言中,一个野指针是指一个指针变量,它没有被初始化或指向一个无效的内存地址。野指针可能会导致程序崩溃或产生不可预测的错误行为。
野指针的常见情况包括:
1、没有初始化指针变量
如果一个指针变量没有被初始化,它会指向一个未知的内存地址,这可能会导致程序崩溃或产生不可预测的结果。正确的做法是在使用指针之前,将其初始化为 NULL 或一个有效的内存地址。
2、指向已经被释放的内存
如果一个指针指向了已经被释放的内存,那么在使用这个指针时就会发生访问无效内存的错误。正确的做法是释放内存后,将指针变量设置为 NULL。
3、指向未分配的内存
如果一个指针指向了未分配的内存,那么在使用这个指针时就会发生访问无效内存的错误。正确的做法是在使用指针之前,先分配内存并将指针指向这块内存。
为避免野指针的问题,应该始终将指针初始化为 NULL 或一个有效的内存地址,并在释放内存后将指针设置为 NULL。此外,应该尽可能避免使用野指针,例如在使用指针之前,先检查指针是否为 NULL。
8、指针使用时的注意事项
在C语言中,指针是一种非常重要的数据类型,它可以用来存储内存地址,并且可以通过间接引用符号 "*" 来访问该内存地址中存储的值。在使用指针时需要特别注意以下几点:
-
初始化指针:在使用指针之前必须先初始化指针变量,否则会出现未知的结果。初始化指针可以将指针变量初始化为NULL或者是一个有效的内存地址。
-
指针的类型:指针变量必须与指向的数据类型匹配。例如,如果指针变量指向一个整型变量,则应该使用 int *p; 而不是 char *p; 。
-
指针的地址:指针变量本身也有一个地址,可以通过使用取地址符号 "&" 来获取指针变量的地址。例如, &p 将返回指针变量 p 的地址。
-
指针的间接引用:指针变量存储的是内存地址,可以通过间接引用符号 "*" 来访问该内存地址中存储的值。例如, *p 将返回指针变量 p 所指向的内存地址中存储的值。
-
指针的运算:指针变量可以进行算术运算,例如加法和减法。这些运算将根据指针所指向的数据类型来调整指针变量的值。
-
指针的释放:在使用完指针后,必须释放指针变量所指向的内存空间,否则会出现内存泄漏的问题。可以使用 free() 函数来释放指针变量所指向的内存空间。
总之,使用指针是C语言编程中非常重要的一部分,需要注意以上几点才能正确地使用指针。