运行以下程序时,出现这类错误:*** stack smashing detected ***: ./test_global terminated
。错误缘由多是由于scanf("%d%d", &row, &col)
接收的是int
型,可是我使用的是short int
,长度是Int
的一半。修改为int
后错误消失。linux
#include<stdio.h> int main(){ int row, col; scanf("%d%d", &row, &col); printf("%d %d", row, col); return 0; }
使用gcc编译时出现的警告以下:
出现的错误以下:
ios
运行以下程序时,会无终止地打印-1。缘由是变量p所指向的变量k在addr()函数执行后自行销毁,k所使用的内存被分配给loop()中的变量i,从而致使p指向i。而此时对p的操做是减1,对i的操做是加1,致使i的值始终为-1,没法跳出循环。shell
#include<stdio.h> void addr(); void loop(); long *p; int main(){ addr(); loop(); } void addr(){ long k; k = 0; p = &k; } void loop(){ long i, j; j = 0; for (i = 0; i<10;i++){ (*p)--; j++; printf("%d\n", i); } }
程序运行输出结果以下:
程序调试结果以下:
数组
虽然运行下面代码不会出错,可是对数组a[10]的写操做超出了维度,致使在地址为a+10的地方也写入了数据,可是容易引起潜在bug。函数
#include<stdio.h> int main() { int i; int a[10]; for (i = 0; i <= 10; ++i) { a[i] = 0; printf("%d\n", i); } exit(0); }
对于将指针做为参数进行传递时,若是是将在子函数内赋值给一个新申请的空间,那么就要注意在传递指针时,须要传递指针的地址,即指针的指针。错误程序以下:oop
#include<stdio.h> void allocateInt(int * i, int m); void main() { int m = 5; int * i = &m; printf("i address: %x\n", &i); allocateInt(i, m); printf("*i = %d\n", *i); } void allocateInt(int * i, int m) { printf("i address: %x\n", &i); i = (int *) malloc(sizeof(int)); *i = 3; }
虽然对该问题的解释通常是:在传递参数时,系统为子函数的变量新申请一部分空间,所以在void allocateInt(int * i)
中,i的地址和在void main()
中的地址是不一样的,而void allocateInt(int * i)
中的i是局部变量,在子函数运行结束会被释放掉,所以void main()
中的i是没法获得malloc的地址的,更不可能获得新的赋值。
下面经过gdb调试以及反汇编来进行说明:性能
allocateInt(i, m);
语句时,变量i和m的内存地址以下图所示,&i=0x7fffffffdaf0,&m=0x7fffffffdaec:allocateInt
函数。进入时,i=0x7ffff7ffe168, m=0,也就是说i和m还并无被传递赋值,结果以下所示:void allocateInt(int * i, int m)
中的printf("i address: %x\n", &i);
,以下图所示:push %rbp
,也就是把rbp寄存器入栈;mov %rsp,%rbp
,其中rsp是堆栈指针。也就是把堆栈指针的值赋值给rbp寄存器;sub $0x10,%rsp
,也就是把堆栈指针所指向的地址减小16个字节。这是由于变量i和m一共占用了16个字节;mov %rdi,-0x8(%rbp)
,也就是把寄存器rdi的值(rdi=0x7fffffffdaec,以下图所示)赋值给i。由于i的地址就是rbp-0x8;mov %esi,-0xc(%rbp)
,做用相似于第4条,将寄存器esi的值(esi=0x5,以下图所示)赋值给m。lea
命令能够网上查找,主要就是一种更加有效的mov方法;callq 0x4004a0 <printf@plt>
,意思是调用print函数。可是这里并非直接调用print函数,而是调用相似于print函数在进程中的别名。由于这是公用库中的函数,所以不一样进程中都会调用,因此只在进程中存留一个函数地址或者别名就好。具体参见stackoverflow上的一篇文章What does @plt mean here?。运行以下代码时,本意是用g_logger.WriteLog()将"in A()"写入文本文件中,可是结果倒是将"in A()"打印在了shell里。ui
// file: main.cc #include <iostream> #include "CLLogger.h" using namespace std; extern CLLogger g_logger; class A { public: A() { CLStatus s = g_logger.WriteLog("in A()", 0); if(!s.IsSuccess()) cout << "g_logger.WriteLog error" << endl; } }; A g_a; CLLogger g_logger; int main() { return 0; } // file: CLLogger.h #include "CLStatus.h" class CLLogger { public: CLLogger(); virtual ~CLLogger(); CLStatus WriteLog(const char *pstrMsg, long lErrorCode); private: CLLogger(const CLLogger&); CLLogger& operator=(const CLLogger&); private: int m_Fd; }; // file: CLStatus.h class CLStatus { public: CLStatus(long lReturnCode, long lErrorCode); CLStatus(const CLStatus& s); virtual ~CLStatus(); public: bool IsSuccess(); public: const long& m_clReturnCode; const long& m_clErrorCode; private: long m_lReturnCode; long m_lErrorCode; };
缘由是g_a是定义在g_logger以前,所以在运行到语句CLStatus s = g_logger.WriteLog("in A()", 0);
时,g_logger仍未定义。但因为在文件开头声明了extern CLLogger g_logger;
,所以编译器不会报错,而此时默认将声明为外部变量的g_logger中的文件操做符m_Fd赋值为0,以下:
spa
int a = -5; int b = 0; ................................................ if(a > 0){ if(a <= 0){ b = 1; b = 2; } } else{ else{ b = 2; b=1; } }
关于分支预测的一些预测方式能够参考一篇博客C++性能榨汁机之分支预测器
参考资料
Visual Studio文档:寄存器使用
探究Linux下参数传递及查看和修改方法
gdb 调试入门,大牛写的高质量指南
GDB的调试命令
What does @plt mean here?
C++性能榨汁机之分支预测器