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