Error[8]: Undefined offset: 3, File: /www/wwwroot/outofmemory.cn/tmp/plugin_ss_superseo_model_superseo.php, Line: 121
File: /www/wwwroot/outofmemory.cn/tmp/plugin_ss_superseo_model_superseo.php, Line: 473, decode(

一、指针是什么

在计算机内存中,每一个内存单元都有其对应的地址,用以标识该单元。就如同小区的楼号、门牌号具体定位到每个户主一样,计算机通过这个地址来标识每一个内存单元。一个内存单元的大小是1个字节,里面可以存储数据,就如同房间里面住人一样。

而指针实际上就是一个内存单元的地址,但在大多数情况下,所说的指针指的是指针变量,指针变量存储指针(也就是内存单元的地址),这方面如同整形变量存储整数一样。

(下文的指针均指代指针变量)

        &这一 *** 作符可以取得某一变量的地址,由此可以将其存放到指针变量里,指针变量存储的是变量的起始地址

int main()
{
    int a = 0;     //在内存中开辟一个4字节大小的空间,存放数据0
    int* p = &a;   //取出变量a的地址,将其所占4字节的第一个字节存放至p中
}
二、指针的大小

不同机器上的指针大小不同,在32位机器上,指针大小为4字节;在64位机器上,指针大小为8字节。

前文中提到每个内存单元都有其对应的地址,对于32位机器来说,可以假设有32根地址线,在寻找内存单元的地址时,每根地址线产生高电压或低电压(对应二进制中1或0)

那么这32根地址线总计可以产生2^32个地址(全0到全1),以此对内存单元进行访问,而由于每个内存单元是1字节大小,所以其理论上其可访问4G的内存(实际是不可能的),这就是为什么32位系统至多支持4G内存的原因。

也就是说,在32位机器,地址是由32个0或1组成的二进制序列,1bit存储1个二进制数,所以需要32bit也就是4字节的空间来存放某一地址,因此32位机器上的指针大小为4字节。

同理,也就能说明64位机器上的指针是8字节。

三、指针类型 1、指针的类型

不同变量有不同的类型,如浮点型,整型,字符型等,与之相对,指针也具有不同的类型,但是无论何种类型的指针大小都是4或8字节

指针的定义方式是 type+*,(其中*的位置与type相邻还是与变量名相邻取决于个人喜好)

如以下三种,其中NULL是空指针,表示 "未分配" 或者 "尚未指向任何地方" 的指针

char* pc = NULL;

int* pi = NULL;

double* pd = NULL;

一般来说,type*与其所对应的变量类型相同,整型变量就是int*,浮点型变量就是float*或double*

2、不同指针类型的作用 步长

指针可以加或减一个整数,表示从其标识的地址向高地址或低地址移动多少步长,步长的大小取决于指针类型,如

//以下代码仅为说明指针步长,没有实际意义
int a = 0;
char ch = 'm';
int* pi = &a;
char* pc = &ch;
pi += 1;     //此时pi会从a的起始地址向高地址走4字节
pc += 1;     //此时pc会从ch的起始地址向高地址走1字节

整形指针一个步长是4字节,字符型指针一个步长是1字节,指针的步长与其所对应变量占内存空间大小一致。

解引用

指针的类型决定指针解引用时可以 *** 作几个字节,字节数仍与其所对应变量所占内存空间大小一致,如char*的指针解引用可以 *** 作1个字节,int*的指针解引用可以 *** 作4个字节。可以通过调试观察,如下图


 

 也就是说,当对一个指针解引用时,指针会对其标识的起始地址开始,一个步长的字节数中的内容进行修改。

三、野指针 野指针

野指针是说,指针的指向不可知,不知道其标识的地址或指向地址不正确

野指针成因

一般情况下会因为指针未初始化、指针越界访问、指针指向的空间释放,三种原因导致出现野指针

1、指针未初始化

这一点不同于一般变量,编译器不会为局部变量的指针赋值(指针作全局变量时一般会被赋值为空,即NULL),所以用户必须对局部变量指针初始化,在用户也不确定其指向时,可以赋值为NULL,即空指针

2、指针越界访问

#include 
int main()
{
    int arr[10] = {0};
    int *p = arr;
    int i = 0;
    for(i=0; i<=11; i++)
   {
        //当指针指向的范围超出数组arr的范围时,p就是野指针
        *(p++) = i;
   }
    return 0; }

3、指针指向内存空间释放

free函数不会将指针置空,若此时仍以此指针进行 *** 作,就是非法行为(空指针也不可以进行解引用 *** 作,只是一般对空指针解引用会直接报错,但是非法访问不一定报错)

#include

int main()
{
    //开辟一个可容纳4个整形大小的空间
    int* p = (int*)malloc(sizeof(int)*4);
    free(p); //系统收回用户开辟的空间
    *p = 0;  //非法行为,空间不属于用户,原则上不可修改
}

还有一种是函数返回局部变量的地址,由于局部变量已经销毁,指针就会成为野指针

#include

int* function()
{
    int a = 0;
    int* p = &a;
    return p;
}

int main()
{
    int* ptr = function();
    *ptr = 0; //此时a变量已经销毁,系统收回其空间,原则上不可修改
    
}
如何规避野指针

1、指针初始化

2、处理好边界条件,防止指针越界

3、free之后将指针置空

4、不能返回局部变量的地址

5、指针使用前检查有效性(是否为空,指向内存是否有效)

四、指针运算

1、指针+-整数,这一点在前文已经提及

2、指针-指针

这一运算必须保证二者是同一类型的指针,得到的结果是二者间相差的字节数除以步长,这个数字可正可负,也可能是0(指向同一地址),一般用于获取两个指针间的元素个数

//如求字符串长度时可用如下方法
int my_strlen(char *s)
{
     char *p = s;
     while(*p != '[+++]' )
         p++;
     return p-s; 
}

3、指针的关系运算

这一点与一般变量的关系运算相同,地址也有高地址和低地址,可以判断相等,比较大小等

)
File: /www/wwwroot/outofmemory.cn/tmp/route_read.php, Line: 126, InsideLink()
File: /www/wwwroot/outofmemory.cn/tmp/index.inc.php, Line: 165, include(/www/wwwroot/outofmemory.cn/tmp/route_read.php)
File: /www/wwwroot/outofmemory.cn/index.php, Line: 30, include(/www/wwwroot/outofmemory.cn/tmp/index.inc.php)
C语言指针详解(初级)_C_内存溢出

C语言指针详解(初级)

C语言指针详解(初级),第1张

一、指针是什么

在计算机内存中,每一个内存单元都有其对应的地址,用以标识该单元。就如同小区的楼号、门牌号具体定位到每个户主一样,计算机通过这个地址来标识每一个内存单元。一个内存单元的大小是1个字节,里面可以存储数据,就如同房间里面住人一样。

而指针实际上就是一个内存单元的地址,但在大多数情况下,所说的指针指的是指针变量,指针变量存储指针(也就是内存单元的地址),这方面如同整形变量存储整数一样。

(下文的指针均指代指针变量)

        &这一 *** 作符可以取得某一变量的地址,由此可以将其存放到指针变量里,指针变量存储的是变量的起始地址

int main()
{
    int a = 0;     //在内存中开辟一个4字节大小的空间,存放数据0
    int* p = &a;   //取出变量a的地址,将其所占4字节的第一个字节存放至p中
}
二、指针的大小

不同机器上的指针大小不同,在32位机器上,指针大小为4字节;在64位机器上,指针大小为8字节。

前文中提到每个内存单元都有其对应的地址,对于32位机器来说,可以假设有32根地址线,在寻找内存单元的地址时,每根地址线产生高电压或低电压(对应二进制中1或0)

那么这32根地址线总计可以产生2^32个地址(全0到全1),以此对内存单元进行访问,而由于每个内存单元是1字节大小,所以其理论上其可访问4G的内存(实际是不可能的),这就是为什么32位系统至多支持4G内存的原因。

也就是说,在32位机器,地址是由32个0或1组成的二进制序列,1bit存储1个二进制数,所以需要32bit也就是4字节的空间来存放某一地址,因此32位机器上的指针大小为4字节。

同理,也就能说明64位机器上的指针是8字节。

三、指针类型 1、指针的类型

不同变量有不同的类型,如浮点型,整型,字符型等,与之相对,指针也具有不同的类型,但是无论何种类型的指针大小都是4或8字节

指针的定义方式是 type+*,(其中*的位置与type相邻还是与变量名相邻取决于个人喜好)

如以下三种,其中NULL是空指针,表示 "未分配" 或者 "尚未指向任何地方" 的指针

char* pc = NULL;

int* pi = NULL;

double* pd = NULL;

一般来说,type*与其所对应的变量类型相同,整型变量就是int*,浮点型变量就是float*或double*

2、不同指针类型的作用 步长

指针可以加或减一个整数,表示从其标识的地址向高地址或低地址移动多少步长,步长的大小取决于指针类型,如

//以下代码仅为说明指针步长,没有实际意义
int a = 0;
char ch = 'm';
int* pi = &a;
char* pc = &ch;
pi += 1;     //此时pi会从a的起始地址向高地址走4字节
pc += 1;     //此时pc会从ch的起始地址向高地址走1字节

整形指针一个步长是4字节,字符型指针一个步长是1字节,指针的步长与其所对应变量占内存空间大小一致。

解引用

指针的类型决定指针解引用时可以 *** 作几个字节,字节数仍与其所对应变量所占内存空间大小一致,如char*的指针解引用可以 *** 作1个字节,int*的指针解引用可以 *** 作4个字节。可以通过调试观察,如下图


 

 也就是说,当对一个指针解引用时,指针会对其标识的起始地址开始,一个步长的字节数中的内容进行修改。

三、野指针 野指针

野指针是说,指针的指向不可知,不知道其标识的地址或指向地址不正确

野指针成因

一般情况下会因为指针未初始化、指针越界访问、指针指向的空间释放,三种原因导致出现野指针

1、指针未初始化

这一点不同于一般变量,编译器不会为局部变量的指针赋值(指针作全局变量时一般会被赋值为空,即NULL),所以用户必须对局部变量指针初始化,在用户也不确定其指向时,可以赋值为NULL,即空指针

2、指针越界访问

#include 
int main()
{
    int arr[10] = {0};
    int *p = arr;
    int i = 0;
    for(i=0; i<=11; i++)
   {
        //当指针指向的范围超出数组arr的范围时,p就是野指针
        *(p++) = i;
   }
    return 0; }

3、指针指向内存空间释放

free函数不会将指针置空,若此时仍以此指针进行 *** 作,就是非法行为(空指针也不可以进行解引用 *** 作,只是一般对空指针解引用会直接报错,但是非法访问不一定报错)

#include

int main()
{
    //开辟一个可容纳4个整形大小的空间
    int* p = (int*)malloc(sizeof(int)*4);
    free(p); //系统收回用户开辟的空间
    *p = 0;  //非法行为,空间不属于用户,原则上不可修改
}

还有一种是函数返回局部变量的地址,由于局部变量已经销毁,指针就会成为野指针

#include

int* function()
{
    int a = 0;
    int* p = &a;
    return p;
}

int main()
{
    int* ptr = function();
    *ptr = 0; //此时a变量已经销毁,系统收回其空间,原则上不可修改
    
}
如何规避野指针

1、指针初始化

2、处理好边界条件,防止指针越界

3、free之后将指针置空

4、不能返回局部变量的地址

5、指针使用前检查有效性(是否为空,指向内存是否有效)

四、指针运算

1、指针+-整数,这一点在前文已经提及

2、指针-指针

这一运算必须保证二者是同一类型的指针,得到的结果是二者间相差的字节数除以步长,这个数字可正可负,也可能是0(指向同一地址),一般用于获取两个指针间的元素个数

//如求字符串长度时可用如下方法
int my_strlen(char *s)
{
     char *p = s;
     while(*p != '' )
         p++;
     return p-s; 
}

3、指针的关系运算

这一点与一般变量的关系运算相同,地址也有高地址和低地址,可以判断相等,比较大小等

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/langs/874627.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-05-13
下一篇 2022-05-13

发表评论

登录后才能评论

评论列表(0条)

保存