关于C语言指针那些琐事儿

关于C语言指针那些事儿

  • 指针是什么?你可以把它理解为一个变量类型,对!没错,一个变量类型,无论是int也好char也罢,都只是一个指针类型,没有差别,但是也有差别,也许你们常常会像下面这样使用指针
#include <stdio.h>
int main(int argc, char const *argv[])
{
    int a = 123;
    int* pointer = &a;
    printf("a:%d\npointer:%p\n*pointer:%d\n",
        a, 
        pointer,
        *pointer);
    return 0;
}
  • 打印出来的结果显而易见,a的值是123,pointer的值是个地址0x7ffff91d4d2c,*pointer是0x7ffff91d4d2c里面储存的值123
a:123
pointer:0x7ffff91d4d2c
*pointer:123
  • 但是这不是我们要讨论的指针,真正的指针是要抛开类型的限制,我们看下面一段代码
#include <stdio.h>
int main(int argc, char const *argv[])
{
    int a = 123;
    char* pointer = (char*)&a;
    printf("a:%d\npointer:%p\n*pointer:%d\n",
        a, 
        pointer,
        *pointer);
    return 0;
}
  • 这段代码有何不同呢?没错!pointer的类型变成了char*,我们来看看结果,发现并没有什么变化,pointer的值发生变化是因为进程运行时分配给进程的内存都是随机的
a:123
pointer:0x7ffff842f12c
*pointer:123
  • 这说明只要是指针,不管是什么数据类型,它都可以指,我们再来以%c输出*pointer看看
a:123
pointer:0x7fffcd3a390c
*pointer:{
  • 很显然{对应的ASCII码值就是123,你以为这就结束了?真的只要带上星号的类型都是一样的吗?我们都知道char是一个字节,int是四个字节,所以char指针加减1是偏移1个字节,int指针每次加减1就是偏移4个字节,这也就是不同类型指针间最大的区别,既然知道了这点,我们来看看下面的代码。
#include <stdio.h>
int main(int argc, char const *argv[])
{
    char s[8] = {'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'};
    int* pointer = (int*)s;
    while (*pointer)
    {
        printf("%c\n", *pointer);
        pointer++;
    }
    return 0;
}
  • 根据上面的理论,这段代码的输出应该是a和e,结果也确实不出所料
a
e
  • 看到现在你是不是对指针有了一点新的感觉了呢?别急,还没结束呢?我们都知道C语言中函数名就是个地址,和数组名一样,那么函数名不也可以赋值给一个指针吗?这是当然的,因为有函数指针嘛,我们现在要讨论的是普通数据类型的指针可以吗?再来看下面的代码。

void function(){}
#include <stdio.h>
int main(int argc, char const *argv[])
{
    void (*fp)() = function;
    int *p = (int *)function;
    printf("fp:%p\np:%p\n",fp,p);
    return 0;
}
  • 这里定义了一个函数指针fp和一个普通指针p,都指向函数function,我们先来看看结果
fp:0x7f3722b95149
p:0x7f3722b95149
  • 值果然是一样的,这至少能说明int指针也可以指向一个函数所在的地址,现在我们来玩一下骚操作。
#include <stdio.h>
void function()
{
    printf("Hello Pointer\n");
}
int main(int argc, char const *argv[])
{
    function();
    int *p = (int *)function;
    *p = 0;
    function();
    return 0 ;
}
  • 这里我们在function函数里面写了一个输出语句,然后在main中调用它,然后我们让p指针指向function,然后修改p指针指向的内存的值为0,并再次调用function,果然不出所料
Hello Pointer
[1]    1127 segmentation fault (core dumped)  ./demo
  • 程序直接发生了异常,我们来用gdb分析一下,这个过程
gef➤  p function
$4 = {void ()} 0x8001149 <function>
gef➤  x 0x8001149
0x8001149 <function>:   0xfa1e0ff3
  • 可以看到函数名所指的内存是0x8001149,我们可以我们在改变p的值之前内存中的值是0xfa1e0ff3,我们继续运行,直到p被重新赋值
gef➤  x 0x8001149
0x8001149 <function>:   0x00000000
  • 我们可以看到这个时候0x8001149的值已经变成了0,到这里你是不是对指针有有了不一样的感觉?让我们继续。
#include <stdio.h>
int main(int argc, char const *argv[])
{
    int a = 11 , b = 111111;
    short* p = (short*)&a;
    printf("a_s:%d\n",*p);
    p = (short*)&b;
    printf("b_s:%d\n",*p);
    return 0 ;
}
  • 我们同样先来看看输出结果
a_s:11
b_s:-19961
  • 结果似乎也在意料之中,毕竟b的值超出了short可以储存的范围,但是到底是为什么呢,我们用位域来研究一下这个问题
#include <stdio.h>
struct num{
    short a:1,b:1,c:1,d:1,e:1,f:1,g:1,h:1,i:1,j:1,k:1,l:1,m:1,n:1,o:1,p:1;
};
int main(int argc, char const *argv[])
{
    struct num a;
    a.a = 1;
    a.b = 1;
    a.c = 1;
    a.d = 1;
    a.e = 1;
    a.f = 1;
    a.g = 1;
    a.h = 1;
    a.i = 1;
    a.j = 1;
    a.k = 1;
    a.l = 1;
    a.m = 1;
    a.n = 1;
    a.o = 1;
    a.p = 0;
    short* sp = &a;
    printf("p:%d\n",*sp);
    char* cp = &a;
    printf("sp:%d\n",*cp);
    return 0 ;
}
  • 我们还是先来看看输出
p:32767
sp:-1
  • 32767的二进制是0111 1111 1111 1111,那么-1是1111 1111为什么1111 1111是-1而不是255呢,这就是最左边的一位可以看作是符号位,所以1111 1111其实是-1的补码,原码实际上是1000 0001,这样印证了计算机是以补码形式存储数据的,所以我们可以很直观的看到cp指针打印的只是short类型2个字节中的一个字节,而且是低8位,看到这里,是不是对指针有点更加不一样的感觉了呢?至于那个-19961的问题大家可以自己去研究一下。

  • 到这里我们就可以做到为某一个类型手动指定栈区的大小了,尤其是结构体类型,像下面这样

#include <stdio.h>
struct a{};
int main(int argc, char const *argv[])
{
	char buffer[128];
	struct a* sa = (struct a*)buffer;
}
  • 关于C语言指针其实还有很多奇妙的东西,关键其实就两个字,瞎搞!
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Fantety

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值