C/C++(字符串)

字符串

c语言没有字符类型,字符串是有双引号硬起来的一串字符。

字符串常量

系统自动默认的在字符串末尾添加一个\0;

stack:栈
heap:堆
data(初始化[全局变量,static 局部变量,常量])(未初始化) 
text:

大小实际大小加\0所占的一个字符。
存储位置:data域

printf("sizeof(\"china\") = %d\n",sizeof("china"));//6
printf("%s\n","china");//china
char *p = "china";//编译无错
printf("p = %p p+1 = %p p[0] = %c 0[p] = %c\n",p,p+1,p[0],0[p]);
printf("p = %p p+1 = %p p[0] = %c 0[p] = %c\n","china","china"+1,"china"[0],0["china"]);
/*
sizeof("china") = 6
p = 0040405A p+1 = 0040405B p[0] = c 2[p] = i
p = 0040405A p+1 = 0040405B p[0] = c 0[p] = i
*/

c语言,将字符串常量处理一个指向data段这段字符串的首地址。
对比数组,三要素(起始地址,步长,范围)char *:代表了整个字符串,起始地址,步长,\0。三要素代表了整个字符串。

修改字符常量:

char arr[] = {'c','h','i','n','a'};
arr[2] = 'x';//修改
char arr1[6] = "china"; 
printf("sizeof(arr) = %d arr = %p arr+1%p\t\n",sizeof(arr),arr,arr+1);
printf("sizeof(\"china\") = %d \"china\" = %p \"china\"+1%p\t\n",sizeof("china"),"chaina","china"+1);
sizeof(arr)     = 6   arr   = 0061FEA6    arr+1  = 0061FEA7
sizeof("china") = 6 "china" = 004040DC "china"+1 = 0040405B

因为字符数组和字符串具有相同的性质。所以如果为了修改字符串的内容,可以将字符串拷贝到数组中去。
数组是栈上的空间,“china”字符串常量是data域中,一块常量区。只要放到栈上就能任意的改变。

char arr[] = {'c','h','i','n','a'};
arr[2] = 'x';//修改
char arr1[6] = "china"; 
printf("sizeof(arr) = %d arr = %p arr+1%p\t\n",sizeof(arr1),arr1,arr1+1);
printf("sizeof(\"china\") = %d \"china\" = %p \"china\"+1%p\t\n",sizeof("china"),"chaina","china"+1);
arr1[2] = 'x';//可以修改
"china"[2] = 'x';//修改不了,报错

字符数组与字符串

字符数字和字符串之间具有相同的性质,但并不表示字符串可以与任意的字符数组划等号。

等价关系

sizeof(字符数组)的大小 >= sizeof(字符串)的大小。

不等价

sizeof(字符数组)的大小 小于 sizeof(字符串)的大小,此时不存在等价关系,此时的字符数组只是字符数组。

更好的做法自适应,不定义数组的大小。

printf("%s\n","chi\0na");//chi
char arr[2] = "china";//字符数组的大小大于等于字符串的大小时两者等价。
printf("arr = %s\n",arr);//ch({[]}

字符串的输入与输出

char arr[] = "china";
printf("%s\n",arr);//china
puts(arr);//自带回车
putd("");//换行,输出一个空串并且自带换行

输入:
输入的字符串,如果越界,此时的数组仅仅是字符数组而已。不能越界。scanf()函数遇到空格会结束。[^\n]可解决scanf()函数遇到结束的问题。
gets()函数输入字符串,也会存在越界风险,但空格也会读入。
fgets(arr,10,stdin);读入的字符放入数组中,最长10个数字(留一个放\0),stdin从键盘输入。

char arr[6] = "china";
scanf("%s\n",arr);//arr本身是地址,无需取地址
scanf("%[^\n]s\n",arr);//解决scanf()的空格换行问题
gets(arr);//
fgets(arr,10,stdin);
printf("%d\n",'\0');//0 asc码
printf("%d\n",NULL);//0,NULL,专门的标识符

c语言是将如何处理常量字符串?将其编译为一个指向rodata一个字符的首地址。
这个首地址,能代表字符串
首地址+步长+范围(自动追加'\0')
通过字符指针的方式,可以使用常量字符串,但不可以修改。

char *p = "china";
p[0] = 'x';//不可以

若要操作内容:利用数组形式

char arr[6] = "china";//将data 的rodata段的字符拷贝带了arr代表的字符串中。
arr[2] = 'x';//此时操作字符等价于操作字符串,并且可以修改。

是不是随便一个字符串都可以放到字符数组中
当然不是,只有两者等价的关系下才可以。sizeof(字符数组的大小) >= sizeof(字符串的大小)

char ch[2] = "china";
printf("%s\n",ch);//是不行的,
char ch[6] = "china";//才可以,实际长度加一
char [] = "china";//或者自适应

可得出结论,对于字符串的处理就变成了对字符指针或者是字符数组的处理

字符数组的操作(原生)

字符串的长度:不包含'\0'
字符串的大小:包含'\0'

char *p = "china";//将指针赋值给了p sizeof(p) = 4的首地址
char arr[] = "china";//将指针指向的内容赋值给了arr sizeof(arr) = 6;
char *q = p;
int count = 0;
while(*q++) {//'\0',也就是0,假
    count++;
    //q++;//可以放面
}
for(count = 0;*q++;count++);
printf("count = %d\n",count);

int len = strlen(arr);//
int len = strlen(p);//
printf("len = %d\n",len);//5

strlen()函数求数组的大小

数组传递的时候谣传三要素(数组名,步长,范围),传字符串的时候,只需要传字符指针或者字符数组,因为字符串自带结束标志'\n'
自己封装一个求字符串长度的函数

int myStrlen(char * str) {
    int _len;
    for(_len = 0;*str++;_len++);
    return _len;
}

strcat()链接两个字符串

把后面的一个参数合并到第一个参数的后面。concatenate

char firstName[] = "assassin";
char lastName[] = "wunworld";
strcat(firstName,lastName);
printf("%s\n",firstName);

char firstName[20] = "assassin";//要有足够的空间
char lastName[10] = "wunworld";

char *p,*q;
p = firstName;
q = lastName;
while(*p) {//p指向'\0'的位置,++不能放在括弧里,会移动到'\0'后面的一个外置
    p++;
}
/*while(1) {
    *p = *q;//拷贝
    if(*p == '\0') {
        break;
    }
    p++;
    q++;
}*/
/*while(1) {
    if((*p = *q) == '\0')//简化
        break;
    p++;
    q++;
}*/
/*while(*p = *q) {//'\0'即0,
    p++;
    q++;
}*/
while(*p++ = *q++); 

printf("%s\n",firstName);//assassinwunworld

完整代码:

#include<stdio.h>
char *myStrcat(char *src,char *dist);
int main() {
    char firstName[30] = "assassin";
    char middleName[10] = "seafwg";
    char lastName[10] = "wunworld";
    myStrcat(myStrcat(firstName,middleName),lastName);
    printf("%s\n",firstName);

    return 0;
}

char *myStrcat(char *dest,char *src) {//返回char * 是因为链式操作
    char *d = dest;
    while(*dest) 
        dest++;
    while(*dest++ = *src++);//判断条件加赋值操
    return d;
}

strcpy()字符串的拷贝

char name[222];//被拷贝要有足够的空间
char *pName = "China";
char name2 = "assassin";
strcpy(name,name2);
printf("%s\n",name);//assassin
char *myStrcpy(char *dest,char * src) {
    char *d = dest;
//先拷贝,在判断,再加加
    /*while(1) {
        *dest = *src;
        if(*dest == '\0') {
            break;
        }
        dest++;
        src++;
    }*/
    while(*dest++ = *src++);
    return d;
}

strcmp()比较两个字符串是否相等

一次比较ASCII的值,相同返回0,小于返回-1,大于返回1;

char *s1 = "china";
char *s2 = "china";
//s1 == s3;//这样无疑判断的是两个字符串的地址
strcmp(s1,s2);

eg:简单的登录校验:

#include<stdio.h>
int main() {
    char name[100] = {0};
    char pawd[100] = {0};
    /*
    do{
        printf("输入用户名:");
        scanf("%s",name);
        printf("输入密码:");
        scanf("%s",pawd);
    }while(!(strcmp(name,"assassin") == 0) && !(strcmp(pawd,"123456") == 0));
 */
    int count = 4;
    while(1) {
        if(count < 4) {
            printf("你还有%d次机会\n",count);
        }
        printf("输入用户名:");
        scanf("%s",name);
        printf("输入密码:");
        scanf("%s",pawd);
        if(strcmp(name,"assassin") == 0 && strcmp(pawd,"123456") == 0) {
            break;
        }else{
            count--;
            if(count == 0) {
                exit(-1);
            }
        }
    }
    printf("欢迎光临!您好,%s\n",name);

    return 0;
}

完整优化代码:

#include<stdio.h>
char myStrcmp(char * s1,char *s2);
int main() {
    char name[100] = {0};
    char pawd[100] = {0};
    /*
    do{
        printf("输入用户名:");
        scanf("%s",name);
        printf("输入密码:");
        scanf("%s",pawd);
    }while(!(strcmp(name,"assassin") == 0) && !(strcmp(pawd,"123456") == 0));
 */
    int count = 4;
    while(1) {
        if(count < 4) {
            printf("你还有%d次机会\n",count);
        }
        printf("输入用户名:");
        scanf("%s",name);
        printf("输入密码:");
        scanf("%s",pawd);
        if(myStrcmp(name,"assassin") == 0 && myStrcmp(pawd,"123456") == 0) {
            break;
        }else{
            count--;
            if(count == 0) {
                exit(-1);
            }
        }
    }
    printf("欢迎光临!您好,%s\n",name);

    return 0;
}

char myStrcmp(char * s1,char *s2) {
    //两者中没有空的
    while(*s1 != '\0' && *s2 != '\0') {
        if(*s1 > *s2)
            return 1;
        else if(*s1 < *s2)
            return -1;
        else{
            s1++;
            s2++;
        }
        /* 优化1
            if(*s1 != *s2) {
                return *s1-*s2;
            }else{
                *s1++;
                *s2++;
            }
         */
        /*优化2
          for(;*s1 != '\0' && *s2 != '\0';s1++,s2++) {
            if(*s1 != *s2) {
                break;
            }
          }
         * */
        /*优化3
          for(;*s1 && *s2;s1++,s2++) {//在代码中任何'\0'只要是比较就是0,*s1 != '\0'(就是*s1非0,即存在) <=>*s1
            if(*s1 != *s2) {
                break;
            }
          }
         * */
        /*优化4
          for(;*s1 && *s2 && *s1 == *s2;s1++,s2++) {//但不建议这么写,以后不利于优化,但可以表明小面判断的可以到for循环中添加,简化循环中的操作
              break;
          }
         * */
    }
    //两者中有空的字符串(可以优化成 return *s1 - *s2;)
    if(*s1 == '\0' && *s2 != '\0')
        return -1;
    else if(*s1 != '\0' && s2 == '\0')
        return 1;
    else
        return 0;
    //return *s1 - *s2;//两个字符串相减确定了大小关系
}

多文件编程

1.把一类共功能的函数写到一个XXX.c文件中。
2.把XXX.c中的所有函数声明写到XXX.h文件中。
3.在XXX.h中加入避免头文件重复包含的语句

ifndef MYSTRING_H

define MYSTRING_H

XXX.c中文件的函数声明部分...

endif

4.将XXX.h文件包含到XXX.c中,自包含,自己实现的用双引号
5.在main函数中包含XXX.h,谁用谁包含
第一步是实现过程,第二步是声明过程,第三步(3,4)把1做成库,给别人提供就可以了

posted @ 2018-01-17 00:45  seafwg  阅读(546)  评论(0编辑  收藏  举报