栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > C/C++/C#

指针基础、理解、使用、例子教学...

C/C++/C# 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

指针基础、理解、使用、例子教学...

几个简单的例子帮助你理解并学会使用指针!!!

1、学习前沿2、指针基础3、指针与地址4、数据交换5、const关键字6、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
int main(int argc, char **argv)
{
        int a = 10;
        int b = 20;
        int *p = &a;
        int **pp = &p;
        
        printf("&a = %pn", &a);
        printf("p = %pn", p);
        printf("*pp = %pn",*pp);
        printf("a = %dn", a);
        printf("*p = %dn", *p);
        printf("n");
        
        printf("&p = %pn", &p);
        printf("pp = %pn", pp);
        printf("&a = %pn", &a);
        printf("p = %pn", p);
        printf("a = %dn", a);
        printf("**pp = %dn",**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 
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%hxn", *pint, *pchar, *pshort);
        printf("&a = %p, &pint = %p, &pchar = %p, &pshort = %pn", &a, &pint, &pchar, &pshort);

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

        printf("*pint = 0x%x, *pchar = 0x%02x, *pshort = 0x%hxn", *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 = %dn", a, b);
        swap(a,b);
        printf("after: a = %d, b = %dn", a, b);

        printf("before: a = %d, b = %dn", a, b);
        pswap(&a,&b);
        printf("after: a = %d, b = %dn", 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 

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 
#include 

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: %dn", g_bss_var, stack_var, s_bss_var);
        
        printf("arr address: %pn", arr);
        printf("str address: %p str point address: %pn", &str, str);
        printf("c_rodata_var address: %pn", &c_rodata_var);
        printf("stack_var address: %pn", &stack_var);
        printf("ptr address: %p ptr point address: %pn", &ptr, ptr);
        printf("g_bss_var address: %pn", &g_bss_var);
        printf("s_bss_var address: %pn", &s_bss_var);
        printf("g_data_var address: %pn", &g_data_var);
        printf("s_data_var address: %pn", &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

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

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/768145.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号