C++语言零碎知识点汇总

1,指针函数、函数指针、函数指针数组
2 , 自定义比较函数, 结构体,sort函数中的less算子和greater算子,优先队列的重载运算符
3 ,malloc
4 ,C语言中位运算符&和|是怎么运算的
5 ,c语言数据类型的范围
6,p++ 和 ++p
7,strlen函数和sizeof操作符
8,进制转换的方法

  • 再谈C语言指针函数、函数指针、函数指针数组
    1、 指针函数
      指针函数就是返回指针值的函数,本质是一个函数。所以指针函数等价于“返回值为指针的函数”。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include 
using namespace std;
int *GetNum(int x); //指针函数声明形式
void main(void)
{
    cout << "===============start================" << endl;
    int num;
    cout << "Please enter the number between 0 and 6: ";
    cin >> num;
    cout << "result is:" << *GetNum(num) << endl;    //输出返回地址块中的值
    system("pause");
}

int *GetNum(int x) {
    static int num[] = { 0,1,2,3,4,5,6 };	//要声明为static类型的,不然函数内存释放的时候,该数组值也会是释放
    return &num[x];  //返回一个地址
}

2、函数指针
  函数指针就是一个指向函数的指针。每个函数在编译时,会被分配一个入口地址,一般用函数名来表示,这个地址就是该函数的指针。
语法:声明形式:type (*func)(参数列表 )
从上面的定义形式可以看出,函数指针和指针函数的直观上区别在于指针符号*与函数名/指针名有没有用括号()包裹起来,从这一点来看是很容易区分两者的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include 
using namespace std;

int max(int a, int b) {
    return a>b ? a : b;
}

void main(void)
{
    cout << "===========start===========" << endl;
    int(*func)(int, int);       //定义一个指向该函数形式的指针变量
    func = max;
    int a, b;
    cout << "Please enter two numbers:";
    cin >> a >> b;
    cout << "max=" << (*func)(a, b) << endl;    //运用指针变量调用函数
    cout << "max=" << max(a, b) << endl;        //使用原函数调用
    cout << "max=" << func(a, b) << endl;       //使用函数指针名调用,func = max
    system("pause");
}

上例给出了函数指针的两种最普遍的调用形式,一个是直接用定义的指针变量(* func)调用,一个是用指针名调用,后者看起来就像是将原函数使用了新名称去调用一样,可以看成函数的别名。再次强调一下:指针变量名和指针符号 * 一定要用括号包裹起来。

3、函数指针数组
就是每个元素都是函数指针的数组,直接在函数指针名后面加上数组符号[ ]即可。
语法:声明形式:type (*func[ ])(参数列表 )

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
#include
using namespace std;

void fun1()
{
    cout << "调用函数fun1" << endl;
}
void fun2()
{
    cout << "调用函数fun2" << endl;
}
void fun3()
{
    cout << "调用函数fun3" << endl;
}
int main()
{
    //定义三个函数指针
    void(*pfun)() = &fun1;
    void(*pfun2)() = &fun2;
    void(*pfun3)() = &fun3;
    //接下来就是定义一个数组把他们三个装进去。
    void(*pfunarr[3])();
    void(*pfunarr[3])();
    pfunarr[0] = pfun;
    pfunarr[1] = pfun2;
    pfunarr[2] = pfun3;
    /*  或者这样赋值
    pfunarr[0] = &fun1;
    pfunarr[1] = &fun2;
    pfunarr[2] = &fun3;
    */
    //调用运行
    pfunarr[0]();
    pfunarr[1]();
    pfunarr[2]();
    /*  或者这样调用
    (*pfunarr[0])();
    (*pfunarr[1])();
    (*pfunarr[2])();
    */
    system("pause");
    return 0;
}

4、指向函数指针数组的指针
语法:声明形式:type (* (*func )[ ])(参数列表 )

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
#include
using namespace std;

void fun1()
{
    cout << "调用函数fun1" << endl;
}
void fun2()
{
    cout << "调用函数fun2" << endl;
}
void fun3()
{
    cout << "调用函数fun3" << endl;
}
int main()
{

    //void(*pfun)() = &fun1;
    //void(*pfun2)() = &fun2;
    //void(*pfun3)() = &fun3;
    //接下来就是定义一个数组把他们三个装进去。
    void(*pfunarr[3])();
    void(*(*pfunarr2)[3])();
    //在这里呢看着个指针数组,他就是一个具有3个以函数指针为内容的元素的函数指针数组。
    pfunarr[0] = &fun1;
    pfunarr[1] = &fun2;
    pfunarr[2] = &fun3;

    pfunarr2 = &pfunarr;
    (*pfunarr2)[0]();
    pfunarr[0]();
    system("pause");
    return 0;
}
分析其定义形式:void(*(*pfunarr2)[3])()
其中(* pfunarr2)[3]表示数组指针,而void(* )( )表示函数指针,两者结合起来就是指向函数指针数组的指针。

5、指针,地址,引用
引用:引用是一个变量的另一个名字,又称别名。
定义方式:int a=10; int &b=a;
在这里,意思就是给a变量起了一个新名字b,因此b不可再次被重新定义。
引用必须初始化,无空引用,并且引用不分等级。

例如:交换函数swap()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
void swap(int &a,int &b)
{
   int temp=a;
   a=b;
   b=temp;
}
void main()
{
   int x=10,y=20;
   swap(x,y);
}

等价指针为:
void swap(int *const a,int *const b)
{
   int tmp=*a;
   *a=*b;
   *b=temp;
}
void main()
{
int x=10,y=20;
swap(&x,&y);
}

其中还有一个要注意的是
int pos;
char ch[10];
int a[3];
scanf(%d,&pos);   //一定要写&,不然程序会崩的
scanf(%s,ch);
scanf(%d,&a[1]);	//注意a[3]包括a[0],a[1],a[2]这三个值,没有a[3]这个值

变量a 本质上代表一个存储单元。CPU通过该存储单元的地址访问该存储单元中的数据。所以a本来代表两个值:存储单元的地址和储单元中的数据。于是就有了二异性。为了消除这种二义性,C语言规定a表示存储单元中的数据,&a表示存储单元的地址。
其中 要求a对应的存储单元中的数据一定是另一个存储单元的地址。
定义int× b;int a=5; b=&a;
于是, ×b表示另一个存储单元中的数据。 而给指针赋值的时候b=&a;就*b表示a对应的存储单元的地址中的数据。 &a表示a对应的存储单元的地址。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include
using namespace std;
int main(){
	int* b;
	int a=5;
	b=&a;
	cout<<*b;   //结果为5
	return 0;

}

其中在试验报告中看到自己当初写的:
1.	指针就是地址,地址就是指针;指针变量是一个变量,它保存了基本类型变量的地址。
如果指针变量p保存了变量a的地址,那么称作p指向了a,*p 就是变量a。如果p是一个指针变量,*p 表示了以 p 的内容为地址的变量,就是p指向的变量。
2.	*p++;等价于*(p++);因为++和*的优先级一样,故结合方向是从右向左。
3.	*(p++);和*(++p);二者是有区别的。前者是先取*p的值,然后p加1;后者是先p加1,再取p的值。即如果p指向的是数组a元素a[0],则前者得到a[0]的值+1,后者得到a[1]的值。
4.	++(*p);将p指向的元素的值加1。
5.	如果p指向元素a[i], 
*(p--);先得到p指向的地址的元素a[i],然后p减1。 
*(++p);执行结果得到a[i+1],p加1。 
*(–p);执行结果得到a[i-1],p减1。
6.    数组名作为函数实参时,向形参(数组名或指针变量)传递的是数组首元素地址。数组名做函数参数时并不是单纯的把数组内部的值传递给形参数组,而是把该数组首元素的地址传递给形参数组,也就是说将实参数组的地址分享给了形参数组,这样就实现了一个地址两方调用。

6、 方法改变数组元素

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include
using namespace std;
void xiu(int *a){
	a[0]=a[1];
}
/*
void xiu(int a[]){
	a[0]=a[1];
}
void xiu(int a[4]){
	a[0]=a[1];
}
这三种方法都可以实现改变s数组元素的内容,因为数组本身就是引用类型
*/
int main(){
	int s[4]={1,2,3,4};
	xiu(s);
	for(int i=0;i<4;i++)
	cout<

6、return语句
return 语句的一般形式为:
return 表达式;
或者:
return (表达式);
有没有( )都是正确的,为了简明,一般也不写( )。例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
return max;
return a+b;
return (100+200);
~~~ 

**return 语句可以有多个,可以出现在函数体的任意位置,但是每次调用函数只能有一个 return 语句被执行**,所以只有一个返回值(少数的编程语言支持多个返回值,例如Go语言)

**函数一旦遇到 return 语句就立即返回,后面的所有语句都不会被执行到了。**

**return 语句是提前结束函数的唯一办法。return 后面可以跟一份数据,表示将这份数据返回到函数外面;return 后面也可以不跟任何数据,表示什么也不返回,仅仅用来结束函数。**

------

- 自定义比较函数 1.1、定义 operator<(): 使用该方法的前提是有默认的比较函数会调用我们的 operator<()。 比如我们有如下类,那么我们可以这样定义 operator<。

struct Edge
{
int from, to, weight;
};
bool operator<(Edge a, Edge b)
{
//使用大于号实现小于号,表示排序顺序与默认顺序相反。若使用小于号实现小于号,则相同。可用此法方便理解。
return a.weight > b.weight;
}

1
2

例子:

#include

#include

#include

#include

#include
using namespace std;

struct Edge
{
int from, to, weight;
Edge(int from, int to, int weight):from(from), to(to), weight(weight){}
};

bool operator<(Edge a, Edge b)
{
return a.weight > b.weight;//原来为<,小顶堆(从大到小),反转后为从小到大
}

int main()
{
priority_queue pq;
pq.push(Edge(0, 1, 7));
pq.push(Edge(1, 2, 4));
pq.push(Edge(2, 3, 10));
pq.push(Edge(3, 4, 5));

//默认的优先队列是最大堆(less,从大到小),使用我们定义的比较方法后反转为最小堆。
while(!pq.empty())
{
    cout << pq.top().weight << endl;
    pq.pop();
}

vector vec;
vec.push_back(Edge(0, 1, 7));
vec.push_back(Edge(1, 2, 4));
vec.push_back(Edge(2, 3, 10));
vec.push_back(Edge(3, 4, 5));
sort(vec.begin(), vec.end());

//sort()函数默认为增序,这里反转为降序。
for(int i = 0; i < vec.size(); ++i)
{
    cout << vec[i].weight << endl;
}

set se;
set::iterator iter;
se.insert(Edge(0, 1, 7));
se.insert(Edge(1, 2, 4));
se.insert(Edge(2, 3, 10));
se.insert(Edge(3, 4, 5));

//set默认为增序,这里反转为降序。
for(iter = se.begin(); iter != se.end(); ++iter)
{
    cout << iter -> weight << endl;
}
return 0;

}
对应的输出为:
4
5
7
10
10
7
5
4
10
7
5
4

1
2
3

2、定义一个普通的比较函数:
还是用前面的设定:

struct Edge
{
int from, to, weight;
};
bool cmp(Edge a, Edge b)
{
return a.weight > b.weight;
}

//然后使用sort()函数
sort(data.begin(), data.end(), cmp);

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39

例子:

```
#include 
#include 
#include 
using namespace std;
 
struct Edge
{
	int from, to, weight;
	Edge(int from, int to, int weight):from(from), to(to), weight(weight){}
};
 
bool cmp(Edge a, Edge b)
{
	return a.weight > b.weight;
}
int main()
{
	vector vec;
	vec.push_back(Edge(0, 1, 7));
	vec.push_back(Edge(1, 2, 4));
	vec.push_back(Edge(2, 3, 10));
	vec.push_back(Edge(3, 4, 5));
	sort(vec.begin(), vec.end(), cmp);
	//函数默认为增序,这里反转为降序。
	for(int i = 0; i < vec.size(); ++i)
	{
		cout << vec[i].weight << endl;
	}
	
	return 0;
}
```

3、定义 operator()():
operator()重载函数需要被定义(声明)在一个新的结构体内。如下:

#include

#include

#include
using namespace std;

struct cmp
{
bool operator()(const int &a, const int &b)
{
return a > b;
}
};

int main()
{
set se;
set::iterator iter;
se.insert(7);
se.insert(4);
se.insert(10);
se.insert(5);
//sort()函数默认为增序,这里反转为降序。
for(iter = se.begin(); iter != se.end(); ++iter)
{
cout << *iter << endl;
}

return 0;

}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314

三种方法,这里总结如下:  
1、**operator<() 仅适用于自定义结构体(operator()()重载后形参必须要有结构体)。operator<() 函数的添加可以从容修改自带排序功能的容器(set, priority_queue等)的比较规则,在定义该容器时只需set或priority_queue即可,不需要添加其他参数。在使用sort()函数时也不用指定比较函数。**  
2、定义比较函数,可用于STL里sort()的第三个参数。  
3、**operator()() 则适用于内建类型与自定义结构体(operator()()形参可以是内建数据类型)以及sort(),但需要类似这样定义容器set或priority_queue, cmp>,其中cmp为仅包含operator()()函数的结构体。
  
4.   结构体,sort函数中的less算子和greater算子,优先队列的重载运算符


```
一、对于sotr函数等: 
sort函数中默认是从小到大排序
1、自定义bool函数形式1(自定义比较函数) 
bool cmp(const int &a,const int &b)
//此时重载<运算符,return false,所以a(小)排在b(大)的后面,为从小到大 
{//此时a为别名 
    return a>b;//此时顺序为从大到小 
} //自定义比较函数的形式同样也适用于pair类型数据排序。
 此时sort函数里这么写sort(v.begin(),v.end(),cmp);
 系统默认ab时返回为真,
 那么最终得到的排序结果也相应的从小到大变成从大到小。
 
 2、自定义bool函数形式2(自定义比较函数) 
 struct node{
    int x, y;
};

struct cmp1{
    bool operator()(node a, node b){
        return a.x > b.x; //从大到小,优先队列中从小到大出队 
    }
}

struct cmp2{
    bool operator()(node a, node b){//重载()相当于重载< 
        return a.x < b.x; //从小到大,优先队列中从大到小 
    }
}

int main(){
    priority_queue, cmp1> que1; //小顶堆
    priority_queue, cmp2> que2; //大顶堆
    return 0;
}
 
 
 二、对于结构体:
 1、可以通过结构体外定义全局自定义比较函数进行比较,如: 
 struct ss
{
    int a,b;
};
bool cmp(const ss &a,const ss &b)
{ 
    return a.ab.a; 就会从大到小排列 
 
 2、重载运算符<(这种方法不能应用于pair类型的,只能应用于结构体或类对象排序) 
 struct node{
    int x, y;
};

bool operator <(const node& a, const node& b){
	//const node &a不能少 ,此时()外不需要加const 
    return a.x > b.x; //重载为降序排列
} 

三、对于priority_queue,****从队尾出队: 
1、
priority_queue q;      //默认为less,小的优先级高,但是队尾出队,所以按照元素从大到小的顺序出队
priority_queue, greater > q;    //通过操作,按照元素从小到大的顺序出队
2、重载运算符< (运算符重载函数放到结构体外,同样重载运算符的操作不能用于pair类型数据的排序,
只能作用于结构体或类对象。) 
struct node{
    int x, y;
};

bool operator <(const node& a, const node& b){
    return a.x > b.x;//这其实就是但会false 
	 //less默认小顶堆,从小到大排,但是队尾先出队,所以从大到小出队,
	 //然后翻转为a.x > b.x(operator后面为<即为less),改为大顶堆,大的靠前(按从小到大的顺序) 
}

//bool operator >(const node& a, const node& b){
//    return a.x < b.x //greater默认小顶堆,改为大顶堆(从大到小) 
//}
int main(){
    priority_queue, less > que;//因为less就是operator后面的<,从小到大出队 
    //priority_queue, greater > que;//从大到小出队 
    return 0;
}

3、重载运算符< (运算符重载函数放到结构体内) 
struct node{
    int x, y;
    bool operator <(const node& a) const { //必须加const
        return x > a.x;//改为从小到大 出队 ,若放到结构体中就为从大到小排序 
    }
    //bool operator >(const node& a) const { //必须加const
    //    return x < a.x; //从大到小顺序出队 
    //}
};
int main(){
    priority_queue, less > que;//从小到大 
    //priority_queue, greater > que;//从大到小 
    return 0;
}


 
set、map的自定义比较函数和重载运算符与优先队列priority_queue类似。
set > st; //按照从大到小,默认是less
typedef pair  P;
set

st; //按照pair的第一个元素来排,第一个相等的话按第二个来排 set > st;//按照从大到小 struct cow{ int x; int y; cow(int x,int y):x(x),y(y){} bool operator<(const cow &b)const{ //bool operator<(const cow&a,const cow &b)const //不能写为上面这一句, if(x==b.x){ if(yb.x就为从大到小 } 优先队列 greater从大到小 less 从小到大(优先队列默认为less,sort函数等默认为less,但是队尾出队,所以大的先出队) 这个为小顶推 集合(queue,set等等)试图这两个元素x和y代入比较运算符(对于less算子(operator后面的<,返回结果为真, 就从小到大排列),调用x < y,对于greater算子(operator后面的>,返回结果为真就是从大到小) ,调用x > y),若返回结果为真,则x排在y前面 bool operator <(const node& a, const node& b){ //因为operator后面为< 所以为重载less算子 return a.x > b.x; //从大到小排列,优先队列中小值优先(从小到大出队) return a.x < b.x; //从小到大排列,优先队列中大值优先(从大到小出队) } bool operator >(const node& a, const node& b){ //因为operator后面为>,所以为重载greater算子 return a.x < b.x //从小到大排列,优先队列中大值优先(从大到小出队) return a.x > b.x //从大到小排列,优先队列中小值优先(从小到大出队) } bool operator <(const node& a) const { //因为在结构体内,必须加const return x > a.x;//从大到小排列,优先队列中从小到大出队 } struct cmp1{ bool operator()(node a, node b){//重载()相当于重载< return a.x > b.x; //从大到小排列,优先队列中小值优先(从小到大出队) return a.x < b.x; //从小到大排列,优先队列中大值优先(从大到小出队) } } 最大堆(大顶堆)-less算子 小的往前排(重载函数名中为<)-从小到大排; 优先队列从队尾出队,所以大的先出队 最小堆(小顶堆)-greater 大的往前排(重载函数名中为>) - 从大到小排 ```

##### 三,c语言的malloc和free函数 ![upload successful](\aoyue\images\pasted-93.png) ![upload successful](\aoyue\images\pasted-95.png) ![upload successful](\aoyue\images\pasted-94.png) -----

  在程序中数据是按字节的形式存储,在计算机底层,数据是按二进制的形式存储在计算机的物理设备上。   例: 字符串 ‘B’ 在内存中的存储形式为: 01111010   按照计算机编码规则,字符B首先需要转换为ASCII码,然后再转换为二进制,不足位数前面需补零。 **我们把二进制的运算称之为位运算。** 由于c语言拥有位运算的特性,所以我们可以采用位运算的特性从底层来操作计算机。 c语言位运算包括以下几种运算方式: 1: &(按位与) —注意逻辑与为两个&& 2: |(按位或) —注意逻辑或为两个|| 3: ^(按位异或) 4: >>(二进制右移) 5: <<(二进制左移) 6: ~(按位取反) 参加位运算的类型必须为整型或字符型,如果所输入的类型不是这些类型,那么将遵循c语言一般转换规则 一、按位与计算 按位与同逻辑与有着类似之处,当两个位数都是1时,则得1 ,否则结果为0 例: ``` int a1=0x84;int a2=ox90;int a3; a3 =a1&a2; a3 =0x80; 将a1 a2 转换为二进制 a1 10000100 & a2 10010000 = a3 10000000 ``` 按位取与可以将两个数之间,只有一个为1的位数据清零。 二、按位或运算 按位或的功能就是当两个都为0,结果位0,否则为1; 例: ``` int a1=0x84;int a2=ox90;int a3; a3 =a1|a2; a3 =0x94; 将a1 a2 转换为二进制 a1 10000100 a2 10010000 a3 10010100 ``` 按位取或常用于: 加入我们需要保证一个数字的低4位为1时,我们可以采用一个低四位都位1的数同这个数字进行位或计算,就可以保证低四位全部为1。 三、按位异或 按位异或的功能就是,两个数的二进制位进行异或运算时,此时检测 一个为1 一个为0 得到结果为1,否则为0(即:两个数相同为1,不同值就为0)。 例: ``` int a1=0x84;int a2=ox90;int a3; a3 =a1^a2; a3 =0x14; 将a1 a2 转换为二进制 a1 10000100 a2 10010000 a3 00010100 ``` 按位异或可以用于按位取反操作。 a1 =0x84; a2=0x0F; a3=0x8C; **根据按位异或的属性,我们可以看出一个数同0异或得到本身,与自身异或得到0; 在我们交换变量时**,我们就可以采用 int a1=0x85;int a2=0x90; a2=(a1^a1)^a2; a1=(a2^a2)^a1; 四、二进制右移运算 当二进制进行右移运算时,遵循以下规则: 1 对于无符号位的数据右移时,左侧的新位一律补0 2 对于有符号位的数据右移时,左侧的新位一律补1 对于移动一位相当于整除一个2,移动2位相当于整除两个2, 依次类推 五、二进制左移运算 二进制左移运算就是把数据向左移动,右侧新增加的位添0 例: int a=8; a<<2 =32; 二进制 8 = 0000 1000;移动两位 :0010 0000 ; 从上例可以看出,向左移动多少位就是 当前数乘以2的多少位次方所得到的结果。 六、位运算项目应用 **6.1 验证奇偶** 能被2整除的数为偶数,反之则为奇数: 利用这一规则 我们可以验证 if(a&1){a为奇数} **6.2 数据互换** int swap(){ a=a^b; a=a^a; b=a^b; } ![upload successful](\aoyue\images\pasted-111.png) ![upload successful](\aoyue\images\pasted-108.png) -----

数据类型的范围

unsigned int 0~42,9496,7295 int(即int32,与long类型一样大小) -21,4748,3648~21,4748,3647 (21亿 1e9) unsigned long 0~42,9496,7295 (42亿 1e9) long -21,4748,3648~21,4748,3647 long long的最大值:922,3372,0368,5477,5807 (1e18) long long的最小值:-922,3372,0368,5477,5808 unsigned long long的最大值:184,4674,4073,7095,5161 __int64的最大值:922,3372,0368,5477,5807 (1e18) __int64的最小值:-922,3372,0368,5477,5808 unsigned __int64的最大值:1844,6744,0737,0955,1615 关于__int128的介绍: 128位的int类型的数,官方上写了GCC提供了两种128位整数类型,分别是__int128_t和__uint128_t,分别用于声明有符号整数变量和无符号整数变量。 由于这种大整数无法使用函数printf()输出其值,所以自己做了一个整数转字符串函数myitoa(),用于实现128位整数的输出。 详见大整数运算这篇文章 ------------------------------------------------------------------

*p++ 和 ++*p

#include
using namespace std;
int main(int argc, char argv[]) {
const char
p = “hello”;
cout << p << endl;
cout <<
p++ << endl;
cout << ++p << endl;
//cout << ++
p << endl; // 编译报错,*p 为一个字符,++ 不能作用于常量和表达式

int a = 6;
int *pa = &a;
cout << *pa++ << endl;
cout << ++*pa << endl;

return 0;

}
运行结果如下:
h
h
l
6
-1268913224
~~~

解读:
p++ ,由于 (取值运算符)和 ++ (自增运算符)同属第二优先级别的运算符,所以当它们同时作用于变量 p 上时,按照从右向左的结合顺序依次进行运算。所以 p++ 等同于 (p++) :

第一步 :执行 p++,执行完后,p 的值增加 1(此时 p 已经指向第二个字符),并且返回 p 增加前的值。
第二步:执行 *(p++),灰色表示已经执行完了,而表达式 (p++) 的值为原始 p 的值,即还是指向字符串常量第一个字符的地址,所以最终结果为:h。

++p,按照从右向左结合的顺序,等价于 (++p),先执行 ++p (++在前面,先加后用,在后面先用后加),执行完后,p 指向第三个字符,即 l,并且表达式 (++p) 返回 p 增加后的值,所以最终结果为 l。

类似的,pa++等价于 (pa++),执行完的结果是:表达式值为 6(通过这个结果可以证明: 取值运算符作用的是 (pa++)表达式,而不是作用于变量 pa 本身),pa 值增加1,指向一个未知内存。当执行 ++pa 时,由于 pa 此时指向的位置未知,所以对其里面的内容进行自增运算的结果也未知,所以最终的结果为一个垃圾数。


strlen函数和sizeof操作符

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

strlen(char*)函数求的是字符串的实际长度(存在string.h这个头文件中,string也是不实行的),它求得方法是从开始到遇到第一个'\0',如果你只定义没有给它赋初值,这个结果是不定的,它会从aa首地址一直找下去,直到遇到'\0'停止。
char aa[10];cout<
  • 小知识
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
continue语句用于循环语句中,作用是不执行循环体剩余部分,直接进行下次循环。
而switch是没有continue语句的。
continue语句只用在for、while、do-while等循环体中, 常与if条件语句一起使用, 用来加速循环。

常见的就是与if连用。

比如下面这个程序:
int main()
{
    int i;
    for(i = 0; i < 10; i ++)
    {
        if(i%2==0) continue;//如果i为偶数 调用continue;
        printf("%d,", i);//输出i值
    }
}

进制转换

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
1.itoa()函数(可以将一个10进制数转换为任意的2-36进制字符串)
  函数原型:char*itoa(int value,char*string,int radix);
例如:itoa(num, str, 2); num是一个int型的,是要转化的10进制数,str是转化结果,后面的值为目标进制。

    #include 
    #include 
    int main()  
    {  
        int num = 10;  
        char str[100];  
        _itoa(num, str, 2);  //c++中一般用_itoa,用itoa也行,
        printf("%s\n", str);  
        return 0;  
    }
-----------------
2.strtol()函数

       函数原型:long int strtol(const char *nptr, char **endptr, int base)
    base是要转化的数的进制,非法字符会赋值给endptr,nptr是要转化的字符,例如:
    
    #include
    int main()  
    {  
        char buffer[20]="   10549stend# #12";  
        char *stop;  
        int ans=strtol(buffer, &stop, 8);   //将八进制数1054转成十进制,后面均为非法字符
        printf("%d\n",ans);  
        printf("%s\n", stop);   
        return 0;
    }
    输出结果:
		556
		9stend# #12
---------------
3.自定义函数
string intToA(int n,int radix)    //n是待转数字,radix是指定的进制
{
	string ans="";
	do{
		int t=n%radix;
		if(t>=0&&t<=9)	ans+=t+'0';
		else ans+=t-10+'a';
		n/=radix;
	}while(n!=0);	//使用do{}while()以防止输入为0的情况
	reverse(ans.begin(),ans.end());//这个翻转是低位在右,高位在左;而且这样的话数组下标不会越界(比如传过来一个2进制数,数组下表为0的位置存的是最高位,然后又进位了,这样就会下标越界)
	return ans;	
}
  • 随机数的使用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include
using namespace std;

int main(){
	time_t t1,t2;
	t1=time(NULL);
	t2=time(0); 
	cout<

你可能感兴趣的:(C++语言零碎知识点汇总)