5.6 a,b交换比较
#include <iostream>
#include <cmath>
using namespace std;
int main(){
int a = 10, b = 5;
int large1 = ((a + b) + abs(a - b)) / 2;
int large2 = (a*(a/b) + b*(b/a)) / (a/b + b/a);
/* b < 0时, b>>31是b的符号位1,b & 1 为b, a -= b就是a = a - (a - b) = b
* b > 0时, b>>31是b的符号位0,b & 0为0, a -= b就是a = a - 0 = a
* */
b = a - b; a -= b & (b >> 31);
int large3 = a;
int c[a] = {a, b};
int z = a - b;
z = (z>>31) & 1;
int large4 = c[z];
int pair[2] = {a, b};
int large5 = pair[a < b];
return 0;
}
交换两个数的宏
#define swap1(a, b) (a=a+b;b=a-b;a=a-b;)/* a+b可能会溢出 */
/* swap2 虽然不会溢出,a b只能是整型
* b=a^b=a^b^b=a^(b^b)=a
* a=a^b^a=a^a^b=b
* */
#define swap2(a, b) (a=a^b;b=a^b;a=a^b;)
#define swap3(a, b) {int size = sizeof(a); char* temp = (char*)malloc(size);memcpy(temp, &a, size);\
memcpy(&a, &b, size); memcpy(&b, temp, size); free(temp);}//C++
#define swap4(a, b) {char tempBuf[10];memcpy(temp, &a, sizeof(a)); memcpy(&b, &a, sizeof(a));\
memcpy(&b, tempBuf, sizeof(b));}//C
5.7 C和C++的关系
在C++中调用C编译器编译后的函数为什么要加extern "C"?
extern是C/C++语言中表明函数和全局变量作用范围的关键字,该关键字告诉编译器其声明的函数和变量可以在本
模块或其他模块使用。extern "C"作用是实现C与其他语言的混合编程,被extern "C"修饰的变量和函数是按照C
语言方式编译和连接的,而不是C++方式编译的。C++ 中多了重载。也就是说,函数名一样,函数的参数和返回值
可以不同,为了做到这个,函数在编译时都会重新命名用 C 编译器编译的函数没有按这种规则重新命名,那么你
加上 extern,告诉 c++ 编译器“这是 C 编译器编译的程序,要按 C 的命名方式来识别函数”
6.1宏定义
1.下面代码的输出结果是什么?
#include <stdio.h>
#define SUB(x,y) x-y
#define ACCESS_BEFORE(element,offset,value) *SUB(&element, offset) = value
int main() {
int array[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
int i;
ACCESS_BEFORE(array[5], 4, 6);
printf("array: ");
for (i = 0; i < 10; ++i) {
printf("%d", array[i]);
}
printf("\n");
return (0);
}
此处程序最后是*&array[5]-4=6.。根据优先级,算术运算优先级高于赋值运算符优先级,最后是6-4=2,因为减号返回一个数而不是合法的左值,所以编译报错
2.声明一个#define宏定义用以表示1年终有多少秒?
#define SECONDS_PER_YEAR (60*60*24*365)UL
后缀UL表示SECONDS_PER_YEAR是一个长整形,C语言中默认宏中数字是int
3.写出标准的宏MIN,这个宏输入两个参数并返回较小的一个
#define MIN(A, B) ((A) <= (B) ? (A) : (B))
U表示该常数用无符号整型方式存储,相当于 unsigned int ,整形默认int
L表示该常数用长整型方式存储,相当于 long
F表示该常数用浮点方式存储,相当于单精度 float,浮点数默认double,
在C++中,函数重载中通常用在函数参数匹配上,尤其是重载的时候,比如说void fun(int), void fun(float),调用fun(0.5)时,可能会觉得应该调用void fun(float),因为0.5是浮点数,但实际上这个调用在语法标准上是有歧义的。因为0.5是默认的double类型,double转换为int和float的优先级是一样,所以加上后缀f,像fun(0.5F),这样就指明调用第二个函数。避免导致歧义。
数值常数的后缀不区分大小写
整形常数的表示形式有:十进制形式,以0开头的八进制形式,以0x开头的十六进制形式,无二进制形式
6.2 const
定义局部变量
const int n = 5;
int const n = 5;//这两种写法没有区别
在定义指针变量时
int b = 500;
const int *a = 500;
*a = 600;//情况1:错误,不能通过常量指针(*a)修改指针指向的值
int b = 500;
int const *a = &b;
b = 600;
cout<< *a <<endl;//情况2:但可以通过其他引用b改变*a的值
int c = 700;
a = &c;//常量指针也可以指向其他的地址,通过引用c改变a的值
int b = 500;
int * const a = &b;//情况3,const修饰变量时,一定要初始化;情况1和2不一定要初始化
*a = 600;//指针常量指向的地址不能修改,但是可以修改地址保存的值
cout <<a++<<endl;//错误,指针常量不能对指针直接操作
int b = 500;
const int* const a = &b;
int* p = &b;
*p = 600;//情况4,b编程600,指针指向的位置不能改变并且也不能通过这个指针改变变量的值,但是
依然可以通过其他的普通指针改变变量的值
const int &a = 100;//常数引用,即不能改变引用的值
在函数中使用
void fun(const int a) //函数内不允许修改参数a
const int fun();//不允许修改函数的返回值
const char* GetString(void);
char* str = GetString();//编译错误
const char* str = GetString();//const修饰函数返回值,则函数返回值只能赋值const修饰的同类型指
针
int fun() const;//C++中,函数不能修改类中别的成员值,只能用于别的成员函数中
const和#define相比有什么不同?
const和#define都可以定义常量;const常量有数据类型,编译器可以进行类型安全检查,但是宏常量只是进行字符替换没有安全检查,可能会产生意想不到的错误;有些集成化的调试工具可以对const常量进行调试,但是不能对宏常量进行调试;在C++程序只使用const常量而不使用宏常量。
在C语言中,const总是占用内存,名字是全局符,C编译器不能把const看成编译器件的常量,因此const bufsize = 100;char buf[bufsize];会编译错误,编译期间找不到bufsize的值。在C++中不能写const bufsize声明,但在C中可以,const在C中是外部链接的,在C++中是内部链接的,因此在C++中需要使用extern 关键字,extern const bufsize才可以。
有类如下:const修饰的函数f()怎么才能修饰成员变量?
class A_class
{
void f() const
{
……
}
}
数据成员加上mutable后可以放在const修饰的成员函数中
#include <iostream>
#include <iomanip>
using namespace std;
class C
{
public:
C(int i):m_Count(i){}//C(int i):m_Count(i){} //冒号后面m_Count(i)表示对成员变量初始化
int incr() const
{
return ++m_Count;
}
int decr() const
{
return --m_Count;
}
private:
mutable int m_Count;
};
int main()
{
C c1(0), c2(10);
for(int tmp, i = 0; i < 10; i++)
{
tmp = c1.incr();
cout<<setw(tmp)<<setfill(' ')<<tmp<<endl;
tmp = c2.decr();
cout<<setw(tmp)<<setfill(' ')<<tmp<<endl;
}
return 0;
}
冒号的用法
https://segmentfault.com/a/1190000000345680
//位段,bit field
typedef struct_XXX{
unsigned char a:4;//结构体内域的定义
unsigned char c;
}XXX;
//初始化结构体成员列表
class myClass
{
public:
myClass();
~myClass();
int a;
const int b;
}
myClass::myClass():a(1),b(1)//初始化结构体成员列表
{
}
/*
初始化列表与在构造函数内对结构成员赋值作用相同,都是初始化结构体成员,但还是有些区别的。初始化列表
是对变量初始化,而在构造函数内是进行赋值操作,两种操作在const类型的数据上不一样。const类型数据必须
初始化,不能对const类型应用赋值运算符,因此const类型的数据只能进行列表初始化
*/
//这样代码就会出错
myClass::myClass()
{
a = 1;//没错,效果相当于在初始化列表中初始化
b = 1;//出错,const类型不能进行赋值操作
}
//列表初始化的顺序要与变量声明的顺序一致
myClass::myClass():b(1),a(b)
{
}
//这样执行的结果b=1,a是随机数;而不是b=1,a=1。
//对于继承的类来说,在初始化列表也可以进行基类的初始化,初始化的顺序是先基类初始化,再按子类的变
//量声明顺序初始化。
//条件运算符 ? :,需要注意的是条件运算符优先级很低
int i = 3, j = 2;
cout<< i>j?i:j;//出错,?:优先级低于<<,执行顺序为(cout<<i)>j?i:j,cout<<i不能与整形进行比较
cout<<(i>j)?i:j;//输出1或0,相当于(cout<<(i>j))作为?:的判决条件,i>j时输出1,否则0
cout<<(i>j?i:j);//i>j输出i,否则输出j
//域操作符
void A::f() //f()函数是类A的成员函数
6.3 sizeof
结构体对齐,结构体长度一定是最长的数据元素的整数倍,如果结构体内存在长度大于处理器位数的元素,那么就以处理器的位数为对齐单位。但是结构体内类型相同的连续元素和数组一样将在连续的空间,不会被直接对齐。
struct
{
short a1;
short a2;
short a3;
}A;
sizeof(A);//6,相同的元素像数组一样存储在连续的空间,所以是6
struct
{
long a1;
short a2;
}B;
sizeof(B);//8,a1四字节,a2 两字节,4+2=6不是4的整数倍,补空字节,8字节
class A
{
private:
short a1;
short a2;
short a3;
};//sizeof(A),6字节
class B
{
private:
bool m_bTemp;
int m_nTemp;
bool m_bTemp2;
};
|bool|--|--|--|
|-----int-----|
|bool|--|--|--|
//12字节,不连续,都按最长的四字节对齐
class C
{
private:
int m_nTemp;
bool m_bTemp;
bool m_bTemp2;
};//8字节,4+2=6,补空到4的整数倍8字节
数据对齐是,是指数据所在的内存地址必须是该数据长度的整数倍。在访问内存时,如果地址按4字节对齐访问效率会高很多,原因在于访问内存的硬件电路。一般情况下,地址总线总是按照对齐后的地址来访问。例如当需要从0x00000001开始访问4字节内容,系统首先需要以0x00000000开始读4字节,从中取得3字节,然后再用0x00000004作为开始地址获取下一个4字节,再从中得到第一个字节,两次组合得到想要的内容,但是如果地址一开始就是对齐到0x00000000,则系统只需要一次读写即可。
#include <iostream>
using namespace std;
class A1
{
public:
int a;
static int b;
A1();
~A1();
}
int main()
{
count<<sizeof(A1)<<endl;//4字节;静态变量是存在全局数据区,sizeof计算栈中分配的大小
return 0;
}
char* ss = "0123456789";
//sizeof(ss); 4字节,任意指针大小是4字节
//sizeof(*ss); 1字节,*ss表示第一个字符0
char ss[] = "0123456789";
//sizeof(ss); 11字节,sizeof计算到数组的‘\0’的位置
//sizeof(*ss); 1字节,*ss是第一个字符
char ss[100] = "0123456789";
//sizeof(ss); 100字节,ss表示内存中分配的大小
//strlen(ss); 10字节,strlen计算到'\0'位置
int ss[100] = "0123456789";
//sizeof(ss); 400字节,内存中分配100*4
//strlen(ss); strlen参数只能是char*, 且必须有'\0'结尾
//sizeof是运算符,可以用类型做参数,strlen是函数
数组做sizeof的参数不退化,传递给strlen就退化为指针;sizeof在编译时就计算,是类型或变量的长度,这就是sizeof(x)可以用来定义数组维度的原因;strlen是在运行时计算字符串长度的,而不是计算内存的大小;sizeof后面是类型必须有括号,变量名不用加括号,因为sizeof是操作符,不是函数。
char str[20] = "0123456789";
int a = strlen(str); //a = 10;
int b = sizeof(str); //b = 20;
数组作为参数传递给函数时传递的是指针而不是数组,传递的是数组的首地址,如func(char [])等价于func(char*).C++里传递数组永远都是传递指向数组首元素的指针;如果要在函数内使用数组,需要
void fun(usigned char* p1, int len)
{
usigned char* buf = new usigned char[len+1];
memcpy(buf, p1, len);
return;
}
#include <iostream>
#include <string>
using namespace std;
int main(int argc, char* argv[])
{
//To output "TrendMicroSoftUSCN"
string strArr1[] = {"Trend", "Micro", "Soft"};
string *pStrArr1 = new string[2];
pStrArr1[0] = "US";
pStrArr1[1] = "CN";
for(int i = 0;i < sizeof(strArr1)/sizeof(string); i++)
{
cout<<strArr1[i];
}
for(int j = 0; j < sizeof(pStrArr)/sizeof(string); j++)
{
cout<<pStrArr1[j];
}
return 0;
}
//实际输出是TrendMicroSoftUS,strArr1是由3段构成,sizeof(strArr1)/sizeof(string) = 12/4;
//而sizeof(pStrArr)/sizeof(string) = 1/1
正确应该改为
for(int j = 0; j < sizeof(*pStrArr)*2/sizeof(string); j++)
{
……
}
//*pStrArr表示数组中成员,2是数组长度,sizeof是数组成员类型
(1) unsiged 影响的是最高位bit的意义(正/负),数据长度是不会改变的
//sizeof(unsigned int) = sizeof(int)
(2) typedef short WORD ,sizeof(short) = sizeof(WORD)
(3) 对函数使用sizeof, 在编译阶段会被函数返回值的类型取代。如:int f1() {return 0;}
count<<sizeof(f1())<<endl; //f1()返回值为int,因此被替换成int
(4) char a[] = "abcdf ";//sizeof(a) = 7,包括'/0'
int b[20] = {3, 4}; //sizeof(b) = 20*4
char c[2][3] = {"aa", "bb"};//sizeof(c) = 6
7.1 指针基本问题
指针与应用的区别?非空区别:不存在指向空值的引用,函数入参声明为引用可以利用这点省去入参非空检查,而指针可以指向空;可修改性:指针可以被重新复制以指向另一个不同的对象,而引用总是指向初始化时指定的对象,但对象的内容可以修改;应用区别:
#include <iostream>
using namespace std;
int main()
{
int* pi;
*pi = 5;//错误,整数指针pi并没有指向具体的地址,给其复制因不知道该往什么地址放值而错误
const double di;//const 常量一定要初始化
return 0;
}
void swap(int* p, int* q)
{
int* temp;
*temp = *p;
*p = *q;
*q = *temp;
}
//int* temp,*temp = *p是不符合逻辑的,因为int* temp并没有为temp分配内存,*temp = *int不是指向
//而是复制;系统临时随机地为*temp分配内存,使其存储*p的值,且函数结束后不收回,会造成内存泄露;在某
//些编译器下可以编译过,但是在vs2008等严格的编译器会报错
12.位运算与嵌入式
7.要求设置一绝对地址0x67a9的整形变量的值为0xaa66。编译器是ANSI编译器。
//把整形数强制转换成为指针是合法的
int *ptr;
ptr = (int*)0x67a9;
*ptr = 0xaa66;
8.找出下面代码的错误
unsigned int zero = 0;
unsigned int compzero = 0xFFFF;
//不严谨,没有考虑处理器字长;对于unsigned int 不是16位的机器来说,上述代码是错误的,改为
unsigned int compzero = ~0;
//static分配在内存的全局变量区,在函数体内时,函数退出时并不释放, 下次执行函数时仍会使用上次退出时
的值。
#include<iostream>
#include<stdlib.h>
#include "train/train.h"
using namespace std;
int sum(int a)
{
int c = 0;
static int b = 3;
c += 1;
b += 2;
b += 3;
printf("Init b:%d\t", b);
return (a+b+c);
}
int main()
{
int I;
int a = 2;
for(I=0;I<5;I++)
{
printf("%d.\n", sum(a));
}
return 0;
}
/*
Init b:8 11.
Init b:13 16.
Init b:18 21.
Init b:23 26.
Init b:28 31.
*/
//大小端描述字节顺序
//大端:从地地址开始先存储高位字节,符合人的书写习惯
#include<iostream>
#include<stdlib.h>
#include "train/train.h"
using namespace std;
typedef struct bitStruct
{
int b1:5;
int :2;
int b2:2;
}bitStruct;
int main()
{
bitStruct b;
printf("%d.\n", sizeof(bitStruct));
memcpy(&b, "EMC", sizeof(b));
printf("%d, %d.\n", b.b1, b.b2);
return 0;
}
/*
高<----------------------------------------低
ASCII: 1M(0x4D) E(0x45)
01001101 01000101
--=====
b2 b1
b1 00101 有符号数扩展高位都是0,结果为5
b2 10 有符号数扩展高位1后得到原码,计算补码得到结果-2
*/
13.数据结构基础
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <conio.h>
typedef struct student
{
int data;
struct student *next;
}node;
node* creat(void)
{
node* head, *p, *s;
int x, cycle = 1;
head = (node*)malloc(sizeof(node));
p = head;
while(cycle)
{
printf("\nplease input data");
scanf("%d", &x);
if(x != 0)
{
s = (node*)malloc(sizeof(node));
s->data = x;
printf("\n%d", s->data);
p->next = s;
p = s;
}
else
{
cycle = 0;
}
}
head = head->next;
p->next = NULL;
printf("\n%d", head->data);
return head;
}
int length(node *head)
{
int n = 0;
node *p;
p = head;
while(p != NULL)
{
p = p->next;
n++;
}
return n;
}
void print(node* head)
{
node* p;
int n;
n = length(head);
printf("\nNow, These %d records are: \n", n);
p = head;
if(head != NULL)
{
while(p != NULL)
{
printf("\n %d", p->data);
p = p->next;
}
}
return;
}
node* del(node* head, int target_num)
{
node *p1, *p2;
p1 = head;
//p1为要删除的节点,p2为p1前面的节点
while(p1->data != target_num && p1->next != NULL)
{
p2 = p1;
p1 = p1->next;
}
if(target_num == p1->data)
{
if(p1 == head)
{
head = p1->next;
free(p1);
}
else
{
p2->next = p1->next;
}
}
else
{
printf("\n %c could not not been found", target_num);
}
return head;
}
node* insert(node* head, int num)
{
node* p0;
node* p1;
node* p2;
p1 = head;
p0 = (node*)malloc(sizeof(node));
p0->data = num;
//p0为待插入的节点,数据从小到大顺序插入节点
while(p0->data > p1->data && p1->next != NULL)
{
p2 = p1;
p1 = p1->next;
}
if(p0->data <= p1->data)
{
if(head == p1)
{
p0->next = p1;
head = p0;
}
else
{
p2->next = p0;
p0->next = p1;
}
}
else
{
p1->next = p0;p0->next = NULL;
}
return head;
}
node* sort(node* head)
{
node *p;
int n; int temp;
n = length(head);
if(head == NULL || n == 1)
{
return head;
}
p = head;
for(int j = 1;j < n;j++)
{
p = head;
for(int i = 0; i < n - j; ++i)
{
if(p->data > p->next->data)
{
temp = p->data;
p->data = p->next->data;
p->next->data = temp;
}
p = p->next;
}
}
return head;
}
/* 逆置单链表 */
node* reverse(node* head)
{
node *p1, *p2, *p3;
if(head == NULL || head->next == NULL)
{
return head;
}
p1 = head;
p2 = p1->next;
while(p2)
{
p3 = p2->next;
p2->next = p1;
p1 = p2;
p2 = p3;
}
return head;
}
/* 找到单链表中间节点 */
node* searchmid(node* head)
{
node *temp = head;
node *p = head;
while(head->next->next != NULL)
{
p = p->next->next;
temp = temp->next;
}
return temp;
}
13.5 栈
// 用两个栈实现队列功能
//入队列:入栈A
//出队列: 如果栈B不为空,直接弹出栈B的数据;如果栈B为空,则依次弹出A的数据放入栈B中,再弹出栈B的数据
#include <iostream>
#include <stack>
using namespace std;
template<class T>
struct MyQueue
{
void push(T &t)
{
s1.push(t);
}
T front()
{
if(s2.empty())
{
if(s1.size() == 0) throw;
while(!s1.empty())
{
s2.push(s1.top());
s1.pop();
}
}
return s2.top();
}
void pop()
{
if(s2.empty())
{
if(s1.size() == 0) throw;
while(!s1.empty())
{
s2.push(s1.top());
s1.pop();
}
}
if(!s2.empty())
s2.pop();
}
stack<T> s1;
stack<T> s2;
};
int main()
{
MyQueue<int> mq;
int i;
for(i=0;i<10;++i)
{
mq.push(i);
}
for(i=0;i<10;i++)
{
cout<<mq.front()<<endl;
mq.pop();
}
return 0;
}