310 likes | 552 Views
第 11 章 字符和内存处理. 一、字符数组、指针和字符串的初始化作用 二、 strlen 函数确定字符串有效长度 三、 strcpy 函数拷贝字符串. 一、字符数组、指针和字符串的初始化作用 1. 字符数组、指针和字符串的初始化作用 C++ 把类型 char 、 signed char 和 unsigned char 视 为重载意义上不同的类型。 char 用于定义字符变量,字符变量本身又可提升为整 型变量,为了与整型变量接口的需要,字符便分成有符号 signed char 的和无符号 unsigned char 的。
E N D
第11章 字符和内存处理 • 一、字符数组、指针和字符串的初始化作用 • 二、strlen函数确定字符串有效长度 • 三、strcpy函数拷贝字符串
一、字符数组、指针和字符串的初始化作用 • 1.字符数组、指针和字符串的初始化作用 • C++把类型char、 signed char和 unsigned char视 • 为重载意义上不同的类型。 • char用于定义字符变量,字符变量本身又可提升为整 • 型变量,为了与整型变量接口的需要,字符便分成有符号 • signed char的和无符号unsigned char的。 • 有符号整数可带符号进行高位扩展,无符号数扩展时 • 高位以0进行数据填充。
char型变量是有符号还是无符号的,C和C++语言并未 • 对此明确规定。 • 因此一般不将char变量当作一字节的整型变量进行算 • 术运算。在微软的编译器中char视为有符号的。 • 字符是构成计算机信息交流的基本单位,程序是用一序 • 列串在一起的有意义的字符构成的,由此形成文本字符串。 • 字符在编程的意义上是通过关键字char定义的变量, • 称为字符变量,字符数组是每一个元素都是字符构成的数 • 组。 • 在大多数情形下char、 signed char和 unsigned char • 定义的变量具有相同的特性,但在实际编程中依然习惯采用 • char类型处理字符、字符数组和字符串。
字符数组本身最后一个元素不必是其值为0的元素。例如:字符数组本身最后一个元素不必是其值为0的元素。例如: • char c[ ]={33,34,35,36,37}; • 局部范围内可以等价地写为: • char c[5]; c[0 ]=‘!’;c[1]=‘\“’; • c[2 ]=‘#’; c[3 ]=‘$’; c[4]=‘%’; • 也可以用单引号一个一个的初始化字符数组,但此种用 • 法略嫌繁琐,但这种方法可以输入不可见字符。例如: • char nodisplay [ ]= {'\a','\n','\t','\r','\0'}; • //响铃、换行、水平制表、回车与空字符 • 可等价地改写为: • char nodisplay[5 ]={7,10,9,13,0};
值得注意不以 0结尾的字符数组在参入系统提供的字符 • 串操作函数运算时可能会出现一些意外,因为这些函数基本 • 上内嵌如下的循环代码: • while (*dest=*source) {;...;dest++; source++;...;} • 循环的控制条件是以 0 为基准的,遇 0就结束循环,不 • 然则继续循环,因此记住这一点就可以放心地使用字符数组 • 是否要以零结尾。 • C/C++的字符串是双引号括起来的字符序列。例如字符串 • "abc"只有3个字符,但存储这个字符串需要4个字节的内 • 存,在c字符之后添补一个结束字符‘\0’,字符‘\0’的ASCII码 • 值是0,它是一个不可显示的字符,作为结束判断的过滤条 • 件。字符'0'的ASCII码值是48。
[例] 无结尾字符'\0'的数组输出显示的结果是不确定的 • # include<stdio.h> • void main(void) • { char c[ ]="abc"; • printf ("c->%p->%s,sizeof (c)=%d\n", c,c,sizeof (c)); • } • //输出:c->0065FDF4->abcd8??,sizeof(c)=4 • 字符串常数可以初始化字符指针,也可初始化 • 字符数组,但作用不同。
[例] 字符串初始化指针和数组的差异 • # include<stdio.h> • void main (void) • { char* s="123456789"; • printf ("s->%p:%s,%s sizeof (s)=%d\n",s,s,s+4,sizeof(s)); • char c[ ]="abcdefghi"; • printf ("c->%p: %s, %s sizeof (c) = %d\t",c,c,c+7,sizeof(c)); • for (int k=0;k<9;k++) printf ("%c,",s[k]); • printf ("%d\n",s[9]); • } //程序运行输出结果: • s->00420FA0:123456789,56789 sizeof (s)=4 • c->0065FDE8:abcdefghi,hi sizeof(c)=10 1,2,3,4,5,6,7,8,9,0
1 2 3 4 5 6 7 8 9 ‘\0’ a b c d e f g h I ‘\0’ • s= 00420FA0 s+4 • 全局数据区 • c= 0065FDE8 c+7 • 堆栈数据区 • 局部字符指针s的定义和初始化语句{char*s="123456789";} • 可以分解为: • char* s; s="123456789"; 定义语句 char* s=“123456789”;的内存分配 定义语句 char c[ ]=“abcdefghi”;的内存分配
表示s是一个仅占4字节内存的局部变量,指针s可以指表示s是一个仅占4字节内存的局部变量,指针s可以指 • 向任意的字符变量。 • 字符串常数“123456789”需要10字节的内存安放。编 • 译器对于这样的字符串专门安排在全局数据区,这样的字符 • 串常数区为const Data区。 • 指针s获得该字符串的首地址00420FA0。 • 此时隐含着: • s[0]= '1', s[1]= '2', s[2]= '3', s[3]= '4', ..., • s[8]= '9', s[9]= '\0' • 定义语句 {char c[ ]="abcdefghi";} 等价于: • char c[ ]={'a','b','c','d','e','f','g','h','i','\0'};
字符数组c是局部数组,字符数组在main函数的堆栈空字符数组c是局部数组,字符数组在main函数的堆栈空 • 间,其首地址为0065FDE8。 • 相似的字符串初始化形式对应的含义是不同的: • 初始化字符数组的字符串是初始化列表的洗练形式,字 • 符数组c的内存区域可以更新。 • 初始化字符指针的字符串是只读字符串,指针指向字符 • 串的首地址。 • 只读字符串占住的全局const Data区不应遭到覆盖的违 • 规操作。
定义语句 {char c[10 ]="abcd";} • 相当于数组的截断初始化形式: • char c[ ]={'a','b','c','d', '\0',0,0,0,0,0}; • 不能写成: char c[10 ]; c[10]= "abcd"; • 字符元素相当于一字节的整型变量,字符串对应一个地 • 址,等号两边数据类型是不平级的。
[例]从屏幕读取文本串到字符数组中 • %s转换说明符要求char*型的指针参数,格式字符s读取 • 直到下一个空白字符的所有字符, 在后面添加‘\0’字符。为存 • 储读取的文本串和终止字符‘\0’,字符数组应足够长。 • #include<stdio.h> • void main (void) • { char a[12],b[6],*p=a; • printf ("键入文本串\n"); • scanf ("%s %s", a,b); • printf ("%s, %s\n", a,b); • scanf ("%s ",p); printf ("%s \n",a); • }
程序运行交互结果: 程序另一次运行交互结果: • 键入文本串 键入文本串 • 12345 6789 12345 6789 • 12345, 6789 12345,6789 • 12345 6789 12345 6789 • 12345 12345 • 空格前的字符串存入“12345”存入数组a,空格后的字符串 • “6789”存入数组b。相当于初始赋值: • char a[12]= "12345"; char b[6]= "6789"; • scanf ("%s",p)从屏幕读取文本串放入p指针指向的充 • 裕的内存空间。如果p指针指向的数组内存不够容纳键入的 • 文本串或p指针指向系统为字符串常数分配的静态const区 • 域,运行产生不可预料的结果。
[例] 字符串初始化二维字符数组 • #include <stdio.h> • void show (char*p) • //输出以p定位的其后5个字符的十进制数 • { int k=0; • //不要求实参字符指针指向'\0'结尾的字符串 • while( k<5) printf ("%d,",p[k++]); } • void main (void) • { char a[ ][5]={"1","23"}; • char b[2][5]= {49,0,0,0,0,50,51,0,0,0}; • show (a[0]); show (a[1]); printf ("\n"); • //输出49,0,0,0,0,50,51,0,0,0, • show (b[0]); show (b[1]); • //输出 49,0,0,0,0,50,51,0,0,0,}
[例]二维数组初始化完整形式 • # include<stdio.h> • extern void f (char s[3][5],int n); • void main (void) • { char s[ ][5]={{' ',' ','*',' ',' '},{' ','*',' ','*',' '}, {'*','*','*','*','*'}}; • f (s,0); • printf ("\n"); f(s,1); • }//结果如下: • 32,32,42,32,32,32,42,32,42,32,42,42,42,42,42 • * • * * • *****
void f (char s[3][5], int n) • { for (int j=0; j<3; j++) • { for (int k=0; k<5; k++) • if (n==1) • printf ("%c", s[ j ][ k ]); • else • printf ("%d,", s[ j ][ k ]); • if (n==1) • printf ("\n"); • } • }
[例] 二维数组初始化截断形式 • # include<stdio.h> • extern void f (char s[3][5],int n); • void main (void) • { char b[ ][5]= {{' ',' ','*'},{' ','*',' ','*'}, {'*','*','*','*','*'}}; • f (b,0); printf ("\n");f(b,1); • } //设执行文件为a.exe • 结果如下: • 32,32,42,0,0,32,42,32,42,0,42,42,42,42,42, • * • * * • *****
[例] 字符串初始化字符指针数组和二维字符数组 • # include<stdio.h> • void main(void) • { char *pa[ ]= {"a","bc","def","higk"}; • int n=sizeof (pa)/sizeof (pa[0]); int k; • for (k=0;k<n;k++) • printf ("%p->%s ",pa [ k ], pa [ k ]); • printf ("\nsizeof(pa)=%d, sizeof (pa[0]) = %d\n", • sizeof(pa),sizeof(pa[0])); • char ca[ ][5] = {"1","23","456","7890"}; • n = sizeof (ca)/sizeof (ca [0]); • printf ("sizeof(ca)=%d,sizeof(ca[0])=%d\n", • sizeof(ca),sizeof(ca[0])); • for ( k=0;k<n;k++) • printf ("%p->%s ",ca [ k ],ca[ k ]); • }
全局内存区的数据是可索引的,局部变量对于上层主控全局内存区的数据是可索引的,局部变量对于上层主控 • 函数是不可操纵的。 • [例]字符串初始化局部二维字符数组与生存期 • # include<stdio.h> • char* sa (int n) //返回char*型指针值的函数 • {//static • char ca[ ][5]={"1","23","456","7890"}; • return ca [n]; • } • void main(void) • { for (int k=0;k<4;k++) • printf ("%p->%s ", sa(k), sa(k)); • }
该程序运行输出的结果是残缺的(输出局部数组的地该程序运行输出的结果是残缺的(输出局部数组的地 • 址,局部数组的内容是不确定的): • 0065FD84-> *e 0065FD89-> • 0065FD8E-> 0065FD93-> • 如果在局部变量ca前加上static关键字则输出结果为: 004232F8->1 004232FD->23 • 00423302->456 00423307->7890
[例]字符串初始化指针数组与生存期 • # include<stdio.h> • char* ga(int i) • { char *pa[ ]={"a","bc","def","higk"}; • return pa[i]; • } • void main(void) • { for (int k=0; k<4; k++) • printf ("%p->%s ",ga (k), ga (k)); • } • //程序运行输出结果: • 00420080->a 0042007C->bc • 00420070->def 00420FD4->higk
[例]二维字符数组行地址初始化指针数组 • # include<stdio.h> • char* ca(int i) • { char c[ ][5]={"a","bc","def","higk"}; • char* pa[ ]={c[0],c[1],c[2],c[3]}; • return pa[i]; • } • void main (void) • { for (int k=0;k<4;k++) printf ("%p->%s ",ca(k),ca(k)); • } • 程序输出结果是残缺的: • 0065FD84-> *e 0065FD89-> 0065FD8E-> 0065FD93->
[例]返回char**指针值的函数 • # include<stdio.h> • char** ppn (char *pp[ ],int n) { return pp+n; } • void main (void) • { char *pa[4]= { "a","bc","def","ghijk“ }; • for (int k=0; k<4; k++) • printf ("%s",*ppn(pa,k)); • } • //输出:abcdefghijk
二、strlen函数确定字符串有效长度 • 函数原型: size_t strlen(const char*s); • 作用: • 计算入口字符串的净长度(结尾字符'\0'前的字符个数) • unsigned int strlen1(const char*s) • /**函数的要点说明**/ • { unsigned int length=0; • // length纪录字符串的个数 • while(*s!='\0') {s++;length++;} • //长度length中不含'\0' • return length; • //返回不含结尾符的字符串长度 • } • //字符数组若无结尾字符则结果是游移的
#include<string.h> • //字符串处理函数的原型在头文件string.h中 • #include<stdio.h> • void main (void) • { char c[ ]="ab\0d"; • printf ("strlen (c)=%d,sizeof (c)=%d,%s\n", • strlen1 (c),sizeof(c),c); • c[2]='c'; • printf ("strlen(c)=%d,%s\n",strlen(c),c); • c[4]='e'; • printf ("strlen (c)=%d,%s\n",strlen(c),c); • printf ("strlen (ab\053d)=%d,%s\n", • strlen ("ab\053d"),"ab\053d"); • }
//输出结果: • strlen (c)=2,sizeof(c)=5,ab • //由sizeof获得字符数组的长度 • strlen (c)=4,abcd • //对于 char c[ ]="abcd";存在sizeof(c) = strlen(c)+1 • strlen (c)=11,abcde?????? • //结果strlen(c)=11是不确定的 • strlen (ab+d)=4,ab+d // \053是字符+的八进制ASCII码 • 对于 {char d[10]="12345"; char *s="12345678";} • 存在 strlen(d)=5,sizeof(d)=10,strlen(s)=8, • sizeof(s) = sizeof (char *) = 4 或2。当若干字符串用空格 • 分隔的时候,相邻的字符串合为一体。
这种合并相邻字符串的方式可将一行写不下的长字符串这种合并相邻字符串的方式可将一行写不下的长字符串 • 分散到多行。例如: • char c[ ]="12 34" "56\n"; • 相当于 char c[ ]="12 3456\n"; • char* s="abcd" " ef\n" ; • 相当于 char* s="abcd ef\n" ; • 因此: printf ("%d,%d,%s",strlen (c),sizeof (c),c); • 输出: 8,9,12 3456 • printf ("%d,%d,%s",strlen (s),sizeof (s),s); • 输出: 8,4,abcd ef
三、strcpy函数拷贝字符串 • 函数原型: char* strcpy(char *dst,const char*src); • 作用: 将源串src中的字符拷贝到目标串dst中 • char* strcpy1(char *dst,const char*s) • { char * p=dst; • while ((*p=*s) !='\0') {p++; s++;} • *p='\0'; return dst; } • #include<string.h> • #include<stdio.h> • void main (void) • { char b[ ]="while Hsiang river is flowing northward" ; • char * c=new char [sizeof(b)]; • printf (“%s\n”,strcpy1 (c,b));delete [ ] c; }
说明: • 上面while((*p=*s) !=‘\0') {p++; s++;}循环流程是明确的 • 无歧义的,先将原字符串中的字符拷贝给目标内存的相应位 • 置,判断是否遇到结尾字符,如果未遇到结尾字符 则继续 • 拷贝。 • 这个循环可改为 [while(*s!=‘\0’) *p++=*s++;], • 语句 [*p++=*s++;] • 分解为 {*p=*s;p++;s++} • 但不应改为下面的循环: • while((*p++=*s++) !='\0') ; • //循环体为空语句
不同的编译器对循环语句中条件判断中的表达式不同的编译器对循环语句中条件判断中的表达式 • *p++=*s++有不同的分解方法。[while((*p++=*s++) !='\0');] • 未必分解为while((*p=*s) !=‘\0’) {p++; s++;} • 例如:vc6.0中对空语句表示的循环运行时弹出Debug • Error错误。因此不要在循环语句的条件表达式中出现形如 • while (p[k++]=s[i++]),for(;*p++;),while(*p++=*s++)等分解 • 次序含糊的表达式s[k++],*p++等。 • while (e!=0)等价于while(e),while (*s!=‘\0’)可以改 • 为while(*s)。