博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
c语言---(没有字符串,故以数组实现)字符串:strlen、sizeof、length、size strcpy 和其他函数fopen sprintf memset memcpy
阅读量:3920 次
发布时间:2019-05-23

本文共 7613 字,大约阅读时间需要 25 分钟。

在C语言中有专门的字符串变量,通常用一个字符数组来存放一个字符串。字符串总是以’\0’作为串的结束符。

 当把一个字符串存入一个数组时,也把结束符 ‘\0’存入数组,并以此作为该字符串是否结束的标志。有了’\0’标志后,就不必再用字符数组的长度来判断字符串的长度了。

strlen、sizeof、length、size对比

1、(C中)数组或字符串的长度:sizeof(单目运算符、关键字)、strlen(函数)

1.1、sizeof单目运算符:获取编译时分配(确定了)的总空间的字节数(如果有则包括"\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

1.2、strlen函数:计算字符串长度(从起始位置知道"\0",但不包括"\0")

当数组名作为参数传入时,实际上数组就退化成指针了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.3、sizeof、strlen的比较以及实例:

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

2、(C++中)string中length()和size() 注:c中没有string类型👉以字符数组存放并以\0结尾标识

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()方法返回的是字节数,不管是否有汉字。

字符串函数:

1、strlen:string length 字符串长度

2、strcpy:string copy 字符串复制(遇到’\0’就结束拷贝)

char *strcpy(char *dest, const char *src)    //const使在函数中不能修改*src其原先的值{
  char *strDest = dest; //保存原始的strDest  assert((dest!=NULL)&&(src!=NULL)); //检验参数,增加代码健壮性  while((*dest++=*src++)!='\0');    return strDest;}

3、strcat:string catenate 字符串连接

//把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;}

4、strcmp:string compare 字符串比较

#include 
int 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;}

5、strstr:判断第二个str是否是第一个str的子串

返回值:若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;}

fopen

FILE * fopen(const char * path, const char * mode);如果文件打开失败则返回 NULL,并把错误代码存在error中。

sprintf

int sprintf(char *str, const char *format, ...) 发送格式化输出到 str 所指向的字符串。printf固定输出到stdout

memset

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

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的内存地址溢出。

你可能感兴趣的文章
Centos设定PATH
查看>>
Linux操作系统4.5.6.7代差别
查看>>
文件系统EXT3,EXT4和XFS的区别
查看>>
Centos7 udev
查看>>
Nmcli 网络管理命令行工具
查看>>
Linux IP地址配置
查看>>
firewalld和iptables
查看>>
SELinux
查看>>
nmcli双网卡绑定
查看>>
nmcli 网卡链路绑定team
查看>>
Linux下profile和bashrc四种的区别
查看>>
Linux文件查看指令整理
查看>>
Linux的三个时间参数
查看>>
Linux 用户ID和组ID
查看>>
Linux /etc/passwd文件
查看>>
Linux 档案搜寻指令整理
查看>>
RAID和LVM
查看>>
网关和路由的区别
查看>>
rc.local文件
查看>>
service命令
查看>>