程序是对数据的表示和处理。对于数值型数据,可以直接处理;对于数值型以外的数据,例如文本(字符和字符串),图形图像,音频视频,还涉及到数据的表示(数据编码)的问题。

1 C语言对字符和字符常量的约定

字符编码有ASCII、GB2312,Unicode等。处理字符或字符串数据时,其实质是对字符编码的操作。ASCII编码使用一个字节的整数来编码西文字符,而整数在计算机中是区分signed和unsigned的,对于unsigned的,一个字符的表示范围是0-255(0x00000000-0xFFFFFFFF),对于signed,通常使用补码来编码,也就是区分符号,一个字节的值域(表示的整数范围)是:-128~127,虽然ASCII只使用了0x00000000~0x0FFFFFFF的区段(0-127),但字符类型为了与数值型整数在处理时达到统一,char类型也可以区分unsigned和signed,char默认是signed的。

ASCII对数字字符进行连续编码,字符转数字只需减去'0'即可:

char ch = '3'; int d = ch-'0'; // 字符转数字

ASCII对大写字母和小写字母也进行了连续编码,为方便处理(包括大小写转换),但其编码值做了特殊考虑:

Bin

Oct

Dec

Hex

缩写/字符

解释

(二进制)

(八进制)

(十进制)

(十六进制)

0100 0001

101

65

0x41

A

大写字母A

0110 0001

141

97

0x61

a

小写字母a

大、小写字母只有在第6位(从低位到高位)存在区别,这样在大小写转换时特别方便。

char str[] = "AbCdEf"; char *p = str; while(*p!='\0') (*p++) |= 'a'-'A'; // 如果是大写,改成小写,将log2('a'-'A')=log2(32),将低位到高位的第6位置1; printf("%s\n",str); // abcdef p = str; while(*p!='\0') (*p++) &= ~('a'-'A'); // 如果是小写,改成大写,将log2('a'-'A')=log2(32),将低位到高位的第6位置0; printf("%s\n",str); // ABCDEF

转义字符也可以用ASCII码来表示字符,但只能用8进制或16进制,8进制0开头例如'\023'且最多3位、16进制x开头例如'\xa'且最多两位;

printf("%c %c %c\n",'\060',48,'\x30');// 0 0 0,用8进制表示转义字符时,前导0可以省略 printf("%c %c %c\n",'\101',65,'\x41');// A A A,用8进制表示转义字符时,如果超过3位,不要用前导0 printf("%c %c %c\n",'\0101',65,'\x41');// 1 A A // '\0101'在内存中变成了831h,四个位存储'\010',也就是8h,四个位存储'1',也就是31h, // 其整数就是831h,截断成一个字节后就是31h,也就是字符'1'

在C中,unicode编码的字符一般以wchar_t类型存储,固定使用两个字符的长度。

typedef unsigned short wchar_t;

#include<; #include<; #include <locale.h> int main(void) { char s[]="中";//汉字在C/C++中是用2个字节表示 printf("%d %d\n",s[0],s[1]); char m[3]; m[0]=-42; m[1]=-48; m[2]=0; puts(m);//两个字节连起来凑成一个汉字。 printf("%c%c\n",s[0],s[1]); printf("%c%c\n",214,208);//d6,d0 //都是字符256模数的关系(补码+其负数补码 = 模) setlocale(LC_ALL, "chs"); wchar_t wc = L'\x4E2D'; wprintf(L"%c\n",wc); system("pause"); return 0; } /* -42 -48 中 中 中 中 */

中英文混合的字符统计:

#include <; #include <; int gbk_strlen(char* str) { char* p = str; //p用于后面遍历 while(*p) //若是结束符0,则结束循环 { if(*p < 0 && (*(p+1)<0 || *(p+1) < 63)) //中文汉字情况 { str++; //str移动一位,p移动移动2位,因此长度加1 p += 2; } else p++; //str不动,p移动一位,长度加1 } return p-str; //返回地址之差 } int main() { char str[] = "abc你好123中国456"; printf("%d\n",strlen(str)-gbk_strlen(str)); // 4 getchar(); }

2 C语言对字符串的约定

C语言中,字符串是借助于字符型的一维数组来存放的,并规定以字符'\0'作为字符串结束标志。由前面的知识我们知道,‘\0'是一个转义字符,称为“空值”,它的ASCII编码值为0。‘\0'作为标志占用存储空间,但不计入串的实际长度。

在字符串输出或逐个字符处理时,或者需要其长度信息,或者有一个结束标志,如果选择一个结束标志,在字符串处理时会更加方便。循环时可用字符结束标志作为循环结束标志,在用作函数参数时,不需要额外提供一个长度参数。

例如字符串处理的库函数都是以'\0'结束标志为循环终止条件的,包括strlen(),如果用strlen()去处理字符数组,除非其有一个显式'\0'的声明,否则会出错。

char str[] = "abc456";// 有一个隐式的'\0'存储 char chs[] = {'a','b','c','4','5','6'}; printf("%s %s\n",str,chs); // abc456 abc456烫abc456 printf("%d %d %d %d",sizeof(str),sizeof(chs),strlen(str),strlen(chs)); // 7 6 6 14,14是一个随机值;

3 C语言中表示字符串常量的约定

虽然C语言中没有字符串数据类型,但却允许使用“字符串常量”。字符串常量是由双引号括起来的一串字符,在表示字符串常量时,不需要人为在其末尾加入‘\0’,例如字符串常量”Hello!”不必写成“Helllo!\0”,C编译程序将自动完成这一工作,在末尾加上结束标志'\n'。

字符串常量与其它常量不一样,其存储在内存的常量区。

4 C语言中字符串常量给出的是地址值。

一个字符串常量都分别占用内存中一串连续的存储空间。这些连续的存储空间实际上就是字符型的一维数组。这些数组虽然没有名字,但C编译系统却以字符串常量的形式给出存放每一字符串的存储空间的首地址,不同的字符串具有不同的起始地址。也就是说,在C语言中,字符串常量被隐含的处理成了一个以'\0'结尾的无名字符型一维数组。因此,若有以下定义:

char*sp, s[10];

则以下赋值语句是不合法的。

s = "Hello!";

因为,字符串常量在赋值过程中给出的是这个字符串在内存中所占的一串连续存储单元的首地址,而s是一个不可重新赋值的数组名,因此,些赋值不合法,而以下赋值是合法的。

sp="Hello!";

此赋值语句并不是把字符串的内容放入sp中,而只是把字符串在内存中所占的首地址赋予了char类型的指针变量 sp,使指针变量 sp 指向该字符串。

当然,也不能使用sp去修改其指向的常量值,如

sp[2] = 'L'; // 非法操作

如果需要操作字符元素,将其存储到数组中即可。

5 字符数组与字符串的区别

字符数组的每个元素中可存放一个字符,但它并不限定最后一个字符应该是什么,而在C语言中,因为有关字符串的大量操作都与串结束标志'\0'有关,因此,在字符数组中的有效字符后面加上'\0'这一特定操作后,我们就可以把这种一维字符型数组"看作”字符串变量,但它又不同于一般的变量。要注意的是,仅仅可以在字符数组内存放字符串,不能通过赋值语句将字符串常量或其他字符数组中的字符串直接赋给字符串变量。换句话说,字符串是字符数组的一种具体应用。

看汇编:

5: char arr[] = "Hello"; 00401028 mov eax,[string "hellow" (00422f78)] 0040102D mov dword ptr [ebp-8],eax 00401030 mov cx,word ptr [string "hellow"+4 (00422f7c)] 00401037 mov word ptr [ebp-4],cx 6: char *p = "Hello"; 0040103B mov dword ptr [ebp-0Ch],offset string "hellow" (00422f78)

字符串常量存储在常量区,例如上例"Hello"存储在(00422f78)。

6 字符串与文本文档

小的文本文档你可以用一个字符串或字符串数组来处理,使用'\n\做换行来分段即可。

大的文本文档你可以通过链表(链式存储)来存储,链表的一个节点可以存储一个段落。

与文本文档相关的输入输出,使用FILE结构体和相应的一些文件处理函数即可完成。

-End-