(二) C语言的字符串

上一篇:https://blog.csdn.net/qq_40088639/article/details/111562273

二、C语言的字符串

1. C语言的字符串构成

字符串由零个多个字符组成,使用双引号括起来(单个字符,使用的是单引号括起来),并且是一个有限的字符序列。比如:"hello world"、"字符串"、"12345678"。对于单个字符,使用双引号括起来,也是一个字符串,比如:"A"、"8"。

 

2. 字符串的存储

计算机使用的是一片连续的内存来存储一个字符串,因为字符串是由字符来组成的,所以,实际上就是把字符串拆分成单个字符进行存储。C程序中一般使用字符型数组或者一个字符型指针来存储一个字符串。

 

3. 字符串的结束符(空字符)

存储字符串,为了能和单个字符进行区分(比如:“A”和’A’),在编译阶段编译器会在字符串的末尾加上一个结束符。

 

4. 字符数组的赋值方式

字符数组可以用来存储字符串,直接进行赋值即可。在定义字符数组的时候,可以指定数组的长度,也可以不指定,如果指定数组的长度,长度一定要是:字符串实际的长度+1。要腾出一个空间,让系统加上结束符。在字符串中自动添加结束符是多余的,系统又会自动添加一个。可以使用sizeof()来查看变量实际占用的内存空间大小,内存空间大小是在编译阶段就确定了的。

示例程序01:指定和不指定数组长度

u8_t str_buf1[]="123456\0";

u8_t str_buf2[]="123456";

//u8_t str_buf2[]={"123456"};  /* 花括号可以省略 */

u8_t str_buf3[7]="123456";

 

int main(void)

{

         printk ("sizeof(str_buf1): %d", sizeof(str_buf1));

         printk ("sizeof(str_buf2): %d", sizeof(str_buf2));

         printk ("sizeof(str_buf3): %d", sizeof(str_buf3));

 

         printk ("strlen(str_buf1): %d", strlen(str_buf1));

         printk ("strlen(str_buf2): %d", strlen(str_buf2));

         printk ("strlen(str_buf3): %d", strlen(str_buf3));

 

         return 0;

}

结果:

/*内存大小由编译器自动分配*/

sizeof(str_buf1): 8  /* 实际占用的内存大小:字符串长7 + 系统加上的结束符\0 */

sizeof(str_buf2): 7  /* 实际占用的内存大小:字符串长6 + 系统加上的结束符\0 */

 

sizeof(str_buf3): 7  /* 实际占用的内存大小:定义数组时,指定的值7 */

                 /* 这里的str_buf3[6]=’\0’,是系统加上的。所以,如果*/

                 /* 定义的时候,指定数组的大小为6,那么编译器会报*/

                 /* 警告!即使编译通过,运行程序时,内存已经不合法,*/

/* 比如使用strlen()求串长的时候,会得到异常值[系统也会加上结束符]。但是,并不一定是在str_buf3[6]之后加的,有可能str_buf3[6]之后连续的很长一块内存都不是空余的,假如在某个位置n加上了结束符,但是n位置之前有0值,strlen()遍历的时候,得到的也不是具体的串长。所以,要遵循规则,多申请一个空余的字节!!*/

/*严格一点的编译器就会报错,没法编译下去*/

 

strlen(str_buf1): 6  /* 遇到结束符(程序加上的),结束 */

strlen(str_buf2): 6  /* 遇到结束符(系统自动加上的),结束 */

strlen(str_buf3): 6  /* 遇到结束符(系统自动加上的),结束 */

 

 

示例程序02(不指定长度):字符数组也可以存储字符,但是花括号不能省略,并且系统不会加上结束符。

u8_t str_buf1[]={'1','2','3','4','5','6'}; /* 花括号可以省略 */

u8_t str_buf2[]="123456";

int main(void)

{

         printk ("sizeof(str_buf1): %d", sizeof(str_buf1));

         printk ("sizeof(str_buf2): %d", sizeof(str_buf2));

         return 0;

}

结果:

sizeof(str_buf1): 6  /* 不是一个字符串,系统不会再增加另一个字符[结束符] */

sizeof(str_buf2): 7  /* 是一个字符串,系统增加一个字符[结束符] */

 

对于上面程序,如果分别使用strlen()函数计算两个字符数组的长度,结果会如何?如下。

程序03:

u8_t str_buf1[]={'1','2','3','4','5','6'};

u8_t str_buf2[]="123456";

int main(void)

{

         printk ("sizeof(str_buf1): %d", sizeof(str_buf1));

         printk ("sizeof(str_buf2): %d", sizeof(str_buf2));

 

         return 0;

}

结果:

main: sizeof(str_buf1): 6

main: sizeof(str_buf2): 7

 

main: strlen(str_buf1): 12  /* 异常值 */

main: strlen(str_buf2): 6   /* 正常值 */

 

分析:

对于函数strlen(),给一个地址作为入口,它就会不停计数,直到遇到结束符’\0’,也就是0。函数原型如下:

/**

 *

 * @brief Get string length

 *

 * @return number of bytes in string

 */

size_t strlen(const char *s)

{

 size_t n = 0;

 

 while (*s != '\0') {

  s++;

  n++;

 }

 

 return n;

}

当字符数组存储的不是字符串,而是字符,并且字符中不包含结束符的时候,函数strlen()的遍历什么时候结束?

答:遇到0才结束。不同的系统会运行出不同的值。它会从某个地址开始,一直遍历内存上的值,直到遇到0,什么时候遇到0,取决于系统,比如收到其他程序,像bootloader的影响。比如,新增一个数组,第一个元素就赋值为0,则计算的长度就是正确的(一般来说C语言会在连续的内存地址上存储数组)。示例程序如下:

u8_t str_buf1[]={'1','2','3','4','5','6'};

u8_t str_buf3[]={NULL,'2','3','4','5','6'};  //0就是NULL

 

int main(void)

{

         printk("sizeof(str_buf1): %d", sizeof(str_buf1));

         printk ("strlen(str_buf1): %d", strlen(str_buf1));

         return 0;

}

结果:

sizeof(str_buf1): 6

strlen(str_buf1): 6  /* 刚好碰上0 */

 

示例程序(指定长度):字符数组存储多个字符0

u8_t str_buf1[10]={'1','2','3','4','5','6'};

用这种方法定义时,系统会从未初始化的元素开始,将之后的元素赋为'\0'。所以上面定义的str_buf1数组,内存中所有元素的值存在形式为:'1','2','3','4','5','6','\0','\0','\0','\0'

 

5. 小结

(1)字符型数组可存储多个字符,也能存储一个字符串。区别就是对于字符串,系统会自动加上结束符,而对于存储多个字符的情况而言,如果没指定数组长度,系统不会加上结束符,需要在程序中手动加上,要么指定数组长度。

正确的写法:

//u8_t str_buf1[]={'1','2','3','4','5','6'};

u8_t str_buf1[]={'1','2','3','4','5','6', '\0'}; /* 等价 */

u8_t str_buf1[7]={'1','2','3','4','5','6'};      /* 等价 */

(2)在程序中,NULL的值就是0,所以,结束符有多种说法:NULL、0、’\0’

(3)字符数组可以指定/不指定长度,仅针对字符数组而言。而对于其他类型数组,在定义时必须有明确的长度(长度值不能是一个值不确定的量),否则编译出错!

 

6. 字符指针的使用

直接进行赋值,示例程序如下:

u8_t *str_ptr0 = "123456";

int main(void)

{

         printk("sizeof(str_ptr0): %d", sizeof(str_ptr0));

         printk ("strlen(str_ptr0): %d", strlen(str_ptr0));

         printk ("0x%02x", str_ptr0);

         printk ("0x%02x", &str_ptr0[0]);  /* 可以当数组来使用(结合下标对元素进行引用) */

         printk ("%s", str_ptr0);

 

return 0;

}

结果

sizeof(str_ptr0): 4   /* sizeof()运算符计算指针的大小,永远是4字节 */

strlen(str_ptr0): 6   /* 字符串在内存中的存在形式:123456\0 */

0x8014494        /* 字符’1’的地址*/

0x8014494        /* 字符’1’的地址*/

123456

 

6. 字符串常量

在C语言中,字符串被放到常量区。对于字符型指针,可先声明,后赋值。

示例程序:

u8_t *str_ptr0;

//const u8_t *str_ptr0;

int main(void)

{

        str_ptr0 = "123456";

         printk("sizeof(str_ptr0): %d", sizeof(str_ptr0));

         printk ("strlen(str_ptr0): %d", strlen(str_ptr0));

         printk ("0x%02x", str_ptr0);

         printk ("0x%02x", &str_ptr0[0]); 

         printk ("%s", str_ptr0);

 

         return 0;

}

结果:

sizeof(str_ptr0): 4

strlen(str_ptr0): 6

0x8009f5c

0x8009f5c

123456

分析:

  1. 定义了一个普通字符指针,并没有定义空间来存放字符串"123456"。
  2. 编译器把"123456\0"当成常量,并把它放到程序的常量区。
  3. str_ptr0是一个普通的指针变量,所以str_ptr0[指针]是被放在全局区上的[ 这里定义的是一个全局变量 ], 只不过是它所指向的东西(值)被放在常量区[ 这里加不加const都一样 ]。

也可以使用单个字符对字符串指针进行赋值,但是不建议这么使用,很迷惑。示例程序如下:

u8_t *str_ptr1 = '1';

int main(void)

{

         printk("0x%x", &str_ptr1);

         printk("%d", str_ptr1);

         printk("%c", str_ptr1);

 

         return 0;

}

结果:

main: 0x1008718

main: 49

main: 1

下一篇:https://blog.csdn.net/qq_40088639/article/details/111571807

你可能感兴趣的:(C语言,c语言,string,字符串)