【指针】C/C++指针相关知识

【指针】C/C++指针相关知识

提到指针,需要联系到计算机的内存等相关的知识,这类理论知识我就不过多阐述了,可能比较枯燥,我会以实践为主来分析指针。

首先我们需要了解一下 & 符号,这个符号相信大家都不陌生,是C语言输入函数:scanf() 需要的,比如一个整形的变量a需要 scanf("%d",&a); 完成对变量a键盘输入。

但有没有发现,字符串类型也就是字符数组的输入,为什么不用加这个&符号就可以呢?

#include <iostream>
#include <cstdio>
using namespace std;

int main(int argc, char const *argv[]) {
    char str[] = {'\0'};
    scanf("%s", str);
    printf("%s\n",str);
    return 0;
}

因为这个字符数组可以看做成一个指针,具体的原因我们先放一放。

试想一下之前有没有这样的一个经历,也就是我们输入一个整形或者浮点型数据的时候,忘了加 & 符号,如下图,结果编译器没有报错,那是为什么呢?(现在比较新的GCC可能会给出警告之类的)

int main(int argc, char const *argv[]) {
    int a;
    scanf("%d",a);
    printf("%d\n", a);
    return 0;
}

最后输出的a肯定是不对的。

那我们现在分析一下a的地址的值,格式化输出%p可以打印地址。

int main(int argc, char const *argv[]) {
    int a;
    //scanf("%d",a);
    printf("%p\n", a);
    printf("%p\n", &a);
    return 0;
}

结果为:

在这里插入图片描述
这下我们应该明白了,原来scanf从键盘输入的值没有赋给真正a的地址,而是给了0000…这个地址位置。

现在我们理解了&符号取地址的含义(当然不要和位运算里的按位与运算弄混了)

下面我们回到刚才讲的字符数组为什么输入不需要加&。

我们先打印一下字符数组str的地址和str[0]的地址,你就会发现有意思的事情了。

代码如下:

int main(int argc, char const *argv[]) {
    char str[] = "Hello World!";
    //scanf("%s", str);
    printf("str的地址:%p\n",str);
    printf("&str的地址:%p\n",&str);
    printf("str[0]的地址:%p\n",&str[0]);
    return 0;
}

运行结果:
在这里插入图片描述

注意,我们发现str、&str和str[0]的地址是一样的。

下面我分别说明一下它们三者的含义:

str : 数组第一个元素的首地址
&str : 整个数组的地址
&str[0]: 数组第一个元素的首地址

那么这样可以得出一个结论,对于字符串类型的输入,我们也可以scanf("%s",&str[0]); 这样也是正确的。(可以去试验一下)

接下来我们来详细使用一下指针,我们定义一个指针变量p,为其赋值为NULL;

这样它就是一个空指针。

代码:

int main(){
    int* p = NULL;
    printf("%p\n", p);
    return 0;
}

运行结果

在这里插入图片描述
根据运行结果不难发现它是一个空指针,这样定义一个指针也是一个比较好的习惯,避免程序中出现野指针,也就是没有明确指向的地址的指针。

下面我们来了解一下指针的使用,可以看一下代码以及注释:

int main(){
    int* p = NULL;
    int a = 12;
    //指针 p 指向变量 a 的地址。
    p = &a;
    //对指针变量 p 进行 *也就是取值运算。
    printf("%d\n", *p);
    return 0;
}

运行结果:
在这里插入图片描述
然后我们再做这样的一个操作,对p进行赋值,那么a的值会变吗?也就是加入这样一行代码
*p = 8; 那么a的值是多少呢?如果你已经理解了地址,那么就能猜出来a的值也是8了。

代码如下:

int main(){
    int* p = NULL;
    int a = 12;
    //指针 p 指向变量 a 的地址。
    p = &a;
    //对指针变量 p 进行 *也就是取值运算。
    printf("*p = %d\n", *p);
    *p = 8;
    printf("a = %d\n", a);
    return 0;
}

运行结果:
在这里插入图片描述

下面来分析一下用的比较多的,也就是指针与一维数组:

一个一维数组在内存里面的地址是连续的,假设一个整形数组a = [1,2,3,4,5] ,其在内存中的地址是连续的,而一个int形的数组占4个字节,因此假设a数组首地址为0x00,那么其元素的地址应该是逐渐加4递增,如下图。

在这里插入图片描述
代码实现(因为%p输出的地址的数据是16进制的,比较起来不太明显,我们可以用%d输出指针的地址一样能得出结论)

int main(int argc, char const *argv[]) {
    int a[5] = {1,2,3,4,5};
    printf("%d\n", &a[0]);
    printf("%d\n", &a[1]);
    return 0;
}

运行结果:
在这里插入图片描述
下面是关于指针与一维数组遍历的相关代码(一些解释在注释里):

int main(int argc, char const *argv[]) {
    //我们定义一个指针,和一个一维数组
    int a[5] = {1,2,3,4,5};
    int* p = NULL;
    //指针p指向数组a
    p = a;
    //打印数组 a
    for(int i = 0 ; i < 5 ; i++){
        cout << a[i] << " ";
    }
    cout << endl;
    //因为指针变量 p 指向 数组a的首地址,因此p也可以看做成一个数组
    for(int i = 0 ; i < 5 ; i++){
        cout << *(p+i) << " ";
        //cout << p[i] << " ";
    }
    cout << endl;

    //另一种利用遍历数组的方式 但是这种方式会更改地址,不建议使用
    while(*p){
        cout << *p << " ";
        p++;
    }
    return 0;
}

运行结果:

在这里插入图片描述
现在我们理解了对于一个指针,如果指向了一个一维数组,可以把这个指针看做数组。

对于字符型的指针,需要注意的有一点,如果你让一个指针指向字符串常量,那么需要注意的是不要修改这个指针的值,建议用const char*
代码如下:

int main(int argc, char const *argv[]) {
    const char* p = "HITMAN47";
    printf("%s\n", p);
    return 0;
}

如果要实现一个字符指针输入字符串,那么就需要开辟内存才能够输入(如果不知道malloc函数的用法,可以去查一下C++ API帮助文档)。
代码如下:

int main(int argc, char const *argv[]) {
    char* p;
    p = (char*)malloc(sizeof(char)*100);
    scanf("%s", p);
    printf("%s\n", p);
    return 0;
}

对于一些指针的基本知识就分析到这里,当然还有其他很多知识,比如函数指针,结构体指针等。

由于篇幅太长,只分析到这里,以后需要的话再做深入分析,最好通过一些实际操作,对于一些基础的题目,尝试用指针来做,多进行分析,才能够理解指针,灵活使用指针。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值