字符串:

1、字符串字面量(String literal)

  • 一对双括号括起来的字符序列。
  • c++中称为字符串字面值,程序执行过程中保持不变的数据。
  • 字符 \ 可以延续字符串字面值。
  • 处理长字符串的方法:当两条或更多条字符串常量相邻时(仅用空白符分割),编译器会将其合并额外i一条字符串。
printf("when ytou come to, take it. "
        "--yogi berra\n");
输出:when ytou come to, take it. --yogi berra
  • c语言将字符串作为字符数组来处理。包含空字符 **’ \0 ’ **(所有位都是0的字节,字节码值为0,字符’0’的字节码值为48).
  • 字符串" " 作为单独一个空字符来存储。
  • 编译器将字符串看作一个 char * 类型的指针。
  • 可以出现在赋值运算符右边:
char *p;
p = "abc"; // p 指向字符串 abc 的第一个字符。
  • c语言允许对指针取下标,可以对字符串取下标:
char ch;
ch = "abc"[1]; // ch 的新值是字母 b, 可以取值的下标是 0:a, 2:c, 3:空字符
  • 改变字符串字面值是非法的。
char *p = "abc";
*p = 'd'; // 错误,不允许修改字符串字面值。
  • 字符串字面量与字符常量
  • 字符串字面量是用指针来表示的,指针指向存放字符"a"的内存单元,字符常量 'a’是用整数来表示的。

2、字符串变量

  • 只要保证字符串是以空字符结尾的,任何一维的字符数组都可以用来存储字符串。
  • 字符串的长度取决于空字符的位置,而不是取决于用于存放字符串的字符数组的长度。。有STR_LEN+1 个字符的数组可以存放多种长度的字符串,范围是从空字符串到长度为 STR_LEN 的字符串。
  • 初始化字符串变量: 字符串变量的声明中可以忽略其长度,编译器会自动计算其长度。
char data[] = "June 14";
// 编译器为 data 分配了8个字符的空间
一旦编译了程序,data 的长度就固定为8了。不能再修改了。
  • 字符串字面值与字符指针
char date1[] = "June 14"; // data1是一个数组
char * date2 = "June 14";// data2 是一个指针
  • 两者都可以用作字符串,任何希望传递字符数组或字符指针的函数都可以接收两种声明的data作为参数。
  • 区别:
  • date1 声明为数组,可以修改存储在数组date1中的字符,但是date2指向字符串字面值,不能修改。
  • date1是数组名,date2是变量。
  • 如果希望修改字符串,则需要建立字符数组来存储字符串,使用未初始化的指针变量作为字符串是严重的错误。
  • 改变字符串字面值是严重错误的原因:
  • 编译器为了节省内存只会为相同的字符串字面值存储一份副本,一个指针更改了内容,另一个也会受到影响;
  • 字符串字面值可能存储在内存中“只读”区域,试图修改会导致程序奔溃。

3、字符串的读写

  • printf 函数会逐个写字符串中的字符,直到遇到空字符才停止。可以使用转换说明 **%.ps ** 来只打印 p 个字符。
char str[] = "Are we having fun yet? ";
printf("%.6s\n",str); // 只打印 Are we
  • puts函数输出字符串后,会添加一个额外的换行符,从而前进到下一个输出行的开始处。
  • scanf 会跳过空白字符然后读入字符直到遇到空白字符位置,会始终在字符串末尾存储一个空字符。空格符和制表符也会使得scanf读入停止。
  • 使用gets函数可以一次读入一整行输入。
  • 区别:
  • gets函数不会跳过开始的空白字符,scanf会跳过。
  • gets函数持续读到换行符才停止,scanf 会在任意空白字符处停止,gets会忽略换行符不会将其存入数组中,会用空字符代替换行符。
// const 保证了不会修改 s 指向的字符。
int count_spaces(const char *s){
    int count = 0;
    for(; *s != '\0'; s++)
        if(*s == ' '){
            count++;
            // *s = 'e';
        }
    return count;
}

void test2(){
    // str1是指针,不能修改字符串字面值, str是数组,里面可以修改
    // char *str1 = "abc dfg h";
    char str[] = "abc dfg h";
    printf("%s\n",str);
    int count = count_spaces(str);
    printf("%d\n",count);
    puts(str);
}
int main(int argc, char * argv[]){
    test2();
    return 0;
}

4、c语言的字符串库:

  • c语言运算符无法操作字符串,不能使用其运算符赋值和比较字符串。
  • 不能使用 = 运算符将字符串复制到字符数组中去。
char str1[10],str2[10];
str1 = "abc"; // 错误
str2 = str1; // 错误
  • 利用 =初始化字符数组是合法的。
char str1[10] = "abc";
  • 关系运算符或判等运算符比较的是两个指针的地址,也不是字符串本身的比较,是无意义的。
  • strcpy函数:
  • 将字符串s2复制到字符串s1,返回s1(指向目标字符串的指针)
char *strcpy(char *s1, const char * s2);
strcpy(str2,"abcd");
strcpy(str1,str2);
  • strcpy不会检查str1大小是否大于 str2,strcpy会一直复制到第一个空字符为止,会越过str1指向的数组的边界继续复制。
  • 安全的做法是使用 strncpy函数
strncpy(str1,str2,sizeof(str1));从str2中拷贝str1长度的字符。
strncpy(str1,str2,sizeof(str1) - 1);
str1[sizeof(str1) - 1] = '\0'; // 防止str2长度大于str1,使得拷贝后的str1没有以空字符结尾。
  • strlen函数:
  • 函数返回字符串s 的长度,不包含最后一个空字符。
size_t strlen(const char *s); // size_t表示C语言中的一种无符号整型。
  • 当用字符数组作为参数时,strlen不会返回数组本身的长度,而是返回存储在数组中的字符串 的长度。
  • strcat函数
  • strcat函数将字符串s2的内容追加到s1的末尾,并返回指向字符串s1的指针。
char *strcat(char *s1, const char *s2);
  • 如果str1指向的数组无法容纳str2指向的字符串,则strcat的结果是无法预测的。
  • 安全的是使用strncat函数
strncat(str1,str2,sizeof(str1) - strlen(str1) - 1);
  • strcmp函数
  • 函数比较字符串s1,s2函数返回一个小于、等于、大于0的值。
int strcmp(const char *s1, const char *s2);
  • 函数按字典顺序进行字符串比较。
  • 第i个字符小于的较小。
  • 所有字符都一致的情况下,短的字符串小于长的字符串
  • ASCII字符集性质:
  • A~Z, a~z, 0~9 几组的数值码是连续的。
  • 所有的大写字母都小于小写字母(65-90表示大写字母,97-122表示小写字母)
  • 数字小于字母(48~57表示数字)
  • 空格符小于所有打印字符(空格符值为32)

5、惯用法:

  • 搜索字符串结尾的空字符:
// s 指向空字符
while(*s)
    s++;

//以及
// s指向空字符下一个字符
while(*s++)
    ;
  • 字符串赋值惯用法:
char *strcat(char *s1, const char *s2){
    char *p = s1;
    while(*p)
        p++;
    // while 语句会测试赋值表达式的值,也就是测试复制的字符。
    while(*p ++ = *s ++) // while循环结束是*s赋值于*p是空字符时。
        ;
    return s1;
}

6、字符串数组:

  • 存储字符串的数组。
  • 可以创建二维的字符数组,按照每行一个字符串的方式将字符串存储到数组中。
  • 但是这样会造成空间的浪费。因为大部分字符串是长组非常和短字符串的混合。
  • 建立一个数组其元素是指向字符串的指针。
char *planets[] = {"Mercury","Venus","Earth","Mars","Jupiter","Saturn","Uransus","Neptune"};
  • planets每一个元素都是指向以空字符结尾的字符串的指针,访问行星名字只需要对planets数组取下标。

7、命令行参数:

  • 为了访问命令行参数,需要将main函数定义为含有两个参数的函数,即
int main(int argc,char* argv[]){}
// 或者
int main(int argc, char**argv){}
// 在声明形式参数时,无论a的元素类型是什么,*a的写法总是和a[]是一样的.
  • argc 参数计数,指命令行参数数量,argv 参数向量 ,指向命令行参数的指针数组,命令行参数以字符串的形式存储,argv[0]指向程序名,argv[1]到argv[argc-1]指向余下的命令行参数。argv[argc]是一个空指针,宏NULL代表空指针。