C语言风格字符串 使用规范

C语言没有显式的字符串变量形式,一般使用字符数组或字符指针的形式实现,下面对这两种实现方式的异同,和一些实现细节进行说明。

  1. 字符数组:
    我们可以使用如下方式对字符数组进行初始化:
	char A[]="hello ";
	char B[10]="world";
	cout<<A<<B<<endl;

在第一种方式中,数组的长度没有指定,系统会按照赋值的字符串常量的长度自动为数组A初始化。另一种初始化方式为:

    char A[]={'H','e','l','l','o',' ','\0'};

前者的初始化使用拷贝实现。首先将“hello ”存入静态存储区,之后将该区域数据拷贝到A的地址。也就是说“char p[]=“hello”;”这条语句让“hello ”这个字符串在内存中有两份拷贝,一份在动态分配的栈中,另一份在静态存储区。

让我们再看字符数组B,由于字符串的长度为6,分配的数组大小为10,因此可以正确存储,但会有部分空间未被使用。

下面我们看字符数组的更改。
对于字符串,我们希望可以进行简单的赋值操作,如:

A="HELLO";

但事实是,这并不被允许。因为A作为数组名,是一个指针常量,是不允许被更改和赋值的。只能够在数组初始化时,使用此方法赋值。
那么初始化之后,我们想要对字符数组的值进行更改有什么办法?
其一,是遍历数组,对每个值分别进行更改:

for (int i=0;i<strlen(A);i++){
		A[i]-=32;
	}

像这样将所有的小写字母变为大写。
其二,使用strcpy或memcpy函数进行内存拷贝。

strcpy(A,"HELLO");

但是,由于C语言不进行下标越界检查,因此在对字符数组进行赋值时,要注意不超出下标。否则一旦因为越界在运行中出现某些错误是很难检查到的。
举个例子:

	char B[5]="world";

执行上述字符数组的创建编译器会报错,因为字符串常量的长度为6,数组只分配了5个长度,显然不够。
但是如果我们改成:

    char B[5];
    strcpy(B,"world");

程序就不再报错。但这样的操作是有风险的,因为字符串结束标志’\0’实际上已经越界,被存在了B[5]的位置。可能会因为其他变量的操作,导致这个内存区域的值被改变,这时字符数组B就不再能够正常显示"world"字符串了。

总结:字符数组更适合保存字符串常量。可以使用strcpy进行赋值操作,但容易造成下标越界。

  1. 字符指针
    定义如下:
	char *A="hello";

实际上这和先定义再赋值的方式没有区别:

    char *A;
    A="hello";

当我们使用这种方式定义一个字符指针,编译器会给我们警告:不能将const char类型赋值给char 类型变量。有的编译器甚至直接报错。这种赋值方式是不规范的。
我们尝试改变A中的值:

    char *A="hello";
	cout<<A<<endl;
	A="world";
	cout<<A;

输出:
hello
world
好像,我们确实将A中的值改变了。但事实情况并不是如此。在上面代码执行过程中,发生的事情是:
程序首先在静态变量存储区开辟一段空间存储字符串常量“hello”,接着将字符指针A指向该区域。字符指针属于局部变量,存储在栈区。
而后我们令A=“world”;相当于在静态变量存储区又存入了“world”,将A重新指向“world”。
所以,并不是A中的内容发生改变,而是仅仅A的指向发生改变,最终两个字符串常量“hello”和“world”都存储在了静态存储区。静态存储区的变量直到程序结束才释放。因此这样的方式表示字符串,当字符串不再使用却仍然占据内存空间,类似“内存泄漏”,尽量不要使用。
其次,由于A指向的是const char类型,我们也不能使用strcpy或memcpy改变其中的值。
当然,我们也可以使用malloc对字符串内存进行动态申请。

	char *A=(char*)malloc(6);
	strcpy(A,"hello");

注意,当使用动态申请的内存时,A一定不能够再指向其他的内存区域:

	char *A=(char*)malloc(6);
	strcpy(A,"hello");
	A="world";

这样动态申请的内存会悬空,没有指针指向它,也就没办法将其释放掉,就造成了内存泄漏。
其次,在使用strcpy改变A中值的时候,要记得使用realloc对A的内存进行重新分配,否则如果拷贝过去的字符串长度超出了A的内存范围,在后续程序运行过程中可能会发生各种莫名其妙的错误。并且这类错误具有随机性,因为未定义的内存区存的数据并不一定,所以可能在程序运行的任何阶段发生异常,也有可能不发生任何问题。总之,这种错误很难排查,因此涉及到内存的分配和使用时一定要谨慎,避免超出内存,也要记得及时释放掉,避免内存泄漏。

下面有个例子,理解静态内存区的使用:

char str1[] = "abcd";
char str2[] = "abcd";

const char str3[] = "abcd";
const char str4[] = "abcd";

const char *str5 = "abcd";
const char *str6 = "abcd";

char *str7 = "abcd";
char *str8 = "abcd";


cout << ( str1 == str2 ) << endl;
cout << ( str3 == str4 ) << endl;
cout << ( str5 == str6 ) << endl;
cout << ( str7 == str8 ) << endl;

结果是:0 0 1 1
str1,str2,str3,str4是数组变量,它们有各自的内存空间;字符数组作为局部变量被存储在栈区;
而str5,str6,str7,str8是指针,它们指向相同的常量区域。,"abcd"被存储在静态数据区,而且是全局的。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值