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; }