目录
字节大小
64位机中,
char: 1个字节 short: 2个字节 int: 4个字节 long: 8个字节
float: 4 字节 double : 8 字节 pointer : 8 字节 long long 8 字节
32位机 long 不一样,仍4字节,pointer 4字节。
一、数组
初始化的一个坑:
int ve[128]={0};//可以直接全赋值0
int ve[128]={1};//不能直接全赋值1,只有第一个数是1,其他为0;
bool b[100];
memset(b, true, sizeof(b));// 可以初始化一个全为true的数组,但整型的不可。
所以,整型的还是用循环吧。
upper_bound 取数组中大于目标值的上界。
vector<int> nums={0,1,2,3,4,5};
int j=upper_bound(nums.begin(),nums.end(),3)-nums.begin();
cout<<j; //输出为4. j表示坐标。
lower_bound 取数组中大于或者等于目标值的上界
注:此类函数使用二分查找,故需有序。
unique(vector1.begin(),vector1.end())
//重排元素,使得所有值提前,返回值为重排后最后一个非重复值的后面的值的迭代器,即从返回值到vector1.end()是无意义的值,也是重复值的总数量。 使用之前需先排序。
sort(nums.begin(),nums.end(),less<>()); //这样即为默认的升序! greater是降序。
unique(去重) unique(vec.begin(),vec.end()) .//但这样做只是将相邻的重复的元素位置顶替,vec长度不变。
欲真正的去重可以,vec.erase(unique(vec.begin(),vec.end()),vec.end()).
二、字符串
find( ); //找字符或字符串第一次出现的位置
if(s1.find(“as”)!=npos) 作为判断或者循环条件 。npos取-1. 必须写成 s1.find(“as”) != -1.不然返回值异常
s.find(“as”,1); //表示从位置1开始寻找。
rfind(); //从后往前找第一次出现的位置
find_first_of( ); //和find一样,返回值是索引。
find_last_of(); //从后往前找,等于寻找最后一次出现位置
s.find_first_of(s[i])== s.find_last_of(s[i])
s.find(s[i])== s.rfind(s[i]) //这两种用法都可以简洁地寻找到唯一出现的元素
reverse(s1.begin(),s1.end()); //对s1进行翻转 就这么简单的函数,闭卷还不会用。还先分子串,再反转,再拼接。
string s2(s1.rbegin(),s1.rend()); //s2赋值为 翻转的s1.
erase()
- 直接下标 s1.erase(1,2); 从1开始,删除两个元素。
- 利用find. s1.erase(s1.find(“as”), 2);删除第一次出现as的字符。
isalpha(); //判断一个字符是否为字母。
isalnum();// 判断是否数字或字母
islower();// 判断是否小写字母
isupper();//判断是否大写字母
tolower(),toupper() //转换字符为小写或大写
transform(s.begin(),s.end(),s.begin(),::tolower); //直接转换字符串为小写/大写。
string a=s1.substr(2,4);// 取子串,从2开始,长度4.
s1.substr(5); 只有一个参数表示从5直接到结尾。
strstr() char *p=strstr("dasdfdsfg","ds"); cout<<*p; // 输出d
C++字符串转换(stoi;stol;stoul;stoll;stoull;stof;stod;stold) //字符串转不同整型。
atoi 使用时: string str = "-100"; int num = atoi(str.c_str()); //先改为字符数组才行。
string str1(10,’h’); // str1 = "hhhhhhhhhh" 叫做直接初始化
string str6 = string(10,'h'); // str6 = "hhhhhhhhhh" 这个叫拷贝初始化
string str13 = string("hello world",5) // str13 = "hello" 而非 " world"
三、 容器用法
for (auto &[v, p] : pos) //多维容器循环,如 map<int,map<int,int>>此时p可当作该行的一维容器来单独使用。p.size(),p[2]等等。
循环:
迭代器 for(set<int>:: iterator it=myset.begin(); it!=myset.end();it++){cout<<*it;}
单独地,对于unordered_map 而言,除了迭代器,还有
值传递unordered_map<int,int>hash; for(auto x:hash) x.first.
引用传递 for(auto &x:hash)
结构化绑定同样可值传递和引用传递 for(auto [k,v]:hash){ cout<<k<<v;}
for(auto [k,v]:hash){ cout<<k<<v;}
单独来说迭代器定义及使用。
vector<int>::iterator i1;
i1=++vec.begin();
vec.insert(i1,88); //i1现在指向vec的第二个元素,88插在了第一个元素后,即占据当前第二个元素。
priority_queue<int,vector<int>,greater<int>>q; //优先队列,默认的是less,即降序,greater升序。记忆和sort是相反的。
priority_queue<int>q; //默认降序。
unordered_map 的insert
string a="abc";
unordered_map<string,int> hash;
hash.insert({"fff",1}); // c++ 的花括号的意义? 变量只在该花括号作用域有效!
hash[a]++;
hash["fff"]++; //以上几种方式都是正确的。
vector的insert 插入一段数据时,或者插入另一个vector 时
可以直接vec.insert(pos, begin, end);
max_element 获得vec最大元素的地址
int ma=*max_element(nums.begin(), nums.end()); //带*就直接是数值了
注意:end()是末尾的下一个元素, 故max_element的选取范围自己想清楚了
注:map和set的有序性,我们可以将某些数据利用其来进行排序,比如字符串。
可轻松地按照字典序排列出来。
C++中的unordered_map和unordered_set不能直接以pair作为键名的问题
需要手动传入默认的哈希函数,如:
struct SimplePairHash {
std::size_t operator()(const std::pair<int, int>& p) const {
return p.first ^ p.second;
}
};
使用时 std::unordered_set<std::pair<int, int>, SimplePairHash> S;
S.insert(std::make_pair(0, 1));
这样就可以方便的使用pair了。
at() 函数 返回当前Vector指定位置loc的元素的引用. at() 函数 比 [] 运算符更加安全, 因为它不会让你去访问到Vector内越界的元素.
C++类中初始化vector
堆:
c++堆需要自己创建,我还没学。
扯,优先队列就是堆啊。堆里使用二元组,排序时(以第一个为准)
二元数组按照第一个元素从小到大排序,若第一个相同第二个按照从大到小排序
堆的自定义构建:
static bool cmp(pair<int, int>& m, pair<int, int>& n) {
return m.second > n.second;
}
priority_queue<pair<int, int>, vector<pair<int, int>>, decltype(&cmp)> q(cmp);
decltype是为了解决复杂的类型声明而使用的关键字,称作decltype类型说明符。
简化函数或者表达方式
count(edges.begin(), edges.end(), 2); //vector中指定元素的个数
(find(nums.begin(), nums.end(), 0)!=nums.end()) // 也可得到指定元素的个数
set<int>(nums.begin(), nums.end()).size() // 无名set求大小
四、链表
创建新节点: ListNode *p=new ListNode; //相当于
ListNode *p=(ListNode*)malloc(sizeof(ListNode));
五、函数整理
时间(time)
time_t timep;
time(&timep); //计算过了多少秒 ,这玩意结果就是时间戳,十位,秒级。
printf("%s",ctime(&timep)); //ctime将秒数转化为字符串格式,固定! (东八区)
printf("%s",asctime(gmtime(&timep))); //和ctime格式完全一样,不过是UT(世界时)。
time_t current_time;
current_time = time(NULL); //参数为空时,返回1970年到现在的时长。current_time为整型。
故 srand(time(0)) 用来当随机种子。
string date="2003-01-05";
struct tm *p; // 使用c的tm结构体, 可以自己改变格式。
time(&timep);
p=localtime(&timep); //localtime 和ctime 一样。
printf("%d//ok//%d/%s/%d %02d:%02d:%02d\n", 1900 + p->tm_year, 1+ p->tm_mon,"hhhh", p->tm_mday,p->tm_hour, p->tm_min, p->tm_sec);
cout<<"****"<<endl;
struct tm t{};
t.tm_year = stoi(date.substr(0, 4))-1900;
t.tm_mon = stoi(date.substr(5, 2))-1; //记住月份格式是0-11。
t.tm_mday = stoi(date.substr(8, 2));
mktime(&t); //把上边的t的一顿设置“做成”时间!
// cout<<t;
cout<<t.tm_yday+1<<endl; //这样就算出来了date是该年的第几天。
所以记住tm结构的参数,有的题目很好解决了。
struct tm {
int tm_sec; /* 秒 – 取值区间为[0,59] */
int tm_min; /* 分 - 取值区间为[0,59] */
int tm_hour; /* 时 - 取值区间为[0,23] */
int tm_mday; /* 一个月中的日期 - 取值区间为[1,31] */
int tm_mon; /* 月份(从一月开始,0代表一月) - 取值区间为[0,11] */
int tm_year; /* 年份,其值等于实际年份减去1900 */
int tm_wday; /* 星期 – 取值区间为[0,6],其中0代表星期天,1代表星期一,以此类推 */
int tm_yday; /* 从每年的1月1日开始的天数 – 取值区间为[0,365],其中0代表1月1日,1代表1月2日,以此类推 */
stringstream
#include <sstream>
string text = "alice is a good girl ";
stringstream ss(text); //把text放到ss流里
string t;
ss>>t;//把alice给t了
move(t);//掏空t
如果 s.emplace_back(move(t)) //即用了t之后就掏空他。 但一般会自动掏空。
int dd; string te=”56 23”;
stringstream ss;
ss<<te;
ss>>dd; //实现字符串转数字。
但是, int a=stoi(te); //也可以得到结果56。leetcode用不了。
// stringstream 是字符串分词首选_2022.1.30记.
再学习一遍字符串分词 2022.06.20
string s = "1->2,2->3,3->1";
stringstream ss(graph);
int u,v;
char ch1,ch2;
vector<vector<int>> G(111);
vector<int> deg(111);
while(ss>>u>>ch1>>ch2>>v)
{
G[u].push_back(v);
deg[v]++;
if(!(ss>>ch1))break;
}
两个单词字符串,还可以这么简单的合并处理:
s1 s2.
stringstream ss; ss<<s1<<’ ’<<s2; string cur;
while(ss>>cur) vec.push_back(move(cur));
与stringstream 同系列的.
//istringstream 用于执行C++风格字符串的输入操作
//ostringstream 用于执行C++风格字符串的输出操作
而stringstream既能输出又能输入。优先使用。
stringstream 也可传引用
TreeNode* mydes(stringstream &ss){
string tem;
ss>>tem;
}
TreeNode* deserialize(string data) {
stringstream ss(data);
return mydes(ss);
}
getline
istream& getline (char* s, streamsize n, char delim ); //指定delim为分词符号。
比如 string path=”as/d c/./ t”; string buf; stringstream ss(path);
while (getline(ss, buf, '/'))
cout<< buf; //第一次输出的就是as。 还是依赖于stringstream .
强调:getline(cin,s); 这样输入一行字符串,包含空格。最简单。
左值引用和右值引用
for(
auto&& [s, c]: m)
2
个
&&
是右值引用,也说是左值和右值自适应。
C++
对于左值和右值没有标准定义,但是有一个被广泛认同的说法:
可以取地址的,有名字的,非临时的就是左值;
不能取地址的,没有名字的,临时的就是右值;
lambda函数 (匿名函数)
function的使用
function<void(int,int)> dfs = [&](int x,int y){
int b=x+y;
};
sort(a,a+4,[](const int x,const int y){return x>y;}); //在STL中最常见的使用。 [ ] 便是判定lambda函数的标识。
// true 顺序保持不变, false 二者交换顺序。 但实际并非如此。经过我调试,发现 x 是后者,y是前者,false才是保持不变,true为换顺序。 大于号意味由大到小排序。 两个分号注意!
auto is_subsequence = [] (const stirng& s1, const string& s2) -> bool {
函数内容;
}; // 这就是在函数中嵌入lambda函数, 一次性使用。 当然也可以更简单地在外部定义。
int ret = []()->int{return 133;}(); // 最后边()表示直接函数执行了。
关于stable_sort()和sort()的区别:
你发现有sort和stable_sort,还有 partition 和stable_partition, 感到奇怪吧。其中的区别是,带有stable的函数可保证相等元素的原本相对次序在排序后保持不变。
宏定义:c语言风格 #define pi 3.14
c++ 在类的里边 typedef pair<int,char> paa;
数学函数
__gcd(a,b) 求最大公约数。(2个下划线)
求数组第k小的数 nth_element(a,a+k,a+n),那么
第k大的数为 nth_element(a,a+n-k,a+n)
//函数只是把下标为k的元素放在了正确位置,对其它元素并没有排序,当然k左边元素都小于等于它,右边元素都大于等于它,所以可以利用这个函数快速定位某个元素。 o(n) 时间复杂度.
万能头文件 <bits/stdc++.h> //包含C++所有头文件。
printf的完整输出格式:
printf的格式控制的完整格式:
% - .n l或h 格式字符
下面对组成格式说明的各项加以说明:
①%:表示格式说明的起始符号,不可缺少。
②-:有-表示左对齐输出,如省略表示右对齐输出。
③0:有0表示指定空位填0,如省略表示指定空位不填。
④m.n:m指域宽,即对应的输出项在输出设备上所占的字符数。N指精度。用于说明输出的实型数的小数位数。为指定n时,隐含的精度为n=6位。
⑤l或h:l对整型指long型,对实型指double型。h用于将整型的格式字符修正为short型
比如: printf("%02d:%02d:%02d",h,m,s); //输出时间常用的形式 13:01:00、
sprintf(R,"%02x", decrypt[i]); //不打印,而是将数据按格式 缓存到R中。
c_str(); //把c++的string风格的字符串变为c的字符串。比如:
string a; string a;
scanf("%s",a); scanf("%s",a.c_str());
printf("%s",a);//错误 printf("%s",a.c_str()); //正确!
全排列
prev_permutation(a,a+4); //求数组a的前一个排列是否存在。 最小的排列为1234,最大为4321.
next_permutation(a,a+4);//下一个排列。 执行完这两个语句后,数组a元素顺序随之改变。
宏名 (带来了便捷)
#define _for(i,a,b) for( int i=(a); i<(b); ++i)
#define _rep(i,a,b) for( int i=(a); i<=(b); ++i)
int sum = accumulate(vec.begin() , vec.end() , 0); //累加函数,0为初值。
__builtin_popcount(x) 统计x的二进制1的个数。
remove
remove的时候只是通过迭代器的指针向前移动来删除,将没有被删除的元素放在链表的前面,并返回一个指向新的超尾值的迭代器。由于remove()函数不是vector成员函数,因此不能调整vector容器的长度。(对vector来说)remove()函数并不是真正的删除,要想真正删除元素则可以使用erase()或者resize()函数。
s.erase(remove(s.begin(), s.end(), '_'), s.end()); //这样就是删除s中所有的‘_’
函数指针
int (*pmax)(int, int) = nullptr; //函数指针
pmax = [](int a, int b)->int {return a>b? a:b; };
cout << pmax(5,3);
[](){cout<<"helloworld"<<endl;} ();
当项目比较大,代码变得复杂了以后,函数指针就体现出了其优越性。
拓展还有:回调函数广泛用于开发场景中,比如信号函数、线程函数等,都使用到了回调函数的知识。
reinterpret_cast的原型为:reinterpret_cast<typeid> (expression).type_id必须是一个指针,引用,算术类型,函数指针,成员指针等。它可以把一个整型转换为指针,也可以把一个指针转换为整型。
1LL
在进行类型转换的时候,在其他类型的数字后面乘以一个1LL,就可以避免强制转换时候的精度问题。
算法收集
随机:
洗牌算法 :为了随机打乱序列。
for(int i=suit.length-1;i>0;i--)
{
random1 = Random.next(1,i);
exchange(suit[random1],suit[i]);
}
蓄水池抽样算法:为了每次在序列中随机选中k个元素。
k=1时,每次只保留一个数,当遇到第 i 个数时,以 1/i的概率保留它,(i-1)/i的概率保留原来的数。 这样每趟时间都是o(n)的,但空间只需原地。
质数: 埃氏筛 O(n*logn)
要得到自然数n以内的全部素数,必须把不大于根号n的所有素数的倍数剔除,剩下的就是素数。
线性筛 O(n) , 是多倍O(n).
bool isprime(int n){
vector<int>prime(n,1);
vector<int>x(n+2,1);
x[0]=0,x[1]=0; int count = 0;
for(int i=2; i<=n; i++){
if(x[i]){
prime[count++]=i;
}
for(int j = 0; j<count && i*prime[j]<=n; j++){ //质数会筛掉它本身与先前的质数(包含本身)的乘积
x[prime[j]*i] = 0; // 合数最起码筛掉 它的2倍然后走人。
if(i%prime[j] == 0)
break;
}
}
}
分治:
- 判断最长的重复次数不少于k的字符串。如 给你一个字符串
s
(仅小写字母)和一个整数k
,请你找出s
中的最长子串, 要求该子串中的每一字符出现次数都不少于k
。返回这一子串的长度。
通过hash存储小写字母,因为只用统计个数,可以vector<.char>(128,0); 所谓分治,将 从“异处”进行分割,即在次数少于k的字符处进行分割为若干子串,左右子串分别递归。 return 最大值。
斐波那契: 任何一个正整数,都可由不重复的斐波那契数字相加得到。更严格一些,都由不相邻的斐波那契数相加得到。因为如果存在相邻数a、 b,二者可由下一位(a+b)表示。
回溯:
用回溯算法解决问题的一般步骤:
1、 针对所给问题,定义问题的解空间,它至少包含问题的一个(最优)解。
2 、确定易于搜索的解空间结构,使得能用回溯法方便地搜索整个解空间 。
3 、以深度优先的方式搜索解空间,并且在搜索过程 中 用剪枝函数避免无效搜索。
回溯,和穷举差别不大,关键在于遍历之后将其恢复到原始状态或者数值,不影响下次。
正则表达式
用字符规律p去匹配完整的s。
bool isMatch(string s, string p) {
if(regex_match(s,regex(p)))
{
return true;
}
return false;
}
数据类型
很多程序员都取0x7fffffff作为无穷大,因为这是32-bit int的最大值。
而最精巧的无穷大常量取值是0x3f3f3f3f,其为109 级别,0x3f3f3f3f+0x3f3f3f3f=2122219134,这非常大但却没有超过32-bit int的表示范围,所以0x3f3f3f3f还满足了我们“无穷大加无穷大还是无穷大”的需求。
a/b 求除上限, 等于 (a+b-1)/ b 求下限。
静态类型转换
char a = 'a';
double d = static_cast<double>(a); //输出d是97
父类和子类也可以转换
class Child :public Base{};
Base * base = NULL;
Child * child = NULL;
//把base转为 Child*类型 向下 不安全
Child * child2 = static_cast<Child*>(base);
//把child 转为 Base* 向上 安全
Base * base2 = static_cast<Base*>(child);
关键字及面向对象
全局变量 (extern) :使用方式两种,第一种真简单。
1. 全局变量在整个源文件的作用域都是有效的,只需要在一个源文件中定义全局变量,在其他不包含全局变量定义的源文件中用extern关键字再次声明这个全局变量即可。
2. 也可以在一个源文件中定义这个全局变量,在头文件中用extern关键字再次声明这个全局变量,如果其它源文件要用到这个全局变量,只需要包含这个头文件就可以直接使用了。
结构体: 与C的区别在于, C使用时需要struct Node a, 所以为了简便才会重命名。
而且,c的结构体定义中不能有函数。即c++可以放函数成员。c的结构体可通过函数地址调用。
new 和 delete
int *p=new int; //比c的malloc简便。 int *p=(int *)malloc(sizeof(int));
*p=12;
delete p; //delete + 指针。这也是程序员的要求。
也可直接初始化 int *p1=new int(38);
int *p=new int[5]; p[0]=12; p[1]=23;
delete [] p; //释放数组,平时不熟悉。
引用 int a=12; int & c=a; //a的一个引用c,即c是a的别名。二者用同一块空间。
int arr[2][3]; int (&p2) [2][3] = arr; //总之带上&即可,记住优先级加括号。
int b=12; int *p1=&b; int* (&p2)=p1;
引用与返回值:
int& fun(){ 调用时 int &b = fun();
int a = 12; cout << b; //会报错或者警告,因为a是局部变量,在执行后那块空
return a; 间被释放。 故b指向非法空间。
}
void fun(int &a) fun(b); //所以这么多年所说的“传址”,其实也就是引用做参数。 过程就是 int &a=b;
引用与指针的区别:
-
-
-
- 引用声明就要初始化
- 引用不占存储空间
- 引用效率更高,指针间接式操作
- 引用更安全,指针可以偏移
- 指针更灵活,直接操作地址,而且c通用。
-
-
所以& 有三个作用: 声明变量时的引用,变量前加& 取地址, 位运算。
C++函数特性
函数参数缺省值(默认值)
void fun (int a, char c=’a’, float f=123.12) //只能从后往前缺省,而且必须连续。
调用时,有默认值的,可以不传递实参。 fun (55); 即可。
有默认值,还传递实参,实参会覆盖掉默认值。
注:如果函数先声明,再定义,应该在声明处就缺省,定义时不能重复。
函数重载
函数名字可以相同,参数类型或者数量不同。(不能返回值类型不同作为重载条件)
作用举例: 判断某个参数的类型,自动选择对应的 char 或 int 。
头文件
防止头文件重复包含
#ifndef AAA c++新特性可以直接
#define AAA #pragma once
#endif
叫做“条件编译” 叫做“微软宏”
初始化和赋值
引用 和 const 只能初始化, 不能赋值。其他的数组、结构体等,作用都相同。
malloc 和 new
new会触发构造函数,malloc不会。
free 和delete
delete会触发析构函数,free不会。
内联函数
声明和定义前都需要前加 inline
作用:用相应的代码替换调用,比常规函数稍快,代价是占用更多的内存。
实际使用时,代码量小,调用频繁的,使用内联函数。但编译器不一定会答应。 (函数体过大,递归等)
宏: 内联函数比宏的作用更强大。
类内: 类内定义的都是内联函数。
类外: 也是加了inline才是内联函数。
继承
减少代码重写
如: class Child : public People { }; //被继承的People叫作基类或者父类, 继承的叫派生类或子类。
可以继承多层(多个父类)。
限定词 public,父类怎样,子类就怎样。
protected,继承之后,父类中的public降级成protected。 其他的不变。
private, 继承之后,父类的东西全变成私有。
无限定词时,默认private。
继承的构造函数调用顺序,先父类后子类。
若父类的构造函数带参,子类在继承时需将父类后边参数填补。子只关注父类,不管祖父类等。
析构函数调用顺序,先子类后父类。
覆盖:子类和父类的变量同名时,默认用子类的,若想用父类 可 cfather:: a (即用类名作用域区分) 。
名字相同,真的就会覆盖,不管函数重载。 想用父类成员必须类名作用域。
友元不能被继承。
静态只有一份。(公有)
多态
多态是一种泛型编程思想,同样的代码实现不同的功能。 父类的指针,调用子类的函数。
普通来看: cFather* fa = new cSon;
fa->show(): //这样还是只能使用父类自己的。。重点!
虚函数,父类定义函数时前加virtual ,子类再重写该函数, 那么上边这种情况就会使用子类的函数了。
多态: cFather* fa = new cSon1; //父类虚函数, 指向cSon1 ,那么就执行cSon1的,而不管cSon的。
所以说,父类的一个指针,就可以有多个执行状态,即多态。
虚函数
子类重写的函数,默认是虚函数,可以显式加virtual,也可不加。
注:重写,参数要相同, 返回值类型也要相同。
虚函数不能是内联函数。
构造函数不能是虚函数。
纯虚函数: 如 virtual void fun() = 0; //有纯虚函数的类,必须用子类重写实现该纯虚函数,才能实例化对象。但也 是用子类去实例化了,父类(有纯虚函数)还是不能。
抽象类:有虚函数的类
接口类:全是纯虚函数 , 接口是项目框架的作用。
虚析构 主要作用:多态中,如果释放父类指针,就只调用父类的析构函数,所以加了虚析构,就会子类父类都调用了。
否则正常下,delete哪个类型指针,就调用谁的析构函数。
其他内容:
联编是指一个计算机程序的不同部分彼此关联的过程。按照联编所进行的阶段不同,可以分为静态联编和动态联编,静态联编指在编译阶段就把函数实现和函数调用关联起来,动态联编是指在程序执行的时候才将函数实现和函数调用关联。(c语言全是静态联编,多态大多使用动态联编)
静态编译,编译器在编译可执行文件时,把需要用到的对应动态链接库(.so或.ilb)中的部分提取出来,连接到可执行文件中去,使可执行文件在运行时不需要依赖于动态链接库;动态编译的可执行文件需要附带一个动态链接库。
静态连接就是把(lib)文件中用到的函数代码直接链接进目标程序,程序运行的时候不再需要其它的库文件;动态链接就是把调用的函数所在文件模块(DLL)和调用函数在文件中的位置等信息链接进目标程序,程序运行的时候再从DLL中寻找相应函数代码,因此需要相应DLL文件的支持。
异常:
abort( ); //使程序异常终止
try throw catch
try{show(); //函数调用} catch(参数){ } show(){ throw(x); }
catch( )的参数类型不定,可以函数重载,默认可以catch(…) ;
试题扫盲
1.
int *pia = new int[10]; // 10个未初始化int ,随机值
int *pia2 = new int[10](); // 10个值初始化为0的int
对于内置类型而言,new仅仅是分配内存,除非后面显示加(),相当于调用它的构造函数,对于自定义类型而言,只要一调用new,那么编译器不仅仅给它分配内存,还调用它的默认构造函数初始化,即使后面没有加()
2.
enum string{
x1,
x2,
x3 = 10,
x4,
x5,
} x; 函数外部访问x等于什么? 0
如果是函数外定义那么是0
如果是函数内定义,那么是随机值,因为没有初始化
3.
unsigned char *p1;
unsigned long *p2;
p1 = (unsigned char *)0x801000;
p2 = (unsigned long *)0x810000;
请问p1+5= 什么? 0x801005
p2+5= 什么? 0x810014 结果转为16进制
4.
数组作为函数的参数是会退化为函数指针的,想想看,数组作为函数参数的时候经常是需要传递数组大小的。
5.
设已经有A,B,C,D4个类的定义,程序中A,B,C,D析构函数调用顺序为?
C c;
int main()
{
A*pa=new A();
B b;
static D d;
delete pa;
}
这道题主要考察的知识点是 :全局变量,静态局部变量,局部变量空间的堆分配和栈分配
其中全局变量和静态局部变量时从 静态存储区中划分的空间,
二者的区别在于作用域的不同,全局变量作用域大于静态局部变量(只用于声明它的函数中),
而之所以是先释放 D 在释放 C的原因是, 程序中首先调用的是 C的构造函数,然后调用的是 D 的构造函数,析构函数的调用与构造函数的调用顺序刚好相反。
局部变量A 是通过 new 从系统的堆空间中分配的,程序运行结束之后,系统是不会自动回收分配给它的空间的,需要程序员手动调用 delete 来释放。
局部变量 B 对象的空间来自于系统的栈空间,在该方法执行结束就会由系统自动通过调用析构方法将其空间释放。
之所以是 先 A 后 B 是因为,B 是在函数执行到 结尾 "}" 的时候才调用析构函数, 而语句 delete a ; 位于函数结尾 "}" 之前。
6.
#include<stdio.h>
char *myString()
{
char buffer[6] = {0};
char *s = "Hello World!";
for (int i = 0; i < sizeof(buffer) - 1; i++)
{
buffer[i] = *(s + i);
}
return buffer;
}
int main(int argc, char **argv)
{
printf("%s\n", myString());
return 0;
}
输出结果不确定,野指针了!
函数char *myString()中没有使用new或者malloc分配内存,所有buffer数组的内存区域在栈区
随着char *myString()的结束,栈区内存释放,字符数组也就不存在了,所以会产生野指针,输出结果未知