C/C++语言中的NULL等于0吗????

28 篇文章 1 订阅
6 篇文章 0 订阅

在开始今天的主题之前,我们先来看一下C/C++中关于NULL的标准定义:

#ifndef NULL
    #ifdef __cplusplus
        #define NULL 0
    #else
        #define NULL ((void *)0)
    #endif
#endif

编译器预先定义了一个宏_cplusplus,来判断当前的编译环境是C++还是C语言的,在C++定义为0,在C语言中定义为(void *)0

在C/C++中的区别

在C语言中,C中的标准写法,NULL被替换为一个void*类型的指针右值,值等于0;由于是void*类型,可以隐式转换为其他类型的指针。
在C++中,void*无法自由隐式转换为其他类型的指针,而字面量0可以隐式转换为指针类型。

NULL的本质是什么

从指针,空指针,空指针常量以及指向的内存谈起:

指针角度:

先看以下定义,p是一个函数内的局部变量,则p的值是随机的,也就是说p是一个野指针。

int func()
{
  int *p; 
  ...
}

再看以下函数,p是一个局部变量,分配在栈上的地址,p的值是(void *)0,实际就是0x00000000,意思就是指针p指向内存的0x00000000地址处。这时候p就不是野指针了。

int func()
{
  int *p = NULL; 
  ...
}
什么是空指针呢?

如果将空指针常量转换为指针类型,则保证生成的指针(空指针)将不相等的值与指向任何对象或函数的指针进行比较。
定义char *p=0后,在之后p的任何一种赋值操作之后,p都会成为一个空指针,即p不指向任何实际的对象或者函数。反过来说,任何对象或者函数的地址都不可能是空指针。

什么是空指针常量呢?

值为0的整数常量表达式,或强制转换为void *类型的表达式,称为空指针常量。

空指针指向了哪里的内存

在C/C++中没有标准定义空指针指向何处,取决于系统的实现。我们常见的空指针一般指向0x00000000地址,即空指针内部用全0x00000000来表示,也有一些系统用一些特殊的地址值或特殊的方式表示空指针。
在实际开发中,关键点在于判断哪个是空指针。

NULL的作用

在大部分的CPU中,内存的0x00000000地址处都不是可以随便访问的,所以野指针指向了这个区域可以保证野指针有个存放的地方,否则就会发生段错误。
当尝试访问的时候会阻止你,但是有些地址不是只读的,如果一个指针指向了这个地址,你又在不经意间修改了它,可能会导致一些重要的文件被修改,所以指针初始化成NULL是很有必要的。

注意不要混用’\0’和’0’和0和NULL
  • '\0’是一个转义字符,它对应的ASCII码值是0,本质就是0,;常用于表示字符串的结束标志,以判断字符串有没有到头。
  • '0’是一个字符,它对应的ASCII码值是48,本质是48。
  • 0是一个int类型的数字,本质就是0。
  • NULL是一个表达式,是强制类型转换为void *类型的0,一般用来比较指针是都是一个野指针。

说了这么多,那NULL到底是不是0呢?

1.NULL就是0?
先来看一段代码

#include<stdio.h>
int main()
{
	int* p = NULL;
	printf("%s", p);
	return 0;
}

最终输出结果为:

(null)

输出(null),在执行int *p=NULL,打印出来空白,实际上p的值为0x00000000,在C语言中,NULL的本质是0,但是这个0不是当一个整形数据来解析,而是当一个内存地址来解析的,代表的是内存的0地址。
(void *)0这个整体表达式表示一个指针,地址在哪里取决于指针变量本身,这个指针变量指向0地址(实际是0地址开始的一段内存)。
2.NULL不是0?
如果一个指针被赋予NULL,相当于这个指针执行了0x00000000这个逻辑地址,但是在C语言中0x00000000这个逻辑地址用户是不能使用的,所以当你试图取一个指向NULL的指针的内容时,就会提示段错误,例如以下代码:

#include<stdio.h>
int main()
{
	int* node = NULL;
	int p = 0;
	p = *node;
	printf("%d\n", p);
	return 0;
}

编译运行会报错。
在这里插入图片描述

其实由于指针node执行的是NULL,也就是逻辑地址0x00000000,而这个地址不能访问,所以编译器提示段错误。
那么这里你看到的是NULL还是0呢?根据宏定义可以知道:(void *)0表示把数值0强制转换为void *类型,所以最后运行结果还是0。
变量在定义时,系统会给他自动分配内存空间,指针变量也是一样,如果指针没有指向的话,那么地址就是随机值,如果不小心用的话就会导致数据错误,从而使程序退出。
NULL使指针p指向地址0x00000000,在大多数系统中都将0x00000000作为不被使用的地址,所以运用p也不会毁坏数据。
但也有系统会使用0x00000000,而将NULL定义为其他值,所以不要把NULL和0等同起来。
我们再使用传递方式来看,例如下面代码:

#include <stdio.h>
void Fun(int* node)
{
    static int N = 1024;
    node = &N;
}
int main()
{
    int* node = NULL;
    int p = 0;
    Fun(node);
    p = *node;

    printf("%d\n", p);
    return 0;
}

调试时也会报错:
在这里插入图片描述
Fun函数是值传递,node指针变量的值并不受影响,所以这个程序的效果和上一个程序运行结果都是段错误。
如果要让这个程序输出一个值,应该按照下面的方式书写代码:

#include <stdio.h>
void Fun(int** node)
{
    static int N = 1024;
    *node = &N;
}
int main()
{
    int* node = NULL;
    int p = 0;
    Fun(&node);
    p = *node;
    printf("%d\n", p);
    return 0;
}

运行结果为:

1024

传递一个二级指针也就是传递node指针变量的指针给Fun函数,这样的话结果就对了。

写在最后

在编程过程中,我们必须对自己的指针负责,很多导致bug出现或者找不到问题所在地的就是这种细节。

  • 9
    点赞
  • 31
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
关于c语言c++的课程成绩信息管理系统,共有将近6000行代码,建议使用vs2012或2010便于管理也可使用VC6.0++环境修改运行但查找麻烦,所有的语言没有脱离c和c++,主要采用模块思想,也可以转换成面向对象型的语言,只要将模块函数写进类。同时学c语言的也可以使用,除了使用cout,cin一些很容易上手的c++代码,相当于printf,scanf,主要为了方便输入输出,不用写%d%c... 详细细节也可以访问,百度文库网址 使用注意事项 有着强大的报错功能。 1 全部采用鼠标点击功能,可以看百度网址图片。 2 录用学生信息的细节选项,如果点击错误信息,再次点击将会取消。 3 附加功能的高级打印功能,如果想改变选项,只需要点击另一个即可,当前的状态就会消失。 4 输入学号为53120101--531215**(其不包括****00,例如53120700)。(可以设置) 5 所有成绩范围为0--99。(可以设置) 6 如果想去掉钢琴曲,直接删除MP3,或者改成其他名字即可。 7 打印直方图可以根据班级的不同,向后移动。 7 如果打印不规范,可能窗口较小,可通过调节窗口大小来打印排名...... 8 请包含student.txt默认文件(文件至少一名学生信息),否则将会程序在进行实质功能作用时意外退出(已在包)。 头文件student.h #ifndef _STUDENT_H_ #define _STUDENT_H_ #include #include HWND hWnd; //来自msdn #define PERR(bSuccess, api){if(!(bSuccess)) printf("%s:Error %d from %s \ on line %d\n", __FILE__, GetLastError(), api, __LINE__);} void cls( HANDLE hConsole ) { COORD coordScreen = { 0, 0 }; /* here's where we'll home thecursor */ BOOL bSuccess; DWORD cCharsWritten; CONSOLE_SCREEN_BUFFER_INFO csbi; /* to get buffer info */ DWORD dwConSize; /* number of character cells inthe current buffer *//* get the number of character cells in the current buffer */ bSuccess = GetConsoleScreenBufferInfo( hConsole, &csbi ); PERR( bSuccess, "GetConsoleScreenBufferInfo" ); dwConSize = csbi.dwSize.X * csbi.dwSize.Y;/* fill the entire screen with blanks */ bSuccess = FillConsoleOutputCharacter( hConsole, (TCHAR) ' ', dwConSize, coordScreen, &cCharsWritten ); PERR( bSuccess, "FillConsoleOutputCharacter" );/* get the current text attribute */ bSuccess = GetConsoleScreenBufferInfo( hConsole, &csbi ); PERR( bSuccess, "ConsoleScreenBufferInfo" );/* now set the buffer's attributes accordingly */ bSuccess = FillConsoleOutputAttribute( hConsole, csbi.wAttributes, dwConSize, coordScreen, &cCharsWritten ); PERR( bSuccess, "FillConsoleOutputAttribute" );/* put the cursor at (0, 0) */ bSuccess = SetConsoleCursorPosition( hConsole, coordScreen ); PERR( bSuccess, "SetConsoleCursorPosition" ); return; } HANDLE hOut; HANDLE hIn; void enter(); void ReadFile(char*str="student.txt"); typedef enum grade{you=95,liang=85,zhong=75,pass=65,nopass=0 } Grade; Grade g1=you; Grade g2=liang; Grade g3=zhong; Grade g4=pass; Grade g5=nopass; void DelClass(); //学生类结构 typedef struct student{ int studentid; char name[20]; char sex[5]; char nation[20]; int biryear; int birmonth; char post[10]; int cyu; int cyushe; int cshe; int cdui; int cduishe; struct student* next; double ave; double wave; } Student; Student *stubegin=NULL; Student* stulast=NULL; int total=0; //课程类结构 typedef struct course{ char obj[30]; int time; int xuefen; int mark; Grade rank; } Course; Course c1; Course c2; Course c3; Course c4; Course c5; void InitCourse(); void AddData(Student*); void AltData(); void AddShuju(int *,Student*); void IntEro(int& ,int,int,int,int); //功能介绍 /*****************************************Loading页面*******************************************/ void input();//输入信息功能 void output();//输出信息功能 void addition();//附加功能 /*****************************************输入信息功能*******************************************/ void AddStudent(); void DelStudent(int); void AltStudent(int); void SaveMessage(); void readfile(); /*****************************************输出信息功能*******************************************/ void CalRankAve(int); void CalRankWave(int); void PrintStudent(int); void PrintCourse(int); void PrintWell(); /*****************************************打印总成绩*******************************************/ void PrintTotal(); void PrintStudentID(); void PrintAve(); void PrintWave(); /*****************************************打印科目成绩*******************************************/ void PrintObj(); void PrintCyu(); void PrintCyushe(); void PrintCshe(); void PrintCdui(); void PrintCuishe(); /*****************************************打印优秀职务单科成绩*******************************************/ void PrintObjYou(); void PrintCyuYou(); void PrintCyusheYou(); void PrintCsheYou(); void PrintCduiYou(); void PrintCuisheYou(); /*********************辅助函数**********************/ void printmark(Student *); void AddStudent1(); void SortAve(); void SortWave(); void SortId(); void SortCyu(); void SortCyushe(); void SortCshe(); void SortCdui(); void SortCduishe(); int GetInt(char* ); int Search(int); void PrintGrade(int); int GetXuefen(int,int); /****计算平均成绩和加权成绩**/ void CalAve(Student*); void CalWave(Student*); /*****************************************附加功能*******************************************/ void PrintHistogram(); void PrintTotalHistogram();

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值