程序员面试宝典题目

7 篇文章 0 订阅

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;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值