C 学习笔记 —— 字符串

什么是字符串

C没有显示的字符串数据类型。
字符串是以空字符\0结尾的char类型数组。

定义字符串的三种方式:

  1. 字符串常量
  2. char类型数组
  3. 指向char的指针

字符串常量

"I am a string"
用双引号括起来的内容就是字符串常量。编译器会在末尾自动加空字符\0
他属于静态存储类别,也就是该字符串只会被存储一次,在整个程序生命周期内存在。
printf("s%," "sdf");使用%s在printf中打印字符串转换。

字符串数组

char words[71] = "I am a string in an array.";
char wokd[10] = {
    
    'c','g','a','/0'};
char words3[] = "I am a string in an array.";
  1. 使用字符串数组要有足够的空间存储字符串。末尾会存在空字符。
  2. 注意第二种方式是标准数组初始化形式,但是如果不加最后空字符则他只是char数组,有了空字符他才可以表示字符串。
  3. 所以我们一般采用第三种方式来让编译器自动计算数组大小。非常方便。

因为字符串数组和数组一样,所以我们也可以使用指针表示数组首元素地址

char * p = words;
p == words == &words[0]

指向char的指针

char * pt1 = "Something is pointing at me.";
char pt2[] = "Something is pointing at me.";

以上两个声明几乎相同。但是他们字符串的存储位置不同。
pt1和pt2都是该字符串的首地址。

不同点

#define MSG "I'm special"
int main()
{
    
    
    char ar[] = MSG;
    char *pt = MSG;
    printf("address of \"I'm special\": %p \n", "I'm special"); //0x400660
    printf("          address of MSG: %p\n", MSG);              //0x400660
    printf("              address ar: %p\n", ar);               //0x7ffe88148230
    printf("              address pt: %p\n", pt);               //0x400660
    printf("address of \"I'm special\": %p \n", "I'm special");//0x400660

	//操作字符串常量区
    printf("\n");
    printf("%s \n", MSG);
    pt[2] = '4'; //segment fault
    // printf("%s", MSG);
    return 0;
}

本身字符串是存储在静态常量区,我们打印出他的地址。虽然打印了两次这个字符串,实际程序使用的都是同一个地址,也就是他在静态存储区中只存在一份。
我们发现字符指针是直接指向这个地址,而字符串数组的首地址并不指向这个地址。

也就是有:

  1. 数组形式的定义,表示在计算机分配了一个包含29个元素的数组。把静态存储区中的字符串拷贝到数组中。
  2. 字符指针是直接指向这个字符串所在静态存储区中的地址。

操作常量区字符串

如果我试图修改常量区的字符串,比如上面代码,修改字符串中某一个字符。pt[2] = '4';会在运行时报段错误。
所以建议在使用指针指向的字符串时加const防止修改字符串。

const char *pt = "I'm special";

如果要修改原字符串,则不能使用指针表示法,可以使用字符串数组表示法,这样就可以修改字符串的副本。

定义字符串数组

    const char *mytalents[LIM] = {
    
    
        "Adding numbers swiftly",
        "Multiplying accurately", "Stashing data",
        "Following instructions to the letter",
        "Understanding the C language"
    };
    char yourtalents[LIM][SLEN] = {
    
    
        "Walking in a straight line",
        "Sleeping", "Watching television",
        "Mailing letters", "Reading email"
    };

定义字符串数组,也可以使用上述两种方式。
指针方式:效率更高,因为不用存储字符串副本,但是不能修改原字符串。(绝大多数字符串操作由指针完成)
数组方式:效率低,但是可以修改字符串。

字符串IO(输入/输出)

字符串输入

在读取字符串时,scanf(%s)只能读取一个单词。而我们常常需要读取一整行输入。

gets()函数是直接读取一整行。但是他只有一个唯一的参数,无法检查空间是否能装下用户输入的字符数量。所以C99建议不适用这个函数,而C11直接将该函数废弃。

通常使用fgets()来代替gets()

#define STLEN 14
int main(void)
{
    
    
    char words[STLEN];
    puts("Enter a string, please.");
    fgets(words, STLEN, stdin); //标准输入,从键盘读取
    return 0;
}

fgets()第二个参数会限制读入字符的最大数量。会读入n-1个字符(存储一个空字符),或者遇到读到遇到的第一个换行符为止。
fgets会读入输入的换行符,比如输入 apple, 那么words中存储为apple\n\0

几种字符串输入函数对比

scanf(),gets(), gets_s(), fgets()。

  • gets() 输入太长会擦除数据
  • gets_s() 当输入不符合预期则处理不太方便
  • fgets()通常作为最佳选择
  • scanf() 会在空白字符(空行,空格,制表符或换行符)就读入停止

字符串输出

直接使用puts()函数

字符串函数

字符串函数都在string.h头文件中

长度

size_t strlen(char const *string) 返回值是字符串长度,
他是一个无符号整数类型。

拼接

strcat(char*, char *) 接收两个字符串,并把第二个字符串附加到第一个末尾。然后将第一个字符串修改为拼接后的字符串,第二个不变,返回第一个字符串的地址。

但是strcat有潜在危险,他不会检查第一个字符串是否能够容纳拼接后的字符串。容易造成溢出。如果第一个参数容量不够,则第一个字符串就会丢失’/0’而导致程序崩溃。

 char * a = "abcde";
    char * b = "fgh";
    strcat(a,b); //segment fault

strncat(char*, char *,int ) 在strcat的基础上,增加第三个参数,他表示能添加的最大字符串长度。strncat 函数, 多了一个参数 len ,意思是追加的字符串的 元素的个数 ,从而 避免了 字符串在 追加本身的时候 ,由于丢失‘\0’ ,而 导致的程序崩溃 。还是需要保证第一个字符串容量足够,虽然程序不会崩溃,但是会可能截断第二个参数。

拷贝

char* strcpy( char *dst, char const *src)

与拼接原理类似

strncpy可以保证程序不崩溃,如果

比较

在比较字符串时我们不能用==。因为双等号比较的是地址是否相等。比较字符串内容我们使用strcmp。
strcmp(char* , char*); 字符串比较函数,比较的是两个字符串的内容。而不是整个数组,即使第一个字符串数组长度比第二个大,只要内容相同。就相等。

如果两个字符串相同,则返回0. 如果第一个字符串在第二个字符串前面,则返回负数。如果第一个字符串在第二个字符串后面则返回正数。

strncmp(char*, char*, int); 第三个参数指定,比较的长度,也就是比较两个字符串的前n个字符。

格式化字符串

sprintf()该函数和printf()函数相同,只不过他是把格式化后的字符串放到字符数组中而不是显示到屏幕。
int sprintf( char *buffer, const char *format [, argument,...] );
第一个参数就是指向要写入的那个字符串的指针

int main()
{
    
    
   char str[80];
   float a = 3.14;
   sprintf(str, "Pi 的值 = %f", a);
   puts(str);
   
   return(0);
}

字符串和数字之间转换

数字可以以字符形式存储,也可以以数值形式。

比如213,他可以已字符数组2,1,3,‘0/’的形式存储,也可以以int形式直接存储。

**C语言在运算的时候只能以数值形式,但是在打印显示的时候要求以字符串形式。**比如printf中我们需要把数值使用%d转换成字符串显示出来,而scanf()是把标准输入的字符串给转换成数值保存到某个变量。

数字转换成字符串

  1. 使用sprintf()函数,如上所讲。
  2. 使用itoa,用到stdlib,可将整数转化为不同进制数并输出成字符串

字符串转换成数字

atoi atof atol


#include <stdio.h>
#include <stdlib.h>

int main(void)
{
    
    
    char* s = "3";
    int count = atoi(s);
    for (int i = 0; i < count; i++)
    {
    
    
        printf("%d \n", i);
    }
    
    return 0;
}

但是这些函数有个问题就是他只把开头的整数转成字符,比如"42rng"他会返回整数42,如果字符串中没有数字,他会返回0.这种未定义的情况可能造成不安全的问题。所以我们可以选择下面的函数。

strtol strtoul strtod

long int strtol(const char *str, char **endptr, int base) 把参数 str 所指向的字符串根据给定的 base 转换为一个长整数(类型为 long int 型),base表示进制

#include <stdio.h>
#include <stdlib.h>

int main(void)
{
    
    
    char str[30] = "2030300 This is test";
    char *ptr;
    long ret;

    ret = strtol(str, &ptr, 10); //第一个参数要转换的字符串,第二个参数是转换后的字符串部分,返回值是数值部分
    printf("数字(无符号长整数)是 %ld\n", ret);
    printf("字符串部分是 |%s|", ptr);
    
    return 0;
}

注意没有strtoi

猜你喜欢

转载自blog.csdn.net/chongbin007/article/details/125958968
今日推荐