C++/C/Unix中容易犯错的小题目(2015.8.10更新)


以下问题大多数都是本人在笔试、面试时遇到的.


(2012.10.12)

一、关于局部变量地址

int _tmain(int argc, char *argv[])
{
	int i;
	char ch[2];
	printf("%p %p %p\n", &i, &ch[0], &ch[1]);
	
	return 0;
}

结果为: 0012FF60 0012FF54 0012FF55

证明局部变量是按声明顺序顺序入栈,先入栈的会处于高地址,而数组则是index小的在低地址,index高的在高地址


二、关于小端CPU中整型、短型的研究

struct node{
	union 
	{
		char ch[4];
		short s[2];
		int i;
	};
};

int _tmain(int argc, char *argv[])
{
	struct node A;
	printf("%p %p %p %p\n", &A.ch[0], &A.ch[1], &A.ch[2], &A.ch[3]);
	printf("%p %p\n", &A.s[0], &A.s[1]);
	printf("%p\n", &A.i);
	
	return 0;
}

结果为:

0012FF60 0012FF61 0012FF62 0012FF63

0012FF60 0012FF62

0012FF60


接下来研究关于小端对整型数的存储

struct node{
	union 
	{
		char ch[4];
		short s[2];
		int i;
	};
};

int _tmain(int argc, char *argv[])
{
	struct node A;
	A.i = 0x12345678;
	printf("%x %x %x %x\n", A.ch[0], A.ch[1], A.ch[2], A.ch[3]);
	printf("%x %x\n", A.s[0], A.s[1]);
	printf("%x\n", A.i);
	
	return 0;
}

结果为:

78 56 34 12

5678 1234

12345678


从上面结果,显而易见,高位存放在高地址,低位存放在低地址

 

(2012.10.15更新)

三、关于函数调用中的参数小细节

先看以下代码

int f1()
{
	printf("1\n");
	return 0;
}

int f2()
{
	printf("2\n");
	return 0;
}

void f3(int a, int b)
{
}

int _tmain(int argc, char *argv[])
{
	
	f3(f1(), f2());
	
	return 0;
}


输出结果为:

2

1

 

可以看出,调用函数时,参数是从右到左逆序进栈的,因此会先调用f2(),再调用f1()

 

 

四、关于++i和i++

这个一直是很多初学者比较模糊的地方,也怪某些教材的简化,记得当年我的教材对i的解释大概是

++i : 先对i加1,再返回

i++ : 先返回i的值,再加1

 

实质上,若以这种想法看待问题,就难以解释以下代码

int  f(int a, int b)
{
	return a + b;
}

int _tmain(int argc, char *argv[])
{
	int i = 5;
	printf("%d\n", f(i++, ++i));
	
	return 0;
}


上述结果为:

13

 

经过断点调试,可以发现,在函数f里面,a值为6,b值为7!为何第二个参数的值会比第一个大?按理说逆序进栈,传进去的第二个参数应该小于等于第一个才对,因为先执行了++i,再执行i++。但上述结果证明了++i和i++并不是这么简单的先加后加……

 

实质上,应该这样理解,对于++i,是把i加1,然后返回i变量本身;对于i++,是先把i的值保存到一个临时变量中,把i的值加1,然后返回临时变量。

 

所以,对于上面代码,应该这样理解:

先执行++i,i值变为6,之后执行i++,先把i的值放到一个临时变量中,则该临时变量值为6,然后再对i加1。之后是把参数逆序进栈,先把i的值进栈,则此时为7,再把临时变量的值进栈,值为6。

 

后来通过对上述代码进行反汇编,证实了这一说法。


事实上,熟悉C++关于运算符重载的朋友应该都有了解到,重载前置++运算符时,返回类型为一个对象的引用,重载后置++运算符时,返回类型为一个对象的值(实质为一个临时变量,可以参考《深度探索C++对象模型》)。因此与上面描述的问题是一致的




(2013.3.5更新)

五、关于多线程和多进程区别的总结

多线程和多进程都是用于并发处理的,这里假设读者们已懂这两个名词的含义,不懂的自己查书查资料……

而且以下主要是做一种归纳,可能会涉及较多名词和内容,不懂的读者请自行查资料,不然一展开来说就太复杂了……

主要的区别:

1:开销。创建一个新的子进程的时候,需要为期分配新的进程堆栈,复制父进程堆、栈和数据空间(这个内核通常会通过写时拷贝来优化),另外父子进程共享正文段。 而线程只是在进程的空间里面分配一小块空间,不涉及创建新的子进程进程那些复制操作,开销明显小很多。

2:调度。多线程处于同一地址空间,因此调度的开销比多进程的要小。

3:数据共享,任务间通信,同步问题:进程间通信需要用共享内存(这个使用率最多)、消息队列、信号量、管道、命名管道等方式,而多线程间由于共享统一进程空间,因此共享数据容易,例如设置一个全局变量。


 (2015.8.10更新)

六、关于二维数组和数组指针

int _tmain(int argc, _TCHAR* argv[])
{
int a[3][4]={2,4,6,8,10,12,14,16,18,20,22,24};
int *ap = (int*)a;
int (*bp)[4] = a;
printf("%d %d\n", **(a+1), *(*a+1));
printf("%d\n",*(ap+1));
printf("%d %d\n",**(bp+1), *(*bp+1));
return 0;
}


输出结果为:

10 4

4

10 4


**(a+1)和**(bp+1)这两个都是取了a[1][0]的值,其他三项取的是a[0][1],数组指针必须要谨慎分析。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值