字符串
-
定义:
char *变量名 = “字符串的内容”;
/char 变量名[] = “字符串的内容”;
两种定义有区别:
*变量名
:字符串常量,不可以修改
变量名[]
:字符串变量
!字符串在内存中实际存储时,有额外的一个字符结束标志 ‘\0’,(e.g. h-a-r-d-‘\0’) -
打印:printf中占位符%s,或者直接puts(“字符串名/指向字符指针”)
e.g. char *string = “hardworld”; /
char string1[] = {‘h’,‘a’,‘r’,‘d’,‘w’,‘o’,‘r’,‘l’,‘d’};
char string2[5] = {‘h’,‘a’,‘r’,‘d’};
char string3[] = “hard”;
1.printf("This is a string:%s\n",string);
2.puts(string);
1). 对于,定义未具体声明长度的字符串数组等于字符集合,且没加入结束标识符‘\0\(如上例string1),puts & printf输出有错误,有的编译器出现重复打印有的出现乱码,不要使用这种定义。 对于定义了具体长度的字符串数组(如chart string[5]= {'h','a','r','d'}
),所声明的长度要包括字符串和最后的 ‘\0’,否则出现同样的问题。另外!!,如果非要写chart string[5]= {'h','a','r','d'}
类型,自行在最后加上’\0’,这样不会导致使用库函数时出现问题(“string.h”库函数都考虑了字符串的‘\0’结束标志)
赋值字符串
- 对于使用malloc开辟空间的字符指针,赋值一个字符串常量是正常的
- 不可以对一个声明的字符数组进行直接(=号)赋值,如下代码段中注释部分,报错: error: assignment to expression with array type。不同于初始化(如下arry),因为c语言中不允许赋值的左值为一个数组(Assignment operator shall have a modifiable lvalue as its left operand. A modifiable lvalue is an lvalue that does not have array type, […]),可以使用strcpy/strncpy来赋值,或者对每个字符数组元素进行赋值
char array[] = "hardworld";
char arry[10] = "hardworld";
char arr[10] ;
strcpy(arr,"hardWorld");
arr[0] = 'h';
arr[1] = 'a';
//arr = "hardworld"; // error: assignment to expression with array type
scanf("%s",arr);
大小的计算:
strlen函数 & sizeof关键字
针对char string[] = "hard";
情况
- strlen
- 用于计算有效字符的长度(不包括’\0’,不考虑定义字符串数组中定义的长度)。
- 对于char 型指针定义的字符串, 其计算大小也是有效字符个数。
- sizeof
以szieof(字符串名)(/sizeof(字符串名[0]))
进行计算(/sizeof(字符串名[0])可忽略,char型为1字节)- 对于将字符串直接赋值的未定义长的字符数组(如下代码中string),计算结果比字符个数多1 ,因为计算了‘\0’
- 该关键字方法计算字符串数组长度,对于下例string1赋值
{'h','a','r','d'}
类型的定义,其长度就是元素个数,因为对于系统这是一个字符型数组,字符只是数组中的元素。 - 对于
*变量名
定义的字符串,因为是指针类型而且指针变量内存中占8字节, 所以使用sizeof算大小时,无论字符串长短都为8
int main(){
char string[] = "hard";
char string1[] = {'h','a','r','d'};
char string2[] = {'h','a','r','d','\0'};
char *string3 = "world";
puts(string1); //错误输出
printf("%s\n",string1);
puts(string2); //错误输出
printf("%s\n",string2);
printf("%s\n",string3);
printf("string size:%d \n",sizeof(string)/sizeof(string[0]));
printf("string1 size:%d \n",sizeof(string1)/sizeof(string1[0]));
printf("string2 size:%d \n",sizeof(string2)/sizeof(string2[0]));
printf("string3 size:%d \n",sizeof(string3)/sizeof(string3[0]));
return 0;
}
输出
hardhard
hardhard
hard
hard
world
string size:5
string1 size:4
string2 size:5
string3 size:8
=================================================================================================
动态开辟字符串
malloc()
按需在堆(heap)上开辟内存空间
- 库:“stdlib.h”
- 函数原型
void *malloc(size_t size)
返回指向它的指针
(size_t 正整数定义类似int,相关链接入口) - 分配空间给字符串时要包括终止空字符(‘\0’)
当后期需要扩大指向空间大小时,可以随时修改其大小,也可用于防止非法访问野指针导致的段错误。
int main(){
char *p; //野指针
p = (char *)malloc(sizeof(char)); //p具有具体内存指向
*p = 's';
printf("%c\n",*p);
p = (char *)malloc(sizeof(char)*17); //增大p指向的空间大小,之前指向断开
strcpy(p,"hard world!");
puts(p);
return 0;
}
s
hard world!
- 对于直接赋值值字符串常量的char型指针, 再赋值这会出现非法访问,(本来想这用free函数释放空间的,但程序跑飞,gdb调试报错了 Critical error detected c0000374,思考了一下发现是因为将指针指向字符串,那么就不是原来malloc的内存了,这篇文章有更详细解释。或者使用strcpy复制字符串就不会),可以通过重新使用malloc分配空间给指针(等于又开辟了一个堆空间),就可以赋新数据。或者定义指针指向malloc分配空间的指针,对这个指针操作,就可以对malloc申请的空间进行释放(如下例)
int main(){
char *p;
p = (char *)malloc(sizeof(char)*1); //p具有具体内存指向
char *s = p;
s = "hard"; //字符串常量
printf("%s\n",s);
free(p);
p = (char *)malloc(sizeof(char)*16); //重新分配空间
strcpy(p,"hard world!");
puts(p);
return 0;
}
输出正确
hard
hard world!
空间释放
malloc在堆(heap)上开辟空间,只有在程序结束时才会释放,当程序中有死循环时,反复malloc可能会耗尽计算机中的堆资源,造成内存泄漏!
free()
- 原型
void free(void *ptr);
- 释放使用calloc,malloc 或realloc所分配的内存空间
- 防止悬挂指针(野指针一种)
e.g
int *p = (int *)malloc(sizeof(int));
free(p);
空间扩容
当malloc开辟的空间不够时,将超出大小的数据进行赋值会造成越界
realloc()
- 原型
void *realloc(void *ptr,size_t size);
- 返回值:该函数返回一个指针 ,指向重新分配大小的内存。如果请求失败,则返回 NULL
- 尝试调整(扩大或者缩小)之前调用malloc或者calloc所分配的ptr指向的内存空间的大小,无论是扩张或是缩小,原有内存的中内容将保持不变,如果时缩小则可能导致原有内容丢失,但有时候编译器还是会打出完整的原始字符串,可以通过gdb调试检查缩小后丢失的字符
- 扩容后首地址不变
int main(){
char *p;
p = (char *)malloc(sizeof(char)*5); //p具有具体内存指向
strcpy(p,"Hard");
printf("before extend:%s\t\t%d\t%p\n",p,sizeof(p),p);
p = realloc(p,sizeof("hardworld!"));
strcpy(p,"hardworld!"); //(该字符串有11个字符(不包括'\0'))
printf("after extend: %s\t%d\t%p\n",p,sizeof(p),p);
free(p);
puts("done!");
return 0;
}
输出正常
before extend:Hard 8 0000012235aa1600
after extend: hardworld! 8 0000012235aa1600
done!
=================================================================================================
字符串常用API
gets
- 库:“stdlib.h”
- `char *gets(char * str);
- 获取字符串输入
e.g.
char *str = (char*)malloc(sizeof(char)*128);
gets(str);
puts(str);
strcpy
- 库:“string.h”
char *strcpy(char *dest, const char* src);
dest:目标字符串,src被拷贝字符串- 拷贝包括终止空字符
e.g.strcpy(string,"hard world!");
strcat
- 库:“string.h”
char *strcat(char *dest, const char *src);
dest目标字符串,src拼接字符串- 字符串的拼接
e.g.
strcpy(src, "This is source");
strcpy(dest, "This is destination");
strcat(dest, src);
strcmp,strncmp
- “string.h”
int strcmp(const char *str1, const char *str2)
/int strncmp(const char *str1, const char *str2, size_t n)
- 字符串的比较,如果返回值小于 0,则表示 str1 小于 str2,如果返回值大于 0,则表示 str1 大于 str2,如果返回值等于 0,则表示 str1 与str2相同,(n表示比较的位数)
e.gstrcmp("hard","world");
strncmp("hard","world",3);
strchr
- “string.h”
char *strchr(const char *str, int c)
- 寻找在str字符串中第一次出现c字符的位置,返回一个指针指向第一次出现的位置
strstr
- “string.h”
char *strstr(const char *haystack, const char *needle)
- 在字符串 haystack 中查找第一次出现字符串 needle 的位置,不包含终止符 ‘\0’
strlwr/ strupr
- “string.h”
- strlwr:
char *strlwr(char *str);
- strupr:
char *strupr(char *str);
- 前者将字符串str字母全部改为小写,后者将str中字符全部改为大写,返回修改后的字符串
strtok
- “string.h”
char * strtok(char str[], const char *delims);
- 分割字符数组str[],以delims为分割点,返回到分隔符之前的字符串
- !只有第一次分隔str填写需要分割的数组,剩余的分割str[]需要填写为NULL,见下例
int main(){
char arry[] = "This is typed Hard world";
char *character[6]; //使用指针数组来存储分割的子字符串
char **pArr = character;
*pArr= strtok(arry," ");
while(*pArr != NULL){
//因为已经存入了一个字符串,但指针没偏移,所以需要先偏移,后赋值
//此处偏移相当于是pArr行数组偏移
*++pArr = strtok(NULL, " ");
}
pArr = character; //返回首地址
while(*pArr != NULL){
printf(" %s\t",*pArr++);
}
return 0;
}