虐心记
1. extern的作用
作用a: 当它与"C"一起连用声明函数时,则告诉编译器在编译该函数名时按着C的规则去翻译相应的函数名而不是C++的。
例如,extern "C" void fun(int a, int b);
作用b: extern声明一个变量或者函数时,表示该函数属于外部变量或者函数,这时候可以引用非本文件的函数。
例如,extern int a; 以及extern int fun(void);
2. volitale的作用
由于cpu读取寄存器或者cache的速度要大于内存,编译器对存取内存次数进行优化。也就是说,cpu可能会认为上一次加载到寄存器或者cache的值为本次的值。在多线程中,内存空间是共享的。一个线程修改了变量值,另外一个线程依然从寄存器或者cache上获取该变量的值则可能导致数据不准确。
3. 计算32位的二进制数里面1的个数
这里介绍两种比较有难度的解法。
方法a:
private int pop(int x)
{
// 计算每两位中的 1 的个数 , 并且用该对应的两位来存储这个个数
x = x - ((x >> 1) & 0x55555555);
// 计算每四位中的 1 的个数 , 并且用该对应的四位来存储这个个数
x = (x & 0x33333333) + ((x >> 2) & 0x33333333);
// 8个位的1的个数一定不大于8,也就是说4个位则可以存储
// 计算每八位中的 1 的个数 , 并且用该对应的八位来存储这个个数
x = (x + (x >> 4)) & 0x0F0F0F0F;
// 计算每十六位中的 1 的个数 , 并且用该对应的十六位来存储这个个数
x = x + (x >> 8);
// 计算每三十二位中的 1 的个数 , 并且用该对应的三十二位来存储这个个数
x = x + (x >> 16);
// 32个位的1的个数一定不大于32,也就是说6个位则可以存储
return x & 0x0000003F;
}
该算法的优势在于不需要声明任何变量,而且都是使用位运算,处理高效。
方法b:
private int pop2(int x)
{
int n;
n = (x >> 1) & 033333333333;
x = x - n;
n = (n >> 1) & 033333333333;
x = x - n;
x = (x + (x >> 3)) & 030707070707;
x = x % 63;
return x;
}
4. 如何判断栈是向上增长,还是向下增长?
bool CompareStack(int *p)
{
int newInt;
if(&newInt < p)
return true; // 地址递减
else
return false; // 地址递增
}
bool GetStackOrder()
{
int oldInt;
return CompareStack(&oldInt);
}
局部变量是在函数被调用的时候,在栈申请。由此可以确定,函数里面的局部变量会比被该函数调用的函数的局部变量的内存申请的早。也就是说,该函数的局部变量先入栈,被该函数调用的函数局部变量后入站。由此可以判断增长方向。以上述代码为例,&newInt < p则向上增长;反之,向下增长。
5.如何判断cpu是大端模式还是小端模式
big-endian模式MSB存放在最低端的地址上;little-endian模式LSB存放在最低端的地址上。举例,双字节数0x1234以big-endian的方式存在起始地址0x00002000中,0x12 <-> 0x0000 2000;0x34 <-> 0x0000 2001。对于little-endian模式,则0x34 <-> 0x0000 2000;0x12 <-> 0x0000 2001。
测试方法a:
if((char)((int)0x0001) == 1){
// little-endian
}else{
// big-endian
}
测试方法b:
union __TENDIAN{
int a;
char b;
}tend;
tend.a = 1;
if(tend.b == 1){
// little-endian
}else{
// big-endian
}
6.如何输出C函数调用栈?
#define MAX_DUMP 1024
void app_dump_strace(void)
{
void *array[MAX_DUMP] = {0};
char **strframe = NULL;
unsigned intsize = 0;
unsigned int i = 0;
size = backtrace(array, MAX_DUMP);
strframe = (char **)backtrace_symbols(array, size);
printf("====dump_backtrace=====\r\n");
printf("========Begin==========\r\n");
printf("backtrace level = %d\r\n", size);
for(i = 0; i < size; i++){
printf("%d:\t%s\r\n", i, strframe[i]);
}
printf("=========End===========\r\n");
if(strframe){
free(strframe);
strframe = NULL;
}
}
该函数有两个比较有用的技巧。a.分析应用的函数调用关系;b.判断段错误(通过注册信号SIGSEGV)。如下:
signal(SIGSEGV, DebugBacktrace);
7.数组指定元素初始化
static const char *CmdList[] =
{
[0] = "ATH\r",
[1] = "ATE0\r",
[2] = "AT&W\r"
};
8.打印宏定义
#include <stdio.h>
#define PI 3.1415926
#define __EVAL_MACRO_HELPER(x) #x
#define EVAL_MACRO(x) __EVAL_MACRO_HELPER(x)
#pragma message(EVAL_MACRO(PI))
#pragma message __FILE__ " " EVAL_MACRO(__LINE__)
int main(int argc, char *argv[])
{
return 0;
}