[C/C++]指针详讲-让你不在害怕指针

这篇具有很好参考价值的文章主要介绍了[C/C++]指针详讲-让你不在害怕指针。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

  •  个人主页:北·海
  •  🎐CSDN新晋作者
  •  🎉欢迎 👍点赞✍评论⭐收藏
  • ✨收录专栏:C/C++
  • 🤝希望作者的文章能对你有所帮助,有不足的地方请在评论区留言指正,大家一起学习交流!🤗

目录

前言

 一.&与*的作用与结合方向

二.指针和数组的关系

1.利用指针访问数组里的值

2.利用指针将数组作为实参传递给函数

 三.字符指针与字符数组

四.指针在内存分配中的应用

1.用C语言中的malloc在堆区分配动态内存

1.用C++中的new在堆区分配动态内存

五.使用指针为什么能提高程序的性能

六.指针与const搭配的四种情况

七.引用是实现原理

八.多级指针

九. 指针和函数的关系

1.通过指针使函数返回多个值

2.函数指针

3.函数的返回值类型为指针类型

十.指针和结构体的关系

 


前言

指针在编程语言中扮演着重要的角色,特别是在C语言中。指针提供了直接访问和操作内存地址的能力,使得编程更加灵活、高效,并且可以处理各种复杂的数据结构和算法。

以下是指针的几个重要方面和作用:

  1. 内存管理:指针允许程序直接与内存交互。通过使用指针,可以动态地分配和释放内存,避免了静态内存分配所带来的限制。这在处理动态数据结构(如链表、树、图等)和大规模数据中非常有用。

  2. 传递参数和引用:通过指针,可以在函数之间传递参数和引用。传递指针作为函数参数,可以避免在函数调用时产生副本,节省了内存和时间开销。此外,指针还可以用于函数返回多个值或修改调用者的变量。

  3. 动态数据结构:动态数据结构如链表、树、堆等通常需要使用指针进行内存分配和组织。指针可以在运行时创建、删除、连接和重新组织数据结构,使得数据结构的管理更加灵活和高效。

  4. 数组和字符串操作:在C语言中,数组实际上是通过指针来访问和操作的。指针使得数组可以直接访问和修改其元素,还可以进行指针算术操作,实现数组的遍历和操作。字符串在C语言中本质上是以空字符结尾的字符数组,指针的使用是对字符串进行处理的关键。

  5. 性能优化:指针的使用可以提高程序的性能。通过使用指针来访问和操作数据,可以减少副本的生成和数据的复制,提高程序的执行效率。


 一.&与*的作用与结合方向

作用: &在等号的右边为取地址符号,在等号的左边为引用符号

         *可以用做为乘号,也可用作为对指针变量解引的符号,取该地址存放的数据

结合方向:

“&”和“*”都是右结合的。假设有变量 x = 1,则*&x 的含义是,先获取变量 x 的地址,再获取地址中的内容。因为“&”和“*”互为逆运算,所以 x = *&x。

例如:输入 x、y 两个整数,然后将其中的值大的赋值给 x,小的赋值给 y。即:假设输入 x = 8,y = 9。就将 9 赋值给 x,8 赋值给 y。

void main(){
	//声明两个普通变量
	int x, y;
	//声明两个指针变量
	int *px, *py;
	//声明一个临时变量,用于交换
	int t;
	//输入两个值,赋值给 x、y
	scanf("%d", &x);
	scanf("%d", &y);
	//给指针变量 px、py 赋初值(关联变量 x、y)
	px = &x;
	py = &y;
	//利用指针来对比 x、y 的值,如果 x 的值比 y 的值小,就交换
	if(*px < *py){
		//交换步骤,其中*px == x、*py == y
		t = *px;
		*px = *py;
		*py = t;
	}
	printf("x =  %d, y = %d", *px, *py);
}

二.指针和数组的关系

1.利用指针访问数组里的值

	//利用指针访问数组里面的值
	int nums[] = {1,2,3,4};
	//方法一:下标法
	cout << "第一个数 :" << nums[0] << endl;
	//方法二:指针法
	cout << "第一个数 :" << *(nums + 0) << endl;//数组名就是数组元素的首地址

2.利用指针将数组作为实参传递给函数

void Test1(int nums[],int n) {
	for (int i = 0; i < n; i++) {
		cout << nums[i] << " ";
	}
}

int main() {
	//利用指针访问数组里面的值
	int nums[] = {1,2,3,4};
	int* p = nums;
	//利用指针将数组作为实参传递给函数
	Test1(nums, 4);//数组名就是数组元素的首地址
	//方法二
	Test1(p, 4);//p指向数组,再将指向数组的指针传给被调函数
}

补充对指针加一 : 例如p++,不会将地址的值加一,而是将数组下移一位

 三.字符指针与字符数组

C语言中,没有字符串类型的变量,通常用字符数组或者字符指针存放字符串

1.字符数组的声明

	char str[] = "C++";
	char str1[] = { 'C','+','+' ,'\0'};
   字符数组的声明方式,在使用第二种声明方式的时候,必须在最后加上\0,此符号代表在这个地方该数组就结束了,如果不加该符号,会访问出界的

  字符数组的访问

	char str[] = "C++";
	char str1[] = { 'C','+','+' ,'\0'};
	printf("%s\n", str1);//%s格式化,表示直接输出整个数组
	printf("%c\n", str[0]);//利用数组法取str第一个字符
	printf("%c\n", *(str1 + 1));//利用指针法取str1的第2个字符

由上面可以看出,可以通过%c一个字符一个字符的输出,那么也可也利用循环输出每个字符

	for (int i = 0; i < strlen(str); i++) {
		printf("%c", str[i]);
	}

还可以用过strlen(str)或者sizeof(str)/sizeof(char)来获取该数组的长度

2.字符指针的声明

char *word = "have a good night";

  字符指针的访问

	const char* word = "have a good night";
	printf("%s\n", word);

	printf("%c\n", word[0]);

	for (int i = 0; i < strlen(word); i++) {
		printf("%c", word[i]);
	}

    对字符指针地址的加减

	const char* word = "have a good night";
	word++;
	printf("%s\n", word);//此时就会将指针下移到第一个a的地方,从a的地方进行输出,
    输出为:ave a good night
	word--;
	printf("%s\n", word);//输出为:have a good night

	const char str[] = "C++";
	str++;//报错,在字符数组中不能进行加减操作,表达式必须是可修改的左值
	printf("%s", str);

四.指针在内存分配中的应用

1.用C语言中的malloc在堆区分配动态内存

1.利用malloc创建一维数组

#include <iostream>
#include <stdlib.h>
using namespace std;
int main() {
	int n;
	int* p = nullptr;
	cout << "请输入要存放的数据个数:";
	cin >> n;
	//在堆区分配内存
	p = (int *)malloc(n * sizeof(int));
	//输入
	for (int i = 0; i < n; i++) {
		cin >> *(p+i);
	}
	//输出
	for (int i = 0; i < n; i++) {
		cout << *(p+i) << " ";
	}
	free(p);
}

用malloc进行分配,注意malloc的返回值类型位void*类型,需要进行强制类型转换,由于在堆区分配的动态内存,所以在使用完之后,为了防止内存泄漏,需要用free进行释放

2.利用malloc进行开辟二位数组,与释放二位数组

#include <iostream>
#include <stdlib.h>
using namespace std;
int main() {
	int row = 0 ,col = 0 ;
	int** p = nullptr;
	cout << "依此输入行和列:" << endl;
	cin >> row>>col;
	//在堆区分配内存
	p = (int **)malloc(row * sizeof(int*));
	for (int i = 0; i < row; i++) {
		*(p + i) = (int*)malloc(col * sizeof(int));
	}
	//输入
	for (int i = 0; i < row * col; i++) {
		cin >> p[i / col][i % col];
	}

	//输出
	for (int i = 0; i < row * col; i++) {
		cout << p[i / col][i % col] << " ";
	}
	//释放动态开辟的二维数组
	for (int i = 0; i < row; i++) {
		free(p[i]);
	}
	free(p);
}

原理是,先创建一个指针数组,然后在利用改一级指针去创建动态int类型数组,在输入时候,用到了一个小技巧,用一层循环给二维数组赋值,在使用二维数组时候,应该先将每一层一级指针指向的内存块释放掉,然后再去释放该释放二级指针本身指向的内存块

1.用C++中的new在堆区分配动态内存

用到C++中的new分配内存,用delete释放内存,这里的new和delete只适用于c++中,malloc与free适用于c/c++,直接上例子

1.使用new进行分配一个动态的一维数组

int main() {
	int n;
	cout << "输入数组的大小:";
	cin >> n;
	int* p = new int[n];
	//输入
	for (int i = 0; i < n; i++) {
		cin >> *(p + i);
	}
	//输出
	for (int i = 0; i < n; i++) {
		cout << *(p + i) << " ";
	}

	delete[]p;
}

2.创建二维数组

int main() {
    int rows = 3;
    int cols = 4;

    // 创建动态二维数组
    int** array = new int*[rows];  // 创建一级指针数组

    for (int i = 0; i < rows; i++) {
        array[i] = new int[cols];  // 创建二级指针数组
    }

    // 使用动态二维数组
    for (int i = 0; i < rows; i++) {
        for (int j = 0; j < cols; j++) {
            array[i][j] = i * cols + j;  // 给数组元素赋值
        }
    }

    // 打印动态二维数组
    for (int i = 0; i < rows; i++) {
        for (int j = 0; j < cols; j++) {
            std::cout << array[i][j] << " ";
        }
        std::cout << std::endl;
    }

    // 释放动态二维数组的内存
    for (int i = 0; i < rows; i++) {
        delete[] array[i];  // 释放二级指针数组
    }
    delete[] array;  // 释放一级指针数组

    return 0;
}

和malloc的原理都是一样的,起始可以将一维数组创建的长一点,利用p[i / col][i % col];完全可以将一位数组当二维数组用,二维数组的存储方式也是按行存储的,地址都是连续的

在使用delete时候,数组的释放格式为 delete []p;,单个变量的释放格式为 delete p;

五.使用指针为什么能提高程序的性能

  • 直接访问内存:指针允许直接访问和操作内存中的数据。相比于通过变量的拷贝进行操作,直接访问内存可以减少数据的复制和移动,从而提高程序的执行效率。

#include <iostream>
using namespace std;
void Test1(int *p) {
	(*p)++;
}
void Test2(int m) {
	m++;
}
int main() {
	int num1 = 10,num2=10;
	Test1(&num1);
	cout << "Test1 :" << num1 << endl;//输出结果11
	Test2(num2);
	cout << "Test2 :" << num2 << endl;//输出结果10
}

Test1通过直接访问内存,避免了数据的拷贝和移动,提高了程序的执行效率。

Test2函数通过传递值的拷贝来操作变量。当我们调用该函数后,原始变量的值保持不变

  • 减少内存和时间开销:通过传递指针作为函数参数,可以避免在函数调用时产生变量的副本,从而减少内存的使用和传输的时间开销。特别是在处理大型数据结构或大量数据时,使用指针可以显著减少内存和时间的消耗。

  • 动态内存分配:指针使得动态内存分配成为可能,即在程序运行时根据需要分配和释放内存。相比于静态内存分配,动态内存分配可以更灵活地管理内存,避免内存浪费和限制,并且减少了程序启动时的内存占用。

六.指针与const搭配的四种情况

  • const 写在int之前,则限定不能通过*指针去改变该指针指向的值,但是可以指向别的指针
  • const 写在int之后,则限定可以通过*指针去改变该指针指向的值,但是不能指向别的指针
  • 两个const一个写在int前,一个写在变量名前,限制指针不能指向别的指针,并且不允许修改指针指向的值
  • 总结 : 看const离类型(int)近,还是理变量名近,离谁近,就修饰谁,谁就不能变
#include<iostream>
using namespace std;

int main() {
	int wife = 30;
	int girl = 18;

	//第一种 : 渣男型,普通指针可以随意更改指向与指向地址的值
	int* zha_nan = &wife;
	cout << *zha_nan << endl;

	zha_nan = &girl;
	cout << *zha_nan << endl;

	//第二种 : 直男型,以自我为中心,可以改变指向,但是不可以改变指向的地址的值

	const int* zhi_nan = &wife;
	//*zhi_nan = 25;//报错,不能改变值
	zhi_nan = &girl;
	cout << *zhi_nan << endl;

	//第三种 : 暖男型,专一,不可以改变指向,但是可以改变指向的地址的值

	int* const nuan_nan = &wife;
	//nuan_nan = &girl;//报错,不能改变指向
	*nuan_nan = 25;
	cout << *nuan_nan << endl;

	//第四种 : 超级暖男型,超级专一,不能改变指向,也不能改变指向地址的值

	const int* const _super_nuan_nan = &wife;
	//*_super_nuan_nan = 25;//报错,不能改变指向地址的值
	//super_nuan_nan = &girl;//报错,不能改变指向

	//总结 : const理谁近就修饰谁,理(int)近,则修饰该指针的值不能改变,修饰变量,
	//	    则该指针不能在指向别的变量了
}

七.引用是实现原理

引用的底层也是用指针进行实现的

	int a = 0;
	int b = 2;
	int& c = a;
	c = b;//c是a的别名,将b的值赋值给c,就相当于将b的值也赋值给了a
	cout << c << endl;//2
	cout << a << endl;//2
	//引用变量只能初始化一次,很像 int* const c = a;
    //该指针也只能进行一次初始化,就可以猜测引用的底层也是用该类型指针实现的

在使用引用的时候,编辑器会将引用类型转换为int * const 类型

例子:

void swap1(int& a, int& b) {
	int temp = a;
	a = b;
	b = temp;
}
void swap2(int* const a,int* const b) {
	int temp = *a;
	*a = *b;
	*b = temp;
}
int main() {

	int a = 1;
	int b = 2;
	
	swap1(a, b);//引用实现
	cout << "a = " << a << " b = " << b << endl;//a = 2,b =1
	swap2(&a, &b);
	cout << "a = " << a << " b = " << b << endl;//a = 2,b =1
}

对照swap1和swap2,在形参方面,引用变量会被改为int * const类型,在实参方面会将传入整数改为传入地址

八.多级指针

举例二级指针,懂了引用变量的话,那么对二级指针就可以有个优化了,提出指针引用,代码如下:

void home(int * p) {
	int b = 12;
	p = &b;
}
int main() {
	int a = 10;
	int* p = &a;
	//让p存放b的地址
	home(p);
	cout << *p << endl;//10
}

可以看出以上代码是无法改变p的指向的,改变值需要用一级指针,改变一级指针的指向需要用到二级指针,一次类推,这里直接就不写二级指针了,直接用一级指针的引用代替二级指针

void home(int *& p) {
	int b = 12;
	p = &b;
}
int main() {
	int a = 10;
	int* p = &a;
	//让p存放b的地址
	home(p);
	cout << *p << endl;//12
}

由此可以看出,利用指针引用可以将p的指向改变,这也的引用可以增强代码的可读性,与简洁性

九. 指针和函数的关系

1.通过指针使函数返回多个值

比如给你了一个已经初始化的数组,需要定义一个函数,用于返回这个数组的最大值与次大值,这个时候,为了增强代码的可读性,尽量返回一个数组,因为当返回的数据多了,也只能用返回数组实现了,代码如下:

void _max(int *nums,int n,int* exterm) {
	exterm[0] = nums[0];
	exterm[1] = nums[0];

	for (int i = 0; i < n; i++) {
		//大于次大的
		if (nums[i] > exterm[1]) {

			if (nums[i] > exterm[0]) {
				//大于最大的
				exterm[1] = exterm[0];
				exterm[0] = nums[i];
			}

			if (nums[i] < exterm[0]) {
				//小于最大的
				exterm[1] = nums[i];
			}
		}
	}
}

int main() {
	//求该数组中的最大值与次大值
	int nums[] = { 2,6,4,9,5 };
	int exterm[2] = { 0 };
	_max(nums, 5, exterm);

	cout << exterm[0] << " " << exterm[1] << endl;//9,6
}

2.函数指针

函数指针是指向函数的指针变量。它可以存储函数的地址,并允许我们通过该指针调用相应的函数。函数指针在C和C++中都有广泛的应用,可以用于回调函数、函数参数以及实现函数的动态调用等场景。

要理解函数指针,首先需要了解函数的定义和函数指针的声明以及函数指针的使用方式。

1.函数定义:
返回类型 函数名(参数列表) {
    // 函数体
}

2.函数指针的声明:
返回类型 (*指针变量名)(参数列表);

指针变量名:函数指针的名称。
*:用于指明该变量是一个指针。
返回类型:函数指针指向的函数的返回类型。
参数列表:函数指针指向的函数的参数列表。

3.将函数指针指向函数
指针变量名 = 函数名;

4.通过函数指针调用函数:
(*指针变量名)(参数列表);//方法一
(指针变量名)(参数列表);//方法二

举例:

#include <iostream>

int add(int a, int b) {
    return a + b;
}

int subtract(int a, int b) {
    return a - b;
}

int main() {
    // 声明函数指针
    int (*p)(int, int);

    // 将函数指针指向add函数
    p = add;

    // 通过函数指针调用add函数
    int result = (*p)(10, 5);
    std::cout << "Add: " << result << std::endl;

    // 将函数指针指向subtract函数
    p = subtract;

    // 通过函数指针调用subtract函数
    result = (*p)(10, 5);
    std::cout << "Subtract: " << result << std::endl;

    return 0;
}

3.函数的返回值类型为指针类型

int* creat() {
	return new int(1);//创建一个int类型的变量初始化为1,动态分配的数组不能进行初始化	
}

int main() {

	//函数的返回值类型为指针变量类型,例子,
    //在creat函数里面动态的分配内存,将指向该段内存的指针返回
	int* p = creat();
	cout << "*p :" << *p << endl;//*p : 1

    delete p;
}

十.指针和结构体的关系

利用指针访问结构体里面的值

struct Test
	{
		int a;
		int b;
		int c;
	};
	struct Test ss = { 2,3,4 };
	//声明了结构对象ss,并把ss 的成员初始化为2,3 和4。
	struct Test* ptr = &ss;
	//声明了一个指向结构对象ss 的指针。它的类型是
	//Test *,它指向的类型是Test 。
	printf("%d", ptr->a);
	printf("%d", ptr->b);
	printf("%d", ptr->c);

 [C/C++]指针详讲-让你不在害怕指针,c语言,c++,开发语言文章来源地址https://www.toymoban.com/news/detail-683301.html

到了这里,关于[C/C++]指针详讲-让你不在害怕指针的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • vr全景展示三大问题解析,让你的店铺无处不在

    未来互联网营销趋势走的是可视化发展路线,因此VR全景展示技术作为互联网时代新营销和宣传工具,可以让客户一目了然,提升企业品牌形象和客户信任度。vr全景展示能够给用户带来强烈视觉体验,这种沉浸感让用户可以深度参与其中,感同身受,因此作为品牌营销是非常

    2024年02月08日
    浏览(43)
  • [C语言实现]数据结构堆之《害怕二叉树所以天赋全点到堆上了》

    🥰作者: FlashRider 🌏专栏: 数据结构 🍖知识概要:详解 堆 的概念、小根堆与大根堆的区别、以及代码实现。 目录 什么是堆? 如何实现堆? 代码实现堆(小根堆) 定义堆以及堆的初始化和销毁。 堆的插入 堆的删除 获取堆的元素长度和获取堆顶元素 代码测试 TopK问题 我们先

    2024年02月09日
    浏览(35)
  • 自定义鼠标指针——让你的指针瞬间变美

    首先,在开始今天的内容之前,我们先来看一下上次投票结果: 很明显,我们的“2号选手”顺利夺得了第一。 好,二话不说,开启今天的内容。 先来看一下我的鼠标指针:(额,我是个MC党(注:上一篇博客发迷你世界抢滩登陆只是素材需要,不要喷))  玩过MC的应该都

    2024年02月08日
    浏览(35)
  • 让你立刻学会指针

    ☃️个人主页:fighting小泽 🌸作者简介:目前正在学习C语言和数据结构 🌼博客专栏:C语言学习 🏵️欢迎关注:评论👊🏻点赞👍🏻留言💪🏻 指针是什么? 指针理解的2个要点: 指针是内存中一个最小单元的编号,也就是地址 平时口语中说的指针,通常指的是指针变量

    2023年04月21日
    浏览(48)
  • 〖Web全栈开发④〗— HTML基础详讲(超详细)

    🏘️🏘️个人简介:以山河作礼。 🎖️🎖️: Python领域新星创作者,CSDN实力新星认证,阿里云社区专家博主 🎁🎁:Web全栈开发专栏:《Web全栈开发》免费专栏,欢迎阅读! 浏览器的主要功能就是向服务器发出请求,在浏览器窗口中展示HTML文档、PDF、图片、视频等网络内

    2024年02月05日
    浏览(39)
  • [开发语言][python][c++]:C++中的this指针和Python中的Self -- 26岁生日

    以朋友的新岁祝福开篇,祝笔者也祝大家☺️: 之前一直对 C++ 中的 this 和 python 中的 self 到底是什么关系,为什么 C++ 要显式的写出来,python 则不需要? 模糊不清,趁着周末整理一下相关结论,希望本篇文章可以解答这些问题,同时对C++和Python中的类加深些理解。 python 当

    2024年01月24日
    浏览(67)
  • C/C++|物联网开发入门+项目实战|指针|嵌入式C语言高级|C语言内存空间的使用-学习笔记(9)

    参考: 麦子学院-嵌入式C语言高级-内存空间 内存类型资源地址、门牌号的代名词 指针:地址的代名词 指针变量:存放指针这个概念的盒子 *P char *p *p; C语言娟译器对指针这个特殊的概念,有2个疑问? 1、分配一个盒子,盒子要多大? 在32bit系统中,指针就4个字节 2、盘子里存放

    2023年04月22日
    浏览(62)
  • 【05】STM32·HAL库开发-C语言基础知识 | stdint.h介绍 | 位操作 | 宏定义的使用 | 条件编译 | extern声明 | typdef使用 | 结构体、指针、代码规范介绍。

      stdint.h 是从 C99 中引进的一个标准 C 库的文件,可以在MDK5的安装路径:D:MDK5.34ARMARMCCinclude中找到。   stdint.h 定义了很多类型别名,将有符号的char类型定义别名为int8_t等,使用此套别名有易于移植。   在MDK中需要配置才能支持使用S99标准, 默认是勾选的 。   只

    2024年02月08日
    浏览(43)
  • 【C语言】指针进阶:字符指针&&数组指针&&函数指针

    ✨作者:@平凡的人1 ✨专栏:《C语言从0到1》 ✨一句话:凡是过往,皆为序章 ✨说明: 过去无可挽回, 未来可以改变 🌹 感谢您的点赞与关注,同时欢迎各位有空来访我的 🍁平凡舍 回想之前,我们学了 指针 的一些基础👉 指针与结构体 我们知道了指针的概念: 指针就是

    2023年04月08日
    浏览(42)
  • c语言指针(深入了解指针)

    前沿:       有人曾说过不会指针等于没有学习c语言,而我也是非常认同这个观点的,要想学习好c语言,指针是比不可缺少的,如果指针学不会c语言也就没办法学好,而向如此越重要的东西越比较难学,但难学并不代表学不会,这片文章将由简单到复杂让你深刻的了解指针

    2023年04月08日
    浏览(44)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包