关于字符串数组输入输出和内存分配问题
C语言字符串数组的正确使用是处理字符问题的根本,但是由于C语言本身的特性,字符串数组有很多的使用细节需要注意,下面将以字符串数组输入输出问题为切入点,并且引出字符串数组在内存中的分配和字符写入顺序问题:
先举一个简单的例子:
定义字符串数组,并读入输出(为了看清楚字符串数组读取到了哪个位置,以##为末尾提示符):
char word1[7];
char word2[7];
scanf("%s",word1);
scanf("%s",word2);
printf("%s##%s##\n",word1,word2);
这是一个很简单的程序,定义两个最多能读入六个字符的字符串数组,并分别读入字符,输出。
下面我们用四组特殊数据来测试这段代码:
第一组:
123456
123456
123456##123456##
读入的数据长度为6位,这是正常情况。
第二组:
12345678
12345678
12345678##12345678##
我们尝试将要读入的数据扩大成8位(此时字符串数组已经“溢出”),但我们仍看到了我们想要的结果。
第三组:
123456789012345
123456789012345
123456789012345##123456789012345##
再次扩大要读入数据的长度至15位,运行一切正常。
第四组:
1234567890123456
1234567890123456
##1234567890123456##
再次扩大要读入数据的长度至16位,神奇的事情发生了:
原本先读完数据的word1似乎“没储存任何数据”,word2则输出了完整的16位字符。
我们试着进行分析:
在编译阶段,word1和word2被分配了空间,但是这两个空间的排序是怎样的?空间大小又是怎样的?
如果word1和word2的内存地址是紧挨着的,有没有可能写入溢出的数据时会互相产生影响,从而导致word1中不输出任何内容?
既然C语言中的字符串输出是以字符串数组中的结尾“\0”为终止符,如果word1数组中第一个字符就是“\0”的话,word1就不会输出任何内容
“\0”的首位置出现说明word1的数据一定在运行时被二次写入并覆盖了
我们修改代码来验证猜想:
char word1[7];
char word2[7];
scanf("%s",word1);
scanf("%s",word2);
printf("%s##%s##\n",word1,word2);
printf("word1的地址是 %d\n",word1);
printf("word1[1]的地址是 %d\n",&word1[1]);
printf("word1[15]的地址是 %d\n",&word1[15]);
printf("word2的地址是 %d\n",word2);
printf("word2[1]的地址是 %d\n",&word2[1]);
printf("word2[15]的地址是 %d\n",&word2[15]);
return 0;
当%d与数组一起使用时会输出数组第一个元素的内存地址,我们便可以知道两个数组的首地址关系和各元素的地址关系,下面我们运行试试看:
1234567890123456
1234567890123456
##1234567890123456##
word1的地址是 6487616
word1[1]的地址是 6487617
word1[15]的地址是 6487631
word2的地址是 6487600
word2[1]的地址是 6487601
word2[15]的地址是 6487615
这两个内存地址相差16个字节,说明每个word数组长度为16个字节
需要注意的是,word1的地址大于word2的地址;而内存地址的排列可以看做自上而下的,即word1在下部,word2在上部
word[0]到word[15]的地址依次增大,说明单个字符串数组写入数据是自上而下的
可以用下面的表格来示意:
数组元素 | 内存地址 | 字符内容 |
word2[0] | 6487600 | 1 |
word2[1] | 6487601 | 2 |
… | … | … |
word2[14] | 6487614 | 5 |
word2[15] | 6487615 | 6 |
word1[0] | 6487616 | \0 |
word1[1] | 6487617 | 2 |
word1[2] | 6487618 | 3 |
… | … | … |
word1[14] | 6487630 | 5 |
word1[15] | 6487631 | 6 |
结论:
很明显,位于底部的word1数组自上而下写入了1234567890123456之后,溢出的“\0”写入了下面不知名(未定义)的内存中,而位于上部的word2数组也自上而下写入1234567890123456之后,末尾的“\0”则写入了word[15]中,导致wor1的输出为空
需要说明的是,这段代码在x64位电脑中运行,编译器默认分配的字符串数组空间是16位,在不同的运行环境(编译器)中会有差别
以上是我的理解,欢迎补充和指正!