std :: string和std :: wstring
标准库中的所有字符串功能都位于头文件中
#include<string>
标准库提供了两种basic_string <>:
namespace std
{
typedef basic_string<char> string;
typedef basic_string<wchar_t> wstring;
}
std :: string用于标准的ascii和utf-8字符串,std :: wstring用于宽字符/ unicode(utf-16)字符串
详见:https://www.learncpp.com/cpp-tutorial/17-1-stdstring-and-stdwstring/
提示:
您可能会认为使用较少内存的类型将比使用更多内存的类型更快。这并非总是如此。CPU经常被优化以处理特定大小(例如32位)的数据,并且可以更快地处理匹配该大小的类型。在这样的机器上,32位int可能比16位short或8位char更快
endl和’\n’的差别:
使用std :: endl可能效率有点低,因为它实际上有两个作业:1、它将光标移动到下一行,2、“刷新”输出(确保它立即显示在屏幕上)。当使用std :: cout将文本写入控制台时,std :: cout通常会刷新输出(如果没有,通常无关紧要),所以使用std :: endl flush刷新很重要。
因此,通常首选使用’\ n’字符。’\ n’字符将光标移动到下一行,但不执行冗余刷新,因此它的性能更好。’\ n’字符也更容易阅读,因为它更短并且可以嵌入到现有文本中
关于枚举:
enum string{
x1,
x2,
x3=10,
x4,
x5,
} x;
枚举变量是全局变量的情况下, 枚举值的缺省值是0,不是枚举的第一个值。 其他情况,其值是不定的,而且不限定于所列出的枚举值
指针执行p++操作
unsigned char *p1;
unsigned long *p2;
p1=(unsigned char *)0x801000;
p2=(unsigned long *)0x810000;
请问p1+5= 什么?
p2+5= 什么?
解答:p1+5=p1+51=p1+5sizeof(unsigned char)=p1+51=0x801000+ox5=0x801005
p2+5=p2+51=p2+5sizeof(unsigned long)=p1+54=0x810000+20=0x810000+0x14=0x810014
最后要转换成16进制
p1指向字符型,一次移动一个字符型,1个字节;p1+5后移5个字节,16进制表示为5;
p2指向长整型,一次移动一个长整型,4个字节,p2+5后移20字节,16进制表示为14。
{ char每次移动1个字节;short移动2个字节 ;int , long ,float移动4个字节 ;double移动8个字节}
数组越界异常
处理a.html文件时,以下哪行伪代码可能导致内存越界或者抛出异常
int totalBlank = 0;
int blankNum = 0;
int taglen = page.taglst.size();
A for(int i = 1; i < taglen-1; ++i)
{
//check blank
B while(page.taglst[i] == "<br>" && i < taglen)
{
C ++totalBlank;
D ++i;
}
E if(totalBlank > 10)
F blankNum += totalBlank;
G totalBlank = 0;
}
解析:答案为B,因为while(page.taglst[i] == “
” && i < taglen)这个判断,先执行page.taglst[i] == "
"这个判断,如果这个判断返回值为true,再执行i < taglen这个判断。当i=taglen的时候,执行page.taglst[i] == "
"这个判断就会越界,所以B处,最先出现越界
sizeof运算符操作
void Func(char str_arg[100])
{
printf("%d\n",sizeof(str_arg));
}
int main(void)
{
char str[]="Hello";
printf("%d\n",sizeof(str));
printf("%d\n",strlen(str));
char*p=str;
printf("%d\n",sizeof(p));
Func(str);
}
32位系统下下面程序的输出结果为多少?
答案:6 5 4 4
对字符串进行sizeof操作的时候,会把字符串的结束符"\0"计算进去的,进行strlen操作求字符串的长度的时候,不计算\0的,数组作为函数参数传递的时候,已经退化为指针了,Func函数的参数str_arg只是表示一个指针,那个100不起任何作用的
charstr[] = "glad to test something"; //定义字符串
char*p = str; //p指向字符串首地址,即字符'g'
p++; //p是char*类型,每次移动sizeof(char)字节,故此时p指向 'g'的下一个字符 'l'
int*p1 = reinterpret_cast<int*>(p); //指针p被重新解释为整型指针并被赋值给p1
p1++; //p1是int*类型, 每次移动sizeof(int)字节,故此时p1 指向 'l'后的第四个字符 't'
p = reinterpret_cast<char*>(p1); //指针p1被重新解释为字符型指针并被赋值给p
printf("result is %s\n", p); //从't'开始输出字符串,即得到 "to test something"
变量空间的堆栈分配
设已经有A,B,C,D4个类的定义,程序中A,B,C,D析构函数调用顺序为?
C c;
void main()
{
A*pa=new A();
B b;
static D d;
delete pa;
}
答案:A B D C
解析:这道题主要考察的知识点是 :全局变量,静态局部变量,局部变量空间的堆分配和栈分配
其中全局变量和静态局部变量是从静态存储区中划分的空间,
二者的区别在于作用域的不同,全局变量作用域大于静态局部变量(只用于声明它的函数中),
而之所以是先释放 D 在释放 C的原因是, 程序中首先调用的是 C的构造函数,然后调用的是 D 的构造函数,析构函数的调用与构造函数的调用顺序刚好相反。
局部变量A 是通过 new 从系统的堆空间中分配的,程序运行结束之后,系统是不会自动回收分配给它的空间的,需要程序员手动调用 delete 来释放。
局部变量 B 对象的空间来自于系统的栈空间,在该方法执行结束就会由系统自动通过调用析构方法将其空间释放。
之所以是 先 A 后 B 是因为,B 是在函数执行到 结尾 “}” 的时候才调用析构函数, 而语句 delete a ; 位于函数结尾 “}” 之前
逗号运算符
逗号运算符允许您在允许单个表达式的任何位置计算多个表达式。逗号运算符计算其最右边的操作数
int x = 0;
int y = 2;
int z = (++x, ++y); // increment x and y
z将被赋予评估++ y的结果,其等于3
按位运算符
左移 << x << y x中的所有位左移y位
右转 >> x >> y x中的所有位右移y位
按位NOT 〜 〜X x中的所有位都被翻转
按位AND & x&y x中的每个位和y中的每个位
按位OR | x | ÿ x中的每个位或y中的每个位
按位异或 ^ x ^ y x中的每个位异或y中的每个位(0^1=1,0^0=0,1^1=0)
位操作是少数几种应该明确使用无符号整数数据类型的情况之一。这是因为C ++不保证如何存储有符号整数,也不保证某些按位运算符如何应用于有符号变量
规则:处理位运算符时,使用无符号整数
位标记和位掩码
std :: bitset简介
#include <bitset>
std::bitset<8> bits; // we need 8 bits
如果需要,可以使用一组初始值初始化bitset:
#include <bitset>
std::bitset<8> bits(option1 | option2) ; // start with option 1 and 2 turned on
std::bitset<8> morebits(0x3) ; // start with bit pattern 0000 0011
std :: bitset提供了4个关键功能:
- test()允许我们查询一个位是0还是1
- set()允许我们开启一下(如果该位已经打开,这将无效)
- reset()允许我们关闭一点(如果该位已经关闭,这将无效)
- flip()允许我们从0翻转到1,反之亦然
这些函数中的每一个都采用位位置参数来指示应该操作哪个位。最右边的位的位置是0,随着每个连续的位向左增加。给位索引提供描述性名称在这里很有用(通过将它们分配给const变量,或者使用枚举,我们将在下一章中介绍)
#include <bitset>
#include <iostream>
// Note that with std::bitset, our options correspond to bit indices, not bit patterns
const int option0 = 0;
const int option1 = 1;
const int option2 = 2;
const int option3 = 3;
const int option4 = 4;
const int option5 = 5;
const int option6 = 6;
const int option7 = 7;
int main()
{
std::bitset<8> bits(0x2); // we need 8 bits, start with bit pattern 0000 0010
bits.set(option4); // set bit 4 to 1 (now we have 0001 0010)
bits.flip(option5); // flip bit 5 (now we have 0011 0010)
bits.reset(option5); // set bit 5 back to 0 (now we have 0001 0010)
std::cout << "Bit 4 has value: " << bits.test(option4) << '\n';
std::cout << "Bit 5 has value: " << bits.test(option5) << '\n';
std::cout << "All the bits: " << bits << '\n';
return 0;
}
打印:
位4的值为:1
位5的值为:0
所有位:00010010
请注意,将bitset变量发送到std :: cout会打印bitsets中所有位的值
请记住,bitset的初始化值被视为二进制,而bitset函数使用位位置
位掩码
位标志的原理可以扩展为在单个操作中一次打开,关闭,切换或查询多个位。当我们将各个位捆绑在一起以便将它们作为一组进行修改时,这称为位掩码。
我们来看一下使用位掩码的示例程序。在以下程序中,我们要求用户输入一个数字。然后,我们使用位掩码仅保留低4位,我们打印的值为
#include <iostream>
int main()
{
const unsigned int lowMask = 0xF; // bit mask to keep low 4 bits (hex for 0000 0000 0000 1111)
std::cout << "Enter an integer: ";
int num;
std::cin >> num;
num &= lowMask; // remove the high bits to leave only the low bits
std::cout << "The 4 low bits have value: " << num << '\n';
return 0;
}
输入一个整数:151
4个低位具有值:7
151是二进制的1001 0111,lowMask是8位二进制的0000 1111,1001 0111&0000 1111 = 0000 0111,即小数点后7位,虽然这个例子非常人为,但要注意的重要一点是我们在一次操作中修改了多个位!