1、C语言指针
指针是一个变量,其值为另一个变量的地址,即,内存位置的直接地址。就像其他变量或常量一样,必须在使用指针存储其他变量地址之前,对其进行声明。
&
是一元运算符,返回操作数的内存地址。例如,如果 var 是一个整型变量,则 &var 是它的地址。该运算符与其他一元运算符具有相同的优先级,在运算时它是从右向左顺序进行的。
*
是一元运算符,返回操作数所指定地址的变量的值。指针声明示例如下:
int *ip; /* 一个整型的指针 */
double *dp; /* 一个 double 型的指针 */
float *fp; /* 一个浮点型的指针 */
char *ch; /* 一个字符型的指针 */
2、指针的使用
变量定义的时候给变量初始化,没有给指针初始化,就会出现野指针,该指针的指向并不是我们所希望的,一旦错误的释放了这个指针,就会发生内存的访问。指针使用之后,如果不释放指针所使用的内存,就会造成内存的泄露,这样就会有大量内存由于没能释放,别的程序不可以使用这部分内存,如果一个程序不停申请内存而不去释放内存,很快就会造成系统的崩溃。在使用指针时一定要判断指针是否为空,如果为空,则做相应的操作。如果不做判断,则可能错误的使用空指针。
例如,
#include <stdio.h>
int main ()
{
int var = 20; /* 实际变量的声明 */
int *ip; /* 指针变量的声明 */
ip = &var; /* 在指针变量中存储 var 的地址 */
printf("var 变量的地址: %p\n", &var );
/* 在指针变量中存储的地址 */
printf("ip 变量存储的地址: %p\n", ip );
/* 使用指针访问值 */
printf("*ip 变量的值: %d\n", *ip );
/*使用空指针初始化*/
int *pIntegerVal=NULL;
/*用变量初始化指针*/
int length=5;
int *pIntegerTemp=&length;
/*分配内存初始化指针*/
int *pInteger=(int*)malloc(10*sizeof(int));
//为指针分配大小为10个整数的内存空间。
/*内存申请和释放*/
if(pInteger != NULL)
{
free(pInteger);
pInteger=NULL;//指针释放之后并不为空,要设置其为空
}
pInteger=(int*)malloc(10*sizeof(int));
if(pInteger == NULL)
{
printf("内存申请没有成功\n!");
}
return 0;
}
3、C语言中的 NULL 指针
变量声明的时候,如果没有确切的地址可以赋值,为指针变量赋一个 NULL
值。赋为 NULL
值的指针被称为空指针。
NULL
指针是一个定义在标准库中的值为零的常量。这样能很好的避句野指针。
例如,
#include <stdio.h>
int main ()
{
int *ptr = NULL;
printf("ptr 的地址是 %p\n", ptr );
return 0;
}
注意:
大多数的操作系统上,程序不允许访问地址为 0
的内存,因为该内存是操作系统保留的。然而,内存地址 0
有特别重要的意义,它表明该指针不指向一个可访问的内存位置。
4、指针和数组
数组的名称本质上是指向数组第一个元素的指针。指针可以通过数组索引或直接通过指针算术来访问数组元素。
#include <stdio.h>
int main() {
int arr[5] = {1, 2, 3, 4, 5};
int *ptr = arr; // 数组名 arr 是指向第一个元素的指针
printf("使用数组访问: %d\n", arr[0]); // 通过数组名访问第一个元素
printf("使用指针访问: %d\n", *ptr); // 通过指针访问第一个元素
printf("使用指针算术访问: %d\n", *(ptr + 1)); // 通过指针算术访问第二个元素
return 0;
}
指针和数组的区别:
1)数组名 是指向第一个元素的常量指针,不能修改。
2)指针变量 可以被重新赋值以指向不同的位置。
5、指针与字符串
C语言中,字符串通常表示为字符数组,也可以使用字符指针来操作字符串。
#include <stdio.h>
int main() {
char str[] = "Hello";
char *ptr = str; // 字符数组名是指向第一个字符的指针
printf("字符串内容: %s\n", ptr); // 打印整个字符串
printf("第一个字符: %c\n", *ptr); // 打印第一个字符
printf("第二个字符: %c\n", *(ptr + 1)); // 打印第二个字符
return 0;
}
6、指针和函数
指针可以作为函数的参数和返回值,用来在函数间传递数组、修改调用函数中的变量值,或者动态分配内存。
#include <stdio.h>
void modify(int *p) {
*p = 20; // 修改指针 p 指向的变量
}
int main() {
int a = 10;
printf("修改前: %d\n", a);
modify(&a); // 将 a 的地址传递给函数
printf("修改后: %d\n", a);
return 0;
}
7、指针与动态内存分配
C语言中的动态内存分配通常使用 malloc()
、calloc()
和 free()
等函数来管理内存。指针在动态内存分配中起到了关键作用。
#include <stdio.h>
#include <stdlib.h>
int main() {
int *ptr;
// 动态分配内存,大小为 5 个 int
ptr = (int *)malloc(5 * sizeof(int));
if (ptr == NULL) {
printf("内存分配失败!\n");
return 1;
}
// 使用指针访问分配的内存
for (int i = 0; i < 5; i++) {
ptr[i] = i + 1;
}
// 打印数组内容
for (int i = 0; i < 5; i++) {
printf("%d ", ptr[i]);
}
printf("\n");
// 释放分配的内存
free(ptr);
return 0;
}
8、函数指针
函数指针是指向函数的指针,允许通过指针调用函数。它常用于回调函数和实现多态。
#include <stdio.h>
void say_hello() {
printf("Hello, world!\n");
}
int main() {
// 定义函数指针
void (*func_ptr)() = say_hello;
// 通过指针调用函数
func_ptr();
return 0;
}
9、指针的陷阱和注意事项
指针 是C语言中强大且灵活的工具,用于操作内存地址、传递函数参数、动态内存分配等。但同时也需要小心处理指针以避免内存泄漏和崩溃问题。
1)空指针
使用空指针(未初始化或 NULL
指针)可能导致程序崩溃。在使用指针之前,确保指针已被正确初始化。
#include <stdio.h>
int main() {
int *p = NULL; // 空指针
// 尝试访问空指针可能会导致崩溃
if (p != NULL) {
printf("Pointer value: %d\n", *p);
} else {
printf("Pointer is NULL, cannot dereference it.\n");
}
return 0;
}
2)野指针
指向无效内存的指针被称为野指针。它可能会导致不可预知的行为或崩溃。
#include <stdio.h>
int main() {
int *p; // 未初始化的指针,可能成为野指针
// 没有为 p 赋值,直接使用会导致不可预知的行为
// printf("Pointer value: %d\n", *p); // 错误
// 正确做法:为指针初始化
int value = 42;
p = &value;
printf("Pointer value: %d\n", *p);
return 0;
}
3)悬空指针
当指针指向的内存已经被释放,但指针还在使用时,出现悬空指针。使用悬空指针会导致错误。
#include <stdio.h>
#include <stdlib.h>
int main() {
int *p = (int *)malloc(sizeof(int)); // 动态分配内存
if (p == NULL) {
printf("Memory allocation failed.\n");
return 1;
}
*p = 100;
printf("Pointer value before free: %d\n", *p);
free(p); // 释放内存
// 继续使用 p 就会产生悬空指针错误
// printf("Pointer value after free: %d\n", *p); // 错误
// 正确做法:将指针置为 NULL
p = NULL;
if (p != NULL) {
printf("Pointer value: %d\n", *p);
} else {
printf("Pointer is NULL after free.\n");
}
return 0;
}