zoukankan      html  css  js  c++  java
  • 关于字符串常量的思考

      为什么我会对字符串常量这么感兴趣,源于下面这个代码:
    //blue.c
    #include<stdio.h>
    void main()
    {
     void copy_string(char from[],char to[]);
     char *a="I am a teacher.";
     char *b="You are a student.";
     printf("string a=%s string b=%s ",a,b);
     printf("copy string a to string b: ");
     copy_string(a,b);
     printf(" string a=%s string b=%s ",a,b);
     
    }
    void copy_string(char from[],char to[])
    {
     int i=0;
     while(from[i]!='')
     {
      to[i]=from[i];
      i++;
      to[i]='';
     }
    }
      这段代码能够编译通过,但在运行时却出现错误。再回过头来认真揣摩下这个代码,也没有发现逻辑上的错误。当你百思不得其解的时候,我们不妨把问题的焦点集中在这两行代码: char *a="I am a teacher.";char *b="You are a student.";这两行代码都是把字符串常量的首地址赋值给指针变量,而在copy_string函数中试图引用指针变量改写字符串常量的值,问题就出在a和b指针所指向的内容是只读不能写的,你可以print,但你不能input.当把程序加载进内存时,所有的字符串常量(用两个""括号括起来的内容)都放进.rodata(read only data)段,.rodata段和代码段都是只读不能写的内存段,所以上面的blue.c程序才会在运行时出现错误.
      当然了,蓝月我会在这里提出改正的方法,呵呵。很简单,把char *a="I am a teacher.";char *b="You are a student.";改为:char a[]="I am a teacher.";char b[]="You are a student.";再重新编译连接,就可以正常运行了。
      这时候你或者会提出另一个问题,char a[]="I am a teacher.";这行代码不是也把字符串的首地址赋值给数组变量a吗,这和指针变量有什么不同。为了方便说明问题,现在引出下面两个简单的程序:
    //hello1.c
    #include<stdio.h>
    int main()
    {
     char *a="hello world!";
     printf("%s ",a);
            return 0;
     
    }

    //hello2.c
    #include<stdio.h>
    int main()
    {
     char a[]="hello world!";
     printf("%s ",a);
     return 0;
    }
    上面两个程序都是正确的,不同的地方只有一处。hello1.c用了指针变量,hello2.c用了数组变量。但当把它们分别反汇编,查看其生成的汇编代码,就会发现相对于hello1.c程序hello2.c程序生成的汇编代码会长一些。原因是hello1.c程序是直接把存放在.rodata段的字符串常量的首地址赋值给指针变量,而hello2.c程序是先把.rodata段的字符串常量全部复制到堆栈(堆栈是当程序加载进内存时预留的内存空间),再把堆栈中的字符串常量的首地址赋值给数组变量。
      现在应该一切都明朗了。再回到上面的blue.c程序,copy_string函数之所以能通过数组变量a和b而改写字符串常量是因为它改写的是预先复制进堆栈的字符串常量而不是去改写存放在.rodata段的内存常量
      由于本人能力有限,以上所说如有差错请留言指正
      下面给出hello1.c和hello2.c在linux下的反汇编代码:
    hello1.c的如下:

     .file "hello1.c"
     .section .rodata
    .LC0:
     .string "hello world!"
    .LC1:
     .string "%s "
     .text
    .globl main
     .tybe main,@function
    main:
     pushl %ebp
     movl %esp,%ebp
     subl $8,%esp
     andl $-16,%esp
     movl $0,%eax
     subl %eax,%esp
     movl $.LC0,-4(%ebp)
     subl $8,%esp
     pushl -4(%ebp)
     pushl $.LC1
     call printf
     addl $16,%esp
     movl $0,%eax
     leave
     ret
     


    hello2.c的如下:


    .file "hello2.c"
     .section .rodata
    .LC0:
     .string "hello world!"
    .LC1:
     .string "%s "
     .text
    .globl main
     .tybe main,@function
    main:
     pushl %ebp
     movl %esp,%ebp
     pushl  %edi
     pushl %esi
     subl $16,%esp
     andl $-16,%esp
     movl $0,%eax
     subl %eax,%esp
     leal -24(%ebp),%edi
     movl $LC0,%esi
     cld
     movl $13,%ecx
     rep
     movsb
     subl $8,%esp
     leal -24(%ebp),%eax
     pushl %eax
     pushl $.LC1
     call printf
     addl $16,%esp
     movl $0,%eax
     leal -8(%ebp),%esp
     popl %esi
     popl %edi
     leave
     ret

  • 相关阅读:
    (转)-为什么分布式一定要有Redis?
    (九)redis使用lua脚本
    (八)redis实现分布式锁
    (七)面试题-Redis比较常见的面试题-转载
    (六)redis缓存穿透,击穿,雪崩以及解决方案
    (五)redis的主从复制
    (四)redis的数据持久化-RDB,AOF
    (三)redis的其他功能-Bitmap,HyperLogLog,GEO
    JsTracker
    Web开发者助手 FeHelper
  • 原文地址:https://www.cnblogs.com/Blue-Moon/p/3159668.html
Copyright © 2011-2022 走看看