C/C++ - 基础篇(下)

简介: C/C++ - 基础篇(下)
  1. if(1){} ~ if(1);
  2. 结构体赋值:
nds[0]=tnds[0]; // 结构体直接赋值的话,nds[0] 地址不变,数据变

DFS 中临时数组写法,保证每次回溯时,值也跟着恢复上一层的值:

void dfs(int sum)
{
    if(sum==all) return;
    for(int i=0;i<len;i++)
    {
        if(cnt[i]>0)
        {
            int vis[len]; mem(vis,0);
            vis[i]=1;
            dfs(sum+1);
            if(vis[i]==1)
            {
                // do something...
            }
        }
    }
}

虽然 if(1) //T,if(2) //T,以及ture==1,false==0 已定死规则了,但是 true!=2,其他类似。

set.rbegin() 与 set.end() 不相等,前者 == 后者 - 1,可以用来判断是否到达最后一个。

int fun(){...}  // 如果最后没有 return 的话,默认返回一个随机值。

使用 stringstream:可以吞下不同的类型,然后吐出不同的类型。


//

int a = 30;
stringstream ss;
ss<<a; 
string s1 = ss.str();
cout<<s1<<endl; // 30
string s2; // char s3[100]; ss>>s3; 也可以转换 char[] 类型
ss>>s2;
cout<<s2<<endl; // 30

%f 和 %lf 区别:

    printf("%f\n",1L/2);        // 0.000000
    printf("%f\n",1.0/2);       // 0.500000
    printf("%f\n",1.0L/2);      // -0.000000
    printf("%f\n",1.0F/2);      // 0.500000
    printf("%f\n",1F/2);        // 编译错误
    printf("%lf\n",1.0L/2);     // 0.500000
    printf("%lf\n",1L/2);       // 0.500000
    printf("%lf\n",1.0F/2);     // 0.000000

Ps:printf的%f说明符的确既可以输出float型又可以输出double型。根据“默认参数提升”规则float型会被提升为double型。因此printf()只会看到双精度数。对于scanf,情况就完全不同了,它接受指针,这里没有类似的类型提升。向float存储和向double存储大不一样,因此,scanf区别%f和%lf。      

也就是说输出的时候不管输出的是双精度还是单精度都用%f就没错了,但是输入的时候,输入单精度要用%f而输入双精度要用%lf。


stringstream ss; 清空:用 “ss.str("")” 配合 “ss.clear()”:(前后顺序无关)

    string s1="10",s2="12";
    int a,b;
    stringstream ss;
    ss<<s1;
    ss>>a;
    cout<<a<<endl; // 10
    ss<<s2;
    ss>>b;
    cout<<b<<endl; // 随机值
    cout<<ss.str().capacity()<<endl; // 2
    ss.clear();
    cout<<ss.str().capacity()<<endl; // 2
    ss.str("");  // 一定要加引号才有效
    cout<<ss.str().capacity()<<endl; // 0
    ss<<s2;
    ss>>b;
    cout<<b<<endl; // 12

vector:

/* vec.clear() 与 vector<int>().swap(vec) 区别 */
vector<int> vec;
vec.push_back(12);
vec.clear();
cout<<vec[0]<<endl; // 12
vec.push_back(20);
cout<<vec[0]<<endl; // 20
vector<int>().swap(vec);
cout<<vec[0]<<endl; // 报错
// Ps:clear 表面清空,但是里面的内存并未释放,swap 彻底清空,释放全部内存,跟刚刚新生成的一样。
/* vector<int> 比较:元素是否相同,或者按字典序比较 */
puts(v1 == v2 ? "Yes" : "No");
/* vector.resize(n) 申请空间大小,直接可以使用 v[i] */
vector<int> v;
v.resize(100);
v[3]=15;
/* find(v.begin(),v.end(),泛型)-v.begin() */
// 找到则返回第一次出现的下标,若找不到,则返回 v.size()
push_back() 时预留空间不够用:要重新分配内存,并且拷贝当前已有的所有元素到新的内存区域。如果已有元素很多,这个操作将变的非常昂贵。
Ps:ector的内存管理策略是:一旦空间不足,则增长一倍,对于大数据量,这也许是一块不容小朝的资源。

循环(外 / 内)反复声明变量的优缺点:

1、对于使用 int 等基本数据类型作为循环变量,只要你用的优化方面足够给力的主流的编译器,完全不需要关心在循环外还是循环内定义循环变量。

2、如果循环变量本身是复杂的对象,建议在循环外定义好,并且在 for 循环的赋值语句、判断语句中,都要避免重复创建对象。


pair<...> 用法(伪代码):

typedef pair<int,int> pii;
queue<pii> q;
q.push(make_pair(i,j));
x=q.front().first, y=q.front().second; q.pop();


char[ ] 大小写转换:

for(int i=0;i<len;i++)
{
    if(s[i]>='a' && s[i]<='z') s[i]=toupper(s[i]);      // 转大写
    else if(s[i]>='A' && s[i]<='Z') s[i]=tolower(s[i]); // 转小写
}


  1. '\0' (char)-> 0(int)
  2. '空格'(char)-> 32(int)
  3. char 转 string:
string s;
cout<<(s+'a')<<endl;

优先队列自定义优先级排序:

// 第 1 种方法
struct pq_cmp // 根据先到达的人先处理业务
{
    bool operator()(P p1,P p2)
    {
  // 它与正常的 sort_cmp 的思想反着来的
        return p1.ssum>p2.ssum; // 进入的时间:从小到大
    }
};
priority_queue<P,vector<P>,pq_cmp> pq;
// 第 2 种方法
struct node
{
    char a[20];
    int rk;
    friend bool operator<(node p1,node p2) // 注意写死:friend、< 
    {
        return p1.rk>p2.rk; // 从小到大
    }
};
priority_queue<node> pq;

scanf 中 %*s 妙用:

//Robert is a child of John
char a[20],with[20],b[20];
scanf("%s%*s%*s%s%*s%s",a,with,b);
/*
a==Robert
with==child
b==John
*/
  1. unordered_map / map:
  2. \
/* unordered_map / map 自定义排序 */
typedef pair<string,int> psi;
unordered_map<string,int> ump;
int cmp(psi p1,psi p2)
{
    if(p1.second==p2.second) return p1.first<p2.first;
    return p1.second>p2.second;
}
vector<psi> vec(ump.begin(),ump.end());
sort(vec.begin(),vec.end(),cmp);
// Ps:map:红黑树;unordered_map:hash 散列表。
/* 泛型里面用 char* 替代 string 以及 map 插入时排序自定义 */
struct cmp
{
    bool operator()(const char* s1,const char* s2) const
    {
        return strcmp(s1,s2)<0; // default:map 根据 key 排序(字典序)
    }
};
map<char*,int,cmp> mp;
mp.clear();
char rr[100],rr1[100];
rr1[0]=rr[0]='a';
rr[1]='b';
rr1[2]=rr[2]='c';
mp[rr]=10;
rr1[1]='a';
mp[rr1]=12;
for(map<char*,int>::iterator it=mp.begin();it!=mp.end();it++)
    cout<<it->first<<endl;
/* map / unordered_map 不要轻易使用 size() 函数 */
// 如果中途在比较的时候,有些新的ump[i]原本没有存储的会被创建,导致size会扩增
if(ump[i]!=1)
{...}
printf("%d",ump.size());
/* map 插入自定义排序重写 */
/* C++ STL 中 Map 的按 Key 排序 */
// map这里指定less作为其默认比较函数(对象),所以我们通常如果不自己指定Compare,map中键值对就会按照Key的less顺序进行组织存储。
map<string, int, less<string> > mp; // default ~ map<string, int> mp;
map<string, int, greater<string> > mp;
// 自定义
struct cmp
{
    bool operator()(const string& k1, const string& k2)
    {
        return k1.length() < k2.length();
    }
};
map<string, int, cmp> mp;
/* C++ STL 中 Map 的按 Value 排序 */
// 待更新...
/* erase(it) or erase(key) or erase(mp.begin(),mp.end()) */

判断字符是否为:字母、大小写英文字母、数字、数字或字母:

cout<<isalpha('A')<<endl; // 1
cout<<isalpha('a')<<endl; // 2
cout<<islower('A')<<endl; // 0
cout<<islower('a')<<endl; // 2
cout<<isupper('A')<<endl; // 1
cout<<isupper('a')<<endl; // 0
cout<<isdigit('a')<<endl; // 0
cout<<isdigit('1')<<endl; // 1
cout<<isalnum('a')<<endl; // 2
cout<<isalnum('B')<<endl; // 1
cout<<isalnum('1')<<endl; // 4
cout<<isalnum('.')<<endl; // 0

连续赋值情况:

int a[200],b[200],len=0;
a[len]=b[len++]=1; // a[1]==b[0]==1
a[len++]=b[len]=1; // a[0]==b[1]==1

unordered_set / multiset / hash_set / set:

1、说到这那到底hash_set与unordered_set哪个更好呢?实际上unordered_set在C++11的时候被引入标准库了,而hash_set并没有,所以建议还是使用unordered_set比较好,这就好比一个是官方认证的,一个是民间流传的。
2、set.count():O(logn); unordered_set.count() 比 set.count() 再快 4 倍左右。
3、multiset 可插入重复的值,其他用法与 set 类似。
/* set - count、insert().second */
set<int> st;
int ans=st.count(1); // st 中 1 出现的次数
bool f=st.insert(1).second; // 先插入试试,最后返回是否插入成功
/* set 自定义排序 */
struct node
{
    int val,cnt;
    node(int val,int cnt):val(val),cnt(cnt){}
    bool operator<(const node &nd)const
    {
        //return false 数据插入失败,而不是(插入成功,只是位置不一样)。
        return cnt!=nd.cnt ? cnt>nd.cnt : val<nd.val ;
    }
};
set<node> st;
  1. 在scanf中 “\n” 不是表示接受一个回车符,而是表示忽略所有的空白字符(包括回车、空格、Tab)。所以想要结束输入,输入任意一个非空白字符即可,但是该字符仍然会留在缓冲区中。一般不建议在 scanf 中使用 “\n”。
  2. 双重 for_i 循环的变量情况:


for(int i=0;i<4;i++)
{
    for(int i=1;i<3;i++)
        printf("%d\n",i);
}
/*
1
2
1
2
1
2
1
2
*/



#define 的妙用:

#define add(x,y) x+y // 不是计算好后返回,而是先返回好表达式再计算
printf("%d\n",add(1,2*add(3,4))); // 11
#define P 3
#define f(a) P*a*a
printf("%d\n",f(3+5)); // 3*3+5*3+5==29
#define ABC(x) x*x
int a, k=3;
a = ++ABC(k+1); //9
// 由于带参宏不会对参数自行添加括号运算,因此a 的计算展开式可写为 ++k+1*k+1 这样就很明显了,由于运算优先级的关系,先执行++k,即k先进行自加,k的值变成了4,然后a=4+1*4+1,结果就为9啦~

输出自动填充:

cout<<setw(5)<<setfill('0')<<a[i];
// 等价于
printf("%05d",a[i]);


结构体:

node nd;
// 等价于
node nd();


变量计算时的自动转换规则:(例如:int型除以double型,结果是double型)

自动转换遵循以下规则:

1) 若参与运算量的类型不同,则先转换成同一类型,然后进行运算。

2) 转换按数据长度增加的方向进行,以保证精度不降低。如int型和long型运算时,先把int量转成long型后再进行运算。

    a.若两种类型的字节数不同,转换成字节数高的类型

    b.若两种类型的字节数相同,且一种有符号,一种无符号,则转换成无符号类型

3) 所有的浮点运算都是以双精度进行的,即使仅含float单精度量运算的表达式,也要先转换成double型,再作运算。

4) char型和short型参与运算时,必须先转换成int型。

5) 在赋值运算中,赋值号两边量的数据类型不同时,赋值号右边量的类型将转换为左边量的类型。如果右边量的数据类型长度左边长时,将丢失一部分数据,这样会降低精度,丢失的部分按四舍五入向前舍入。



见名知意:

1、foo(function object oriented):面向对象编程的函数,有时候也不知道取什么名字,就代表“张三、李四”的味道。

指针声明定义语法:

node *left,*right; // T
node* left, right; // F

int */ double,无需 *1.0,只要有一个是小数即可(在末尾添加“.0”):

int a=2;
printf("%f\n",a*1.0/3); // 不推荐
printf("%f\n",a/3.0);   // 推荐


补空格,%2d:

printf("%2d\n",1); //空1

容器:

/* 遍历倒序 */
for(set<int>::reverse_iterator it=st.rbegin(); it!=st.rend(); it++)
{
    cout<<*it<<endl;
}

C语言优先级

image.png

image.png

image.png

image.png



  1. printf(i,i++), fun(j,++j); // 在C中,一律都是从右到左;在Java中,一律都是从左到右。
  2. vector.push:如果是对象的push,则属于拷贝,与原先的对象的地址是不一样的。
  3. static 写在函数里作用域等同于写在main函数外面。
static int a=0;
int main()
{
    return 0;
}
等价于
void fun()
{
    static int a=0;
}
int main()
{
    return 0;
}




C++中int类型默认值:

1. 在全局域中声明的变量会自动初始化为0。

2. 如果变量是在局部域中定义的,则系统不会向它提供初始值0,这些对象被认为是未初始化,其值随机(有的编译器可能会为你初始化为0,但千万别依赖于这种可能行为,因为它会给你的程序带来未定义的行为)。


在C语言中,int a = -028; 这里的 0 默认当作 8 进制来看,而不是十进制。


结构体类似于基本类型,所以传参时,也只是传值。


sizeof & strlen 区别:


image.png


image.png



默认加“\0”


image.png

目录
相关文章
|
Linux C++
嵌入式linux基础:c++(五)构造函数
嵌入式linux基础:c++(五)构造函数
160 0
嵌入式linux基础:c++(五)构造函数
|
Linux 编译器 C++
嵌入式linux基础:c++(四)重载 指针 引用
嵌入式linux基础:c++(四)重载 指针 引用
145 0
嵌入式linux基础:c++(四)重载 指针 引用
|
Linux C++
嵌入式linux基础:c++(三)程序结构
嵌入式linux基础:c++(三)程序结构
163 0
嵌入式linux基础:c++(三)程序结构
|
编译器 调度 C++
C++从入门到精通(第四篇) :C++的基础和灵魂:类和对象(下篇)
在创建对象时,编译器通过调用构造函数,给对象中各个成员变量一个合适的初始值
120 0
C++从入门到精通(第四篇) :C++的基础和灵魂:类和对象(下篇)
|
存储 编译器 C语言
C++从入门到精通(第二篇) :C++的基础和灵魂:类和对象(上篇)
C语言是面向过程的,关注的是过程,分析出求解问题的步骤,通过函数调用逐步解决问题。 C++是基于面向对象的,关注的是对象,将一件事情拆分成不同的对象,靠对象之间的交互完成。
137 0
C++从入门到精通(第二篇) :C++的基础和灵魂:类和对象(上篇)
|
存储 编译器 C语言
C++从入门到精通(第三篇) :C++的基础和灵魂:类和对象(中篇)
如果一个类中什么成员都没有,简称为空类。空类中什么都没有吗?并不是的,任何一个类在我们不写的情 况下,都会自动生成下面6个默认成员函数。
119 0
C++从入门到精通(第三篇) :C++的基础和灵魂:类和对象(中篇)
|
设计模式 测试技术 uml
[学习][笔记]设计模式(基于C/C++实现)之 设计基础
设计模式(基于C/C++实现)之 设计基础
380 0
[学习][笔记]设计模式(基于C/C++实现)之 设计基础
|
C++
【牛客刷题】带你在牛客刷题第六弹(C/C++基础)
哈喽,今天是我们牛客刷题训练第五弹,今天我们来刷一些C/C++的问题,这些问题相对于你刚学习C/C++基础来说会很好的帮助自己理解,我相信,只要我们一步步去分析,肯定是可以得到正确的答案的,来我们一起加油。
111 0
【牛客刷题】带你在牛客刷题第六弹(C/C++基础)
|
安全 编译器 Linux