C语言字符串问题

1.首先看一下 字符串 和字符数组中的细微区别:

  1    。定义

  1. 字符串是最后一个字符为NULL字符的字符数组。字符串一定是字符数组。
  2. 字符数组,完整地说叫字符类型的数组。字符数组不一定是字符串。(字符串最后一个元素必须为NULL)
  3. 字符数组的长度是固定的,其中的任何一个字符都可以为NULL字符。
  4. 字符串只能以NULL结尾,其后的字符便不属于该字符串。
  5. strlen()等字符串函数对字符串完全适用,对不是字符串的字符数组不适用。
  6. sizeof()对于字符串得到的是指针类型的长度(为4)对于字符数组得到的是字符数组的长度。
由于C语言中没有字符串类型,所以用char数组代字符串类型,定义字符数组的方式有两种,下面来看它们的区别:
 
方式一:以单字符形式
要声明初始化一个可存储四个有效值的字符数组,形式如下:
char charArray[4]={'1','2','3','4'};  
 
方式二:以字符串形式
要声明一个可存储四个有效值的字符数组,形式如下:
char stringArray[5]={"1234"}或"1234";//,字符串数组大小要声明为5,因为要留出stringArray[4]给编译器自动存储'\0',如若声明初始化为:char stringArray[4]="1234",报错
看一个例子:
#include <stdio.h>
#include <string.h>


int main(void)
{


    //这是字符数组赋初值的方法
    char s1[]= {'T','a','o','T','a','o','s' ,'b'};
   //char s1[]= "ljsgjlsgjslgjslgj";
    //这是字符串赋初值的方法
    char s2[]= "taotaosb";


    //用sizeof()求长度
    printf("s1's length is %d\n", sizeof(s1));   //长度为8
    printf("s2's lenght is %d\n", sizeof(s2));   //长度为9,最后一位自动添加了 一个'\0'


    //用printf的%s打印内容
    printf("s1=%s\n", s1);   //不能正确显示
    printf("s2=%s\n", s2);   //可以正确显示
    printf("%x\n",s1);
    printf("%x\n",s2);




    printf("s1' length is %d\n", strlen(s1));   //不正确的结果
    printf("s2's length is %d\n", strlen(s2));   //NULL不在计算范围


    return 0;
}

结果:
C语言字符串问题_第1张图片
可以看出:s1的值和 长度  都没有正常打印出我们想要的值。需要注意的几点:
sizeof()运算符求的是字符数组的长度,而不是字符串长度。strlen()函数求的是字符串长度,而不是字符数组。它不适用于字符串以外的类型。strlen()函数会对你传进来的指针 往后查找,一直找到  '\0' 才结束。二sizeof 是计算字符数组的长度,如果传进去普通的字符指针的话会得不到我们想要的结果。(不会出现编译错误的,根据计算机位数的不同结果可能不同。)所以用的时候要 特别小心。可以看下面的例子。
#include <stdio.h>
#include <string.h>
#include <stdlib.h>


int main(void)
{


        char a[30];
        char *b = (char *)malloc(20 * sizeof(char));
        printf("%d\n", sizeof(a));			//30
        printf("%d\n", sizeof(b));			//4
        printf("%d\n",sizeof(&a[3]));                   //4
        printf("%d\n", sizeof(a[3]));			//1
        printf("%d\n", sizeof(b+3));			//4
        printf("%d\n", sizeof(*(b+4)));		//1
        return 0 ;
}
结果:
C语言字符串问题_第2张图片
可以看出来,你传进去一个 指向char类型的指针的时候,返回结果是4,因为是32位的。


字符串数组是二维字符数组,同样道理,二维字符数组不一定是字符串数组。


2.字符串数组是二维字符数组,同样道理,二维字符数组不一定是字符串数组。(和一维的类似)

看一下例子:
#include <stdio.h>
#include <string.h>

int main(void)
{
    int i;
    //字符数组的一种赋值方法
    char s1[][2] = {{'S','B'},
                    {'A','B'},
                    {'d','A'},
                    {'s','\0'},};

    //用字符串对字符数组进行赋值的 一种方法
    char s2[][3] = {"SB","AB","dA","s"};
    for(i = 0;i < 4;i++)
    {
        printf("i = %d ,s1[i] =%d ,  s2[i]= %d\n",i,sizeof(s1[i]),sizeof(s2[i]));
         printf("s1[i] = %s  s2[i] = %s\n", s1[i], s2[i]);

    }
}

结果:


C语言字符串问题_第3张图片

因为多了一个NULL字符,所以 S2字符数组比S1  要多了一个1。 输出S1 时,因为中间没有NULL字符,所以一直显示到有NULL为止。S2因为每一行都有一个'\0' 结尾。所以打印正常。

3.字符指针指向字符串  和 字符数组的  

eg1:

char* s=”hello”;
s[0]='a';//wrong!运行时显示为“段错误”。
运行的时候     “hello” 是常量字符串,存放在常量存储区。该区的值是只读的。任何试图修改该存储区值的都会判定为错误。 s指针 指向 常量存储区的 这个字符串。所以你试图改变 值会出现段错误。而你定义字符数组,可以对字符数组修改的。看下面的例子:

#include <stdio.h>
#include <string.h>
int main()
{
    char *s1 = "hello";
    char s2[] = "hello";
    //s1[0] = 'a';  这句话是错误的
    s2[0] = 'a';
    printf("s1= %s\n",s1);
    printf("s2= %s\n",s2);
    return 0;
}
运行结果:

C语言字符串问题_第4张图片
S2在运行的时候在栈中 专门 分配了内存空间给s2数组的,所以可以对其中的元素 进行修改。在看下一个区别:


2作为返回值时不同。

char* test(){

char ch[5]={'h','i'};//gcc 会警告:返回局部变量。

return ch;

ch指向在函数中分配的内存,这片区域在函数执行过后会释放的,如果你去访问的话会造成不可预期的后果。例子:

#include <stdio.h>
#include <string.h>
char *test()
{
    char ch[10] = "hello";
    //char *s = "hello";
    return ch;
}
int main()
{
    char *s = test();
    printf("s=%s\n",s);
    return 0;
}
执行结果:
C语言字符串问题_第5张图片

可以看到一堆乱七八糟的东西。所以 不可以这样用。但是可以这样用:
char *test()
{
   // char ch[10] = "hello";
    char *s = "hello";
    return s;
}
s是指向 常量区的东西。这篇常量区的东西,在整个程序执行完之前使用 是应该没问题的。所以 打印结果是正常的:

C语言字符串问题_第6张图片
下面在来一个例子,你可以看出来下面的错误吗~

#include <stdio.h>
#include <string.h>

void hehe(char *str)	 //转小写
{
    int i=0;
    while(str[i]!='\0')
    {
        if(str[i]>='A' && str[i]<='Z')	str[i]+=32;
        i++;
    }
    str[i]='\0';
    printf("%s\n",str);
}
int main()
{
    char s[] = "TAOTAOSB";
    hehe("taotaosb");//第1 中传递方法,
    hehe(s); // 第2中 传递方法。

    return 0;
}


第一种调用方法是错误的(会造成段错误问题),第二种方法是正确的。

OK,差不多就这么多了,如有错误 欢迎 指正。非常感谢~
























2.看一个strcpy的问题
#include <iostream>
#include <stdio.h>
#include <string.h>

int main(void)
{
    char s[] = "123456789";
    char d[] = "123";
    strcpy(d,s);
    printf("%s ,\n %s",d,s);
    return 0;
}

看一下打印的结果:

C语言字符串问题_第7张图片
这里可以看到 s的地址是在 高地址,d的地址是在低地址上。而且正好相差4个字节,符合下面我的推论。

首先 strcpy中 源字符串的参数 是const  char * 类型的,但是这里 发生了变化。
原因个人认为是这样的:(水平有限,有错误请指出,感谢~~)
函数在运行的时候:编译器会自动分配一个 栈区,用来存放函数中用到的参数值以及 局部变量的值。操作方式类似数据结构中的栈。
这里 会首先给 s数组  分配 10个字节的内存空间(包括结束符)  ,然后会给 d数组 分配4个字节的 内存空间,不过 这 两个 数组的内存空间是紧挨着的 。栈的 内存空间是从高地址向低地址扩展的,那么 这两个参数在栈中的排序  是这样的:
低地址 <<<----------------高地址
1 2 3 \0 1 2 3 4 5 6 7 8 9 \0
|        |
d数组     s数组
前面4个 字节是d数组的,后面的10个字节是s数组的,发生复制函数之后,栈中内存位置上的变化为:
低地址 ----------------->>>高地址
1 2 3 4 5 6 7 8 9 \0 6 7 8 9 \0
|       |
d数组   s数组

所以打印d是123456789(低地址到高地址)
打印s是56789 (遇见\0打印结束)

所以我们在使用 strcpy的时候一定要注意 :目标字符串 留足够的空间来复制

你可能感兴趣的:(C语言字符串问题)