c 指针总结

指针重要的有两点1指针的存储类型,2指针的空间,很多强制转换出现错误是忽视第二点造成。

int *pInt;char* pChar;

pInt = (int*)malloc(10*sizeof(int));

pChar = (char*)pInt;

对上面的pIntpChar指针来说,pInt[3]pChar[3]所指向的内容是否一样呢?当然不一样,因为int4个字节,char1个字节,步长不一样,所以当然不一样。

其实这里涉及到一个指针指的连续空间和指针类型问题。

在看个例子

#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;
}
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值