您的位置:首页 > 其它

char *s和char s[]的区别与联系以及strlen()和sizeof()的用法

2013-08-15 23:03 393 查看
#include<iostream>
using namespace std;

void reverse(char s[])
{
char *p, *q, temp;
p = s;//p指向字符串s的第一个字符
q = s + strlen(s) -1;//q指向字符串s的最后一个字符。
while(p < q)
{
temp = *p;//交换两字符
*p = *q;
*q = temp;
p++;
q--;
}
}
int main()
{
char s[] = "ABCDE";
char *p = s;
char *q = s + strlen(s) - 1;
cout << "交换前:" << endl;
while(p <= q)
{
cout << *p <<" ";
p++;
}
cout << endl;

reverse(s);

char *p1 = s;
char *q1 = s + strlen(s) - 1;
cout << "交换后:" << endl;
while(p1 <= q1)
{
cout << *p1 <<" ";
p1++;
}
system("pause");
return 0;
}

运行结果如下:



需要注意的知识点:

一:char *s和char s[]的区别与联系:

char *s1 = "hello";

char s2[] = "hello";

【区别所在】

char *s1 的 s1 是指针,而指针是指向一块内存区域,它指向的内存区域的大小可以随时改变,而且当指针指向常量字符串时,它的内容是不可以被修改的,否则在运行时会报错。

char s2[] 的 s2 是数组,对应着一块内存区域,其地址和容量在生命期里不会改变,只有数组的内容可以改变。

【内存模型】

s1: | *======> | h | e | l | l | o |\0 |

s2: | h | e | l | l | o |\0 |

场景一)

char *s1 = "hello";

char s2[] = "hello";

s2=s1; //编译ERROR

s1=s2; //OK

分析:s2其地址和容量在生命期里不能改变

场景二)

char s2[] = "hello";

char *s1 = s2; //编译器做了隐式的转换 实际为&s2



char *s1 = &s2;

分析:以上两个指针赋值完全等价,由于编译器会做这个隐式转换也容易导致初学者误认为 char *s 与char s[]是一回事。另用第二种在一些编译器甚至会报警告信息。

场景三)

char *s1 = "hello";

char s2[] = "hello";

s1[0]='a'; //×运行ERROR( 这一句好像在一些的编译器不会出错,原因待查)

s2[0]='a'; //OK

分析:运行时会报错,原因在于企图改变s1的内容,由于s1指向的是常量字符串,其内容是不可修改的,因此在运行时不会通过。而s2指向的是变量区字符串,可以修改。所以在上面程序中应用void reverse(char s[])而不是 void reverse(char *s).

场景四)

让我们来给一个指针的指针赋值,在使用某些含char**参数的函数时会用到,场景二的增强版。

char *s1="hello";

char s2[]="hello";

char *s3=s2; //★注意这句必须要★

char **s4=&s3; //s2(char[])要用两步才能完成赋值

char **s5=&s1; //s1(char*) 只需一步

cout << "s4 = " << *s4 << endl;//打印结果:s4=[hello]

cout << "s4 = " << *s4 << endl;//打印结果:s5=[hello]

分析:这个例子应当说最能反映出char *与char []的差异

下面是一些char *s1 和 char s2[]相同的地方(同样编译器对char[]做了隐式变化):

1)作为形参完全相同

如:

void function(char *s1);

void function(char s1[]);

2)只读取不修改的时候

如:

char *s1="hello";

char s2[]="hello";

cout << "s1[1]=" << s1[1] << endl; //s1[1]=[e]

cout << "s2[1]=" << s2[1] << endl; //s2[1]=[e]

cout << "s1=" << s1 << endl;//s1=[hello]

cout << "s2=" << s2 << endl; //s2=[hello]

二:strlen()的用法:

#include<iostream>
using namespace std;
int main()
{
char str[]="abcdefg";
char str0[8]={'a','b','c','d','e','f','i','g'};
char str1[]="abcdefig";
char str2[]="01234\056789" ;
char str3[]="abcse\0fgkij";
cout << "strlen(&str[5])=" << strlen(&str[5]) << endl;
cout << "strlen(str0)=" << strlen(str0) << endl;
cout << "strlen(str1)=" << strlen(str1) << endl;
cout << "strlen(str2)=" << strlen(str2) << endl;
cout << "strlen(str3)=" << strlen(str3) << endl;
cout << str2 << endl;
system("pause");
return 0;
}


运行结果如下:



需要明确的第一点,strlen()所作的仅仅是一个计数器的工作,它从内存的某个位置(可以是字符串开头,中间某个位置,甚至是某个不确定的内存区域)开始扫描,直到碰到第一个字符串结束符'\0'为止,然后返回计数器值。

该例中,strlen(&str[5])从字符str[5]即'f'开始计数,当到达'g'之后遇到'\0'并停止计数。因此结果为2。

str2和str3的主要区别就在于C/C++字符串中,允许形如“/056”这样的所谓转义字符存在,它们仅表示一个字节位(byte),一般用于输出无法直接通过键盘输入的字符。

str0的sizeof为8,导致没有/0结束,因此strlen的返回是不可预期的。

str1的sizeof实际上是9,会自动加/0结束。

str2的\056会被解释成8进制数056表示的字符,因此strlen是9

str3的\0会被解释为结束符

C++ strlen 与 sizeof 的深度认识

一、由几个例子说开去。

第一个例子:

char* ss = "0123456789";

sizeof(ss) 结果 4 ===》ss是指向字符串常量的字符指针

sizeof(*ss) 结果 1 ===》*ss是第一个字符

char ss[] = "0123456789";

sizeof(ss) 结果 11 ===》ss是数组,计算到/0位置,因此是10+1

sizeof(*ss) 结果 1 ===》*ss是第一个字符

char ss[100] = "0123456789";

sizeof(ss) 结果是100 ===》ss表示在内存中的大小 100×1

strlen(ss) 结果是10 ===》strlen是个函数内部实现是用一个循环计算到/0为止之前

int ss[100] = {0,1,2,3,4,5,6,7,8,9};

sizeof(ss) 结果 400 ===》ss表示再内存中的大小 100×4

strlen(ss) 错误 ===》strlen的参数只能是char* 且必须是以'/0'结尾的

char q[]="abc";

char p[]="a\n";

sizeof(q),sizeof(p),strlen(q),strlen(p);

结果是 4 3 3 2

第二个例子:

class X

{

int i;

int j;

char k;

};

X x;

cout<<sizeof(X)<<endl; 结果 12 ===》内存补齐

cout<<sizeof(x)<<endl; 结果 12 同上

第三个例子:

char szPath[MAX_PATH]

  如果在函数内这样定义,那么sizeof(szPath)将会是MAX_PATH,但是将szPath作为虚参声明时(void fun(char szPath[MAX_PATH])),sizeof(szPath)却会是4(指针大小)

二、sizeof深入理解。

1.sizeof操作符的结果类型是size_t,它在头文件中 typedef 为unsigned int类型。该类型保证能容纳实现所建立的最大对象的字节大小。

2.sizeof是算符,strlen是函数。

3.sizeof可以用类型做参数,strlen只能用char*做参数,且必须是以'/0'结尾的。sizeof还可以用函数做参数,比如:

short f();

cout <<"sizeof(f()) = " << sizeof(f()) << endl;

输出的结果是sizeof(short),即2。

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是个操作符不是个函数。

如:char* ss = "0123456789";

cout << sizeof ss << endl;// 结果 4 ===》ss是指向字符串常量的字符指针

8.当适用于一个结构类型或变量时, sizeof 返回实际的大小, 当适用一静态空间数组, sizeof 返还全部数组的尺 寸。 sizeof 操作符不能返回动态地被分派了的数组或外部的数组的尺寸

9.数组作为参数传给函数时传的是指针而不是数组,传递的是数组的首地址,如:

fun(char [8])

fun(char [])

都等价于 fun(char *)

在C++里传递数组永远都是传递指向数组首元素的指针,编译器不知道数组的大小如果想在函数内知道数组的大小, 需要这样做:进入函数后用memcpy拷贝出来,长度由另一个形参传进去

fun(unsiged char *p1, int len)

{

unsigned char* buf = new unsigned char[len+1]

memcpy(buf, p1, len);

}

10.计算结构变量的大小就必须讨论数据对齐问题。为了CPU存取的速度最快(这同CPU取数操作有关,详细的介绍可以参考一些计算机原理方面的书),C++在处理数据时经常把结构变量中的成员的大小按照4或8的倍数计算,这就叫数据对齐(data alignment)。这样做可能会浪费一些内存,但理论上速度快了。当然这样的设置会在读写一些别的应用程序生成的数据文件或交换数据时带来不便。MS VC++中的对齐设定,有时候sizeof得到的与实际不等。一般在VC++中加上#pragma pack(n)的设定即可.或者如果要按字节存储,而不进行数据对齐,可以在Options对话框中修改Advanced
compiler页中的Data alignment为按字节对齐。

11.sizeof操作符不能用于函数类型,不完全类型或位字段。不完全类型指具有未知存储大小的数据类型,如未知存储大小的数组类型、未知内容的结构或联合类型、void类型等。如sizeof(max)若此时变量max定义为int max(),sizeof(char_v) 若此时char_v定义为char char_v [MAX]且MAX未知,sizeof(void)都不是正确形式

三、结束语

sizeof使用场合。

1.sizeof操作符的一个主要用途是与存储分配和I/O系统那样的例程进行通信。例如: 

  void *malloc(size_t size), 

  size_t fread(void *ptr,size_t size,size_t nmemb,FILE *stream)。

2.用它可以看看一类型的对象在内存中所占的单元字节。

void *memset(void *s,int c,sizeof(s))

3.在动态分配一对象时,可以让系统知道要分配多少内存。

4.便于一些类型的扩充,在windows中就有很多结构类型就有一个专用的字段是用来存放该类型的字节大小。

5.由于操作数的字节数在实现时可能出现变化,建议在涉及到操作数字节大小时用sizeof来代替常量计算。

6.如果操作数是函数中的数组形参或函数类型的形参,sizeof给出其指针的大小。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: