C语言指针使用

一、C语言指针介绍

1.1、基本概念

在C语言中,指针是一个变量,其值为另一个变量的地址,即内存位置的直接地址。当一个变量被声明为指针类型时,编译器将为该变量分配一个可以存储内存地址的空间。指针在C语言编程中非常重要,它们用于动态内存分配、函数参数传递、数据结构(如链表和树)等。

1.2、指针介绍

(1)指针是一个变量,它存储的是另一个变量的内存地址,而不是直接存储数据值。

(2)指针是有类型的,指针的类型决定了“指针的+-整数的步长”,以及解引用时候的权限。

(3)指针通过取地址运算符(&)来获取变量的地址,而箭头运算符(->)用于访问指向结构体或类的指针成员,它不能直接用于获取变量的地址。

(4)指针之间存在运算,多级指针其实就是指向指针的指针。

二、C语言指针基础

 2.1、指针的定义

以下是常见的指针定义,区分其含义以便更好的理解指针(优先级问题)。

int p; //一个普通的整型变量 
int *p; //P先与*结合,说明P是一个指针,然后再与int结合,说明指针指向型为int型.所以P是一个返回整型数据的指针
int **p; //(多级指针略)P先与*结合,再与*结合,然后再与int结合,说明该指针所指向的元素是整型数据
int p[4]; //P先与[]结合,然后与int 结合,说明p是一个整型数组。
int *p[4]; //P先与[]结合,因为其优先级比*高,然后再与*结合,然后再与int 结合,所以P是一个由返回整型数据的指针所组成的数组
int (*p)[4]; //P先与*结合,然后再与[]结合,然后再与int 结合,所以P是一个指向由整型数据组成的数组的指针 
int (*p)(int); //P先与指针结合,然后与()结合,然后再与()里的int结合,所以P是一个指向有一个整型参数且返回类型为整型的函数的指针

 注意:每定义一个指针,都应该思考这个指针的类型是什么?指针指向的类型是什么?该指针的值是什么?(指向了哪里?)

2.2、指针的基本运算

 (1)地址指向运算

#include <stdio.h>
int main() {
   int numb=1;
   int *p;
   p=&numb;//加地址符,指向numb 
   (*p)++;
   printf("%d %d\n",numb,*p);
   int numb1[5]={11,22,33,44,55};
   int *p1;
   p1=numb1;//数组不需要加地址符 
   for(int i=0;i<5;i++){//p1的指向会变 
	    printf("%d ",*p1);
	    p1++;
   }
   p1=p1-5;//让p1重新指向开始的位置
   for(int i = 0; i < 5; i++) {//p1的指向不会变 
	    printf("%d ", *(p1 + i));
   }
   
   char a[20]="You are a girl";
   int *ptr=(int *)a;
   ptr+=5;
   printf("\n%d",*ptr);//随机输出,指针类型不能用于强制转化

   return 0;
}

(2)多级指针运算

#include <stdio.h>
int main() {
    int a = 10;
    int *p1 = &a;
    int **p2 = &p1;
    int ***p3=&p2;
    printf("%d\n", a);
    printf("%d\n", *p1);//p1指向a 
    printf("%d\n", **p2);//p2指向p1,则三者值都一样 
    printf("%d\n", ***p3);//p3指向p2,四者值都一样 
    int b=20;
	p1=&b;//p1指向b 
    printf("%d %d %d\n",*p1,**p2,***p3);//p1指向改变,对应p2,p3都变
    b=30;
    printf("%d %d %d\n",*p1,**p2,***p3);//b数值改变,p1,p2,p3都变 
    int *k=&a;
    p2=&k;
    printf("%d %d\n",*p1,***p3); //p2指向改变,p3变,p1不变 
    return 0;
}

(3)指针间运算

#include <stdio.h>
int main() {
    int arr[] = {10, 20, 30, 40, 50};
    int *p1 = arr;         // 指向数组的第一个元素
    int *p2 = arr + 2;     // 指向数组的第三个元素
    // 指针加法
    printf("p1 + 1 = %p\n", p1 + 1); // 移动到下一个元素
    // 指针减法
    printf("p2 - 1 = %p\n", p2 - 1); // 移动到前一个元素
    // 指针差值
    printf("p2 - p1 = %ld\n", p2 - p1); // 计算两个指针之间的元素个数
    // 指针自增
    p1++;
    printf("p1++ = %p\n", p1); // 自增后指向下一个元素
    // 指针自减
    p2--;
    printf("p2-- = %p\n", p2); // 自减后指向前一个元素
    // 指针比较
    if (p1 == p2) {
        printf("p1 == p2\n");
    } else {
        printf("p1 != p2\n");
    }
    // 指针间接引用
    printf("*p1 = %d\n", *p1); // 访问指针p1所指向的值
    return 0;
}

2.3、const修饰指针

(1) 指向常量的指针

const int *ptr;

其中ptr是一个指向const int的指针。你不能通过ptr修改它所指向的值,但可以改变ptr本身,使其指向其他地址。

const int value = 10;
const int *ptr = &value;
// *ptr = 20; // 错误:不能通过ptr修改value的值
int anotherValue = 30;
ptr = &anotherValue; // 可以改变ptr指向的地址

(2)常量指针 

int *const ptr;

其中 ptr是一个常量指针。你可以通过ptr修改它所指向的值,但不能改变ptr本身,使其指向其他地址。

int value = 10;
int *const ptr = &value;
int anotherValue=30;
*ptr = 20; // 可以通过ptr修改value的值
// ptr = &anotherValue; // 错误:不能改变ptr指向的地址

(3)指向常量的常量指针

const int *const ptr;

其中 ptr是一个指向const int的常量指针。你既不能通过ptr修改它所指向的值,也不能改变ptr本身,使其指向其他地址。

const int value = 10;
int anotherValue=30;
const int *const ptr = &value;
// *ptr = 20; // 错误:不能通过ptr修改value的值
// ptr = &anotherValue; // 错误:不能改变ptr指向的地址

三、C语言指针的应用

3.1、指针内存分配问题

在C语言中,指针的内存分配主要有两种方式:静态分配动态分配

(1)静态分配

静态分配是在编译时完成的,内存空间在程序运行期间是固定的。通常用于局部变量和全局变量。例如:

int value = 10; // 静态分配
int *ptr = &value; // 指针指向静态分配的内存

(2)动态分配 

动态分配是在程序运行时完成的,使用标准库函数malloc、calloc或realloc来分配内存,使用free来释放内存。

int *ptr = (int *)malloc(sizeof(int)); // 动态分配
if (ptr != NULL) {
    *ptr = 10; // 使用动态分配的内存
}
free(ptr); // 释放动态分配的内存

动态内存分配函数

malloc(size_t size):分配指定大小的内存块,返回指向该内存块的指针。

calloc(size_t num, size_t size):分配指定数量的内存块,并初始化为零。

realloc(void *ptr, size_t size):调整之前分配的内存块的大小。

free(void *ptr):释放之前分配的内存块。 

扩展:计算本地电脑可给指针最多分配多少内存,代码如下:

#include <stdio.h>
#include<stdlib.h> 
int main() {
    void *p;
	int cnt=0;
	while((p=malloc(100*1024*1024))) {
		cnt++;
	}
	printf("电脑可分配内存:%d00MB",cnt);
    return 0;
}

 3.2、指针的常见用途

(1)动态内存分配

C语言使用指针进行动态内存分配,例如malloc,calloc,realloc和free函数。这些函数返回一个指向已分配内存的指针,或者在释放内存时使用指针。

举例:

#include <stdio.h>
#include <stdlib.h>
int main() {
   /* 使用 malloc 分配内存 */
   int *ptr = (int*) malloc(5 * sizeof(int));
   if(ptr == NULL) {
      printf("没有成功分配内存(Memory not allocated)\n");
      return 0;
   }
   else {
      printf("成功分配内存(Memory successfully allocated)\n");
      /* 使用这块内存 */
      for(int i = 0; i < 5; i++)
         ptr[i] = i + 1;
      /* 打印数组元素 */
      for(int i = 0; i < 5; i++)
         printf("%d, ", ptr[i]);
   }
   /* 使用 realloc 重新分配内存 */
   ptr = (int*) realloc(ptr, 10 * sizeof(int));
   if(ptr == NULL) {
      printf("没有成功分配内存(Memory not allocated)\n");
      return 0;
   }
   else {
      printf("\n成功分配内存(Memory successfully allocated).\n");
      /* 使用这块内存 */
      for(int i = 5; i < 10; i++)
         ptr[i] = i + 1;
      /* 打印数组元素 */
      for(int i = 0; i < 10; i++)
         printf("%d, ", ptr[i]);
   }
   free(ptr);  /* 释放内存 */
   return 0;
}


(2)数组、字符串和结构体

指针用于处理数组和字符串。例如,字符串在C语言中表示为字符数组,通常使用字符指针处理。结构体指针用于动态分配结构体并访问其成员。

举例:

#include <stdio.h>
int main() {
    int arr[5] = {1, 2, 3, 4, 5};
    int *p1 = arr;  // 指向数组的指针
    for(int i = 0; i < 5; i++) {
        printf("%d ", *(p1 + i));  // 使用指针访问数组元素
    }
    printf("\n"); 
    char str[] = "Hello, World!";
    char *p2 = str;  // 指向字符串的指针
    while(*p2 != '\0') {
        printf("%c", *p2);  // 使用指针访问字符串中的字符
        p2++;
    }
    return 0;
}

(3)数据结构

许多数据结构,如链表,树,图等,都依赖于指针。这些数据结构的节点通常包含指向其他节点的指针。

举例:

#include <stdio.h>
#include <stdlib.h>
// 定义链表节点
typedef struct Node {
    int data;
    struct Node* next;
} Node;
// 创建新节点
Node* createNode(int data) {
    Node* newNode = (Node*)malloc(sizeof(Node));
    if(newNode == NULL) {
        printf("不能创建新结点(Unable to create a new node)\n");
        exit(0);
    }
    newNode->data = data;
    newNode->next = NULL;
    return newNode;
}
// 插入新节点到链表的末尾
void insertNode(Node** head, int data) {
    Node* newNode = createNode(data);
    if(*head == NULL) {
        *head = newNode;
        return;
    }
    Node* temp = *head;
    while(temp->next != NULL) {
        temp = temp->next;
    }
    temp->next = newNode;
}
// 打印链表
void printList(Node* head) {
    Node* temp = head;
    while(temp != NULL) {
        printf("%d -> ", temp->data);
        temp = temp->next;
    }
    printf("NULL\n");
}
int main() {
    Node* head = NULL;
    insertNode(&head, 1);
    insertNode(&head, 2);
    insertNode(&head, 3);
    printList(head);
    return 0;
}

(4)函数参数的传递

如果想在函数中修改变量的值,或者传递大型数据结构(如数组或结构)而不复制整个结构,那么可以使用指针。这被称为“通过引用传递”。

#include <stdio.h>
//通过指针接收一个整数,并将其值加一
void addOne(int* ptr) {
    (*ptr)++;  // 增加指针ptr所指向的值
}
int main() {
    int num = 0;
    printf("原始数据: %d\n", num);
    addOne(&num);
    printf("修改后: %d\n", num);
    return 0;
}

(5)函数指针

可以创建指向函数的指针,这在编写可插入函数、回调函数或者函数表等高级编程任务时非常有用。

举例:用 C语言编写程序,定义两个函数 mean 和 square 分别计算三个数的平均值和平方和,在主函数中输入三个数并调用它们,要求使用函数指针,当用户输入1时求平均值,输入2时求平
方和。

#include <stdio.h>
double mean(int a, int b, int c){
	return (a+b+c)/3.0;
};
int square(int a, int b, int c){
	return(a*a+b*b+c*c);
};
int main() {
    int choice;
    int num1, num2, num3;
    double result;
    double (*func_ptr)(int, int, int);//函数指针 
    printf("请输入三个整数:");
    scanf("%d %d %d", &num1, &num2, &num3);
    printf("请选择操作:\n");
    printf("1. 求平均值\n");
    printf("2. 求平方和\n");
    scanf("%d", &choice);
    if (choice == 1) {
        func_ptr = mean; 
    } else if (choice == 2) {
        func_ptr = square; 
    } else {
        printf("无效选择。\n");
        return 1;
    }
    result = func_ptr(num1, num2, num3);
    if (choice == 1) {
        printf("三个数的平均值为:%.2f\n", result);
    } else if (choice == 2) {
        printf("三个数的平方和为:%.2f\n", result);
    }
    return 0;
}

  • 14
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值