C语言小知识---特殊的字符串

x33g5p2x  于2021-10-09 转载在 其他  
字(2.5k)|赞(0)|评价(0)|浏览(475)

字符串在程序中很常见,和整数、小数、字符其他数据类型一样,是代码中必不可少的一部分。可是字符串却又和其他类型不完全一样,还有自己的个性。

下面直接通过一段代码来看。

定义了一个字符1.一个字符串1,然后将他们打印出来,打印结果如下:

从打印的结果来看,二者并没有差别。

下面接着打印一下这两个的长度,使用sizeof()函数,分别计算s1和s2的长度并打印出来。

可以看到字符s1的长度是1,字符串s2的长度是2, 在内存中查看s1和s2的存储地址

从内存存储位置可以看出字符s1占了一个字节,字符串s2占了两个字节,后面的一个字节是空的。所以通过sizeof()函数计算出来s2所占空间的大小是2。

在这里要顺便说一下计算长度的函数sizeof()strlen(),这两个都可以计算字符串的长度,但是 sizeof() 函数计算出来的是实际占据空间的大小,而strlen()计算出来的是有效字符的长度。所以对于字符串而言sizeof()函数计算的结果会比strlen()计算出来的结果大1。

比如这里定义一个字符串“abc”,然后分别使用这两个函数计算它的长度。

char s2[]="123";
printf( " %d %d\r\n\r\n\r\n", sizeof(s2),strlen(s2));

从打印的结果可以看出,字符串有效长度是3,但是字符串所占空间大小是4。

接下来看看对字符的操作和字符串的操作是否一样。

对字符s1和字符串重新赋值,此时编译器报错,不允许直接字符串s2赋值,这是由于字符串在内存中是按照字符数组来存储的,也就是存储在一系列连续的存储空间内,每一个字符占一个字节。所以不能对字符串整体进行操作,要改变字符串的值,只能像修改数组的值一样,每一位单独操作。

所以这里要改变字符串中存储的字符,就直接给数组的第0位赋值。

可以看到对s1和s2的第0位重新赋值后,内存中存储的数据也发生了变化,和打印出来的数据一样。

注意内存中存储的字符是以ASCII码的方式存储的,而串口打印出来的直接就是字符。ASCII码 0x31 对应的字符就是1,ASCII码 0x32 对应的字符就是2。

同样将一个字符赋值给另一个字符时可以正常赋值,但是不能将一个字符串直接赋值给另一个字符串。

要将字符串赋值给另一个字符串时,也需要向数组一样,一位一位的赋值。

打印结果如下

在内存中查看

内存中的结果和打印的结果一致。接下来在看一个例子,将s1和s2传递到其他函数中进行操作。

char s1='1';
char s2[]="1";

void change1(char s3)
{
  s3 = s3 + 1;  
  printf( "s3 -> %c \r\n", s3 );  
}
void change2(char s4[])
{
  s4[0] = s4[0] + 1;
  printf( "s4 -> %s \r\n", s4 );  
}

void main( void )
{
   //省略无关代码 
    while( 1 )
    {     
        change1(s1);
        change2(s2);
        printf( "s1 -> %c s2 -> %s\r\n\r\n\r\n", s1,s2 );   
             
        delay_ms( 5000 );
    }
}

在将s1和s2的值传递到外部函数之后,在外部函数里面对字符加1,然后打印出来,从外部函数退出之后,接着打印s1和s2的值。

打印结果如下

s1的值是字符1,然后将s1传递给s3,然后s3加1之后,打印出字符2,符合代码逻辑。

s2的值是字符串1,然后将s2传递给s4,然后s4加1之后,打印出字符2,符合代码逻辑。

接下来从子函数退出后,分别打印s1和s2的值,可以发现s1的值依然是1,但是s2的值却变成的2。

如果程序一直执行的话,会发现s1和s3的值一直没有变,但是s2和s4的值却一直在增大。

这是什么原因呢?字符串传递给子函数后,字符串本身也会被子函数改变?

为了一探究竟,下面开始单步调试,同时查看内存中数据的变化情况。

当代码执行到 s3 = s3 + 1;这一行时,系统中s3的存储位置为A,数值为字符’1’,此时s1的存储位置为0x00000C。下面对s3执行加1操作。

执行完加1操作之后,s3的值变成了字符’2’,此时s1的值依然是字符’1’,未发生变化。

接下来执行第二个函数,对字符串s4进行操作。

可以看出当执行到 s4[0] = s4[0] + 1; 这一行时,此时s4在内存中的地址是0x00000D,而s2的内存地址也是0x00000D,难道s4和s2是一个地址吗?继续执行程序观察。

当对s4中第0位的字符执行完加1操作之后,s4中的字符变成了’2’,而此时s2中的字符值也变成了’2’,说明s4和s2在内存中的确是同一个地址。

两个函数执行完成之后,继续打印s1和s2的值,由于在子函数执行的时候,s1的地址上值未发生改变,s3的值是s1的值加1,s2和s4指向的是同一个地址,s4每次循环都要加1,所以s2的值每次也会加1。

为什么对于字符没有发生变化,而字符串发生了变化?这是因为在C语言中字符串属于引用类型,而字符属于值类型?

值类型包括:byte,short,int,long,float,double,decimal,char,bool 和 struct。

引用类型包括:string 和 class。

系统对于值类型操作时,如果将一个值类型赋值给另一个值类型,那么系统就会重新开辟一个空间,然后将值复制一份,然后再将值存入新开辟的空间中。这样当操作新数据的时候,不会影响以前的数据,因为两个值都在自己的存储空间中。

系统对于引用类型操作时,如果将一个引用类型赋值给另一个引用类型,系统检测到这两个值如果全完一样的话,那么系统就不会重新开辟空间给新的引用类型,而是直接让新的引用类型地址指向旧的引用类型,让这两个值公用同一个内存空间,也就是相当于给一个地址空间起了两个名字,这样改变任意一个值时,地址空间内的值也会发生变化。

所以在使用字符串的时候,一定要注意这一点,这是它和其他类型值的最大区别。
如果不加小心,在传递值的过程中,数据很容易出错。

相关文章