【C++】指针是啥东西?看这篇博客就够了!

时间:2024-10-01 16:32:14

指针到底是啥东西?很多人都有这样的问题,今天我就为大家来解答

首先看一行代码:

int a;

很显然,这行代码的用途是定义变量,那么再看一行代码

int *a;

这下懵了吧,你们以为这是一行错误的代码,但恰恰相反,这行代码能够正常运行

这行代码定义的就叫做 指针

前期我们可以把它认为是一个数据结构,像这样

int* a;

 这样也是不会报错的。废话不多说,先来了解作用

1、认识指针

指针指针,顾名思义就是指向一个东西的针,但是编程中并没有针,所以我们只需要把它理解成指向一个东西的东西就行了,而它指向的那个东西叫做 地址 。

地址说白了就是存储变量的地方,比如说我们int a,就相当于定义了一个整型变量a,它有一个地址,也只有它有那个地址。

地址的格式一般是0x开头,后面一大串数字。行了,回归正题。

int* a是指向int类型的指针,同样地,char* a就是指向字符类型的指针。

int *a[10];
int[10] *a;

 看上面的两行代码。

第一行是指针数组,是一个全是指针的数组,数组的每个下标都是一个指针

第二行是数组指针,是指向数组的指针。

现在,指针你基本就了解了大概,接下来直接上代码来实际操作

注意:如果你是第一次接触指针,请你务必要自己用编译器,跟着我输入我提供的代码,你也可以修改代码中的值,自己去探索指针的功能

2、学习指针

接下来我们就该学习怎么用指针来指东西了。

int a;
int *b=&a;

注意,这里的&很重要,这是取地址符,你们学习scanf的时候应该接触过。一定要加上,不然程序会报错

这段代码是让指针b指向指针a的过程。 

是不是很简单?那怎么通过b来知道a的值呢

同样非常简单,只需要

cout << *b;

这里的*同样很重要!!!

很好,那指针之间怎么互相复制呢?

int* p1  = &a;
int* p2 = p1;

在看一段代码

int *a=NULL;

 你一定以为NULL不能赋给指针对不对,恰恰相反,这样是完全正确的,指针不能乱赋值,万一赋值道系统重要信息的地址,你的电脑就惨咯

当然,这种错误代码我就不给了,省的你们自己非想要尝试

任何指针都可以被赋值为NULL。

接着再看一段代码

char array1[20] = "abcdefghijklmnopqrs";
char *ptr1 = array1;
int *ptr2 = (int*)ptr1;

ptr1++;
ptr2++;
cout << &array1 << endl;
cout << *ptr1 << endl;
cout << ptr2 << endl;
cout << *(char*)ptr2 << endl;
  1. array1 是一个字符数组,已被初始化为包含19个字符的字符串,并以空字符\0作为结尾,尽管在初始化时未显式包含。

  2. ptr1 是一个指向字符的指针,它已被初始化为指向array1的首地址。

  3. ptr2 是一个指向整数的指针,它通过将ptr1强制转换为整数指针类型来获得。

  4. ptr1 和 ptr2 都执行了递增操作。

接下来,我们逐一分析 cout 语句的输出:

cout << &array1 << endl;

这条语句输出array1数组的地址。

cout << *ptr1 << endl;

由于ptr1是字符指针,并且已经递增,所以这条语句输出array1中的第二个字符,即b

cout << ptr2 << endl;

ptr2是指向整数的指针,递增后它指向array1中原本第二个字符的地址,但以整数指针的视角来看。这条语句输出ptr2当前指向的地址。

cout << *(char*)ptr2 << endl;

这条语句首先将ptr2强制转换回字符指针,然后解引用以输出当前指向的字符。由于ptr2原本指向array1的第二个字符(在转换为整数指针并递增后),并且整数指针的递增通常基于整数的大小(在大多数平台上为4字节),这条语句实际上可能输出array1中第六个字符之后的内容,具体取决于整数大小和字节对齐。

下面是输出

上面就是指针的基础,接下来该学点新的了

函数指针

void change(int a)
{
    a++;      //在函数中改变的只是这个函数的局部变量a,而随着函数执行结束,a被销毁。
}

int main()
{
    int age = 19;
    change(age);
    printf("age = %d\n",age);   // age = 19
    return 0;
}

实参传递给形参,是按值传递的,也就是说,函数中的形参是实参的拷贝份,形参和实参只是在值上面一样,而不是同一个内存数据对象。这就意味着:这种数据传递是单向的,即从调用者传递给被调函数,而被调函数无法修改传递的参数达到回传的效果。

有时候我们可以使用函数的返回值来回传数据,在简单的情况下是可以的,但是如果返回值有其它用途(例如返回函数的执行状态量),或者要回传的数据不止一个,返回值就解决不了了。

传递变量的指针可以轻松解决上述问题。

void change(int* pa)
{
    (*pa)++;   //因为传递的是age的地址,因此pa指向内存数据age。当在函数中对指针pa解地址时
               //会直接去内存中找到age这个数据,然后把它增1。
}
int main(void)
{
    int age = 19;
    change(&age);
    printf("age = %d\n",age);   // age = 20
    return 0;
}

那么函数的指针是什么呢

每一个函数本身也是一种程序数据,一个函数包含了多条执行语句,它被编译后,实质上是多条机器指令的合集。在程序载入到内存后,函数的机器指令存放在一个特定的逻辑区域:代码区。既然是存放在内存中,那么函数也是有自己的指针的。

其实函数名单独进行使用时就是这个函数的指针。

int add(int a,int b)		//函数的定义
{
	return a + b;
}

int (*function)(int,int); 	//函数指针的声明

function = add;		//给函数指针赋值
function = &add;		//跟上面是一样的

int c = function(1,2);	 	//跟函数名一样使用
int d = (*function)(1,2);	//跟上面的调用是一样的

 上面的代码就实现了函数指针

 

 除了函数,还有一个东西有指针,它常常出现在我们的程序中,但是却很容易被忽视

它就是常量,你没听错,常量也有指针

int	a = 97;
int	b = 98;
int* const p = &a;
*p 	= 98;		//正确
//p 	= &b;		编译出错

常量指针本身就是个常量 。常量指针必须初始化,而且一旦初始化完成,则它的值就不能改变了。

同理,指向常量的指针也很容易

int a = 97;
int b = 98;       
const int* p = &a;
int const *p = &a; 	//两者的含义是一样的
//*p = 98;	    编译出错
p = &b;			//正确

3、练习 

最后的最后,给大家准备一些题,确保你学会了指针

void GetMemory(char* p)
{
    p = (char*)malloc(100);
}

void Test(void)
{
    char* str = NULL;
    GetMemory(str);
    strcpy(str,"hello world");
    printf(str);
}
运行的结果是什么?
 

int array[] = {1,2,3,4,5,6,7,8,9,10,11,12,13,14,15};
int *p = array;
p += 5;
int* q = NULL;
*q = *(p+5);
printf("%d %d",*p,*q);
运行结果是什么?
 

int arr[5] = {0,1,2,3,4};
const int *p1 = arr;
int* const p2 = arr;
*p1++;
*p2 = 5;
printf("%d,%d",*p1,*p2);
*p1++ = 6;
*p2++ = 7;
printf("%d %d \n", *p1, *p2);
 

4、解析

第一道题:我们一看函数传递,指针只是浅拷贝,申请的内存在临时对象p中,并没有传递到函数外面,然后又对str地址进行写操作,str初始地址为NULL,不能进行书写,所以系统会崩溃。

第二道题:一看很开心是指针类型的加减法,下标从0开始,但是数字从1开始,所以应该是6 11。但是你忽略了q是一个NULL指针,不能进行书写,所以会崩溃。

第三道题:指针指向数组,数组退化成指针,前两个指针操作是对的。但是后面*p1++ = 6; 不可以通过p1进行值的修改,*p2++ = 7;不能对p2进行修改。所以这道题是编译出错。

好了,相信你看完本篇博客,一定对指针有所了解,点赞过20更新下一篇C++教程!