在家是专门离线记录的,现在在公司,就在这写写,自己能看懂就行。
1. 资源是有限的,不是无限的,因此资源是需要被分享的,这从根本上导致了程序bug,导致了程序的复杂性。
eg.:钓鱼岛之争,因为钓鱼岛背后的能源,能源有限,却由两个不同利益团体需要。
eg.:内存地址空间是有限的,却被不同的进程或线程公用, 内存管理函数来了,操作系统做的事情就麻烦了。
eg.: 以arm920t为例,每一种模式能用的寄存器最多也就18个(r0-r15, cpsr, spsr),而这18个寄存器被所有函数公用,这么少的资源,既然能组成这么庞大的嵌入式世界,本身 就很神奇,所以写汇编麻烦,不容易懂,高级语言厉害之一就在于不用程序员去管理那些麻烦的寄存器。 sp(r13)的共享导致栈溢出是个经常出现的问题。
eg.:栈溢出: 一个函数破坏了别的函数的栈,因为同一个进程下地址空间是一致的,栈的分配也确定了,栈被该进程下所有函数共享,栈溢出导致bug。
eg.:慎用全局变量的原因,因为该全局变量被不同源文件,不同函数共享等等,在有优先级的多线程程序中,全局变量所以最好加保护,就像领土国家要派士兵保卫一样。
2. 在c中有很多是 成对(pair)的, 即其生命周期中执行了2次,另外成对的也必须执行两次。
eg.:是执行,而不是出现,典型的是内存分配和释放,简化考虑在一个函数中实现,在一个函数中执行了成功两次malloc, 在该函数执行完成前必须两次执行free内存(非出现),因为有可能出现if等分支导致free出现多次,但真正只执行两次。 当然free 后要 使指针为NULL避免野指针。
eg.: c和c++对于if等成对控制分支没有Verilog那么严格,Verilog中if 和 else 必须成对,且if中用到的变量在else中也必须赋值,如果不这样很容易导致锁存器,导致bug。
eg.: 变量(对象)属性成对:从c来看为 值(类型) + 地址。 从汇编来看为 地址 + 变量占用的内存字节(以及排列方式);
3. c/c++中一个变量的两个必要属性, 值与地址。 地址本身是无法单独存在的,其存在的价值是能够找到其对应的变量的值, 所以c中函数有所谓的值传递,地址传递。 比如指针,如果指针未被正确赋值,就成了野指针(如执行free后未置NULL)。 怎么解释呢,感觉绕口, 指针本身是个变量,所以指针变量也有地址和值,只是其值存的是其它变量的地址,通过指针变量的值访问到其它变量的地址,进而访问到对应变量的值。
eg.: 这是今天主要想写的,贴代码说,因为也是弄了半天才真正明白。
#include <stdio.h>
typedef signed long acpi_native_int; //32 位,4字节
#define _AUPBND (sizeof (acpi_native_int) - 1)
#define _ADNBND (sizeof (acpi_native_int) - 1)
#define _bnd(X, bnd) (((sizeof (X)) + (bnd)) & (~(bnd)))
#define _bnd(X, bnd) (((sizeof (X)) + (bnd)) & (~(bnd))) // 下一个变量地址(必须按对齐规则,本例中是4字节对齐)
#define va_arg(ap, T) (*(T *)(((ap) += (_bnd (T, _AUPBND))) - (_bnd (T,_ADNBND))))
#define va_end(ap) (ap = (va_list) NULL)
#define va_start(ap, A) (void) ((ap) = (((char *) &(A)) + (_bnd (A,_AUPBND))))
char *test(const char *fmt, ...)
{
char *args = 0;
va_start(args, fmt); // 经宏展开得到获得了test函数第一个可选形参的地址,因为test的参数在栈中从高到低为test中从右到左。
int args_value = *(int *)args; // 根据获得args的地址获得args的值
int *var_addr = (int *)args_value; // args的值同时也是main中传来的var的地址
int var = *var_addr; // 根据var的地址解析得值
va_arg(args, int *);
int num = (*((int *)args));
printf("var = %d\n", var);
printf("num = %d\n", num);
return args;
}
int main(void)
{
int var = 2;
test("hello", &var, 20);
return 0;
}
如上代码是在看linux的sscanf函数时用来验证的,看注释分析来分析去,也就是在变量的地址和值见绕。
4. 还有好些,是离线记的。