个人c++笔记

目录

一、数组... 2

二、字符串... 2

三、 容器用法... 3

四、 链表... 3

五、函数整理... 4

算法收集... 6

关键字及面向对象... 7

字节大小

 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()

  1. 直接下标 s1.erase(1,2);  1开始,删除两个元素。
  2. 利用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_mapinsert

    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的选取范围自己想清楚了

注:mapset的有序性,我们可以将某些数据利用其来进行排序,比如字符串。

       可轻松地按照字典序排列出来。

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;      /* 从每年的11日开始的天数取值区间为[0,365],其中0代表11日,1代表12日,以此类推 */

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;

            }

        }

    }

分治:

  1. 判断最长的重复次数不少于k的字符串。如 给你一个字符串 s (仅小写字母)和一个整数 k ,请你找出 s 中的最长子串, 要求该子串中的每一字符出现次数都不少于 k 。返回这一子串的长度。

       通过hash存储小写字母,因为只用统计个数,可以vector<.char>(128,0);  所谓分治,将 从“异处”进行分割,即在次数少于k的字符处进行分割为若干子串,左右子串分别递归。          return 最大值。

斐波那契:  任何一个正整数,都可由不重复的斐波那契数字相加得到。更严格一些,都由不相邻的斐波那契数相加得到。因为如果存在相邻数a、 b,二者可由下一位(a+b)表示。

回溯:

用回溯算法解决问题的一般步骤:

1、 针对所给问题,定义问题的解空间,它至少包含问题的一个(最优)解。

2 、确定易于搜索的解空间结构,使得能用回溯法方便地搜索整个解空间 。

3 、以深度优先的方式搜索解空间,并且在搜索过程 中 用剪枝函数避免无效搜索。

回溯,和穷举差别不大,关键在于遍历之后将其恢复到原始状态或者数值,不影响下次。

正则表达式

       用字符规律p去匹配完整的s。

          bool isMatch(string sstring 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;

引用与指针的区别:

        1. 引用声明就要初始化
        2. 引用不占存储空间
        3. 引用效率更高,指针间接式操作
        4. 引用更安全,指针可以偏移
        5. 指针更灵活,直接操作地址,而且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()的结束,栈区内存释放,字符数组也就不存在了,所以会产生野指针,输出结果未知

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
牛客网的课程《Linux高并发服务器开发》中包含了项目笔记,其中涉及到了WebServer的开发。根据引用的信息,这门课程的学习进度可能由于个人原因而拖延了一段时间。在第五章的项目学习中,可能包含了关于多进程服务器开发的学习内容。不过具体的学习笔记可能还没有迁移到pad上,暂时无法获取详细的内容[2]。根据引用,在服务器开发中,使用单Reactor和多线程的方法可以提高效率,并充分利用多核CPU的性能优势。然而,使用多线程开发可能需要在各自的线程上加锁保护临界区据,相对较为复杂。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [牛客网c++web服务器项目学习笔记-第0章 课程介绍](https://blog.csdn.net/weixin_45139984/article/details/132205586)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *2* *3* [【牛客网C++服务器项目学习】Day12-网络编程的两种事件处理模式](https://blog.csdn.net/qq_42518941/article/details/122283291)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值