注意*
字符串在内存中是连续的
H | E | L | L | O | \0 |
72 | 101 | 108 | 108 | 111 | 0 |
知识扩展*:C语言规定,字符串以0结尾,\0就等于数字0
知识扩展*:字符串格式是规定以“\0”结尾,在打印的时候“\0”并不会显示,统计长度的时候也是统计有效长度。这单纯地只是一个标志位,并不是元素,如果没有这个标志,那么如果有两段字符串在内存中连续存储,就无法判断是两端字符串还是一段字符串,\0是字符串结束符,不是可见字符,不可见字符很多,比如空格就是个典型的不可见字符,它只能通过前后字符的位置间接看见,不可见字符不可被打印。
用数组和指针声明字符串
char array[0xFF] {"Hello"};
char* str1 {(char*) "Hello"];
std::cout<<array;
std::cout<<&array;
std::cout<<&array[0];
字符串是一个常量类型,也就是const char*,所以要用显示类型转换
以上,如果打印array,就会打印出它的值,如果想要打印它的地址,就要在前面取址,这是因为cout 会将 字符数组的首地址指向的数据输出,同理,第三次打印也是打印出H,而不是地址,如果想打印它的地址值,要这么写
char str[]{ "123456" };
char * ch = &str[0];
std::cout << &ch;
char array[5]{};
array[3] = '*'; array[4] = '*';
std::cout << &array[3];
知识扩展*:cout输出的是个 char*类型,当做字符串处理,将会一直输出,直到遇到'\0'为止,所以这里打印了两个*,而不是一个*
同样,在下面的例子中,如果打印str,也会打印出它所指的内存空间的值,如果要打印它所指的内存空间,就要用道%p
char* str{ "123456" };
std::cout << str<<std::endl;
printf("%^p",str);
第一行打印出了123456,第二行打印出了地址
知识扩展*:在上面两个例子中,双引号做了3件事: 1.申请了空间(在常量区),存放了字符串 2. 在字符串尾加上了'/0' 3.返回地址,然后让array和str接收了这个地址
如果当前计算机是中文编码GBK,则字符在内存中显示为
张 | 三 | \0 | ||
d5 | c5 | c8 | fd | 00 |
因为每个汉字是两个字节,而英文还是一个字节
如果用宽字节来声明字符串,则每个字符在内存中显示为两个字节,不论中英文
‘
H | e | 张 | 0 | ||||
48 | 00 | 65 | 00 | 20 | 5f | 00 | 00 |
假设编译器采用utf-16,则‘张’在内存中顺序为205f,而打印它的内存地址后,显示为0x5f20,这是因为字符串的顺序是从左到右,数据储存的时候是低位在前高位在后,是从右到左。
此时输出张,控制台不一定能输出,因为编译器虽然认识张,但是控制台可能采用的ANSI编码,此时就要调用头文件locale,并且通过setlocale函数设置中文每个字符占用两字节
知识扩展*:以十六进制输出字符串每个字符
wchar_t wcharA[0xff] {"Hello"};
for(int x=0;x<10;x++)
{
std::cout<<std::hex<<wcharA[x]<<std::endl;
}
scanf输入字符串
这里假设输入的Name是tony,所以Name需要5个字节,四个字节是tony,最后一个字节放0,如果是’张三‘,则需要5个字节,因为中文占两个字节
char Name[0x5];
scanf("%s",Name);
printf("%s",Name);
wchar_t wcharA[0xFF];
wscanf ("%s", wcharA);
wprintf ("%s", wcharA);
wchar_t输入输出前要加w,如果要输出中文,还要设置对应的头文件
安全问题
因为字符串也是数组,如果输入的字符串长度超过了数组长度,可能就会造成数据越界访问攻击,输入的字符串多出的部分,可以达到修改数据 的目的。
scanf_s
scanf_s("%s",str,可接受的最大字符值)
特别注意*:std::cout会把char类型的指针当成字符串来处理,不显示地址,显示它的值
strlen
语法 strlen(str)
返回char类型的字符串长度,不能计算中文
wcslen
语法 wcslen(str)
返回宽字节字符串长度,可以计算中英文
思考
如何计算char类型的字符串长度,以及如何计算char类型的中英文混合
char strA[6]{ "HELLo" };
int count = 0;
while ('\0' != strA[count])
{
count++;
}
std::cout<<count;
注意*:这里是字符\0,而不是字符0,字符\0的ASCII为0,而字符0的ASCII是48
char strA[10]{ "HELLo你好" };
int count = 0;
for (int i = 0; strA[i]; i++)
{
if (strA[i] < 0) i++;
count++;
}
std::cout << count;
这里。strA[i],i最大的时候的值为0,所以可以在循环中直接这么写。GBK规定,只要第一个字符大于127,就是中文字符,所以只要字符取值超过127就表示中文,而每个字符取值范围是-128~127,超过127就是负数了