C_指针基础

1、学习前沿

  • 指针的大小与数据类型无关,只与系统的位数有关。
    32位的操作系统,指针长度为4字节64位的操作系统,指针长度为8个字节
  • 指针是用来存放地址的。
  • LSB和MSB------内存存储数据的两种方式
    LSB:Least Significant Bit. “最低有效位”------小端字节序
    MSB:Most Significant Bit. “最高有效位”------大端字节序
    一般系统默认的内存存储方式为小端字节序
    在这里插入图片描述
  • 虚拟地址空间
    当创建一个进程时,操作系统会为该进程分配一个4GB的虚拟地址空间。(以32 位的操作系统为例,一个指针长度是 4 字节,4字节指针的寻址能力是从 0x0000 0000~0xFFFF FFFF)。
    每个进程都会有一个自己的 4GB 虚拟地址空间。这 4GB 的地址空间是“虚拟”的,并不是真实存在的。

2、指针基础

*:取值运算符
&:取地址符

解析:

int main (int argc, char **argv)  //int argc:参数个数;char **argv:指向指针的指针
{
	int a;
	int *p=&a;	//定义p是一个指向int类型的指针

	char ch='a';
	char *p=&ch; //定义p是指向char类型的指针

	char *str="hello";
	char **p=&str; //定义*p是指向char *类型的指针
}

代码演示:

#include<stdio.h>
int main(int argc, char **argv)
{
        int a = 10;
        int b = 20;
        int *p = &a;
        int **pp = &p;
        
        printf("&a = %p\n", &a);
        printf("p = %p\n", p);
        printf("*pp = %p\n",*pp);
        printf("a = %d\n", a);
        printf("*p = %d\n", *p);
        printf("\n");
        
        printf("&p = %p\n", &p);
        printf("pp = %p\n", pp);
        printf("&a = %p\n", &a);
        printf("p = %p\n", p);
        printf("a = %d\n", a);
        printf("**pp = %d\n",**pp);
        
}

代码解析:

  • int a =10 :
    在内存中开辟了一段内存空间,大小为4个字节(int类型4个字节);a表示这段内存单元,&a表示这段内存单元的地址;10以16进制(0x0000000a)的形式存储在那段内存单元中。

  • &a
    &a是int *类型。

  • &p
    &p是int **类型。

  • int *p = &a:
    定义指针p指向a,p的内存单元存放a的地址。

  • int **pp = &p:
    指针pp指向指针p,pp的内存单元存放指针p的地址。

执行结果:

&a = 0x7fff27afb130
p = 0x7fff27afb130
*pp = 0x7fff27afb130
a = 10
*p = 10

&p = 0x7fff27afb138
pp = 0x7fff27afb138
&a = 0x7fff27afb130
p = 0x7fff27afb130
a = 10
**pp = 10

结果解析:

  • *p
    p表示取指针p的内存单元存储的值,由于p的内存单元存储了a的地址,所以p取了a的值[^1]
  • **pp
    **pp表示取*pp的值,*pp取指针pp的内存单元储存的值,由于pp存储的是p的地址,所以*pp取p的内存单元存储的值[^1]。又由于p的内存单元存储了a的地址,所以**pp取了a的值[^2]
    在这里插入图片描述

拓展:
如果a = 20;a出现在等号的左边,就是把20这个值写到a的内存单元里面去。
如果b = a;a出现在等号的右边,就是把a的内存单元的值取出来给b。

通过*p改变a的值:*p = 20;
通过**pp改变a的值:**pp = 20;

3、指针与地址

代码演示:

#include <stdio.h>
int main(void)
{
        int a = 0x12345678;
        int *pint = &a;
        char *pchar = (char *)&a;
        short *pshort = (short *)&a;

        printf("*pint = 0x%x, *pchar = 0x%02x, *pshort = 0x%hx\n", *pint, *pchar, *pshort);
        printf("&a = %p, &pint = %p, &pchar = %p, &pshort = %p\n", &a, &pint, &pchar, &pshort);

        pint++;
        pchar++;
        pshort++;

        printf("*pint = 0x%x, *pchar = 0x%02x, *pshort = 0x%hx\n", *pint, *pchar, *pshort);

}

代码解析:
char *pchar = (char *)&a:
因为a是int类型的,所以要强制转换为char *类型

short *pshort = (short *)&a:
因为a是int类型的,所以要强制转换为short *类型

执行结果:

*pint = 0x12345678, *pchar = 0x78, *pshort = 0x5678
&a = 0x7ffe171b01dc, &pint = 0x7ffe171b01e0, &pchar = 0x7ffe171b01e8, &pshort = 0x7ffe171b01f0
*pint = 0x171b01e0, *pchar = 0x56, *pshort = 0x1234

结果解释:

  • 一般系统默认的内存存储空间为小端字节序

  • *pchar = 0x78:
    因为char只有1个字节,所以只输出78[^1]

  • *pshort = 0x5678:
    因为short只有2个字节,所以只输出5678[^1]

  • pchar++:
    有效位自加1个字节。
    所以*pchar = 0x56[^2]

  • pshort++:
    有效位自加2个字节。
    所以*pshort = 0x1234[^2]
    在这里插入图片描述

4、数据交换

代码演示:

int main()
{
        int a = 8;
        int b = 10;
        printf("before: a = %d, b = %d\n", a, b);
        swap(a,b);
        printf("after: a = %d, b = %d\n", a, b);

        printf("before: a = %d, b = %d\n", a, b);
        pswap(&a,&b);
        printf("after: a = %d, b = %d\n", a, b);
}

int swap(int a,int b)
{
        int temp;
        temp = a;
        a = b;
        b = temp;
}

int pswap(int *p1, int *p2)
{
        int temp;
        temp = *p1;
        *p1 = *p2;
        *p2 = temp;
}

运行结果:

before: a = 8, b = 10
after: a = 8, b = 10
before: a = 8, b = 10
after: a = 10, b = 8

结果解释:
调用swap函数交换不成功,是因为函数是按值传递,把a,b传过去在函数swap里面交换了,却只在函数swap里生效,没有改变main里面的a,b。
通过调用pswap(&a,&b)函数后,
在这里插入图片描述

5、const关键字

定义:
const定义的是常量,值不能被改变。

const int a = 100

const 和指针变量一起使用时,可以限制指针变量本身,也可以限制指针指向的数据
const 和指针一起使用会有3种不同的顺序,如下所示:

const int *p1 // int const *p1 = &a;  
int * const p2 = &a;
const int * const p3 = &a;
  • 第一种情况指针所指向的数据*p1是只读的不能修改,指针p1本身的值可以修改
*p1 = 30;//不合法	p1 = &b;//合法	a = 20;//不合法
  • 第二种情况指针本身的值p2是只读的不能修改,指针所指的数据*p2可以修改
*p2 = 30;//合法		p2 = &b;//合法
  • 第三种情况指针本身的值p3和它指向的数据*p3都有是只读的,不能修改
*p3 = 30;//不合法	p3 = &b;//不合法

由此可见:指针可操作两个内存空间,一个是自己的内存空间,另一个是指向的内存空间。

6、C程序内存布局

在这里插入图片描述

程序所占内存的分类

  • 当程序没有运行,只有文本段(代码段)和数据段
  • 系统空间:每个程序都有4GB的虚拟内存空间(32位的操作系统)
  • 参数区,堆区,栈区,数据段是用来存放数据的。
  • 栈区的空间是由上往下增长的。
  • 堆区的空间是由下往上增长的。
  • 参数区:
    参数区放命令行传给main()函数的参数:argc,argv。
  • 栈区:
    栈区存放局部变量,当它在花括号内,就在栈区占一段内存空间,用完后,这段内存就被释放了。
    局部变量:
    定义:花括号内所以定义的变量是局部变量,只能在本花括号使用。
int main(void)
{
        int stack_var;
        const int rodata_var = 50;
        static int s_var;
        char *ptr = "hello world";
        char arr[] = "hello";
}

未定义的局部变量,其值是随机值
未初始化的静态局部变量,其值为0

  • 堆区:
    malloc()动态类型分配内存,在内存中分配一段内存使用。
int *p = (malloc)100;
free (p);
p = NULL;

free(p),是放弃了指针对这个内存的占用,放弃之后,内存的值会改写成随机值。但是指针本身并没有被删除!指针仍然指向原来的那块内存!因此在free掉指针后,还要把指针指向null,即p = NULL

  • 数据段:
    数据区存放全局变量静态变量以及文字常量

全局变量:
定义:在花括号外定义的变量,是全局变量。

#include <stdio.h>

int g_data_var = 20;
int g_bss_var;
static int s_bss_var;

已初始化的全局变量,可以被所有其他c文件或本C文件中的函数调用,如果另一个c文件想调用全局变量,要用extern声明

extern int g_data_var;

未初始化的全局变量,其值为0,可以被所有其他c文件或本C文件中的函数调用

未初始化的静态全局变量,其值为0,static关键字声明的函数或变量,只能被本C文件调用

文字常量区:

char *ptr = "hello world";

*p指向"hello world","hello world"在数据段的文字常量区,不可改变,报段错误。

*(str+1) = 'a'  //segment fault

数组arr在栈区,可以改。

arr[1] = 'a'

指针和数组同等的写法:

*(str+1) = 'a'
str[1] = 'a'

arr[1] = 'a'
*(arr+1) = 'a'

代码演示:

#include <stdio.h>
#include <stdlib.h>

int g_data_var = 20;  //已初始化的全局变量
int g_bss_var;  //未初始化的全局变量
static int s_bss_var;  //未初始化的静态全局变量

int main(int argc, char **argv)
{
        int stack_var;  //未初始化局部变量
        const int c_rodata_var = 50;  //常量
        static int s_data_var = 30;  //静态局部变量
        static int s_bss_var;

        char *str = "hello world";  //局部变量
        char arr[] = "hello";  //局部变量
        char *ptr = NULL;
        ptr = malloc(100);

        printf("g_bss_var: %d  stack_var: %d s_bss_var: %d\n", g_bss_var, stack_var, s_bss_var);
        
        printf("arr address: %p\n", arr);
        printf("str address: %p str point address: %p\n", &str, str);
        printf("c_rodata_var address: %p\n", &c_rodata_var);
        printf("stack_var address: %p\n", &stack_var);
        printf("ptr address: %p ptr point address: %p\n", &ptr, ptr);
        printf("g_bss_var address: %p\n", &g_bss_var);
        printf("s_bss_var address: %p\n", &s_bss_var);
        printf("g_data_var address: %p\n", &g_data_var);
        printf("s_data_var address: %p\n", &s_data_var);
        
        free(ptr);
        ptr = NULL;

        return 0;
}

g(global):全局变量
s(static):静态变量
data:已初始化
bss:未初始化
stack:在栈区中
rodata:常量

执行结果:

g_bss_var: 0  stack_var: 0 s_bss_var: 0
arr address: 0x7ffc3e0d0052
str address: 0x7ffc3e0d0040 str point address: 0x561485266948
c_rodata_var address: 0x7ffc3e0d003c
stack_var address: 0x7ffc3e0d0038
ptr address: 0x7ffc3e0d0048 ptr point address: 0x5614863c3260
g_bss_var address: 0x561485467024
s_bss_var address: 0x561485467020
g_data_var address: 0x561485467010
s_data_var address: 0x561485467014

&**后续会更新《指针再复习》,全是硬货,等着你们来实操~~~~~~**

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值