菜单开关

周梦康 发表于 2017-09-18 1559 次浏览 标签 : C语言快速入门

免费领取阿里云优惠券 我的直播 - 《PHP 进阶之路》

引入

实验

我想通过以下方式来打印字符数组ab

#include <stdio.h>

int main(int argc, char const *argv[]) {
  char a[3] = {'d','e','f'};
  char b[3] = {"abc"};

  printf("%s\n", b);
  printf("%s\n", a);
  return 0;
}

运行

zhoumengkang@OSX10111-3c15c2ba060a:~/Downloads$ gcc test4.c -o test4
zhoumengkang@OSX10111-3c15c2ba060a:~/Downloads$ ./test4
abcdef��6R�
def��6R�
zhoumengkang@OSX10111-3c15c2ba060a:~/Downloads$ ./test4
abcdefȪ
R�
defȪ
R�
zhoumengkang@OSX10111-3c15c2ba060a:~/Downloads$ ./test4
abcdef��R�
def��R�
zhoumengkang@OSX10111-3c15c2ba060a:~/Downloads$ ./test4
abcdef�*�^�
def�*�^�

规律

打印 b 的时候总是会把 a 也附带在后面;
a 本身也会附带一些“诡异”的乱码。

原因

因为我们在定义ab的时候都没有在末尾加上\0,这样在打印字符串的时候,会一直从字符串首字符的指针地址向后,一直输出,直到遇到\0
而 a 和 b 都是在栈上分配内存,所以是向下增长,所以 b 的地址比 a 的小;
当打印 b 的时候,末尾没有找到\0,而紧接着后面就是 a 了,所以继续打印。一直打印完 a 还是没有发现\0,继续向后输出,直到发现\0
这样就解释了上面发现的规律。

改进

  char a[4] = {'d','e','f','\0'};
  char b[4] = {"abc"};  // 等同于 char b[4] = "abc";

定义的时候多申请一个字节的内存(也就是数组数加一),然后末尾加上\0;然后再打印就不会出现诡异的乱码了。
双引号初始化的时候,会申请的内存空间够用的情况下,末尾加上\0,详情见之前数组的笔记:https://mengkang.net/1008.html#blog-title-4

字符串

在 C 语言中,字符串实际上是使用 null 字符 \0 终止的一维字符数组。
初始化字符串时,请务必使用双引号,java 里面也如此。

#include <stdio.h>

int main(int argc, char const *argv[]) {
  char *c = "abcdef";
  printf("%s\n", c);

  return 0;
}

以指针的方式来定义的时候,则会默认在字符串的末尾加上一个\0,这样打印的时候就不会越界了。

字符串初始化的原理

按照我们前面对指针的理解:这里的c是一个char类型的指针,应该是赋值一个char字符的地址才对呀。这么用才前面说的基本数据类型和指针对应的思路

char a = 'a';
char *c = &a;

的确,上面的方式没问题。

那么为什么字符串也可以赋值给字符指针变量呢?

char *c = "abcdef";

双引号的秘密

双引号做了3件事:

  • 在常量区申请内存,存放字符串
  • 在字符串尾加上了'/0'
  • 返回字符串的首地址

所以这里的赋值操作是将abcdef\0的首地址赋值给了c

字符串的生命周期

假如在一个函数里声明了一个常量字符串,那么在函数运行结束后,它的内存会回收吗?

#include <stdio.h>

char * a();

int main(int argc, char const *argv[]) {
  
    a();
    a();

    return 0;
}

char * a()
{
    char *b = "string";
    printf("%p\n", b);
    return b;
}

运行结果

$ ./test
0x1069bdfa6
0x1069bdfa6
$ ./test
0x1077e1fa6
0x1077e1fa6
$ ./test
0x10d6cdfa6
0x10d6cdfa6

可以看出,字符串的内存在函数运行结束后是没有被回收的。

嗨,老铁,欢迎来到我的博客!

如果觉得我的内容还不错的话,可以关注下我在 segmentfault.com 上的直播。我主要从事 PHP 和 Java 方面的开发,《深入 PHP 内核》作者之一。

[视频直播] PHP 进阶之路 - 亿级 pv 网站架构的技术细节与套路 直播中我将毫无保留的分享我这六年的全部工作经验和踩坑的故事,以及会穿插着一些面试中的 考点难点加分点

评论列表