精华内容
下载资源
问答
  • 结构体中字符串赋值

    万次阅读 多人点赞 2016-06-26 23:49:05
    #include using namespace std; struct student { int num; char name[10]; }; int main() { student st; st.num = 10; //st.name = "linjunjie... //字符串用=赋值 strcpy(st.n
    
    #include <iostream> 
    using namespace std; 
    
    struct student 
    { 
      int num; 
      char name[10]; 
    }; 
    
    int main() 
    { 
      student st; 
      st.num = 10; 
      //st.name = "linjunjie"; //字符串不能用=赋值
      strcpy(st.name, "linjunjie");
      return 0; 
    }
    
    


    结构体中string成员变量赋值

    #include<string>
    using namespace std;
    struct temp
    {
    string s;
    };
    int main()
    {
    const string p="aaa";
    temp *q;
    q=(struct temp*)malloc(sizeof(struct temp));
    q->s=p;
    }


      上述那种情况的赋值会导致程序中断
      需要用用new来分配内存,malloc不会调用结构函数
      结构体内的string不定长,不能动态分配内存。

    #include<string>
    using namespace std;
    struct temp
    {
    string s;
    };
    int main()
    {
    const string p="aaa";
    temp *q;
    //q=(struct temp*)malloc(sizeof(struct temp));
    q = new temp;
    q->s=p;
    

      C++的结构体和类都有默认构造函数的,不写都会自动实现一个。
      malloc只是分配内存。
      new除了分配内存还会调用构造函数的。

    c语言结构体指针初始化

    今天来讨论一下C中的内存管理。

    记得上周在饭桌上和同事讨论C语言的崛起时,讲到了内存管理方面
    我说所有指针使用前都必须初始化,结构体中的成员指针也是一样
    有人反驳说,不是吧,以前做二叉树算法时,他的左右孩子指针使用时难道有初始化吗
    那时我不知怎么的想不出理由,虽然我还是坚信要初始化的

    过了几天这位同事说他试了一下,结构体中的成员指针不经过初始化是可以用(左子树和右子树指针)
    那时在忙着整理文档,没在意
    今天抽空调了一下,结论是,还是需要初始化的。
    而且,不写代码你是不知道原因的(也许是对着电脑久了IQ和记性严重下跌吧)
    测试代码如下

     

    C代码 复制代码
    1. #include    
    2. #include    
    3. #include    
    4.   
    5. struct student{   
    6.   char *name;   
    7.   int score;   
    8.   struct student* next;   
    9. }stu,*stu1;    
    10.   
    11. int main(){    
    12.   stu.name = (char*)malloc(sizeof(char)); /*1.结构体成员指针需要初始化*/  
    13.   strcpy(stu.name,"Jimy");   
    14.   stu.score = 99;   
    15.   
    16.   stu1 = (struct student*)malloc(sizeof(struct student));/*2.结构体指针需要初始化*/  
    17.   stu1->name = (char*)malloc(sizeof(char));/*3.结构体指针的成员指针同样需要初始化*/  
    18.   stu.next  = stu1;   
    19.   strcpy(stu1->name,"Lucy");   
    20.   stu1->score = 98;   
    21.   stu1->next = NULL;   
    22.   printf("name %s, score %d \n ",stu.name, stu.score);   
    23.   printf("name %s, score %d \n ",stu1->name, stu1->score);   
    24.   free(stu1);   
    25.   return 0;   
    26. }  
    #include 
    #include 
    #include 
    
    struct student{
      char *name;
      int score;
      struct student* next;
    }stu,*stu1; 
    
    int main(){     
      stu.name = (char*)malloc(sizeof(char)); /*1.结构体成员指针需要初始化*/
      strcpy(stu.name,"Jimy");
      stu.score = 99;
    
      stu1 = (struct student*)malloc(sizeof(struct
    student));/*2.结构体指针需要初始化*/
      stu1->name =
    (char*)malloc(sizeof(char));/*3.结构体指针的成员指针同样需要初始化*/
      stu.next  = stu1;
      strcpy(stu1->name,"Lucy");
      stu1->score = 98;
      stu1->next = NULL;
      printf("name %s, score %d \n ",stu.name, stu.score);
      printf("name %s, score %d \n ",stu1->name, stu1->score);
      free(stu1);
      return 0;
    }
    



    写测试代码的过程中我明白了,同事所说的二叉树遍历算法中所用的左子树和右子树指针不需要初始化,其实是这样的,左子树和右子树指向的必须是二叉树节点类型的结构体指针(你填一个长度相同的指针也可以),而该结构体指针是需要初始化的(见注释2),也就是并没有通过malloc来分配内存,而是将另一个指针的值赋给它

    顿时觉得挺无语的,确实,看了很多大学里的教材,对于二叉树的遍历等算法定义的结构体无非是以下形式
     

    C代码 复制代码
    1. struct node{   
    2.   int data;   
    3.   struct node* lchild, rchild;   
    4. };  
    struct node{
      int data;
      struct node* lchild, rchild;
    };
    


    使用时都直接的
     

    C代码 复制代码
    1. struct node* root;   
    2.  root = (struct node*)malloc(sizeof(struct node));   
    3.  root->data = 3;   
    4.   
    5.  struct node* nlchild;   
    6.  nlchild = (struct node*)malloc(sizeof(struct node));   
    7.  root->lchild = nlchild;   
    8.  nlchild->data = 2;    
    9.   
    10.  struct node* nrchild;   
    11.  nlrchild = (struct node*)malloc(sizeof(struct node));   
    12.  root->rchild = nrchild;   
    13.  nrchild->data = 4;   
     struct node* root;
      root = (struct node*)malloc(sizeof(struct node));
      root->data = 3;
    
      struct node* nlchild;
      nlchild = (struct node*)malloc(sizeof(struct node));
      root->lchild = nlchild;
      nlchild->data = 2; 
    
      struct node* nrchild;
      nlrchild = (struct node*)malloc(sizeof(struct node));
      root->rchild = nrchild;
      nrchild->data = 4; 
    


    这样子给人造成一种错觉好像结构体成员指针是不用初始化的。

    可是,只要是指针,要使用它前就必须保证指针变量的值是一个有效的值;否则,它指向的内存一定是垃圾数据!
    C语言的内存管理很重要,集魄力和麻烦于一身,看你自己的心态如何了。如果你积极的面对,你正在控制一切;如果你觉得烦躁,你正不得不控制一切。C仍旧是博大精深的语言,信C哥!

    /*附加:仍旧是指针*/
     

    C代码 复制代码
    1. stu1 = (struct student*)malloc(sizeof(struct student));/*2.结构体指针需要初始化*/  
      stu1 = (struct student*)malloc(sizeof(struct
    student));/*2.结构体指针需要初始化*/
    


    这一句可能会有人把sizeof里边也填成struct student*
    可以理解这样的行为,因为stu本来就是struct student*,可是这样子你就没有为结构体分配足够的内存,使用中会因为内存错误同样报错的。
    当然,仅仅为结构体指针分配内存还不够,结构体成员指针仍然需要分配内存,如下
     

    C代码 复制代码
    1. stu1->name = (char*)malloc(sizeof(char));  

     

     

     

     

     

     

     

     

     

     

    自己在用结构体指针的时候遇到的引用问题,网上找的一段文字觉得挺不错的,可能对大家有帮助。

    在使用结构体指针变量的时候,往往容易犯一个“低级”错误。即定义一个结构体指针变量后就直接对结构体指针变量所指向的结构体成员进行操作,从而产生一些莫名其妙的错误。我们必须要给结构体指针变量赋予一个有效的结构体变量地址,才能正常操作结构体指针变量。比如:

    struct UART{

                 int a;

                 uchar b;

              }

    main()

    {

          struct UART  *p;

          p->a = 0xXXX;

          p->b = 0xXX;

         printf("%i,%c",p->b,p->a);

    }

    这个程序输出的值将是不可预知的,因为“在程序中只是定义了一个结构体指针变量,并没有给该结构体指针变量赋一个有效值,因此该结构体变量所指向的地址将不确定,从而不能得到预期结果”

    应该改为:

    struct UART{

                 int a;

                 uchar b;

           }

    main()

    {

          struct UART  *p;

         struct UART dd;

          p = &dd;               //这句一定要有,否则将出现不可预知的问题

          p->a = 0xXXX;

          p->b = 0xXX;

         printf("%i,%c",p->b,p->a);

    }

     

     

    C/C++中

     

    结构体(struct)知识点强化 为了进一部的学习结构体这一重要的知识点,我们今天来学习一下链表结构。

      结构体可以看做是一种自定义的数据类型,它还有一个很重要的特性,就是结构体可以相互嵌套使用,但也是有条件的,结构体可以包含结构体指针,但绝对不能在结构体中包含结构体变量。

       struct test
       {
       char name[10];
       float socre;
       test *next;
       };//这样是正确的!
       struct test
       {
       char name[10];
       float socre;
       test next;
       };//这样是错误的!

       利用结构体的这点特殊特性,我们就可以自己生成一个环环相套的一种射线结构,一个指向另一个。

      链表的学习不像想象的那么那么容易,很多人学习到这里的时候都会碰到困难,很多人也因此而放弃了学习,在这里我说,一定不能放弃,对应它的学习我们要进行分解式学习,方法很重要,理解需要时间,不必要把自己逼迫的那么紧,学习前你也得做一些最基本的准备工作,你必须具备对堆内存的基本知识的了解,还有就是对结构体的基本认识,有了这两个重要的条件,再进行分解式学习就可以比较轻松的掌握这一节内容的难点。

      下面我们给出一个完整的创建链表的程序,不管看的懂看不懂希望读者先认真看一下,想一想,看不懂没有关系,因为我下面会有分解式的教程,但之前的基本思考一定要做,要不即使我分解了你也是无从理解的。

       代码如下,我在重要部分做了注解:

       #include
       using namespace std;

       struct test
       {
       char name[10];
       float socre;
       test *next;
       };

       test *head;//创建一个全局的引导进入链表的指针

       test *create()
       {
       test *ls;//节点指针
       test *le;//链尾指针
       ls = new test;//把ls指向动态开辟的堆内存地址
       cin>>ls->name>>ls->socre;
       head=NULL;//进入的时候先不设置head指针指向任何地址,因为不知道是否一上来就输入null跳出程序
       le=ls;//把链尾指针设置成刚刚动态开辟的堆内存地址,用于等下设置le->next,也就是下一个节点的位置

       while(strcmp(ls->name,"null")!=0)//创建循环条件为ls->name的值不是null,用于循环添加节点
       {
       if(head==NULL)//判断是否是第一次进入循环
       {
       head=ls;//如果是第一次进入循环,那么把引导进入链表的指针指向第一次动态开辟的堆内存地址
       }
       else
       {
       le->next=ls;//如果不是第一次进入那么就把上一次的链尾指针的le->next指向上一次循环结束前动态创建的堆内存地址
       }
       le=ls;//设置链尾指针为当前循环中的节点指针,用于下一次进入循环的时候把上一次的节点的next指向上一次循环结束前动态创建的堆内存地址
       ls=new test;//为下一个节点在堆内存中动态开辟空间
       cin>>ls->name>>ls->socre;
       }

       le->next=NULL;//把链尾指针的next设置为空,因为不管如何循环总是要结束的,设置为空才能够在循环显链表的时候不至于死循环
       delete ls;//当结束的时候最后一个动态开辟的内存是无效的,所以必须清除掉
       return head;//返回链首指针
       }

       void showl(test *head)
       {
       cout<<"链首指针:"< <
       while(head)//以内存指向为null为条件循环显示先前输入的内容
       {
       cout< name<<"|"< socre<
       head=head->next;
       }
       }

       void main()
       {
       showl(create());
       cin.get();
       cin.get();
       }
       上面的代码我们是要达到一个目的:就是要存储你输入的人名和他们的得分,并且以链状结构把它们组合成一个链状结构。

    程序种有两个组成部分
       test *create()
       和 void showl(test *head)
       这两个函数,create是用来创建链表的 ,showl是用来显示链表的。

       create函数的返回类型是一个结构体指针,在程序调用的时候我们用了showl(create());,而不用引用的目的原因是引导指针是一个全局指针变量,我们不能在showl()内改变它,因为showl()函数内有一个移动操作head=head->next;,如果是引用的话我们就破坏了head指针的位置,以至于我们再也无法找会首地址的位置了。

       下面我们来分解整个程序,以一个初学者的思想来思考整个程序,由浅入深的逐步解释。

      首先,我们写这个程序,要考虑到由于是一个链表结构,我们不可能知道它的大小到底是多大,这个问题我们可以用动态开辟堆内存来解决,因为堆内存在程序结束前始终是有效的,不受函数栈空间生命期的限制,但要注意的是我们必须有一个指针变量来存储这一链状结构的进入地址,而在函数内部来建立这一指针变量显然是不合适的,因为函数一旦退出,这个指针变量也随之失效,所以我们在程序的开始声明了一个全局指针变量。

       test *head;//创建一个全局的引导进入链表的指针
       好解决了这两个问题,我们接下去思考

      有输入就必然有输出,由于输出函数和输入函数是相对独立的,为了不断测试程序的正确性好调试我们先写好输出函数和main函数捏的调用,创建函数我们先约定好名为create。

       我们先写出如下的代码:

       #include
       using namespace std;

       struct test
       {
       char name[10];
       float socre;
       test *next;
       };

       test *head;//创建一个全局的引导进入链表的指针

       test *create()
       {

       return head;//返回链首指针
       }

       void showl(test *head)
       {
       cout<<"链首指针:"< <
       while(head)//以内存指向为null为条件循环显示先前输入的内容
       {
       cout< name<<"|"< socre<
       head=head->next;
       }
       }

       void main()
       {
       showl(create());
       cin.get();
       cin.get();
       }
       程序写到这里,基本形态已经出来,输入和调用我们已经有了。

      下面我们来解决输入问题,链表的实现我们是通过循环输入来实现的,既然是循环我们就一定得考虑终止循环的条件,避免死循环和无效循环的发生。

       在create()函数内部我们先写成这样:

       test *create()
       {
       test *ls;//节点指针
       test *le;//链尾指针
       ls = new test;//把ls指向动态开辟的堆内存地址
       cin>>ls->name>>ls->socre;
       head=NULL;//进入的时候先不设置head指针指向任何地址,因为不知道是否一上来就输入null跳出程序
       le=ls;//把链尾指针设置成刚刚动态开辟的堆内存地址,用于等下设置le->next,也就是下一个节点的位置

       le->next=NULL;//把链尾指针的next设置为空,因为不管如何循环总是要结束的,设置为空才能够在循环显链表的时候不至于死循环
       delete ls;//当结束的时候最后一个动态开辟的内存是无效的,所以必须清除掉
       return head;//返回链首指针
       }
       在循环创建之前我们必须考虑一个都不输入的情况。

      程序一单进入create函数我们首先必然要创建一个节点,我们先创建一个节点指针,后把者个节点指针指向到动态开辟的test类型的动态内存地址位置上。

       test *ls;
       ls = new test;
       程序既然是循环输入,而结构成员test *next又是用来存储下一个接点的内存地址的,每次循环我们又要动态创建一个新的内存空间,所以我们必须要有一个指针来存储上一次循环动态开辟的内存地址,于是就有了

       test *le;
       接下来在进入循环前我们要创建链表的第一个节点,第一个节点必然是在循环外创建,于是就有了

       cin>>ls->name>>ls->socre;
      程序执行者的情况是位置的,所以我们必然要考虑,一上来就不想继续运行程序的情况,所以我们一开始先把head引导指针设置为不指向任何地址也就是

       head=NULL;

      为了符合le也就是链尾指针的设计思路,我们在循环前一定要保存刚刚动态开辟的内存地址,好在下一次循环的时候设置上一个节点中的next成员指向,于是我们便有了:

       le=ls;
       为了实现循环输入我们又了下面的代码:

     

     

    转自:http://www.myexception.cn/cpp/1501569.html

    http://www.cnblogs.com/losesea/archive/2012/11/15/2772526.html

    展开全文
  • 对于这个声明方式,会造成的误解是:声明了一个字符指针(它会指向一个位置),将“字符串赋值给 指针表达式"*a"所指向的地址。但正解是:声明了一个字符指针后,并用字符串常量的第一个字符的地址赋值给指针变量a...

    对于语句  char *a="hello";

    对于这个声明方式,会造成的误解是:声明了一个字符指针(它会指向一个位置),将“字符串”赋值给 指针表达式"*a"所指向的地址。但正解是:声明了一个字符指针后,并用字符串常量的第一个字符的地址赋值给指针变量a。
    即正确顺序是:

    • 1.分配内存给字符指针;
    • 2.分配内存给字符串;
    • 3.将字符串首地址赋值给字符指针;

    这里有两点需要考虑清楚的地方:

    *a只是指向一个字符。举例如下:

    #include <iostream>
    #include <string>
    using namespace std;
    
    
    int main() {
    
        char *a ="abcdefg";
        cout << "输出字符: " << *a << endl;
        cout << "第二次输出字符: " << *(a+1) << endl;
        cout << "输出字符串: " << a << endl;
    
        return 0;
    }

    结果如下

    输出字符: a
    第二次输出字符: b
    输出字符串: abcdefg

     若字符串常量出现在在表达式中,代表的值为该字符串常量的第一个字符的地址。所以”hello”仅仅代表的是其地址。 
    原声明方式相当于以下声明方式:

    char *a;  
    a="hello";/*这里字符串"hello"仅仅代表其第一个字符的地址*/  

     

    1.但还是不明白为什么字符串可以赋值给字符指针变量

     

    char *p,a='5';
    p=&a;                     //显然是正确的,
    p="abcd";              //但为什么也可以这样赋值??

    双引号做了3件事:  
    1.申请了空间(在常量区),存放了字符串 
    2. 在字符串尾加上了'/0'    
    3.返回地址
    返回的地址,赋值给了指针变量p    

    2.以char *p = “hello”为例,把p换成数组,然后再赋值就不行了

    字符串常量"hello"出现在一个表达式中时,"hello"表达式使用的值就是这些字符所存储的地址(在常量区),而不是这些字符本身。

    所以,可以把字符串赋值给指向字符的指针p,而不能把字符串赋值给一个字符数组。

    char a[10] = “hello”; //这样可以,这种情况是c语言初始化所支持的

    如果写成char a[10]

    然后 a = “hello” 这样就错误了。

    因为同样是a数组,char a[10] = “hello”;这种是数组的初始化,和a[0] = ‘h’ a[1] = ‘e’…是一个道理

    但是换成char a [10],然后a = “hello”是不行的,“hello”赋值的值是一个地址,而a虽然也有地址,与指针不同,指针的值是地址,而数组的值虽然同为地址,却是一个常量,不能给常量赋值

    代码测试

    #include <iostream>
    #include <string>
    
    using namespace std;
    
    int main() {
        char *p = "hello";
        cout << p << endl;
        char a[10];
        a = "hello";
        return 0;
    }

    报错 error C3863: array type 'char [10]' is not assignable

    而修改后,正常运行

    #include <iostream>
    #include <string>
    
    using namespace std;
    
    int main() {
        char *p = "hello";
        cout << p << endl;
        char a[10] = "hello"; //数组初始化
        return 0;
    }

     

     

    字符数组,字符指针,字符串常量 知识回顾

    1.以字符串形式出现的,编译器都会为该字符串自动添加一个0作为结束符,如在代码中写 "abc",那么编译器帮你存储的是"abc\0"

    2."abc"是常量吗?
    1.当作为字符数组初始值的时候,"abc"不是常量

    char str[] = "abc";

    因为定义的是一个字符数组,所以就相当于定义了一些空间来存放"abc",而又因为字符数组就是把字符一个一个地存放的,所以编译器把这个语句解析为

    char str[4] = {'a','b','c','\0'};

    2.当赋值给 字符指针变量的时候:如char* ptr = "abc" 此时是常量

    char* ptr = "abc" //规范不允许这种c的写法

    规范写法

    const char* ptr = "abc";

    因为定义的是一个普通字符指针,并没有定义空间来存放"abc",所以编译器得帮我们 找地方来放"abc",显然,把这里的"abc"当成常量并把它放到程序的常量区是编译器 最合适的选择。简言之,如果char* ptr = "abc";写在函数体内,那么虽然这里的"abc\0"被 放在常量区中,但是ptr本身只是一个普通的指针变量,所以ptr(指针)是被放在上的, 只不过是它所指向的东西(值)被放在常量区罢了

    3.数组的类型是由该数组所存放的东西的类型以及数组本身的大小决定的

    如char s1[3]和char s2[4],s1的类型就是char[3],s2的类型就是char[4], 也就是说尽管s1和s2都是字符数组,但两者的类型却是不同的

    4.字符串常量的类型可以理解为相应字符常量数组的类型

    如"abcdef"的类型可以看成是const char[7]

    5.sizeof是用来求类型的字节数的。

    如int a;那么无论sizeof(int)或者是sizeof(a)都 是等于4,因为sizeof(a)其实就是sizeof(type of a)

    6.对于函数参数列表中的以数组类型书写的形式参数,编译器把其解释为普通 的指针类型

     

    void func(char sa[100],int ia[20],char *p) 
    
    则sa的类型为char*
    ia的类型为int*
    p的类型为char*

     

    7.根据上面的总结,来实战一下

    1)对于char str[] = "abcdef";就有sizeof(str) == 7,因为str的类型是char[7] str本身可变

    2)也有sizeof("abcdef") == 7,因为"abcdef"的类型是const char[7] 字符串常量

    3)对于char *ptr = "abcdef";就有sizeof(ptr) == 4,因为ptr的类型是char*,即

    
    
    #include <iostream>
    #include <string>
    using namespace std;
    int main() {
    
        char *p = "hello";
        cout << sizeof(p) << endl;  //4
        cout << sizeof(char *) << endl; //4
        return 0;
    }

     

    4)对于char str2[10] = "abcdef";就有sizeof(str2) == 10,因为str2的类型是char[10]

    5)对于void func(char sa[100],int ia[20],char *p);

     

    sizeof(sa) == sizeof(ia) == sizeof(p) == 4

     

    因为前面有说过编译器把数组类型的书写的形参,解释为普通的指针类型

    sa的类型是char*, ia的类型是int*,p的类型是char*

     

     

     

     

     

     

     

     

     

    对于C/C++中的 字符指针和字符数组,总是在碰到的时候无法确定而不得不现场测试,来确定末尾是否包含'\0',函数到底如何使用等等。真是劳民伤财,现在总结一下:

    字符指针的赋值

    (1)指向一个字符串常量

    char *src = "abcded"; //这种方式由系统自动给该字符指针指定了一个内存中的位置,并且该位置的字符数组为{'a', 'b', 'c', 'd', 'e', 'd', '\0'};

    如果此时再次对 src赋值,src = "mmmt", 则src指向另外一个由系统指定的内存块(由"mmmt"本身返回)。这种方式赋值的src为一个指向字符串常量指针,不能对src指向的位置的内容做改变的操作,即不能执行 *src = 'a', *(src+1) = 't' 等操作;但是可以改变src指向的位置,即像之前的 src = "mmmt";

    (2)指向一个字符数组

    char tmp[4] = {'a', 'c', 'e', 'f'};

    char* src = tmp;  

    (3)使用  new,然后可以像字符数组一样赋值,即指向一个字符数组

    char* src = new char[10]; //这种方式由程序在堆内存上开辟了一个数组,并将地址赋值给src

     

     

     

    字符串常量和字符数组比较

     

    (1)字符串常量由系统自动分配一个内存区域,且该区域中的内容不能改变(即无法通过指向该字符串的指针进行修改);

     

    (2)字符数组或者为系统自动分配的全局数据区或栈上的内存,或者通过new操作来分配的堆上的内存,字符数组中的内容可变(即可以通过指向该字符数组的指针进行修改)

     

    (3)字符数组中不默认含有'\0',除非明确赋值,而字符串常量在末尾自动含有 '\0'.

     

     

     

    strcpy的使用

    (1)用strcpy时候, 如果源字符串是一个字符指针,则没有问题,因为字符指针自动带'\0',在'\0'位置复制结束;

      而如果源是一个字符数组(即将字符数组转换为字符指针来使用),则将会从字符数组的首地址开始复制,如果字符数组中明确指定了'\0'元素,则会在'\0'处停止,而若没有'\0'元素,则程序可能会不停的复制,直到在程序的内存中碰到'\0',这样可能会得到不希望的结果。

     

    展开全文
  • 1.定义时用字符串赋值 char s[15] = "helloworld"; 不先定义,然后赋值,如: char s[10]; s = “helloworld”; 原因是s是指向一个栈区数组首元素的指针,不给指针赋上字符串。 注意!C语言中字符串以字符 ‘\0...

    字符串的输入与赋值是非常容易搞混淆的一块,这里总结一下顺序存储的字符串输入方式:

    一、栈内静态存储

    1.定义时用字符串赋值

    char s[15] = "helloworld";
    

    不能先定义,然后赋值,如:

    char s[10];
    s = “helloworld”;

    原因是s是指向一个栈区数组首元素的指针,不能给指针赋上字符串。
    注意!C语言中字符串以字符 ‘\0’ 结尾,所以实际上上述数组存储的是:h,e,l,l,o,w,o,r,l,d,\0。看到的有10个字符,实际上还加上一个结尾 \0 表示串的终结,长度为11 。
    若定义的数组时s[10],打印出来会在helloword后添加若干乱码,而且每次乱的都不一样。就是因为没读到终结符 \0 。

    2.对数组中的字符逐个赋值

    char s[15]={'h','e','l','l','o','w','o','r','l','d'};
    

    3.使用strcpy()函数

    char s[15];
    strcpy(a, "helloworld");
    

    4.从终端读入字符串
    scanf:

    char s[15];
    scanf("%s", s);
    

    这里的s是读入的整个一行,遇到空白字符,包括空格,制表符,换行符时会停止输入。
    gets:

    char s[15];
    gets(s);
    

    s同样代表一行,但是只在遇到换行符的时候停止读入。

    二、堆中动态分配

    和栈中类似,声明时有区别,其他都类似,因为不论堆还是栈,本质上还是顺序存储。

    char* s = (char*)malloc(sizeof(char) * 15); 
    strcpy(s, "helloworld");
    

    强调一下,这里同样不允许这种赋值方式:
    char* s = (char*)malloc(sizeof(char) * 15);
    s = “helloworld”;

    因为本质上字符串还是存在数组中,s是指向数组首元素的指针。就算是*s也是指该数组的第一个元素s[0]。

    展开全文
  • C++内存分配及字符串赋值

    千次阅读 2013-03-02 16:38:52
    并由此解释在用g++编译时,字符串常量赋值给字符指针类型时提示“warning:deprecated conversion from string constant to 'char *'”警告的原因。 一、C++内存分配  C++编译器将应用程序的内存空间分成四个...

           本文介绍在Ubuntu12.10g++环境下C++内存分配问题。并由此解释在用g++编译时,字符串常量赋值给字符指针类型时提示warningdeprecated conversion from string constant to 'char *'警告的原因

    一、C++内存分配

           C++编译器将应用程序的内存空间分成四个部分,从内存低地址开始依次为:代码和常量区(用于存储只读的代码数据和常量值)、全局变量和静态变量区(用于存储全局变量和静态变量)、堆区(用于保存newmalloc申请的内存)、栈区(用于保存函数返回地址、局部变量等)。

    我们将用如下代码来测试我们的假设:

    #include <iostream>
    #include <stdio.h>
    #include <string.h>
    #include <malloc.h>
    
    using namespace std;
    
    int quanju;/*全局变量,全局区/静态区(static)*/
    void fun(int f_jubu); /*代码区*/
    int main(void)
    {
           int m_jubu;/*栈区(stack)*/
           static int m_jingtai;/*静态变量,全局区/静态区(static)*/
           const int conInt = 5;/*main中的局部变量,位于栈区,被const修饰,变量值不可变*/
           static const int sconInt = 6;/*常量,位于常量区*/
           char *m_zifum,*m_zifuc = "hello";/*指针本身位于栈。指向字符串"hello",位于代码区/常量区*/
           char* zifuc_hw = "hello world";/*字符串"hello world",位于常量区*/	
           
           /*正确的赋值方式*/
           char *zif = (char*)malloc(sizeof(char) * 6);
           strcpy(zif, "hello");
           cout<<zif<<endl;
           cout<<m_zifuc<<endl;
    
           void (*pfun)(int); /*栈区(stack)*/
           int (*pmain)(void);
           pfun=&fun;
           pmain = &main;
           m_zifum = (char *)malloc(sizeof(char)*10);/*指针内容指向分配空间,位于堆区(heap)*/
           pfun(1);
           cout<<"全局变量		:"<<&quanju<<endl;
           cout<<"main静态变量		:"<<&m_jingtai<<endl;
           cout<<"main字符串常量(hello)	:"<<(void*)m_zifuc<<endl;
           cout<<"main字符串常量(h w)	:"<<(void*)zifuc_hw<<endl;
           cout<<"main局部变量(const修饰):"<<&conInt<<endl;
           cout<<"main静态整型常量	:"<<&sconInt<<endl;
           cout<<"fun函数代码地址		:"<<(void*)pfun<<endl;
           cout<<"main函数代码地址	:"<<(void*)pmain<<endl;
           cout<<"main字符串(堆分配)	:"<<(void*)m_zifum<<endl;
           cout<<"main函数函数指针变量	:"<<&pfun<<endl;
           cout<<"main局部变量		:"<<&m_jubu<<endl;
           cout<<"main局部变量		:"<<&m_zifuc<<endl;
           cout<<"main局部变量		:"<<&m_zifum<<endl;
           return 0;
    }
    void fun(int f_jubu)
    {
           static int f_jingtai;
           char* f_zifuc = "hello";
           cout<<"fun静态变量		:"<<&f_jingtai<<endl;/*静态变量,位于静态区*/
           cout<<"fun字符串常量(hello)	:"<<(void*)f_zifuc<<endl;/*常量,位于常量区,同main中的“hello”地址相同*/
           cout<<"fun形参变量		:"<<&f_jubu<<endl;/*栈区(stack),但是与主函数中m_jubu位于不同的栈*/
           cout<<"fun局部变量		:"<<&f_zifuc<<endl;
    }

    实验结果:

    hello

    fun字符串常量(hello :     0x8048c58

    main字符串常量(hello :  0x8048c58

    main字符串常量(hw :     0x8048c5e

    main静态整型常量 :         0x8048e64

    main函数代码地址 :         0x80487dc

    fun函数代码地址 :           0x8048a73

    fun静态变量 :                 0x804a0f8

    main静态变量 :               0x804a0fc

    全局变量 :                      0x804a0f0

    main字符串(堆分配) :     0x9ed0008

    fun形参变量 :                 0xbfe0e8b0

    fun局部变量 :                 0xbfe0e89c

    main函数函数指针变量 : 0xbfe0e8d4

    main局部变量 :              0xbfe0e8c8

    main局部变量 :              0xbfe0e8d0

    main局部变量 :              0xbfe0e8cc

    main局部变量(const修饰) :0xbfe879d8

    二、堆区和栈区的比较

           两个区的主要区别在于以下几个方面:

    1、管理方式不同;

    2、空间大小不同;

    3、能否产生碎片不同;

    4、生长方向不同;

    5、分配方式不同;

    6、分配效率不同;    

           栈是一块连续的内存空间,具有后进先出的特点因此不存在内存碎片,而且计算机底层直接提供支持,分配效率高。无需程序员管理,编译器会自动申请和释放。

           堆的管理方式一般是采用链表管理可用的内存块,当用户申请一块内存时,系统从可用内存块中查找一块满足要求的内存空间分配给用户使用;用户释放内存后,系统将其回收。因此堆的内存管理需要由程序员来进行,分配效率不如栈。

    三、字符串赋值给指针变量的告警

           在用g++作编译器时,将字符串常量赋值给指针变量将得到warningdeprecated conversion from string constant to 'char *'的警告。例如:char*ptrSt = “hello”;

    这是因为将字符指针变量ptrSt指向一个位于常量区的字符串常量“hello”。如果在运行时修改ptrSt指向内存的值,例如:ptrSt[0]= 'j',将会抛出异常,在linuxg++下,抛出了“Segmentation fault”的异常。

           因此我们在将一个字符串常量赋值给字符指针变量时,应该将该变量限定为const型,即const char*。如果需要在代码中修改字符指针指向的变量的值,应该采用如下两种方式:

    chars[] = “hello”;
    char*ptrSt = s;

    或者

    char*ptrSt = (char*)malloc(sizeof(char) * 6);
    strcpy(ptrSt,“hello”);

    Reference

    1.C++内存分配方式详解——堆、栈、自由存储区、全局/静态存储区和常量存储区

    2.C/C++编程:结合内存分布图分析内存问题

    3.关于程序设计的内存分配问题

    4.关于函数字符数组调用的问题(没有搞清指针的概念)


    展开全文
  •  则正确的叙述为正确答案: C 你的答案: A (错误)数组acX和数组acY等价数组acX和数组acY的长度相同数组acX的长度大于数组acY的长度数组acX的长度小于数组acY的长度字符串赋值时以‘\0’结尾,所以长度要更长1....
  • 计算一个字符串的长度。 任务要求: 任务具体要求如下: 定义一个函数func()完成字符串长度的计算。 在func()函数中,通过while循环遍历字符串,遍历时使用指针遍历。 从键盘输入字符串。 #include <...
  • 字符串的创建与赋值

    2019-12-03 18:32:33
    一、字符串的创建与赋值 字符串或串(String)是由数字、字母、下划线组成的一串字符。Python 里面最常见的 类型。 可以简单地通过在引号间(单引号,双引号和三引号)包含字符的方式创建它。 其中单引号和双引号形式类似...
  • C字符串数组赋值

    万次阅读 多人点赞 2012-08-09 21:38:59
    1、定义的时候直接用字符串赋值 char a[10]="hello"; 注意:不先定义再给它赋值,如  char a[10];  a[10]="hello"; 这样是错误的! 2、对数组中字符逐个赋值 char a[10]={'h','e','l','l','o'}; 3、利用...
  • 字符指针 赋值

    千次阅读 2014-09-17 22:56:47
    字符串赋值给字符指针(char *a=“hello”)的正确理解方式   对于语句 char *a="hello";  对于这个声明方式,会造成的误解是:声明了一个字符指针(它会指向一个位置),将“字符串”赋值给 指针表达式"*a"所...
  • 建议1: 正确操作字符串 字符串应该是所有编程语言中使用最频繁的一种基础数据类型。如果使用不慎,我们就会为一次字符串的操作所带来的额外性能开销而付出代价。本条建议将从两个方面来探讨如何规避这类性能开销:...
  • C字符数组赋值

    2019-11-20 15:03:03
    1、定义的时候直接用字符串赋值 char a[10]="hello"; 注意:不先定义再给它赋值,如 char a[10]; a[10]="hello"; 这样是错误的! 2、对数组中字符逐个赋值 char a[10]={'h','e','l','l','o'}; 3、利用strcpy ...
  • 本文是对阮一峰老师 《ECMAScript 6 入门》 的一篇读书笔记。 本文主要包括以下四个部分: 1. 声明 2. 解构赋值 3. 字符串的扩展 4. 字符串新增方法
  • 学过指针让我清楚明白了指针变量是不直接赋值的,而这里的字符指针却直接被赋值字符串。这让我深感疑惑了,经过查阅,才知道: char * p="EDS"; 双引号在这里做了这三件事情: 1. 申请了空间(在...
  • 1.字符串的拷贝API:strcpy() 原型:char *strcpy(char* dest,const char *src) - 第一个参数dest:目标函数 - 第二个参数src:原函数 char strDest[128] = {'\0'}; 第一种定义初始化数组 char *strSrc =...
  • C++字符数组赋值问题(1) 错误源代码: char *arrA, *arrB; //对arrA赋值完成后,执行类似以下的复制操作时 //程序不报错,但赋值过程无法完成 *(arrB+j)=*(arrA+l); 正确源代码: char arrA[50], arrB[50]; //对...
  • C语言 字符数组赋值的方法 整理

    千次阅读 2015-03-15 18:38:03
    1、定义的时候直接用字符串初始化 char a[10]="hello"; 注意:不先定义再给它赋值,如char a[10]; a[10]="hello";这样是错误的!只有定义初始化是才能这样赋值 2、对数组中字符逐个赋值 char a[10]={'h','e','
  • 一、字符串的地址以及指向字符串的指针变量的定义。 1. 字符常量:用单引号括起来的一个字符。...1. 可以在定义字符指针变量的同时,将一个字符串赋值指针变量。例如:char *ps1 = “form one” 把存放字符串
  • 字符串数组与字符串指针

    千次阅读 2016-01-04 19:01:43
    最近在学指针,感觉内容繁多,概念不好掌握,除了上自己班的编程课,还... 给字符数组赋值分两种情况,单字符赋值和字符串赋值。 单字符赋值:可通过数组下标方式或指针方式,引用数组元素,进行赋值 //数组下标法
  • 字符串和字符数组

    2020-05-06 11:17:43
    字符串是 "" 号赋值的 例: char a[]="hello";//长度为6 最后加了一个 ' \0 ' 或 char a[6]="hello"; 字符串会自动的在最后加一个'\0'的结束符, 字符数组 字符数组是一个一个的赋值 例:char a[5]={'1','2','3','4...
  • 1、定义的时候直接用字符串赋值 char a[10]="hello"; 注意:不先定义再给它赋值,如 char a[10]; a[10]="hello"; 这样是错误的! 2、对数组中字符逐个赋值 char a[10]={'h','e','l','l','o'}; 3、利用...
  • 【多选题】下列运算符的使用正确的有哪些( )【多选题】以下选项中,不符合 Python 语言变量命名规则的有( )【单选题】以下程序的输出结果是________ s = "python 编程 很 容易 学" print(len(s))【多选题】...
  • 字符指针赋值、传值

    千次阅读 2019-05-13 11:44:58
    1.先来理解一下指针的初始化及赋值 int main() { char *a; cout<<"please input the plaintext"<<endl; cin>>a; cout<<a; } a=...是给a赋值 *a=...是给a指向的内存赋值 上述...
  • C风格字符串与string

    2019-05-13 10:05:54
    C风格字符串 C风格字符串并不是一种类型,它指的是一种编程习惯,指C语言中以'\0'结尾的字符串。... 用字符串给字符数组赋值时由于要添加结束符'\0',数组的长度要比字符串的长度大1. puts和p...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 117,817
精华内容 47,126
关键字:

以下能正确进行字符串赋值