c语言中字符串常见初始化时的问题,以及字符串数组与指针区别的分析

1, 首先需要知道的是在头文件ctype.h中定义的各种字符函数在字符串中变得不再适用(把字符串拆成单个字符后其实还是适用的),并且赋值等在基本类型中使用的运算符也大都开始不适用,所以字符串有一套自己的函数用来解决这些存在的问题。他们被保存在string.h这个头文件中。

2, 字符串初始化, 刚刚接触到这个的时候我是非常头疼的,因为分不清什么数组初始化,指针初始化,因为按照以前的知识知道了数组名实际上就是一个存放了数组首元素地址的指针,所以一直以为他们没什么区别,直到我学习到了字符串。
    首先来看一段程序:
                    #include 
                    #include 
                    #define SIZE 81
                    #define LINE 3

                    int main(void)
                    {
                        const char name[SIZE] = "Hello, my name is sheng.";     // 初始化一个大小已经确定了的char数组
                        char hobby[] = "My favorite sport is basketball.";     // 让编译器计算数组大小的初始化数组
                        const char *age = "eighteen.";         // 初始化一个指针
                        const char * sex;    // 对指针分步初始化
                        sex = "girl";
                        const char *song[LINE] =        // 初始化一个指针数组
                        {
                            "Single dog",
                            "Single dog",
                            "Single all the day!"
                        };
                        puts(name);            //    依次打印
                        puts(hobby);
                        puts(age);
                        puts(sex);
                        for(int i = 0; i < LINE; ++i)
                        {
                            puts(*(song + i));         //    *(song + i) == song[i]
                        }
                        return 0;
                    }

                    /*
                        Hello, my name is sheng.
                        My favorite sport is basketball.
                        eighteen.
                        girl;
                        Single dog
                        Single dog
                        Single all the day!

                    */


                    现在将问题一个一个解决。
                    1)首先要明白的是,字符串常量是指一对双引号里面的任何字符,双引号里的字符加上编译器提供的结束标志'\0'一起被存在静态区的数据段中,这也就是其被称为常量的原因了。而在数据段,内容相同字符串只有一份,每当需要的时候便一直使用同一份字符串常量,而指针与数组在使用这份字符串常量的时候方式又是不同的。
                        对于指向char类型的指针来说:指针只需要一个char类型长度的存储空间(通常是一个字节)来存放指针本身,而指针的内容便是它所指向字符串常量的首地址。所以足以见得,当该指针想要修改字符串常量的内容时便会出现错误:   
                            
               const char *age = "eighteen.";         // 初始化一个指针
               age[3] = 'l'   //    这种情况是不允许的! 


                        因为当您妄图修改这个字符串所指向的第四个字符时,由于字符串常量只有一份,所以您实际修改的是静态区中的字符串常量,这是不允许   的,所以这时编译器会报错,或者停止运行!
                        而对于char类型的数组来说就不同了,虽然数组名是一个指针,但是它所指向的是其本身第一个元素的地址,并不可以随便赋值给别的内存空间;数组在初始化时,并没有把指针指向静态区的字符串常量,而是在堆栈区又自己复制了一份与静态区字符串常量的字符串,这个字符串是临时的,当函数结束时,数组也将会被回收,此时这个临时的字符串将会不再存在!
                        此时, 若未定义用const修饰的字符串数组,那么这个数组里面的内容便可以随意更换!
                              
              char hobby[] = "My favorite sport is basketball.";     // 让编译器计算数组大小的初始化数组
              hobby[4] = '\0'    //    让数组提前结束,这样做也是可以的。



                    2)基本的初始化方法前文已有,现在来说不允许的方法。
              char name[SIZE];
              name = "Hello, my name is sheng.";  

   这种方法是不被允许的,因为name是数组名,指向的仅仅是第一个数组元素的地址,而这个语句的意思是将字符串的首地址赋给name,这显然是不对的!        

                            为与其对比:
                          
               const char * sex;    // 对指针分步初始化
               sex = "girl";        //    这种初始化方法是可以的,因为sex为指针,当把字符"girl"赋值给sex时,表示把这个字符串的首地址赋给sex,这种是被允许的!



3,当初始化字符串数组时,需要明白的是:    const char name[SIZE] = "Hello, my name is sheng."; //    其中的SIZE一定要比字符串所有的元素至少大1,因为至少要留一个位置给'\0',作为字符串的结尾
                                    //    这样要时刻考虑数组的容量显得过于麻烦,所以通常情况下可以使用以下方法:
                                    char hobby[] = "My favorite sport is basketball.";     // 让编译器计算数组大小的初始化数组,编译器会自动添加'\0'元素。
                                    或者也可以用指针指向数组首地址的方法来初始化。

4,当用指针初始化字符串常量时,最好在其前面加const,表示其不会修改所指向的字符串常量,这样做更加安全。
                            const对指针的使用方法:

                a) const char *age = "eighteen.";   

  //    表示指针age所指向的字符串是一个字符串常量,不能够被修改。而指针的内容可以被修改,即该指针还可以指向其他的字符串。                                      

                b) char * const age = "eighteen.";    //    表示指针age只能指向这个字符串,不可再用于指向其他的char类型变量。

    
                                                注意a类型实际上可以把非字符串常量也赋值给指针age,这是age就会把它当成一个字符串常量来对待,即不会修改它的值!
                                                而b类指针仍可用于修改数据,所不同的是,他只能修改最初赋给它的值。

5,在观察string.h中的函数原型时,会发现其里面定义的函数原型除了极个别之外,函数的形式参量均为指针类型,表示当调用这些函数时,时刻要记得传给函数的实际参数也要为指针类型。
6,看下面这个例子:
                    #include 
                    #include 
                    #include 
                    #define ANSWER "SIMY"
                    #define MAX 40
                    char * strupr(char *);
                    int main(void)
                    {
                        char input[MAX];    //    声明一个已知容量的字符串数组,用以存放从键盘上键入的内容。
                        printf("Who is my lover?\n");
                        scanf("%s", input);
                        strupr(input);
                        while(strcmp(input, ANSWER))
                        {
                            puts("You're wrong!");
                            scanf("%s", input);
                        }
                        puts("You're right!");
                        return 0;
                    }
                    
                    char * toup(char * input)
                    {
                    
                        while(*(input++))
                        {
                            *input = toupper(*input);    //    把小写转化成大写
                        }
                        return input;
                    }




                    1)注意当程序运行之前一定要为数组分配明确的大小,因为直到程序运行时才能读取键盘的内容,所以程序无法预先知道要为它留多大的空间!
                    2)这个程序我之前一直搞不懂,因为我以为从键盘读入的字符串是存在静态区的字符串常量,我之前一直以为这个函数改变了静态区的字符串常量,所以才会出现很多误区。但是当仔细分析之后发现:
                        我声明input数组的地方位于主函数,所以当toup()函数结束后input数组并不会被回收,但是由于声明的input为数组,所以从键盘键入的字符串实际上是以拷贝的方式存在数组中,所以改变这个数组的内容对原字符串没有影响!

                        而下面这个例子:
                                    
                                        #include 
                                        char *string(void)
                                    
                                        int main(void)
                                        {
                                            char *str=NULL;        //    一定要初始化,好习惯
                                            str=string();
                                            printf("%s\n", str);

                                            return 0;
                                        }
                                        char *string(void)
                                        {
                                            char ptr[]="hello world!";
                                            return ptr;
                                        }


                                        这样做是无效的,因为函数中的ptr[]是一个在函数string中局部的数组,他被存在栈中,当这个函数被调用完毕之后,ptr[]的空间已经被回收,而返回的ptr指向的地址仍旧是ptr[] 存在时的地址,所以没有办法返回有效的地址,所以将无法输出ptr[]的内容!
                                        而解决这个问题的办法,就是在这个数组初始化之前,在其前面添加static!

你可能感兴趣的:(c语言)