本文共 7613 字,大约阅读时间需要 25 分钟。
在C语言中有专门的字符串变量,通常用一个字符数组来存放一个字符串。字符串总是以’\0’作为串的结束符。
当把一个字符串存入一个数组时,也把结束符 ‘\0’存入数组,并以此作为该字符串是否结束的标志。有了’\0’标志后,就不必再用字符数组的长度来判断字符串的长度了。
参数可以是数组、指针、类型、对象、函数等
char sArr[] = "ILOVEC";printf("sArr的长度=%d\n", sizeof(sArr)); 👉结果为7(因为它包括结束字符 null)可以以函数作为参数,例如:short f(); printf("%d\n", sizeof(f())); 输出的结果是sizeof(short),即2。
字符数组 没有\0 有数组长度,不需要标识结束字符串 有\0 需要\0以此来标识结束c语言没有string类型,故用字符数组存放,并用\0标识结束👉实现类似c++中的字符串结构char str[]="xunlei"; 需要末尾胡添加\0👉sizeof(str)=7char str2[]={ 'x','u','n','l','e','i'}; 没必要添加\0👉sizeof(str2)=6
当数组名作为参数传入时,实际上数组就退化成指针了size_t strlen(const char *str) 参数必须是字符型指针(char*)且必须以“\0”结尾{ assert(str != NULL); int len; while((*str++) != '\0') { len++; } return len; }
size_t和int的区别
①int在32位和64位架构中都是4字节(有符号数) ②size_t在不同架构中长度不同(无符号数) 1、32位架构中被定义为:typedef unsigned int size_t; (4字节) 2、64位架构中被定义为:typedef unsigned long size_t; (8字节) 为什么要使用size_t: 不同架构中自适应长度,避免了固定4字节(可能不够大)或者固定8字节(可能浪费)时的问题 影响:由于函数 strlen 返回的是一个类型为 size_t 的值,从而有可能让程序导致意想不到的结果,如下面的示例代码所示: 情况1: if(strlen(x)>= strlen(y))→→→→→真假看具体情况 比如if(strlen(x)>= 5) 情况2: if(strlen(x)- strlen(y)>= 0)→→size_t-size_t=size_t是无符号整型→必定满足>=0→必定为真 比如if(strlen(x)- 5>=0)
1)sizeof操作符的结果类型是size_t,它在头文件中typedef为unsigned int类型。
2)sizeof是运算符,strlen是函数。 3)sizeof可以用类型做参数,strlen只能用char*做参数,且必须是以’’\0’'结尾的。 4)数组做sizeof的参数不退化,传递给strlen就退化为指针了。 5)大部分编译程序 在编译的时候就把sizeof计算过了 是类型或是变量的长度。这就是sizeof(x)可以用来定义数组维数的原因 char str[20]=“0123456789”; int a=strlen(str); //a=10; int b=sizeof(str); //而b=20; 6)strlen的结果要在运行的时候才能计算出来,用来计算字符串的长度,不是类型占内存的大小。 7)sizeof后如果是类型必须加括弧,如果是变量名可以不加括弧。这是因为sizeof是个操作符不是个函数。 8)当使用了一个结构类型或变量时, sizeof 返回实际的大小, 当使用一静态地空间数组, sizeof 返回全部数组的尺寸。 sizeof 操作符不能返回被动态分配的数组或外部的数组的尺寸char* aa = "0123456789";printf("sizeof(aa)=%d sizeof(*aa)=%d strlen(aa)=%d\r\n", sizeof(aa), sizeof(*aa), strlen(aa));//4(指针ss的空间) 1(指针长度) 10(\0前的数据长度)char bb[] = "0123456789";printf("sizeof(bb)=%d sizeof(*bb)=%d strlen(bb)=%d\r\n", sizeof(bb), sizeof(*bb), strlen(bb));//11(s数组后面以\0结尾) 1(第一个char) 10char cc[100] = "0123456789";printf("sizeof(cc)=%d sizeof(*cc)=%d strlen(cc)=%d\r\n", sizeof(cc), sizeof(*cc), strlen(cc));//100(预分配) 1 10int aa[100] = { 0,1,2,3,4,5,6,7,8,9};printf("sizeof(aa)=%d sizeof(*aa)=%d strlen(aa)=%d\r\n", sizeof(aa), sizeof(*aa), strlen(aa));//400 4 0int aa[100] = { 1,1,2,3,4,5,6,7,8,9};printf("sizeof(aa)=%d sizeof(*aa)=%d strlen(aa)=%d\r\n", sizeof(aa), sizeof(*aa), strlen(aa));//400 4 1char aa[]={ 'a','b','c'};printf("sizeof(aa)=%d sizeof(*aa)=%d strlen(aa)=%d\r\n", sizeof(aa), sizeof(*aa), strlen(aa));//3 1 15char aa[]={ 'a','b','c','\0','X'};printf("sizeof(aa)=%d sizeof(*aa)=%d strlen(aa)=%d\r\n", sizeof(aa), sizeof(*aa), strlen(aa));//5 1 3char aa[]={ "abc"}; //字符串以\0结尾printf("sizeof(aa)=%d sizeof(*aa)=%d strlen(aa)=%d\r\n", sizeof(aa), sizeof(*aa), strlen(aa));//4 1 3
c/c++ strlen(str)和str.length()和str.size()都可以求字符串长度。
其中str.length()和str.size()是用于求string类对象的成员函数 strlen(str)是用于求字符数组的长度,其参数是char*。c++中的size()和length()没有区别
如:
string str=“0123456789”; cout <<“str.length()=”<<str.length()<<endl;//结果为10 cout <<“str.size()=”<<str.size()<<endl;//结果为10为了兼容,这两个函数一样。 length()是因为沿用C语言的习惯而保留下来的,string类最初只有length(),引入STL之后,为了兼容又加入了size(),它是作为STL容器的属性存在的,便于符合STL的接口规则,以便用于STL的算法。 string类的size()/length()方法返回的是字节数,不管是否有汉字。
char *strcpy(char *dest, const char *src) //const使在函数中不能修改*src其原先的值{ char *strDest = dest; //保存原始的strDest assert((dest!=NULL)&&(src!=NULL)); //检验参数,增加代码健壮性 while((*dest++=*src++)!='\0'); return strDest;}
//把src所指向的字符串追加到 dst 所指向的字符串的结尾。char* strcat(char* dst, const char* str){ //1.使用指针指向dst字符串 char* ret = dst; //2.移动指针到dst字符串的末尾 while(*ret) ret++; //3.拼接 while(*src != '\0') { *ret++ = *str++; } *ret = '\0'; return dst;}
#includeint strcmp_new(const char* src, const char* dst){ int ret = 0; while( !(ret = *(unsigned char*)src - *(unsigned char*)dst) && *dst) { src ++; dst ++; } if( ret < 0) ret = -1; else if(ret > 0) ret = 1; return ret;}
返回值:若s2是s1的子串,则返回s2在s1的首次出现的地址;如果s2不是s1的子串,则返回NULL。
char* strstr(const char* s1, const char* s2){ int len2; if( !(len2 = strlen(s2)) )//此种情况下s2不能指向空,否则strlen无法测出长度,这条语句错误 { return (char*)s1; } for( ; *s1; ++s1) { if( *s1 == *s2 && strncmp(s1, s2, len2) == 0) return (char*)s1; } return NULL;}
FILE * fopen(const char * path, const char * mode);如果文件打开失败则返回 NULL,并把错误代码存在error中。
int sprintf(char *str, const char *format, ...) 发送格式化输出到 str 所指向的字符串。printf固定输出到stdout
extern void *memset(待填充目标void *buffer, 填充内容int c, 填充长度int count)
①多用于设置为0(清空数组).如:原型是memset(buffer, 0, sizeof(buffer))
②全部设定为特定值,如‘/0’char a[100];memset(a, ‘/0’, sizeof(a));
如果是数组:struct sample_struct TEST[10]; 则memset(TEST,0,sizeof(struct sample_struct)*10);
C标准库中源码void *(memset)(void *s, int c, size_t n){ const unsigned char uc = c; unsigned char *su; for (su = s; 0 < n; ++su, --n)//以char为单位赋值--以字符数组形式赋值 *su = uc; return (s);}
以字节为单位进行赋值
①虽然第二个参数int c 是4byte,但是只有最低byte有效→赋值给const unsigned用来覆盖 ②故中间的int传参的取值推荐0x00~0xFF,若更大则会被裁剪(不生效)比如4字节内存用memset写入0x01→得到0x01010101==十进制的16843009
③特例:0 和 -1 和 INF
0和-1是特例(int)0=32个0 →→ 全部填充0 →→ int类型的0(int)-1的补码=32个1 →→全部填充1 →→int类型的-1另一个特殊情况INF=0x3f3f3f3fhttps://blog.csdn.net/Alearn_/article/details/79618300memset(a,0x3f,sizeof(a))等价于memset(a,INF,sizeof(a))
在C语言中,memset一个结构体是个明智的做法,但是在C++中不是:
https://www.cnblogs.com/my_life/articles/4013501.html在类的构造函数使用memset(this,0,sizeof(*this))会产生什么问题?哪些类可以这样使用?除非:没有virtual function,没有vtable,同时class当中没有包含其他class成员的class可以用这个。在对象内部删除对象自身的行为都是不安全的, 永远不要那么做.memset在c中是用的非常频繁的初始化函数了,当然也被带到了cpp当中,但是下面几种情形是不可以这么使用的:1.类含有虚函数表:这么做会破坏虚函数表,后续对虚函数的调用都将出现异常2.类中含有C++类型的对象:例如,类中定义了一个list的对象,由于在构造函数体的代码执行之前就对list对象完成了初始化,假设list在它的构造函数里分配了内存,那么我们这么一做就破坏了list对象的内存。原因:C++与C是不同的。C++的类虽然兼容C中的结构体,但是他们还是有本质的区别C++里结构就是类,而不是原生的C结构。用memset会把类里的一些必要数据清零。另外需要注意如果类有虚函数,则就有虚表指针,用memset会把虚函数表指针置为0,这样的类就不会有多态了。一个C++类中除了需要空间来存放数据成员之外,还可能保存着虚函数表指针S,偏移量等信息,一但你memset之后全都没有了。所以这是错误的行为。而且memset完全可以用构造函数的初始化列表来代替。 http://zhidao.baidu.com/question/1173859544995379179.htmlstring不能使用memset或者memcpy这些低级函数。你new完后string的构造函数已经调用完毕,你再memset就破坏了他的内部数据不变式。 简而言之就是,因为memset这种低级函数绕过了封装,可能会触发意想不到的行为。============http://blog.csdn.net/pkueecser/article/details/38962505struct结构体含有 string类型,memset(&xx, 0, sizeof(xx)); 访问xx.string导致段错误, 程序异常。花了好久才排查出此问题,谨记! 不可用memset来初始化带有string类型的struct ,可用构造函数来实现!!!http://www.blogbus.com/gcoder-logs/52066919.html
任何类都不能用memset, 一旦暴力,就等于你强奸了她的内部数据,她已经崩溃了
C++的std::string来说,要使用C++风格的初始化。 C++容器中勿使用memset,memcpy等 当一个数据结构中,包含map表,list表,vactor等容器时,勿使用memset初始化该结构。 否则,会导致该结构信息不可用。 map表在初始化时,会自动调用构造函数,若memset后,那么构造函数就会被破坏,导致构造失败,进而导致map表不可用。 切记!切记!memcpy用来做内存拷贝,你可以拿它拷贝任何数据类型的对象,可以指定拷贝的数据长度:
void *memcoy(void *dest , const void *src, usigned int count){ if(dest==NULL && src == NULL) { return NULL; } char *d=(char*)dest;//按字节进行复制 char *s=(char*)src; while(cout-->0) { *d++=*s++; } return dest;}说明:src和dest所指内存区域不能重叠,函数返回指向dest的指针。
char a[100], b[50];
memcpy(b, a, sizeof(b)); //注意如用sizeof(a),会造成b的内存地址溢出。