如何理解int a = 10和 char* str = “1234“

今天继续看右值引用的东西,突然想到之前写的一篇笔记,感觉很有意思,这里抽取出来,分享给大家

如何理解int a = 10和 char* str = “1234”

在此之前,让我们来了解一下C语言程序内存管理相关知识,对于我们理解下面的程序有辅助作用。
一个大佬的文章,和我的下面阐述的观点不谋而合
1,什么是代码区、常量区、静态区(全局区)、堆区、栈区?

先来一段测试代码(应该是用的早期的codeblocks,当时下面的代码还能编译运行)

  • 实验一
int main() {
    //const限定a
    const int a = 10;
    int* b = &a;    // 在vs2017编译器中该语法错误,可能现在的编译器语法审核更加严格
    printf("0x%p\n", &a);
    *b = 100;
    printf("0x%p\n", b);
    printf("%d\n", a);
    return 0;
}
/*
result
0x0061FF10
0x0061FF10
100
*/

从实验一我们发现,通过const修饰的基本类型变量,也无法逃脱通过其他指针变量改变内容的情况。
这里有一个有趣的现象,就是无论是一开始a=10还是后面a指向的内容通过*b=100做了改变,a的地址没有变化。
我们由此不难猜想,10,100数字存储在常量区,当a=10的时候,10会将自身值复制给a所指向的空间。这样一来,也解释了a的值为什么可以改变这一问题,因为a=10,a中存储的是10的副本,当然想怎么修改就怎么修改。
但是事实是这样的吗,我们再看组实验。

  • 实验2

实验2

这里改变str指向的内容是不可行的,按照实验一的解释,不应该是常量“1234”将本身赋值一份给str吗,很显然是不正确的。
首先我们考虑,str是一个char*指针,一般情况下,只有4个字节(看你的系统寻址范围)的空间,还是用来存储一段空间的首地址的。
这里我们就开始猜想,str存储的是常量“12345678”的起始地址,因为“12345678”是常量,所以它不允许修改。
对应于实验一,为什么a指向的空间就可以修改,是因为对于基本类型,常量直接赋值。对于字符串这种非基本类型的,就以首地址赋值。

看一下下面的代码

int main() {
    char* str = "12345678";
    printf("0x%p\n", str);
    printf("%s\n", str);

    str = "12345678";
    printf("0x%p\n", str);
    printf("%s\n", str);
    //free(str);  //这里我的没发生错误,但是现实情况是不允许这么做的
    return 0;
}
/*
result
0x00405044
12345678
0x00405044
12345678
*/

这段代码的输出是不是很有意思。这验证了我们的猜想str = "12345678"赋值两次得到的地址都是一样的,也就是说str每次常量赋值,得到的都是"12345678"的首地址。如果不服气,可以看下面代码,加了一段输出

int main() {
    char* str = "12345678";
    printf("0x%p\n", str);
    printf("%s\n", str);

    str = "12345678";
    printf("0x%p\n", str);
    printf("%s\n", str);
    printf("0x%p", &"12345678");  //输出一下不就得了
    return 0;
}
/*
result:
0x00405044
12345678
0x00405044
12345678
0x00405044
*/

看到了吧&“12345678” 就是0x00405044。
和上面的输出对比,是不是又发现了好玩的东西,两次执行"12345678" 的地址都是一样的,都是0x00405044。
哈哈,发现了新大陆。这是因为"12345678"和我们的代码一样被写进了ROM中,每次加载读的都是同一个位置,因此是一样的,不相信的话,我们可以测试一个函数地址。

  • 实验三
#include <time.h>
int test(){
    return 100;
}
int main() {
    int (*p)() = test;
    //时间函数,可以无视
    time_t timep;
    struct tm *t;
    time (&timep);
    t=gmtime(&timep);
    printf("当前时间:%d-%d-%d %d:%d:%d\n",t->tm_year + 1900, t->tm_mon + 1, t->tm_mday,t->tm_hour + 8,t->tm_min,t->tm_sec);
    printf("test地址:0x%p",p);
    return 0;
}
/*
result
当前时间:2020-7-23 14:19:11
test地址:0x00401445
*/

在这里插入图片描述

在这里插入图片描述
为了具有说服力我这里将时间和图片都贴出来了,图片为两次运行地址,因为每次加载的都是同一块地址,因此地址都是一样的。

至此,结束,这里只贴出有用的代码,帮助大家理解底层。剩余的测试因为现在编译器已经将问题解决了,因此不再描述。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值