C语言实验4:指针

news2025/3/1 0:23:42

目录

一、实验要求

二、实验原理

1. 指针的基本概念

1.1 指针的定义

1.2 取地址运算符(&)

1.3 间接引用运算符(*)

2. 指针的基本操作

2.1 指针的赋值

2.2 空指针

3. 指针和数组

3.1 数组和指针的关系

 3.2 指针和数组的结合

4. 指针和函数

4.1 指针作为函数参数

5. 动态内存分配

5.1 malloc 和 free 函数

三、实验内容

3.1

代码

截图

分析

3.2

代码

截图

分析


一、实验要求

  1. 掌握指针和间接访问的概念,会定义和使用指针变量。
  2. 能正确使用数组的指针和指向数组的指针变量。
  3. 能正确使用字符串的指针和指向字符串的指针变量。

二、实验原理

指针是C语言中非常重要且强大的概念之一。它提供了直接访问内存地址的能力,使得程序可以更加灵活地处理数据。

1. 指针的基本概念

1.1 指针的定义

 指针是一个变量,其值是另一个变量的地址。通过指针,可以直接访问存储在该地址上的数据。

int *ptr;  // 定义一个指向整数的指针

1.2 取地址运算符(&)

用于获取变量的地址。

int num = 10;
int *ptr = #  // ptr指向num的地址

例如

#include<iostream>
using namespace std;
int main() {
	int a=10,*ptr;
	ptr = &a;
	cout << a <<" "<<ptr;
	return 0;
}

它的结果是

表面a的地址为000000ECA8AFF794,共16*4=64位二进制

那么下面的代码为什么地址不相邻呢?

#include<iostream>
using namespace std;
int main() {
	int a=10,b=11,*ptr,*ptr1;
	ptr = &a;
	ptr1 = &b;
	cout << a <<" "<<ptr<<endl;
	cout << b << " " << ptr1;
	return 0;
}

在C语言中,连续定义的两个变量的地址是否相邻,与多个因素有关,其中包括编译器、优化选项、操作系统的内存分配策略等。

  1. 对齐(Alignment): 许多体系结构要求数据按照某种规定的边界对齐,以提高访问速度。因此,编译器可能会在变量之间插入填充字节,以确保数据按照正确的边界对齐。这导致即使两个变量类型相同,它们的地址也可能不相邻。

  2. 优化: 编译器可能会对代码进行优化,包括对变量的存储和访问进行优化。这可能导致变量的地址不是按照它们在代码中的声明顺序来分配的。

  3. 内存分配策略: 操作系统对于内存的分配策略也可能影响变量的地址分布。例如,在某些情况下,操作系统可能会使用随机化技术来增加系统的安全性,这会导致变量的地址不再是连续的。

  4. 数据类型: 如果定义的变量类型不同,它们的大小可能也不同,这会影响它们在内存中的布局。

1.3 间接引用运算符(*)

用于访问指针所指向地址上的值。 

int value = *ptr;  // value等于num的值,通过ptr间接引用

例如

#include<iostream>
using namespace std;
int main() {
	int a=10,*ptr,value;
	ptr = &a;
	value = *ptr;
	cout << a <<" "<<ptr<<"  " <<value<< endl;
	return 0;
}

结果为

即指针ptr为地址,*ptr代表一个数,&a代表a的地址,&a和ptr可以交换值,属于同一类型

*ptr代表数,a代表数,两者可以交换值,属于同一类型

2. 指针的基本操作

2.1 指针的赋值

可以将一个指针指向另一个变量的地址。

int num1 = 10;
int num2 = 20;
int *ptr = &num1;  // ptr指向num1的地址
ptr = &num2;      // ptr现在指向num2的地址

例如

#include<iostream>
using namespace std;
int main() {
	int num1 = 10, num2 = 20;
	int* ptr = &num1;
	cout << *ptr <<" "<<ptr<< endl;
	ptr = &num2;
	cout << *ptr << " " << ptr;
	return 0;
}

结果为

2.2 空指针

指向地址为0的指针被称为空指针。

int *ptr = NULL;  // ptr是一个空指针

可以猜测一下下述代码的输出结果是什么

#include<iostream>
using namespace std;
int main() {
	int* ptr = NULL;
	cout << *ptr <<" "<<ptr<< endl;
	return 0;
}

答案是!

那空指针有什么作用呢?

1.标记未初始化的指针: 在定义指针变量但尚未为其分配有效内存地址之前,可以将指针初始化为NULL,表示它当前不指向任何有效的内存区域。

2.避免野指针: 将指针初始化为NULL可以避免使用未初始化的指针(野指针),从而减少程序中出现的错误。

3.指针作为空指针常量:NULL表示空指针常量,提高代码的可读性。在函数参数或返回值中,空指针常常用于表示某个指针不指向有效的内存。

void process_data(int *data) {
    if (data != NULL) {
        // 处理有效的数据
    } else {
        // 处理空指针情况
    }
}

4.动态内存分配失败的标志: 在动态内存分配时,如果分配失败,malloccalloc通常会返回NULL,这可用于检测内存分配是否成功。

int *dynamicPtr = (int*)malloc(sizeof(int));
if (dynamicPtr == NULL) {
    // 内存分配失败
    // 处理错误的代码
}

5.函数返回空指针: 有时,函数可能返回一个空指针作为错误或特殊情况的标志。

int *find_element(int key) {
    // 查找元素...
    if (element_not_found) {
        return NULL;
    }
    // 找到元素,返回指向元素的指针
}

3. 指针和数组

3.1 数组和指针的关系

数组名本身就是一个指针,指向数组的第一个元素的地址。

int arr[5] = {1, 2, 3, 4, 5};
int *ptr = arr;  // ptr指向arr的第一个元素

记住是arr为地址,不是&arr! 

 3.2 指针和数组的结合

通过指针可以遍历数组的元素。

for (int i = 0; i < 5; ++i) {
    printf("%d ", *(ptr + i));  // 打印数组元素的值
}

例如

#include<iostream>
using namespace std;
int main() {
	int* ptr;
	int a[5] = { 1,2,3,4,5 };
	ptr = a;
	for (int i = 0; i < 5; i++) {
		cout << "第" << i << "个元素为" << *(a + i) << "其地址为"<<a+i<<endl;
	}
	return 0;
}

结果为 

4. 指针和函数

4.1 指针作为函数参数

可以通过指针在函数间传递数据。

void modifyValue(int *ptr) {
    *ptr = 100;
}

int main() {
    int num = 10;
    modifyValue(&num);
    // 现在num的值变成了100
    return 0;
}

但是如果不用指针呢?

#include<iostream>
using namespace std;
void modifyValue(int ptr) {
	ptr = 100;
}
int main() {
	int num = 10;
	modifyValue(num);
	cout << num;
	return 0;
}

结果是10,所以指针的作用就体现出来了

在这段代码中,问题出现在modifyValue函数的参数类型和传递方式上。在C中,函数参数可以通过值传递或引用传递。在这里,modifyValue函数使用的是值传递,这意味着函数接收到的是实参的一个副本而不是实参本身。

当你调用modifyValue(num)时,将num的值(10)传递给modifyValue函数的形参ptr。在函数内部,ptr被修改为100,但这仅仅是对形参的修改,不会影响实参 num 的值。

所以,当你在main函数中输出num的值时,输出的是原始的值,即10,而不是在modifyValue函数内修改后的值100。

4.2 指针作为函数返回值

函数可以返回指针,使得函数能够返回动态分配的内存。

int* createArray(int size) {
    int *arr = (int*)malloc(size * sizeof(int));
    // 初始化数组...
    return arr;
}

例如

#include<iostream>
using namespace std;
int* createArray(int size) {
	int* arr = (int*)malloc(size * sizeof(int));
	return arr;
}

int main() {
	int* ptr;
	ptr = createArray(5);
	cout << ptr<<" "<<* ptr;
	return 0;
}

 结果为

为什么*ptr即数组的第一个值那么奇怪呢?

是因为数组只是分配了空间,并没有赋值

5. 动态内存分配

5.1 mallocfree 函数

用于动态分配和释放内存。

int *ptr = (int*)malloc(sizeof(int));  // 分配一个整数大小的内存
// 使用ptr...
free(ptr);  // 释放内存

sizeof 是一个在 C 和 C++ 等编程语言中常用的操作符,用于获取数据类型或变量在内存中所占用的字节数。sizeof 的语法如下

sizeof(type)
sizeof(expression)

其中,type 是数据类型,而 expression 则是一个表达式或变量。sizeof 返回一个 size_t 类型的值,表示参数所占用的字节数。常用于分配内存。

malloc 是 C 语言中的一个函数,用于动态分配内存。它的名字来源于 "memory allocation"(内存分配)。malloc 函数接受一个参数 size,表示要分配的内存字节数。它返回一个 void 指针,指向分配的内存的起始地址。可以将其转换为适当的类型,以便进行正确的使用。

上述转化为int型指针

free 函数用于释放通过动态内存分配函数(如 malloccallocrealloc 等)分配的内存空间。free 函数接受一个指针 ptr,该指针应该是通过动态内存分配函数分配的内存的起始地址。调用 free 函数会将相应的内存空间标记为可用,以便后续的内存分配操作可以使用该空间。

三、实验内容

3.1

将n个数按输入时的顺序逆序排列,用函数实现。

代码

#include<iostream>
using namespace std;
void sort(int* p, int* q, int n) {
	for (int i = 0; i < n; i++) {
		*(q + i) = *(p + n - 1 - i);//将q指针所代表的数组的第n-i个元素赋值给p指针所代表的数组的第i+1个元素
	}
}
int main() {
	int a[100],b[100],n,*ptr1,*ptr2;
	ptr1 = a;
	ptr2 = b;
	cout << "请输入所需要输入的元素数目:";
	cin >> n;
	for (int i = 0; i < n; i++) {
		cin >> *(ptr1 + i);
	}
	sort(ptr1, ptr2, n);
	for (int i = 0; i < n; i++) {
		cout << *(ptr2 + i)<<" ";
	}
	return 0;
}

截图

分析

  1. void sort(int* p, int* q, int n):这是一个排序函数,接受两个指向整数数组的指针 pq,以及数组的大小 n。该函数通过将数组 p 中的元素逆序复制到数组 q 中来实现排序。

  2. int main():主函数包含以下步骤:

    • 定义两个数组 ab,以及两个指向整数的指针 ptr1ptr2
    • 用户输入所需输入的元素数目 n
    • 通过指针 ptr1 输入数组 a 的元素。
    • 调用 sort 函数,将数组 a 中的元素逆序复制到数组 b 中。
    • 输出数组 b 中的元素,即排序后的结果。
  3. sort 函数中,通过循环遍历数组,将数组 p 中的元素逆序复制到数组 q 中。这通过使用指针算术实现,*(q + i) = *(p + n - 1 - i) 将数组 p 中的第 n-i 个元素赋值给数组 q 中的第 i+1 个元素。

3.2

将一个5×5的矩阵(二维数组)中最大的元素放在中心,4个角分别放4个最小的元素(顺序为从左到右,从上到下依次从小到大存放),写一函数实现。用main函数调用。 

代码

#include<iostream>
using namespace std;
int min_max = -9999, index = 0, b[4] = { 0,4,20,24 };//b数组为快速索引,index为min数组中最大元素的序列号

int find_min_max(int* ptr) {//找出四个元素中的最大值
	min_max = -9999;
	for (int i = 0; i < 4; i++) {
		if (*(ptr + i) >= min_max) {
			min_max = *(ptr + i);
		    index = i;
		}
	}
	return min_max;
}
int compare(const void* a, const void* b) {
	// 比较函数,用于指定排序规则
	// 返回负数表示 a < b
	// 返回零表示 a == b
	// 返回正数表示 a > b
	return (*(int*)a - *(int*)b);
}

void change(int *ptr) {
	//找到最大元素和最小的四个元素
	int max = -9999;
	int min[4] = { 9999,9999,9999,9999 };
	int* ptr2=min;
	//找最大元素和最小的四个元素
	for (int i = 0; i < 25; i++) {
		if (*(ptr + i) > max) {
			max = *(ptr + i);
		}
		min_max = find_min_max(ptr2);
		if (*(ptr + i) < min_max) {//满足进入四个最小元素的数组
			*(ptr2 + index) = *(ptr + i);//替换位置
		}
	}
	//对四个最小元素的位置进行排序
	qsort(ptr2, 4, sizeof(int), compare);
	//更换位置
	for (int i = 0; i < 25; i++) {
		if (*(ptr + i) == max) {//如果是最大元素
			int temp = *(ptr + 12);
			*(ptr + 12) = max;
			*(ptr + i) = temp;
			continue;
		}
	}
	for (int i = 0; i < 25; i++) {
		for (int j = 0; j < 4; j++) {//如果是较小元素
			if (*(ptr + i) == min[j]) {
				int temp = *(ptr + b[j]);
				cout << "temp  " << temp << endl;
				*(ptr + b[j]) = min[j];
				*(ptr + i) = temp;
				continue;
			}			
		}
	}	
}
int main() {
	int a[5][5],* ptr1;
	ptr1 = a[0];
	for (int i = 0; i < 5; i++) {
		for (int j = 0; j < 5; j++) {
			cin >> a[i][j];
		}
	}
	change(ptr1);
	for (int i = 0; i < 5; i++) {
		for (int j = 0; j < 5; j++) {
			cout<< a[i][j]<<" ";
		}
		cout << endl;
	}
	return 0;
}

截图

分析

  1. 变量定义和初始化:

    • int min_max = -9999, index = 0, b[4] = { 0,4,20,24 };:定义了全局变量,其中 min_max 用于存储四个元素中的最大值,index 用于存储 min 数组中最大元素的序列号,b 数组是用于快速索引的数组。
  2. find_min_max 函数:

    • 该函数用于找出四个元素中的最大值,并返回这个最大值。它还会更新 index 变量,以表示最大值在 min 数组中的位置。
  3. compare 函数:

    • 这是一个比较函数,用于在后续的 qsort 函数中进行数组排序。
  4. change 函数:

    • 该函数对数组进行操作,找到数组中的最大元素和最小的四个元素,然后将它们的位置进行调整。
    • 具体操作:
      • 找到数组中的最大元素 max
      • 找到数组中最小的四个元素,用 min 数组保存,并通过调用 find_min_max 函数找到最小元素中的最大值及其位置。
      • 使用 qsortmin 数组进行排序。
      • 将最大元素和最小元素的位置进行交换。
      • 输出结果。
  5. main 函数:

    • 定义了一个5x5的数组 a 和一个指向该数组的指针 ptr1
    • 通过用户输入给数组 a 赋值。
    • 调用 change 函数对数组进行操作。
    • 输出最终的数组。

指针是C语言中非常强大和灵活的特性,但也需要小心使用,因为错误的指针操作可能导致程序崩溃或产生不可预测的结果。

指针需慎用! 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1346793.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

CentOS 7 实战指南:目录操作命令详解

写在前面 想要在 CentOS 7 系统下更高效地进行目录操作吗&#xff1f;不要犹豫&#xff0c;在这里我为你准备了一篇精彩的技术文章&#xff01;这篇文章将带您深入了解 CentOS 7 下目录操作相关命令的使用方法。无论您是新手还是有一定经验的用户&#xff0c;这篇文章都将为您…

vue-springboot基于JavaWeb的家装一体化商城平台guptn

针对用户需求开发与设计&#xff0c;该技术尤其在各行业领域发挥了巨大的作用&#xff0c;有效地促进了家装一体化的发展。然而&#xff0c;由于用户量和需求量的增加&#xff0c;信息过载等问题暴露出来&#xff0c;为改善传统线下管理中的不足&#xff0c;本文将提出一套基于…

区块链的三难困境是什么,如何解决?

人们需要保持社交、工作和睡眠之间的平衡&#xff0c;并且努力和谐相处。同样的概念也反映在区块链的三难困境中。 区块链三难困境是一个术语&#xff0c;指的是现有区块链的局限性&#xff1a;可扩展性、安全性和去中心化。这是一个存在了几十年的设计问题&#xff0c;其问题的…

学习动态规划解决不同路径、最小路径和、打家劫舍、打家劫舍iii

学习动态规划|不同路径、最小路径和、打家劫舍、打家劫舍iii 62 不同路径 动态规划&#xff0c;dp[i][j]表示从左上角到(i,j)的路径数量dp[i][j] dp[i-1][j] dp[i][j-1] import java.util.Arrays;/*** 路径数量* 动态规划&#xff0c;dp[i][j]表示从左上角到(i,j)的路径数量…

【深度解析C++】const成员函数

系列文章目录 &#x1f308;座右铭&#x1f308;&#xff1a;人的一生这么长、你凭什么用短短的几年去衡量自己的一生&#xff01; &#x1f495;个人主页:清灵白羽 漾情天殇_计算机底层原理,深度解析C,自顶向下看Java-CSDN博客 ❤️相关文章❤️&#xff1a;Cthis指针&#xf…

野火霸道-V2+3.2寸屏+FreeRTOS+LVGL移植

摘要 基于野火霸道-V23.2寸屏的开发板&#xff0c;下载器为STLINK分为两个版本&#xff0c;FreeRTOS和裸机版本 裸机 裸机准备 lvgl v8.2版本的源码野火的《触摸画板-3.2寸》与《基本定时器》的代码例程 移植 将基本定时器代码移植到触摸画板-3.2寸的例程中&#xff0c;…

java springboot将接口查询数据放在系统中 一小时系统更新一次 避免用户访问接口查询数据库缓慢

真到了公司 很多数据库表 特别是常用的功能业务对应的 都是几百万条起步的数据 查询会比较缓慢 那么 我们就可以不用每次都真的查询数据库 例如 我这里有一个接口 通过 封装的 IBookService.list 函数去查询数据库 接口返回是这样的 我们先在启动类 条件装配上 这个接口所在的…

MySQL所有常见问题

一、事务 定义:一组操作要么全部成功,要么全部失败,目的是为了保证数据最终的一致性 在MySQL中,提供了一系列事务相关的命令: start transaction | begin | begin work:开启一个事务commit:提交一个事务rollback:回滚一个事务事务的ACID 原子性(Atomicity):当前事…

信号与线性系统翻转课堂笔记20——系统函数与信号流图

信号与线性系统翻转课堂笔记20——系统函数与信号流图 The Flipped Classroom20 of Signals and Linear Systems 对应教材&#xff1a;《信号与线性系统分析&#xff08;第五版&#xff09;》高等教育出版社&#xff0c;吴大正著 一、要点 &#xff08;1&#xff09;了解信…

CSU计算机学院2021年C语言期末题目思路分享(后两道题)

文章目录 E: 实数相加——大数加法的拓展原题题目描述输入输出样例输入样例输出 题目思路实现步骤代码和注释 F: 谍影寻踪——链表的思想和运用原题题目描述输入输出样例输入样例输出 题目思路 一点感想 E: 实数相加——大数加法的拓展 原题 题目描述 C语言就要期末考试了&a…

每日一题——LeetCode922

方法一 双指针&#xff1a; 一个偶指针一个奇指针&#xff0c;偶指针每次都指向nums里的偶数&#xff0c;奇指针每次指向nums里的奇数&#xff0c;两个指针交替push进新数组即可&#xff1a; var sortArrayByParityII function(nums) {var even0,odd0,res[],flagtruewhile(r…

C#,入门教程(02)—— Visual Studio 2022开发环境搭建图文教程

如果这是您阅读的本专栏的第一篇博文&#xff0c;建议先阅读如何安装Visual Studio 2022。 C#&#xff0c;入门教程(01)—— Visual Studio 2022 免费安装的详细图文与动画教程https://blog.csdn.net/beijinghorn/article/details/123350910 一、简单准备 开始学习、编写程序…

面试官:了解CountDownLatch吗

程序员的公众号&#xff1a;源1024&#xff0c;获取更多资料&#xff0c;无加密无套路&#xff01; 最近整理了一份大厂面试资料《史上最全大厂面试题》&#xff0c;Springboot、微服务、算法、数据结构、Zookeeper、Mybatis、Dubbo、linux、Kafka、Elasticsearch、数据库等等 …

多线程编程设计模式(单例,阻塞队列,定时器,线程池)

&#x1f495;"只有首先看到事情的可能性&#xff0c;才会有发生的机会。"&#x1f495; 作者&#xff1a;Mylvzi 文章主要内容&#xff1a;多线程编程设计模式(单例,阻塞队列,定时器,线程池) 本文主要讲解多线程编程中常用到的设计模式,包括单例模式,阻塞队列,定时…

12.30序列检测(重叠、不重叠、连续、不连续、含无关项)——移位寄存器,状态机;状态机(二段式,三段式)

状态机-重叠序列检测 timescale 1ns/1nsmodule sequence_test2(input wire clk ,input wire rst ,input wire data ,output reg flag ); //*************code***********//parameter S00, S11, S22, S33, S44;reg [2:0] state, nstate;always(posedge clk or negedge rst) b…

仓储革新:AR技术引领物流进入智慧时代

根据《2022年中国物流行业研究&#xff1a;深度探析行业现状&#xff08;智能设备及智能软件&#xff09;》&#xff0c;报告中提及&#xff1a;“中国社会物流总额依然保持着较为良好的增长态势&#xff0c;年增速已恢复至常年平均水平。2021年社会物流总额细分中工业物流总额…

机器视觉实战应用:手势、人脸、动作以及手势鼠标构建(一)

CV实战应用手势、人脸、动作以及手势鼠标构建&#xff08;一&#xff09;总起 核心思想 手势识别是一种常见的计算机视觉应用&#xff0c;它可以通过摄像头或者预先录制的视频图像来追踪和识别人类手势。手势识别的应用非常广泛&#xff0c;例如在游戏、虚拟现实、人机交互等…

ActiveMQ漏洞合集

目录 介绍CVE-2015-5254&#xff1a;Apache ActiveMQ任意代码执行漏洞漏洞介绍 & 环境准备漏洞发现Nuclei❌Vulmap✅漏洞验证漏洞利用 CVE-2016-3088&#xff1a;Apache ActiveMQ Fileserver远程代码执行漏洞漏洞发现Nuclei✅Vulmap✅MSF✅第三方工具1&#xff08;漏洞探测…

谷歌Linux内核自动测试平台架构介绍-用自动测试测试难以测试的问题

1 摘要 内核和硬件等低级系统已被证明极难进行有效测试&#xff0c;因此&#xff0c;许多内核测试都是以手动为主方式进行的。现有的大多数测试框架都是为测试与底层平台隔离的高级软件而设计的&#xff0c;而底层平台被假定是稳定可靠的。测试底层平台本身需要一套全新的假设…

单字符检测模型charnet使用方法,极简

Git链接 安装按照上面的说明&#xff0c;说下使用。 把tools下面的test做了一点修改&#xff0c;可以读取一张图片&#xff0c;把里面的单个字符都检测和识别出来。 然后绘制到屏幕上。 import torch from charnet.modeling.model import CharNet import cv2, os import num…