指针重要的有两点1指针的存储类型,2指针的空间,很多强制转换出现错误是忽视第二点造成。
int *pInt;char* pChar;
pInt = (int*)malloc(10*sizeof(int));
pChar = (char*)pInt;
对上面的pInt和pChar指针来说,pInt[3]和pChar[3]所指向的内容是否一样呢?当然不一样,因为int是4个字节,char是1个字节,步长不一样,所以当然不一样。
其实这里涉及到一个指针指的连续空间和指针类型问题。
在看个例子
#include<cstdio>
#include<cstdlib>
//using namespace std;
struct A
{
int a;
int b;
char c;
};
struct B
{
int a;
char b;
char c;
};
int main()
{
struct A *pa=(struct A*)malloc(sizeof(struct A)*2);
pa[0].a=1;
pa[0].b=2;
pa[0].c=3;
pa[1].a=2;
pa[1].b=3;
pa[1].c=1;
struct B *pb=(struct B*)pa;
for(int i=0;i<2;i++)
{
printf("%d %d %d\n",pa[i].a,pa[i].b,pa[i].c);
printf("%p %p %p\n",&pa[i].a,&pa[i].b,&pa[i].c);
}
for(int i=0;i<2;i++)
{
printf("%d %d %d\n",pb[i].a,pb[i].b,pb[i].c);
printf("%p %p %p\n",&pb[i].a,&pb[i].b,&pb[i].c);
}
}
Pa内存布局 大端模式
01 00 00 00 02 00 00 00 03 cd cd cd 02 00 00 00 03 00 00 00 01 cd cd cd
而pb取值 a和b都美问题
因为a对应的int取4字节 而b取1字节 但c是char加a,b在8以内所以取的是00
这个时候pb[1]的地址偏移8 也就是03 cd cd cd 但pb[1].a是int 取4字节,这样是乱码
之后在取2 和 0
这样我们看在做指针操作时要有空间逻辑,进行优化代码。
来看个库函数代码
第一次写的
int memcmp(const void *s1,const void *s2,size_t n)
{
const unsigned char *p1=(const unsigned char *)s1;
const unsigned char *p2=(const unsigned char *)s2;
while(*p1==*p2)
{
if(--n)
{
}
else
return 0;
p1++;p2++;
}
return *p1-*p2;
}
开始改进
int memcmp1(const void *s1,const void *s2,size_t n)
{
const unsigned char *p1=(const unsigned char *)s1;
const unsigned char *p2=(const unsigned char *)s2;
const int *t1=(int *)p1;
const int *t2=(int *)p2;
while(n>=4)
{
if(*t1!=*t2)
{
p1=(unsigned char *)t1;
p2=(unsigned char *)t2;
break;
}
n-=4;t1++;t2++;
}
if(n==0)
return 0;
while(*p1==*p2)
{
if(--n)
{
}
else
return 0;
p1++;p2++;
}
return *p1-*p2;
}
这段代码的好处就在一次比较四个byte,用的是指针转换。
假如n=1000;那memcmp循环1000次而memcmp1循环250次效率提升3/4
在看多级指针 Char **p;p申请了内存,存储的是一个指针,指向一个空间*p
*p还是指针,指向一个空间char
一般进行函数调用时要操作指针会存入一个二级指针或存入一个指针的引用,在解引用
以liuns删除链表说明二级指针用法
一般我们删除会维护一个前向指针
代码如下
typedefstructnode
{
structnode * next;
....
} node;
typedefbool(* remove_fn)(nodeconst* v);
// Remove all nodes from the supplied list for which the
// supplied remove function returns true.
// Returns the new head of the list.
node * remove_if(node * head, remove_fn rm)
{
for(node * prev = NULL, * curr = head; curr != NULL; )
{
node *constnext = curr->next;
if(rm(curr))
{
if(prev)
prev->next = next;
else
head = next;
free(curr);
}
else
prev = curr;
curr = next;
}
returnhead;
}
但linus巧妙的利用二级指针
voidremove_if(node ** head, remove_fn rm)
{
for(node** curr = head; *curr; )
{
node * entry = *curr;
if(rm(entry))
{
*curr = entry->next;
free(entry);
}
else
curr = &entry->next;
}
}
这里*curr和entry维护了前向指针的后继。本质上,linus方案利用了一个特性,链表头可用一个二级指针存储,作为虚拟头节点中的next字段。比如有1 2 3 4 5 1不是要删除的节点那存储1的next的引用当2是要删除的时候*curr维护的就是1的next,entry->next是3,这样就可了,当1是要删除的节点*curr = entry->next;相当*head = (*head)->next。但从效率上说二级指针在汇编角度是间接寻址,这样解引用要花的时间也不少。
从机器看指针最后就是基地址加偏移量再解引用
例子
int a[10][10];
for(int i j=0;i<10;i++)
for(int=0;j<10;j++)
a[i][j]=i;
for(int i=0;i<10;i++)
for(int j=0;j<10;j++)
cout<<i[a][j];//有问题吗
cout<<"\n";
for(int i=0;i<10;i++)
for(int j=0;j<10;j++)
cout<<a[i][j];
其实两个输出是一样的,因为两个的结果是*(*(p+i)+j);
在来个
#include<stdio.h>
#include<string.h>
#include<setjmp.h>
typedef int FUN(void *p1,void *p2);
static int cmp(void *p1,void *p2);
static int cpy(void *p1,void *p2);
#define mkstr(x) #x
#define print(x) do{printf("p1 %s p2\n",mkstr(x));}while(0)
enum
{
CMP,
CPY,
CNUM
};
static FUN *entry[CNUM]={cmp,cpy};
static int cmp(void *p1,void *p2)
{
const char *t1=(const char *)p1;
const char *t2=(const char *)p2;
int result;
result=memcmp(t1,t2,strlen(t1));
return result;
}
static int cpy(void *p1,void *p2)
{
char *t=(char *)p1;
memcpy(p1,p2,strlen(t));
return 0;
}
static jmp_buf jmp;
int main()
{
char p1[100]="fdfd";
char p2[100]="fdfd";
int f;
for(;;)
{
switch(setjmp(jmp))
{
case 0:
f=entry[0](p1,p2);
f=(f<0)?1:f;
if(f==0)
print(==);
longjmp(jmp,f);
break;
case 1:
if(f==0)
{
f=1;
break;
}
print(<);
break;
default :
print(>);
entry[1](p1,p2);
break;
}
if(f)
break;
}
}