其他分享
首页 > 其他分享> > C指针细节

C指针细节

作者:互联网

C指针细节

悬空指针
C语言中的指针可以指向一块内存,如果这块内存稍后被操作系统回收(被释放),但是指针仍然指向这块内存,那么,此时该指针就是“悬空指针”。
例子:

void *p = malloc(size);
assert(p);
free(p); //现在p就是一个悬空指针

“悬空指针”会引发不可预知的错误,而且错误一旦发生,难以定位。这是因为在 free(p) 之后,p 指针仍然指向之前分配的内存,如果这块内存暂时可以被程序访问并且不会造成冲突,那么之后使用 p 并不会引发错误。
在实际开发中,为了避免出现”悬空指针“,在释放内存之后,常常会将指针 p 赋值为 NULL:

void *p = malloc(size);
assert(p);
free(p); 
// 避免“悬空指针”
p = NULL;

这么做的好处是一旦再次使用被释放的指针 p,就会立刻引发“段错误”,方便查找错误点。
段错误是计算机软件运行过程中可能出现的一种特殊错误情况。当程序试图访问不允许访问的内存位置,或试图以不允许的方式访问内存位置(例如尝试写入只读位置,或覆盖部分操作系统)时会发生段错误。

野指针
“悬空指针”是指向被释放内存的指针,“野指针”则是不确定其具体指向的指针。“野指针”最常来自于未初始化的指针,例如:

void *p;
// 此时 p 是“野指针”

因为“野指针”可能指向任意内存段,因此它可能会损坏正常的数据,也有可能引发其他未知错误,所以C语言中的“野指针”危害性甚至比“悬空指针”还要严重。
在实际开发中,定义指针时,一般都要尽量避免“野指针”的出现(赋初值):

void *p = NULL;
void *data = malloc(size);

C语言运算符

内存地址的概念

多级指针案例 取出子函数中临时变量的地址

指针变量的赋值只能赋予地址, 决不能赋予任何其它数据。在C语言中, 变量的地址是由编译系统分配的,C语言中提供了地址运算符&来表示变量的地址。一个指针变量可以认为它是在一个.c文件中是全局的。
int *p = &a;
*指针变量 的作用是获取指针变量指向的内存空间的内容
修改指针,需要用指针的指针

"*"的两种用法:

1)用于定义一个指针变量

2)存储指针变量储存的存储空间的内容

指针常见的应用场景

1)在函数中可以修改主调函数中变量的值

2)让函数可以有多个返回值

多级指针

int* p; int 类型的一级指针;
int** p2; int 类型的二级指针;

二级指针变量只能保存一级指针变量的地址;
有几个* 就是几级指针 int*** 三级指针。

通过int类型三级指针 操作int类型变量的值 ***p

#include <stdio.h>
void swap (int *p1,int *p2)
{
    int t;
    int*p;
    t=*p1;
    *p1=*p2;
    *p2=t;
//    p=p1;
//    p1=p2;
//    p2=p;
}
void swap2(int **m, int**n)
{
    int*p;
    p=*m;
    *m=*n;
    *n=p;
}
void main( )
{
	//&取地址,*取值  
    int a=1,b=2,*p=&a,*q=&b;
    printf("%d,%d,%d,%d\n",a,b,*p,*q);
    printf("%d,%d,%d,%d\n",&a,&b,&(*p),&(*q));
    swap(p,q);
    printf("%d,%d,%d,%d\n",a,b,*p,*q);
    printf("%d,%d,%d,%d\n",&a,&b,&(*p),&(*q));
    swap2(&p,&q);
    printf("%d,%d,%d,%d\n",a,b,*p,*q);
    printf("%d,%d,%d,%d\n",&a,&b,&(*p),&(*q));
 
// ,
//1,2,1,2
//	6422300,6422296,6422300,6422296
//	2,1,2,1
//	6422300,6422296,6422300,6422296
//	2,1,1,2                     
//	6422300,6422296,6422296,6422300

}

int main(){
	int number  = 5;
	int *ptr = &number;
	
	printf("number's address = [%p]\n",&number);//number的地址
	printf("number's value = [%d]\n",number);//number的值
	printf("ptr's address:%p\n",&ptr);//(指针变量)ptr的地址
	printf("ptr's value:%p\n",ptr);//ptr的值
	printf("ptr pointing value:%d\n",*ptr);//ptr指向的变量的值
	/*
	number's address = [0061FF1C]
	number's value = [5]
	ptr's address:0061FF18
	ptr's value:0061FF1C
	ptr pointing value:5	
	*/
	return 0;
}

#include<stdio.h>
#include<stdlib.h>
/**
main函数获取子函数中临时变量的地址
这其实还是值传递和引用传递的问题
*/
function(int** pointer) {
	int i = 123;
	*pointer = &i;
	printf("i的地址%#x\n", &i);
}

main() {
	int* pointer1;
	function(&pointer1);
	printf("pointer1的值%#x\n", pointer1);
	system("pause");
}

i的地址0x1147410c
pointer1的值0x1147410c

联合体(共用体)

长度(大小)等于联合体中定义的变量当中最长的那个,联合体只能保存一个变量的值
联合体共用同一块内存,在嵌入式设备中起到节省内存的目的.
共用体也是用户自定义的数据类型,不过该类想中的所有成员共用一块内存,因此一个成员变量赋值,就等于所有成员都被赋予了相同的值。

    #include<stdio.h>
    #include<string.h>
    union book
    {
    	char name[10];
    	char price[10];
    };//定义一个共用体,有2个成员,name和price,这2个成员共用一块内存,因此他们的地址相同
    int main()
   {
    	union book mybook;
    	strcpy(mybook.name,"呼啸山庄");
    	strcpy(mybook.price,"11元8角");
    	printf("name的地址 :%p\n",&mybook.name);
    	printf("price的地址:%p\n",&mybook.price);//输出结果显示它们的地址相同
    	printf("书名:%s\t",mybook.name);
    	printf("价格:%s\n",mybook.price);//由于price1被赋值时,覆盖了name1成员的值,因此他们的值也是一样的
    	return 0;
   }
    
//name的地址 :0x7ffde4ac2a46
//price的地址:0x7ffde4ac2a46
//书名:11元8角 价格:11元8角

typedef void (*Fun) (void) 的理解——函数指针——typedef函数指针

//返回类型(*函数名)(参数表) 
//定义一个函数指针pFUN,它指向一个返回类型为char,有一个整型的参数的函数
char (*pFun)(int);

//定义一个返回类型为char,参数为int的函数
//从指针层面上理解该函数,即函数的函数名实际上是一个指针,
//该指针指向函数在内存中的首地址
char glFun(int a)
{
    cout << a;
    //return a;
}

int main()
{
//将函数glFun的地址赋值给变量pFun
    pFun = glFun;
//*pFun显然是取pFun所指向地址的内容,当然也就是取出了函数glFun()的内容,然后给定参数为2。
    (*pFun)(2);
    return 0;
}

typedef函数指针

//typedef  返回类型(*新类型)(参数表)
typedef char (*PTRFUN)(int); 
PTRFUN pFun; 
char glFun(int a){ return;} 
void main() 
{ 
    pFun = glFun; 
    (*pFun)(2); 
} </span>

typedef的功能是定义新的类型。第一句就是定义了一种PTRFUN的类型,并定义这种类型为指向某种函数的指针,
这种函数以一个int为参数并返回char类型。后面就可以像使用int,char一样使用PTRFUN了。
第二行的代码便使用这个新类型定义了变量pFun,此时就可以像使用形式1一样使用这个变量了。

标签:变量,int,地址,细节,内存,printf,指针
来源: https://www.cnblogs.com/kato-T/p/16541581.html