title: 刷题遇上的知识点
date: 2016-05-27 13:36:36
categories: C++
tags:
- C++
1 运算符号|
(按位取或运算) 和&
(按位取与运算),逗号运算符
例题分析:
如果x=2014,下面函数的返回值是()
<?cpp
int fun(unsigned int x)
{
int n=0;
while((x+1))
{
n++;
x=x|(x+1);
}
return n;
}
>
题目当中,有一个x|(x+1)
的计算。
对于x|(x+1)
,是计算 x 的二进制中0的数量。
而对于x|(x-1)
,是计算 x 的二进制中1的数量。
如题目中所述,2014的二进制表示为0000 0000 0000 0000 0000 0011 1101 1111
每次计算都是将 x 的二进制数中从右往左第一个 0 变成 1 ,直至所有的数都变成 1 溢出。
例如
第一次循环:
0000 0000 0000 0000 0000 0011 1101 1111|0000 0000 0000 0000 0000 0011 1110 0000
==0000 0000 0000 0000 0000 0011 1111 1111
以此类推。
1.1 逗号运算符
- 在C语言中,多个表达式可以用逗号分开,其中用逗号分开的表达式的值分别结算,但整个表达式的值是最后一个表达式的值。
- 逗号运算符的运算级别是所有运算符中最低的。如下例子。
int a=3, b=5, c;
c = a>b, a+b;// 运行后c值为0,因为逗号运算符的优先级低于赋值运算符,所以先将a>b的结果(为0)赋值给c,之后运算a+b(结果不保存),所以c=0
c = (a>b, a+b); // 运行后c值为8,因为括号的优先级高于赋值运算符,所以先算括号内的表达式,此时计算结果为最后一个表达式的值,即a+b的值,所以c=8
2 sizeof求结构体的长度问题
用sizeof求结构体的长度时,不能简单地使用各个成员的长度和,而是要考虑系统在存储结构体变量时的地址对其问题。
实际中,存储变量时地址要对其,应遵循两点原则:
- 结构体变量中成员的偏移量必须是成员大小的整数倍。
- 结构体大小必须是所有成员大小的整数倍,也就是所有成员大小的公倍数。即长度最大的成员的整数倍。
例如:
<?
struct
{
double a;
int b;
char c;
} A;
?>
对A求sizeof的长度为double类型长度的整数倍,也即8的整数倍。
又如,
<?c
struct
{
int a;
char b;
}B;
?c>
对B求sizeof的长度为int类型长度的整数倍,也即4的整数倍。
来看一例题。
class A
{
int a;
short b;
int c;
char d;
};
class B
{
double a;
short b;
int c;
char d;
};
//在32位机器上用gcc编译以上代码,求sizeof(A),sizeof(B)分别是多少。
A中,a占4个字节,b本应占2个字节,但由于c占4个字节,为了满足条件:
结构体内的成员的首地址相对于结构体首地址的偏移量是其类型大小的整数倍,比如说double型成员相对于结构体的首地址的地址偏移量应该是8的倍数
b多占用2个字节,为了满足条件1,d占用4个字节,一共16个字节。
B中,a占8个字节,b占2个字节,但由于c占4个字节,为了满足条件2,b多占用2个字节,
{: .notice}
即abc共占用8+4+4=16个字节,
为了满足条件1,d将占用8个字节,一共24个字节。
3 零碎的知识点
- 普通的运算符可以重载,特殊的不能重载,比如 . :: 等
- const是常对象,也就是不改变成员变量的值,而成员函数中只有const函数可以确保不改变成员变量的值
- 析构函数一般定义为虚函数,构造函数不能是虚函数
- 重载只要求函数名相同,参数类型和个数不同,不要求返回值类型
- 虚函数不能是:内联函数、静态函数、构造函数
宏
- 宏的嵌套定义过多会影响程序的可读性,而且很容易出错
- 相对于函数调用,宏定义可以提高程序的运行效率
- 宏定义不检查参数正确性,会有安全隐患
动态分配的内存free释放
当使用free释放掉一个指针内容时,需要程序员手动将该指针置为NULL,编译器不会自动置NULL,否则会产生野指针。
reinterpret_cast ————C++中的强制类型转换符
reinterpret_cast<type-id> (expression)
type-id 必须是一个指针、引用、算术类型、函数指针或者成员指针。
它可以把一个指针转换成一个整数,也可以把一个整数转换成一个指针(先把一个指针转换成一个整数,再把该整数转换成原类型的指针,还可以得到原先的指针值)。
有如下例题:
//下面程序运行后的结果为:
char str[] = "glad to test something";
char *p = str;
p++;
int *p1 = reinterpret_cast<int *>(p);
p1++;
p = reinterpret_cast<char *>(p1);
printf("result is %s\n", p);
//结果为:result is to test something
例题分析:
一开始,char类型的指针p指向str的首地址,即指向字符串中的g
执行p++,指向l
然后将char类型的指针p,强制转换成int类型的指针p1
执行p1++,实际移动了四个字节,然后再将int类型的p1指针转换成char类型的p指针,
p指向了t。
所以结果输出为result is to test something
4. strcat and strcpy
strcat函数的原型是:
char *strcat(
char *strDestination,
const char *strSource
);
strcat的作用是连接两个字符串,具体操作时,是将src所指的字符串添加到desk结尾处,覆盖掉desk结尾的‘\0’
strcpy函数的原型是:
char *strcpy(
char *strDestination,
const char *strSource
);
strcpy的作用是,src所指向的字符串复制到以desk开始的地址空间
区别:
- strcat是无空格连接两个字符串
- strcpy是覆盖掉前一个指针开始位置的字符串
5.复杂度计算
常数阶:
例1:
sum = 0; 1次
for(int i = 0; i < n; i ++ ) n次
for(int j = 0; j < n; j ++) n^2次
sum++; n^2次
因为 Θ(2n2+n+1)=n2 (Θ即:去低阶项,去掉常数项,去掉高阶项的常参得到),所以 T(n)==O(n2) ;
例2: O(n2)
for (i=1;i<n;i++)
{
y=y+1; ①
for (j=0;j<=(2*n);j++)
x++; ②
}
解: 语句1的频度是 n−1
语句2的频度是 (n−1)(2n+1)=(2n2−n−1)
所以有 f(n)=2n2−n−1+(n−1)=2n2−2
又有
O(22−2)=n2
该程序的时间复杂度
T(n)=O(n2)
.
一般情况下,对步进循环语句只需考虑循环体中语句的执行次数,忽略该语句中步长加1、终值判别、控制转移等成分,当有若干个循环语句时,算法的时间复杂度是由嵌套层数最多的循环语句中最内层语句的频度f(n)决定的。
例3: O(log2n)
i=1; ①
while (i<=n)
i=i*2; ②
解:语句1的频度是1;
语句2的频度是 f(n) ,则有 2f(n)<=n;f(n)<=log2n
取最大值 f(n)=log2n ,
T(n)=O(log2n)
常用的算法时间复杂度和空间复杂度
排序法 | 平均时间 | 最差情形 | 稳定度 | 额外空间 | 备注 |
---|---|---|---|---|---|
冒泡 | O(n2) | O(n2) | 稳定 | O(1) | n小时较好 |
交换 | O(n2) | O(n2) | 不稳定 | O(1) | n小时较好 |
选择 | O(n2) | O(n2) | 不稳定 | O(1) | n小时较好 |
插入 | O(n2) | O(n2) | 稳定 | O(1) | 大部分已排序时较好 |
快速 | O(logn) | O(n2) | 不稳定 | O(logn) | n大时较好 |
归并 | O(logn) | O(n2) | 稳定 | O(1) | n大时较好 |
堆 | O(logn) | O(n2) | 不稳定 | O(logn) | n大时较好 |
{: rules=”groups”}
参考:zolalad的专栏
{: .notice}