字符数组与字符串
- 字符串是最后一个字符为
NULL
\0
字符的字符数组。字符串一定是字符数组 - 字符数组,即字符类型的数组。字符数组不一定是字符串
- 字符数组的长度是固定的,其中的任何一个字符都可以为
NULL
字符 - 字符串只能以
NULL
结尾,其后的字符便不属于该字符串 strlen()
等字符串函数对字符串完全适用,对不是字符串的字符数组不适用,容易出现问题
//这是字符数组赋初值的方法
char cArr[] = {'Q','U','A','N','X','U','E'};
//这是字符串赋初值的方法
char sArr[] = "quanxue";
//用sizeof()求长度
printf("cArr的长度=%d\n", sizeof(cArr)); //长度为7
printf("sArr的长度=%d\n", sizeof(sArr)); //长度为8,最后一位是NULL
//用printf的%s打印内容
printf("cArr的内容=%s\n", cArr); //不能正确显示
printf("sArr的内容=%s\n", sArr); //可以正确显示
//用strlen()求长度
printf("cArr的长度=%d\n", strlen(cArr)); //不正确的结果
printf("sArr的长度=%d\n", strlen(sArr)); //NULL不在计算范围
从上面例子看来,还要注意以下几点:
- char sArr[] = “quanxue”;这种方式,编译时会自动在末尾增加一个NULL字符
- sizeof()运算符求的是字符数组的长度,而不是字符串长度
- strlen()函数求的是字符串长度,而不是字符数组。它不适用于字符串以外的类型
- char sArr[] = “quanxue”;也可以写成char sArr[8] = “quanxue”;(注意:是8而不是7)
字符数组中插入一个NULL字符,NULL字符前面(包括NULL字符)就成了字符串,一般NULL字符插在有效字符的最后
//因为最后有NULL,所以这就变成了字符串
char cArr[] = {'Q', 'U', 'A', 'N', 'X', 'U', 'E', '\0'};
//因为少定义了一位,最后无NULL,所以这就变成了字符数组
char sArr[7] = "quanxue";
//最后一个元素未赋值
char tArr[16] = "www.quanxue.cn";
sizeof
sizeof(...)
是运算符,在头文件中 typedef
为 unsigned int
,其值在编译时即计算好了,参数可以是数组、指针、类型、对象、函数等。
它的功能是:获得保证能容纳实现所建立的最大对象的字节大小。
由于在编译时计算,因此 sizeof
不能用来返回动态分配的内存空间的大小。实际上,用 sizeof
来返回类型以及静态分配的对象、结构或数组所占的空间,返回值跟对象、结构、数组所存储的内容没有关系。
具体而言,当参数分别如下时,sizeof返回的值表示的含义如下:
- 数组 编译时分配的数组空间大小
- 指针 存储该指针所用的空间大小(存储该指针的地址的长度,是长整型,应该为4
- 类型 该类型所占的空间大小
- 对象 对象的实际占用空间大小
- 函数 函数的返回类型所占的空间大小。函数的返回类型不能是void
strlen
strlen(...)
是函数,要在运行时才能计算。参数必须是字符型指针(char*)
。当数组名作为参数传入时,实际上数组就退化成指针了。
它的功能是:返回字符串的长度。该字符串可能是自己定义的,也可能是内存中随机的,该函数实际完成的功能是从代表该字符串的第一个地址开始遍历,直到遇到结束符NULL。返回的长度大小不包括NULL。
strcpy
strcpy
提供了字符串的复制,只用于字符串复制,会复制字符串的结束符。
char* strcpy(char* dest, const char* src);
strcpy
不需要指定长度,它遇到被复制字符的串结束符\0
才结束,所以容易溢出
memcpy
memcpy
提供了一般内存的复制,对于需要复制的内容没有限制。例如字符数组、整型、结构体、类等。
void *memcpy( void *dest, const void *src, size_t count );
memcpy
是根据其第3个参数决定复制的长度
strncpy
char *strncpy(char *dest, char *src, int n);
功能:把src所指由NULL结束的字符串的前n个字节复制到dest所指的数组中。
- 如果src的前n个字节不含
\0
字符,则结果不会以\0
字符结束 - 如果src的长度小于n个字节,则以
\0
填充dest直到复制完n个字节 - src和dest所指内存区域不可以重叠且dest必须有足够的空间来容纳src的字符串
如果对src发生了截取,返回的dest不是想要的字符串,因为没有\0
,如果对dest进行字符串类操作,会发生错误!!
使用 snprintf
替换这种可能有隐含错误的用法
snprintf
snprintf
是 sprintf
的安全版本,防止溢出
int sprintf(char *str, const char *format, ...);
int snprintf(char *str, size_t size, const char *format, ...);
snprintf(char *dest, size_t size, "%s", char *src);
dest
会输出 size-1
字符和 \0
结尾的字符串
sprintf 与 snprintf
代码如下:
char tlist_1[1024] = {0},tlist_2[1024]={0};
char fname[7][8] = {"a1","b1","c1","d1","e1","f1","g1"};
int i = 0, len_1,len_2 = 0;
len_1 = snprintf(tlist_1,1024,"%s;",fname[0]);
len_2 = snprintf(tlist_2,1024,"%s;",fname[0]);
for(i=1;i<7;i++)
{
len_1 = snprintf(tlist_1,1024,"%s%s;",tlist_1,fname[i]);
len_2 = sprintf(tlist_2,"%s%s;",tlist_2,fname[i]);
}
printf("tlist_1: %s\n",tlist_1);
printf("tlist_2: %s\n",tlist_2);
>> tlist_1: g1;
>> tlist_2: a1;b1;c1;d1;e1;f1;g1;
上述代码表明, snprintf
会清除缓冲区内容, sprintf
不会清除缓冲区内容,针对使用场景分别使用
snprintf
调用 _IO_vsnprintf
实现:
int
_IO_vsnprintf (string, maxlen, format, args)
char *string;
_IO_size_t maxlen;
const char *format;
_IO_va_list args;
{
...
string[0] = '\0';
...
}
sprintf
调用 _IO_vsprintf
实现中,没有相关的操作。