V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
rookiemaster
V2EX  ›  C

char *s = "0123"和 char s[] = "0123"的区别

  •  
  •   rookiemaster · 246 天前 · 3266 次点击
    这是一个创建于 246 天前的主题,其中的信息可能已经有所发展或是发生改变。

    为什么前者不可以修改,后者可以修改,是因为存储区域的问题吗?

    22 条回复    2024-03-30 22:26:19 +08:00
    huang119412
        1
    huang119412  
       246 天前   ❤️ 2
    因为 char s[] = "0123",是语法糖。"0123"是常量字符串,不能修改。但是使用字符数组编译器会把"0123"在栈上复制一份,并把首地址赋值给数组名。
    tuwulin365
        2
    tuwulin365  
       246 天前
    throcean
        4
    throcean  
       246 天前
    前面在编译的时候就会生成字符串常量在静态常量区(好像是这个区,有点忘了),后者是栈上的一个字符数组
    tzxxxx
        5
    tzxxxx  
       246 天前
    https://www.gnu.org/software/c-intro-and-ref/manual/html_node/String-Constants.html
    这个链接中说:The string constant is probably stored in a read-only area of memory.
    似乎还没有说字符串常量一定在只读内存中,不清楚什么场景下会出现。
    qq135449773
        6
    qq135449773  
       246 天前   ❤️ 7
    @tuwulin365 #2

    你不想回答可以不回答,没必要在这里展示你作为 IT 工作者还使用百度这个搞笑的事实。
    leonshaw
        7
    leonshaw  
       246 天前
    两个都可以修改,一个是指针,一个是数组。
    liuidetmks
        8
    liuidetmks  
       246 天前   ❤️ 2
    https://godbolt.org/z/T9d38EWTq

    这个网站挺好,我也是在 v 站看到的
    DiamondY
        9
    DiamondY  
       246 天前
    @tzxxxx #5 单片机会有这样处理方式,字符串直接存在闪存中,cpu 可以通过地址总线读取,但是不能直接进行写入操作
    oIMOo
        10
    oIMOo  
       246 天前
    @liuidetmks #8 这个网站好哎 不过汇编都还给老师了哈哈哈
    hefish
        11
    hefish  
       246 天前
    我写 c 的时候,一直是认为一样的,不同写法而已。
    不过那已经是三十年前的事儿了。
    outgoing4443
        12
    outgoing4443  
       246 天前
    第一种指针 s 指向一个字符串常量"0123",第二种方法定义了一个字符数组 s ,初始化为字符串"0123"
    Nosub
        13
    Nosub  
       246 天前 via iPhone
    你需要的是一本《 c primer plus 》,更进一步如果你要搞清楚作用域和内存分配的问题,可以看看《征服 c 指针》,少问人,多读书
    MrKrabs
        14
    MrKrabs  
       246 天前
    一个栈上放指针,一个直接放数据
    lff0305
        15
    lff0305  
       246 天前
    十多年以前仔细研究过这个 char *s = "0123"。当时用的还是 VC6.
    结果比较有意思(针对 VC6 ,之后的应该改进了)。

    对于 Debug 版,“0123” 会被放到只读数据段里面(好像是叫.rdata). char* s 是个指向这个只读数据段中一小段数据的一个指针。任何对这个数据段的写操作都会引发段错误。

    相应的这避免了两次调用相同的函数,结果不一致的问题。
    比如下面的代码,假设可写,那么会有

    test() {
    char*s = "123";
    printf("%s", s);
    s[1] = 'a';
    }

    test();
    test(); <--- 这里打印出来 1a3 ,和程序的语义不一致。

    有意思的是上面的代码在 Release 版是没有出错的,打印出来两行 123 和 1a3 。
    当时看了下的结果是,
    对于 Debug 版,生成的 exe 有三个段,代码段(可执行),只读数据段(只读),普通数据段(可读可写)。
    而对于 Release 版,连接器把所有三个段捏到一起去了,整个 exe 文件只有一个段,这个段可读可写可执行。

    总之这个问题和系统的段页式内存管理,以及编译器连接器都有关系
    FaiChou
        16
    FaiChou  
       246 天前 via iPhone
    新手确实要过这一关,一楼解释的就很好。
    jaycelhz
        17
    jaycelhz  
       246 天前
    我之前也被还有 char **s 和 char *[]s 搞晕
    xuanbg
        18
    xuanbg  
       246 天前
    指针怎么就不能修改了?
    zooo
        19
    zooo  
       246 天前
    插个题外话,不喜勿喷

    都 2024 年,C 语言这套古怪遗留语言表达该扔垃圾箱了
    Inn0Vat10n
        20
    Inn0Vat10n  
       246 天前   ❤️ 1
    @qq135449773 可这个问题,确实百度都能直接找到答案
    wkla
        21
    wkla  
       246 天前
    C++会提示"const char *" 类型的值不能用于初始化 "char *" 类型的实体。C 不会。
    不过 C 会把这个字符串存只读段里,所以写的话会报段错误。

    我说怎么怪怪的,越看越不对劲。只能说这套该扔就扔吧。
    arloor
        22
    arloor  
       245 天前 via Android
    //main.cpp
    int a = 0; 全局初始化区
    char *p1; 全局未初始化区
    main()
    {
    int a = 4; 栈,4 也是存在栈上
    char s[] = "abc"; 栈 "abc"也是存在栈上
    char *p2; 栈
    char *p3 = "123456"; 123456\0 在常量区(是在 Data 段上),p3 在栈上。
    static int c =0 ; 全局(静态)初始化为 0,就是放在 BSS 段
    p1 = (char *)malloc(10);
    p2 = (char *)malloc(20);
    malloc 分配得来得 10 和 20 字节的区域就在堆区。因为属于动态申请分配内存空间
    strcpy(p1, "123456"); 123456\0 放在常量区,编译器可能会将它与 p3 所指向的"123456"优化成一个地方。
    }
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2603 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 21ms · UTC 06:30 · PVG 14:30 · LAX 22:30 · JFK 01:30
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.