C语言指针

C语言中,指针 是一个非常重要且强大的概念。指针是一个变量,它存储的是另一个变量的内存地址。通过指针,可以访问和操作内存中的数据,这使得指针在数组、字符串、函数、动态内存分配等场景中非常有用。指针也就是内存地址,指针变量是用来存放内存地址的变量,不同类型的指针变量所占用的存储单元长度是相同的,而存放数据的变量因数据的类型不同,所占用的存储空间长度也不同。有了指针以后,不仅可以对数据本身,也可以对存储数据的变量地址进行操作。本文主要介绍C语言中的指针。

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

推荐阅读
cjavapy编程之路首页