第一章:关键字
auto:所有变量在编译器默认缺省情况下都是auto的
register:请求编译器尽可能的将变量存在CPU内部寄存器而不是通过内存寻址访问以提高效率。尽可能是因为一个CPU的寄存器也就那么几个或几十个,如果定义了很多register变量是不可能全部放入的。
用&符号是去内存中取址,取不到放在编译器中的地址。CPU不直接和内存打交道而是通过寄存器去进行交流。因此&符号不能获得register的值。
static(调试版):
#include<iostream>
using namespace std;
static int j;
void fun1(void)
{
static int i = 0;
i++;
}
void fun2(void)
{
j = 0;
j++;
}
int main()
{
for (int k = 0; k < 10; k++)
{
fun1();
fun2();
}
system("pause");
}
//结果:
i=1,2,3,4,5,6,7,8,9,10
j=1,1,1,1,1,1,1,1,1,1
其实主要是考察调试的问题,fun1(),fun2()设断点然后F5,再F10,F11的问题。
static(输出版):
#include<iostream>
using namespace std;
static int j;
int* fun1(void)
{
static int i = 0;
int *a = &i;//这样做可以获得i的地址,也可以*i获得i的值
i++;
return a;
}
int* fun2(void)
{
j = 0;
int *a = &j;
j++;
return a;
}
int main()
{
for (int k = 0; k < 10; k++)
{
fun1();
fun2();
cout << *b << endl;
cout << *c << endl;
}
system("pause");
}
sizeof://是关键字不是函数
int i=0;
sizeof(int)==sizeof(i)==sizeof i==4
double a[100] = {0.0};
double *p=a;
sizeof(a);//值为800
sizeof(p);//值为4
sizeof(a[0])==sizeof(*a)//值为8
sizeof(&a)==sizeof(&a[0])//值为4,有取址符号的都为4,因为地址的sizeof为4
长循环放在内层可以节省效率,减少CPU切换循环层的次数。
符号表是一种用于语言翻译器的数据结构,在符号表中每个标识符都和它的声明或使用信息绑定在一起,比如其数据类型,作用于以及内存地址。const由于没有了存储和读内存的操作,使得它的效率也很高。
0x01<<2+3;//由于+号的优先级大于<<或>>符号,因此结果为0x01左移5位,值为32,一个整形长度位32位,左移或右移总位数不能超过32位。
第四章:指针与数组
*p.f=>*(p.f)//因为.的优先级高于*,因此该句的语义为对p取f偏移作为指针,然后指针去解引用。解引用就是取值的意思。
int *ap[]=>int *(ap[])=>(int *)(ap[])//因为[]的优先级高于*,因此ap是一个元素为int指针的数组。
int (*ap)[]//就是指向数组的指针。
int *fp()=>int *(fp())//因为()的优先级高于*,因此fp是一个函数,返回int *类型,也就是返回指针的函数。函数指针指向函数的入口地址。
int (*fp)()//fp为一个指向函数的指针,也就是函数的指针,这里函数参数为空,返回值为int。
++和--的优先级高于*
练习:
1.int (*(*d)[5])(int *);
*d是一个指针,指针指向一个含有5个元素的数组,每一个元素是一个函数指针,函数参数为int*,返回值为int。
2.int (*(*e)(int*))[5];
*e是一个指针,这个指针是一个函数指针,形参为int*,返回一个指向数组的指针,这个指针是整型的。
我们需要改为:
typedef int (*pArr)[5];//这里的这个括号说明这是一个函数指针数组,int为返回值。如果没有括号这是就是别名的意思。
typedef pArr (*func)(int*);
char a[5] = { 'A', 'B', 'C', 'D', 'E' };
char(*p3)[5] = &a;//这里因为左右相等的原则,只能以char(*p3)[5]的形式,其他都不可以,这里的p3是指整个数组,所以*(p3+1)指的是E后面的那个字符,也就是无效空字符。
#include<stdio.h>
int main()
{
char a[5] = { 'A', 'B', 'C', 'D', 'E' };
char(*p3)[5] = &a;
//char(*p4)[5] = a;
char *p4=*(p3 + 1);
return 0;
}
int *p=NULL;
和
int *p;
*p=NULL;//这个可能会不安全,因为p是一个未知地址,比较好的方法是
int i=10;
int *p=&i;
*p=NULL;
如何给一个地址存入值:
int *p=(int *)0x12ff7c;//如何判断一个内存是否可访问:int i=0;去到内存中取&i的值即可。但其实这种写法在VS上会报错,最好还是别用这种方法,说是因为调试器会保护要写区域的内存不允许访问。
*p=0x100;//将值100存入0x12ff7c
或者直接:*(int *)0x12ff7c=0x100;
#include <iostream>
using namespace std;
struct Test
{
int Num;
char *pcName;
short sDate;
char ch[2];
short str[4];
}*p;//值为20,转为16进制位14
void main()
{
p = (Test*)0x100000;
printf("p+0x01 -> 0x%08x\n", p+0x01);//+0x01等价于加了一整个数组结构,值为0x100014
printf("(unsigned long)p+0x01 -> 0x%08x\n", (unsigned long)p+0x01);//只加一,0x100001
printf("(unsigned int*)p+0x01 -> 0x%08x\n", (unsigned int*)p+0x01);//加了四,0x100004
system("pause");
}
大端模式:
a[0] a[1] a[2] a[3]
1 2 3 4
0x00 0x00 0x00 0x01 0x00 0x00 0x00 0x02 0x00 0x00 0x00 0x03 0x00 0x00 0x00 0x04
大端模式读出来的值是0x00000100,也就是0x100(大端从左往右读)
在32位的x86系统下,是小端模式——认为第一个字节是最低位字节
在内存中存放为:
a[0] a[1]
0x01 00 00 00 0x02 00 00 00
所以读取为:0x02000000(小端从右往左读)
二维数组的定义为:
a=>&a[0]
a[i]=>&a[0]+i*sizeof(char)*4
a[i][j]=>&a[0]+i*sizeof(char)*4+j*sizeof(char)=>*(*(a+i)+j)
#include<stdio.h>
#include<iostream>
using namespace std;
int main()
{
int a[3][2] = { (0, 1), (2, 3), (4, 5) };//因为是小括号,所以a的值为a={1,3,5};
//int a[3][2]={{0,1},{2,3},{4,5}};这样才是正确的取值
int *p;
p = a[0];//a[0]的值是{1,3}
printf("%d", p[0]);//p[0]的值为1
system("pause");
}
算内存最好的方法是画内存布局图
#include<stdio.h>
int main()
{
int a[5][5];
int(*p)[4];
p = a;/*这里如果没有疾病的话是有问题的...报错在于无法将int(*)[5]类型分配给int(*)[4],除非改为int (*p)[5],但这样就体现不出意义了,编译器为VS2013,可能是编译器的问题。不同的编译器的起始
值不一定为0。后面有提到应该把指针初始定为NULL,就是把它拴在尺子的0mm处,估计是因为没有拴指针导致的问题,看下之后第五章有没有解决办法。*/
printf("a_ptr=%#p,p_ptr=%#p\n", &a[4][2], &p[4][2]);
printf("%p,%d", &p[4][2] - &a[4][2], &p[4][2] - &a[4][2]);
return 0;
}
但其实这里最妙的地方在于int(*p)[4]的含义,它是指int[][4],这里的p++是以4个单位一起加的,结果为a[1][0],但如果int *p这里的p++就是一个一个加的,也就是a[0][1]。
#include<stdio.h>
int main()
{
int a[3][4];
int(*p)[4];//int *p;p=a;这玩意也跑不了啊,总而言之思想记住就好。
p = a;//如果这样跑的话这里就ok
printf("a_ptr=%#p,p_ptr=%#p\n", &a[4][2], &p[4][2]);
printf("%p,%d", &p[4][2] - &a[4][2], &p[4][2] - &a[4][2]);
return 0;
}
无法向函数传递一个数组:
无法向函数传递指针变量本身,而是指针变量本身的拷贝值
这样问题就来了,当我需要写一个GetMemory的函数,这个函数负责向内存申请所需的空间。
#include<iostream>
using namespace std;
char *GetMemory(char *p, int num)
{
p = (char*)malloc(num*sizeof(char));
return p;
}
int main()
{
char*str = NULL;
str=GetMemory(str, 10);
/*如果以函数void形式不加return返回值则无效,因为指针本身不会被传递到函数中,而是以_str的拷贝形式传进去的,然后这个_str在函数结束后又释放掉了,因此str还是老样子为NULL,而_str又在堆里自动销毁了。
唯一的用处是以指针指向_str的地址,将其拷贝出来并返回,这样main里才可以查找到这个值。也就是对于要传指针的函数一定要将这个指针传出,否则无法运用到这个指针。*/
strcpy_s(str,6, "hello");
cout << str;
free(str);
system("pause");
}
#include<iostream>
using namespace std;
char **GetMemory(char **p, int num)
{
*p = (char*)malloc(num*sizeof(char));
return p;
}
int main()
{
char*str = NULL;
GetMemory(&str, 10);//这里真真正正传的是str的值,而不是通过_str传的
strcpy_s(str,6, "hello");
cout << str;
free(str);
system("pause");
}
char a[3][4]等效的指针参数为char (*p)[10]
char *a[5]等效的指针参数为char **p
一个中括号可以抵掉一个*号
函数指针:
char *fun(char* p1,char* p2){}
main:
char *(*pf)(char* p1,char* p2);
pf=&fun;
*pf("aa","bb");
#include<stdio.h>
void Function()
{
printf("Call Function!\n");
}
int main()
{
void (*p)();
*(int*)&p=(int)Function;//强制将Function从void型转化为int型
(*p)();//这句话相当于调用了函数Function
return 0;
}
函数指针的主要作用在于
1.实现面向对象编程中的多态性
ps:很好的代码https://blog.csdn.net/philip_puma/article/details/25973139
2.回调函数
typedef void (*event_handler) (unsigned int para1, unsigned int para2);
struct event {
unsigned int ev_id;
event_handler handler;
};
struct event event_queue[MAX_EVENT_SIZE];
这里要搞懂怕是要去看源码了,这种坑不急着开。。。水平还不够。。。
像事件epoll源码里肯定是有的,红黑树那一块会用到。
/*class A{void fun();};
class B{void fun();};
list<EventHandler> _lstEventHandlers;
A a1,a2;B b;
_lstEventHandlers.push_back(a1.fun);
_lstEventHandlers.push_back(a2.fun);
_lstEventHandlers.push_back(b.fun);*/
这个程序是一个事件handle函数,即回调函数,在事件队列中都是用一个函数指针来保存的,程序可以通过扫
描这个事件队列来获取每个事件对应的处理函数,然后调用它。
成员变量这里再加一条:
#include<iostream>
using namespace std;
class A{
public:
int x;
};
typedef int A::* MemberPointer;
int main(int argc, char *argv[])
{
MemberPointer pV;//成员变量指针的定义,等价于int A::* pV;
pV = &A::x;
A a;
a.*pV = 1;//等价于a.x=1;
cout << &a->*pV << endl;
//这几种方式都是等价的
system("pause");
}
#include<stdio.h>
#include<string.h>
char* fun1(char* p)
{
printf("%s\n",p);
return p;
}
char* fun2(char* p)
{
printf("%s\n",p);
return p;
}
char* fun3(char* p)
{
printf("%s\n",p);
return p;
}
int main()
{
char* (*a[3])(char* p);
char* (*(*pf[3]))(char* p);
pf=&a;
a[0]=fun1;
a[1]=&fun2;
a[2]=&fun3;
pf[0][0]("fun1");//等价于(*pf)[0]("fun1");
pf[0][1]("fun2");//等价于(*pf)[1]("fun2");
pf[0][2]("fun3");//等价于(*pf)[2]("fun3");
return 0;
}
第五章:内存管理
栈的效率要比堆的效率高,因为栈是编译时分配空间的,而堆是运行时动态分配空间的,且cpu有专门的寄存器(esp,ebp)来操作栈,而堆都是使用间接寻址。栈的存取速度仅次于寄存器,并且栈内可以数据共享。
struct的内存对齐是按照当前结构体中最大的数据的字节对齐的。有double或64位机子上有long,都是按8字节对齐的。
struct结构体只需要对指针进行内存分配,只要结构体内有指针就需要进行内存分配。
举例:
zclReportCmd_t *reportCmd;
typedef struct
{
uint8 numAttr;
zclReport_t attrList[];
}zclReportCmd_t;
typedef struct
{
uint16 attrID;
uint8 dataType;
uint8 *attrData;
}zclReport_t;
reportCmd=(zclReportCmd_t *)osal_msg_allocate(sizeof(zclReportCmd_t)+sizeof(zclRepor_t));
reportCmd->attrList[0].attrData=(uint8*)osal_msg_allocate(len);
再来一个:
struct image
{
struct header *info;
unsigned char **data;
};
struct image *newimage(int nr,int nc)
{
struct image *x;
x=(struct image *)malloc(sizeof(struct image));//image的指针
x->info=(struct header *)malloc(sizeof(struct header));//info指针
x->data=(unsigned char **)malloc(sizeof(unsigned char *)*nr);//data的第一层指针
x->data[0]=(unsigned char *)malloc(nr*nc);//data的第二层指针
for(i=0;i<nr;i++)
{
x->data[i]=(x->data+nr*i);
}
return x;
}
为char*分配指针内存
char *p1="abcdefg";
char *p2=(char*)malloc(sizeof(char)*strlen(p1)+1*sizeof(char));//这里需要考虑到'\0'的内存空间.
//并且字符串常量需要以\0结束,例如char a[7]={'a','b','c','d','e','f','g'};这个就是未结束,需要多加\0。
//不要因为sizeof(char)为1就省略这个写法,这样只会使代码可以执行下降。
strcpy(p2,p1);//但其实strcpy这个函数已经会报错了,需要改为strcpy_s(p2,8,p1);
初始化很重要,不确定的话就设为0或者设为NULL。
内存越界数组加一减一问题,以及内存泄漏的问题。会产生泄漏的内存是堆上的内存(还有资源或句柄等的泄漏情况),也就是由malloc系列函数或者new操作符分配的函数。如果使用完后没有及时free或delete,这块内存就无法释放,直到整个程序终止。
malloc的函数原型是(void*)malloc(int size);由于malloc会有不成功的可能性,因此一定要加入if(NULL!=p)的语句验证。
char *p=(char*)malloc(int size);
free(p);
p=NULL;//一定要将释放的指针设为空,不然就会变成悬垂指针。
第六章:函数
汇编语言代码往往就是从入口处开始一条一条执行,直到遇到跳转指令比如ARM的B、BL、BX、BLX。
编写strlen函数,用递归的方式解决:
递归1.0
int my_strlen(const char* strDest)
{
assert(NULL!=strDest);
if('\0'==*strDest)
{
return 0;
}
else
{
return(1+my_strlen(++strDest));
}
}
递归2.0
int my_strlen(const char* strDest)
{
assert(NULL!=strDest);
return ('\0'!=*strDest)?(1+my_strlen(strDest+1)):0;
}
递归的效率低是因为每次的函数调用的开销比循环来说大得多,而且递归深度太大可能会出现栈溢出的错误。