赞
踩
C++,调用函数时,根据不同的label来实现重载!
例如:
int memcmp(void *p)
{
int n = 17;
int m = memcmp(&n)
}
如果是C,翻译成汇编语言:
CALL <memcmp>
如果是C++,翻译成汇编语言:
CALL <memcmp_void_p>
再如:
int memcmp(void *v1, void *v2, int size)
如果是C,翻译成汇编语言:
CALL <memcmp>
如果是C++,翻译成汇编语言:
CALL <memcmp_void_p_void_p_int>
对于C++,入参的不一致 → “链接”错误→相比于C更安全,但也更死板!
segment fault 段错误
使用内存没有映射到“段”的地址导致的!在我们的“抽象内存模型”中,内存被分为“stack”、“heap”和“code”三段!NULL = 0,不在任何一个段内,如果对 NULL 进行 dereference,就会报 “segment fault”!
bus error 总线错误
硬件和操作系统对所有数据类型变量的地址进行了限制,int型变量的地址应该是4的倍数!使用指针时候,如果地址没对齐,会报“bus error”!
数据类型 | 对齐 |
---|---|
char | 1 |
short | 2 |
int | 4 |
其它 | 4 |
void *vp =...
*(short *)vp = 7; // 50% → bus error
*(int *)vp = 7; // 75% → bus error
*(char *)vp = 7; // OK!!!
缓冲区溢出(buffer overflow)是指下面这种情况:
int main()
{
int i;
int array[4];
for(i = 0; i < 4; i++) {
array[i] = 0;
};
return 0;
}
int main()
{
int i;
short array[4];
for(i = 0; i <= 4; i++) {
array[i] = 0;
};
return 0;
}
void foo()
{
int array[4];
int i;
for(i = 0; i <= 4; i++) {
array[i] -= 4;
}
}
void DeclearAndInitArray() { int array[100]; int i; for(i = 0; i < 100; i++) { array[i] = i; } } void PrintArray() { int array[100]; int i; for(i = 0; i < 100; i++) { printf("%d\r\n", array[i]); } } int main(int argc, char **argv) { DeclearAndInitArray(); PrintArray(); }
为什么没有进行函数之间的数据传递,仍然能够正确打印出来 0~99呢?
答:调用DeclearAndInitArray(),活动记录中有408(4+100x4+1x4)个字节,数组占据了saved PC下面的400个字节,DeclearAndInitArray()函数RET后,虽然这存放数组的400个字节已经出栈,但是并没有被清0;
继续调用PrintArray(),它的活动记录将占用刚才DeclearAndInitArray()的位置,并且活动记录的结构与DeclearAndInitArray()一模一样,此时,直接打印数组中的数据,会成功打印出DeclearAndInitArray()留在内存中的0~99。
这个特性能用在哪里?答:中断服务函数中!
留给中断服务函数处理事务的时间是很短的,我们可以先构造好一些常量数据,存放在“栈”中某个地方,这样在中断服务函数中就免于构造这些数据的麻烦了!
为了实现“可变参数”…
通常有“可变参数”的编程语言,函数入参都是从右向左压栈!
int printf(const char *control, ...);
…是可变参数!可以是任何类型数据,并且“个数”不限!
printf("%d + %d = %d \r\n", 4, 4, 8);
struct type {
int code;
};
struct type_one {
int code;
...
};
从上到下压栈,可以得到code与结构体变量基地址的距离!反着来,得到这个“距离”就依赖于总长度了,会变得复杂!!!
比如咱们的网络IP地址,以前的网络地址是IPv4的,用4个字节表示IP地址,某天大家意识到,4个字节的IP地址会很快被用光,因此开发了6个字节的IP地址。IPv4和IPv6对应着不同的结构体,我们希望IPv6能够兼容IPv4,所以IPv6的前4个字节结构与IPv4是一样的!要是IPv6的前4个字节后入栈,很难得到其余结构体变量基地址的距离了,会给变成带来麻烦!
int main()
{
int numAgents = 10;
int numTickets = 150;
for(int agent = 1; agent <= numAgents; agent++) {
SellTickets(agent, numTickets / numAgets);
}
return 0;
}
void SellTickets(int agentID, int numTickestoSell)
{
while(numTickestoSell > 0) {
printf("Agent %d sells a ticket \r\n", agentID);
numTickestoSell--;
}
printf("Agent %d: All down.\r\n", agentID);
}
预期结果:
打印出来160行;其中 Agent %d: All down.\r\n 占用10行。
上面这样实现,每个agent会依次买票,并不会10个agents同时一起买票!
希望使用1个“线程”包。
int main()
{
int numAgents = 10;
int numTickets = 150;
InitThreadPackage(false);
for(int agent = 1; agent <= numAgents; agent++) {
char name[32];
sprintf(name, "Agent %s Thread", agent);
ThreadNew(name, SellTickets, 2, agent, numTickets / numAgets); // 把狗狗们都放到起跑线上
RunAllThreads(); // 把门打开,大家一起跑啊!
return 0;
}
void SellTickets(int agentID, int numTickestoSell)
{
while(numTickestoSell > 0) {
printf("Agent %d sells a ticket \r\n", agentID);
numTickestoSell--;
if(RandomChance(0.1)) {
// 模拟条件:10% 概率被强制暂停运行
ThreadSleep(1000);
}
}
printf("Agent %d: All down.\r\n", agentID);
}
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。