字符, 字符数组, 字符指针和字符串.
上回说到, 有一种很常见的情境下我们一直在使用字符数组, 但调用函数的时候并没有传入数组的大小. 这个情景就是字符串.
实际上, c语言标准中并没有所谓的"字符串"类型, 所谓的字符串都是一定字符数组.
调用字符串相关函数的时候, 传入的都只是数组名字, 正如上篇推送所说的, 对于接受数组为参数的函数, 参数实际上是指针. 字符串相关函数都只是读取指针指向的值, 让指针+1
, 等到指针指向的值为'\0'
为止. 因此, 字符串必须以\0结尾.
也正因为如此, 一个字符指针可以作为参数传入如puts
等相关函数. 如, 对于一个字符串数组(char的二维数组)char data[100][100]
, data[0]
是这个二维数组的第0个元素, 也就是一个一维数组 (char [100]
). data[0]
就是指向这个一维数组首元素的指针. 比如, 求这个字符串数组中最长的一个字符串可以这么做:
1 2 3 4 5 6 7 8 9 10 11
| char * getLongest(char strs[][200], int n){ 注释: 还记得为啥要写第二维的大小吗? char * ret = strs[0]; int i = 0; for(i = 0; i < n; ++ i){ if(strlen(s[i]) > strlen(ret)){ ret = s[i]; } } return ret; }
|
值得注意的是, 返回的ret是一个指向字符串数组(char的二维数组)中的某个字符串的首元素的地址, 并不是复制了一份字符串返回. 如果修改了原先字符串数组中的值, 从ret指针读取到的值也会一样改变.
在字符串数组中, 漏写\0
会引发更加奇怪的问题. 对于以下代码:
1 2 3 4 5 6 7 8 9
| char strs[5][5] = {'\0'}; int i,j; for(i = 0; i < 5; ++ i) for(j = 0; j < 5; ++ j){ strs[i][j] = '*'; } for(i = 0; i < 5; ++ i){ puts(strs[i]); }
|
他会输出什么内容呢? 大概率是类似下面这样的东西:
1 2 3 4 5
| *************************乱码乱码乱码 ********************乱码乱码乱码 ***************乱码乱码乱码 **********乱码乱码乱码 *****乱码乱码乱码
|
为什么会出现这种现象呢?
1 2 3 4 5 6 7 8 9 10
| 左滑查看答案--------------------------> puts函数会从传入的指针 左滑查看答案--------------------------> 开始往后遍历输出, 遇到\0 左滑查看答案--------------------------> 才停止输出. 因此, 当他从strs[0][0] 左滑查看答案--------------------------> 打印到strs[0][4]时, 左滑查看答案--------------------------> 下一个字节实际上是strs[1][0], 左滑查看答案--------------------------> 仍然不是\0. 后面的几个字符串被 左滑查看答案--------------------------> 打印了好几次. 当打印完全部字符串后, 左滑查看答案--------------------------> puts会接着往后打印, 由于后面是 左滑查看答案--------------------------> 未初始化的内存, 因此打印出来乱码, 左滑查看答案--------------------------> 直到某个地方恰巧为\0才停止.
|
还有一个有意思的问题, 那就是以下代码是什么意思:
1 2 3 4 5 6 7 8 9
| char str[100]; gets(str); char * p = str; while (*p){ ... do something... ... ++p; }
|
我们来逐层分析这一句: p
是一个指向char*
的指针, *p
是这个char变量所存储的值. while(*p)
就是当这个char变量存储的值不为0的时候继续循环. (非0就是真). 又由于'\0
的ascii码是0, 因此, 这句话的全部意思是当p
指向的char为\0
时退出循环.
下面是附录: 增强可读性后的几个字符串相关函数的实现.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| int strlen(char *org) { char *s = org; while (*s){ s++; } s = s - 1; return s - org; } char *strcpy(char *ret, char *s2) { char *s1 = ret; while ((*s1 = *s2) != '\0'){ s1++; s2++; } return ret; } char *strcat(char *ret, char *s2) { char *s1 = ret; while (*s1 != '\0'){ s1++; } s1--; while (*s1 = *s2){ s1++; s2++; } return ret; }
|