网站排名如何上升,WordPress纯代码标签页面,一二三年级手工折纸,在线做任务的网站这篇文章主要对c的学习做一个基础铺垫#xff0c;方便后续学习。主要通过示例讲解命名空间、c的输入输出cout\cin#xff0c;缺省参数、函数重载、引用、内联函数#xff0c;auto关键字#xff0c;for循环#xff0c;nullptr以及涉及到的周边知识#xff0c;面试题等。为… 这篇文章主要对c的学习做一个基础铺垫方便后续学习。主要通过示例讲解命名空间、c的输入输出cout\cin缺省参数、函数重载、引用、内联函数auto关键字for循环nullptr以及涉及到的周边知识面试题等。为后续类和对象学习打基础。 目录
什么是c? C关键字(C98)
一、命名空间
命名空间定义
1.正常的命名空间的定义
域
命名空间域 展开命名空间 std所有C库命名空间
命名空间可以嵌套
二、C输入输出 :
std命名空间的使用惯例
三、缺省参数
缺省/默认参数
全缺省(给出所有参数)
半缺省(从右往左连续给):
注意
四、函数重载 C支持函数重载的原理--名字修饰(name Mangling)
*有关于预处理、编译、汇编、链接的详解可见
五、引用别名
引用概念
使用方法 引用特性
1.引用必须初始化
编辑
2.引用定义后不能改变指向
3.一个变量可以有多个引用多个别名
常引用只能权限缩小不能权限扩大
使用场景 1、做参数a、输出型参数 b、对象比较大减少拷贝提高效率 这些效果指针也可以但是引用更方便
a、输出型参数
b、对象比较大减少拷贝提高效率
2、做返回值a、修改返回对象 b、减少拷贝提高效率
面试题引用和指针的区别
六、内联函数
面试题宏的优缺点
内联函数概念
查看方式
编辑inline 特性
inline是一种以空间换时间的做法
内联说明只是向编译器发出一个请求编译器可以选择忽略这个请求
inline不建议声明和定义分离分离会导致链接错误。因为inline被展开就没有函数地址 了链接就会找不到。
为什么要防止链接冲突
在.h未做声明和定义分离时为什么会出现这样的报错
以下有三种解决方案
1、当然就是声明和定义的分离
2、加静态static修饰函数时与全局函数相比有链接属性只在当前文件可见。不会进符号表
3、使用内联内联也不支持声明与定义分离
总结若需在.h中定义函数
七、auto关键字(C11)
auto简介
auto使用示例
auto不能推导的场景
八、基于范围的for循环(C11)
范围for的语法
范围for的使用条件
九、指针空值nullptr(C11)
注意 什么是c? C语言是结构化和模块化的语言适合处理较小规模的程序。对于复杂的问题规模较大的 程序需要高度的抽象和建模时C语言则不合适。为了解决软件危机 20世纪80年代 计算机 界提出了OOP(object oriented programming面向对象)思想支持面向对象的程序设计语言应运而生。 1982年Bjarne Stroustrup博士在C语言的基础上引入并扩充了面向对象的概念发明了一 种新的程序语言。为了表达该语言与C语言的渊源关系命名为C。因此C是基于C语言而 产生的它既可以进行C语言的过程化程序设计又可以进行以抽象数据类型为特点的基于对象的 程序设计还可以进行面向对象的程序设计。 C关键字(C98)
C总计63个关键字C语言32个关键字。
以下是学习c时会用到的关键字。 一、命名空间 在C/C中变量、函数和后面要学到的类都是大量存在的这些变量、函数和类的名称将都存 在于全局作用域中可能会导致很多冲突。使用命名空间的目的是对标识符的名称进行本地化 以避免命名冲突或名字污染namespace关键字的出现就是针对这种问题的。 命名空间定义 定义命名空间需要使用到namespace关键字后面跟命名空间的名字然后接一对{}即可{} 中即为命名空间的成员。 1.正常的命名空间的定义 域 ::域作用限定符 编译器搜索原则 不指定域 首先在当前局部域搜索再到全局域。 指定域 如果指定了域直接去指定域搜索 示例 #includestdio.hint x 0;int main()
{int x 1;printf(hello world\n);printf(%d\n, x);printf(%d\n, ::x);return 0;
}输出结果 hello world 1 0 命名空间域 示例 proj1是命名空间的名字一般开发中是用项目名字做命名空间名。 命名空间中可以定义变量/函数/类型 #includestdio.h
#includestdlib.hnamespace proj1
{int rand 0;int x 0;int Add(int left, int right){return left right;}struct Node{struct Node* next;int val;};
}namespace proj2
{int x 1;
}int main()
{printf(%d\n, proj1::x);printf(%d\n, proj2::x);printf(%p\n, rand);printf(%d\n, proj1::rand);printf(%d\n, proj1::Add(1,2));return 0;
} 输出结果 0 1 00007FFD91E94AD0 0 3 展开命名空间 同一个工程中允许存在多个相同名称的命名空间,编译器最后会合成同一个命名空间中。 示例 Queue.h namespace xxx
{struct Node{int val;struct Node* next;};struct Queue{struct Node* head;struct Node* tail;int size;};void Init(struct Queue* pq);void Push(struct Queue* pq, int x);
} List.h #pragma oncenamespace xxx
{struct QNode{int val;struct QNode* next;struct QNode* prev;};void Init(struct QNode* phead);void PushBack(struct QNode* phead, int x);
} #includestdio.h
#includeList.h
#includeQueue.h// 展开命名空间
using namespace xxx;int main()
{//printf(hello world\n);struct QNode node1;struct xxx::QNode node2;struct xxx::QNode node3;return 0;
} 此时就会通过using namespace xjh; 到我自定义的命名空间去寻找展开 std所有C库命名空间 示例
#includeiostream
using namespace std;int main()
{std::cout hello world std::endl;std::cout hello world std::endl;std::cout hello world std::endl;std::cout hello world std::endl;std::cout hello world std::endl;std::cout hello world std::endl;cout hello world endl;return 0;
} 命名空间可以嵌套 示例 #includeiostream
#includestdio.h
using std::cout;
using std::endl;
namespace N1
{int a;int b;int Add(int left, int right){return left right;}namespace N2{int c;int d;int Sub(int left, int right){return left - right;}}
}int main()
{int ret N1::Add(1, 2);int sub N1::N2::Sub(5, 6);cout ret endl;cout sub endl;} 输出结果 3 -1 二、C输入输出 说明 1. 使用cout标准输出对象(控制台)和cin标准输入对象(键盘)时必须包含 iostream 头文件 以及按命名空间使用方法使用std。 2. cout和cin是全局的流对象endl是特殊的C符号表示换行输出他们都包含在包含 在 iostream 头文件中。 3. 是流插入运算符是流提取运算符。 4. 使用C输入输出更方便不需要像printf/scanf输入输出时那样需要手动控制格式。 C的输入输出可以自动识别变量类型。 5. 实际上 cout 和 cin 分别是ostream和istream类型的对象和也涉及运算符重载等知识我们这里只是简单学习他们的使用。 1.左移 相当于扩大2倍 2.流插入 自动识别类型 示例 #includeiostream
using namespace std;int main()
{// 1、左移int i 100;i i 1;const char* str hello world;char ch \n;2、流插入 自动识别类型cout str i ch endl;printf(%s%d %c, str,i,ch);return 0;
} 输出结果 hello world200 hello world200 : 右移缩小2倍 流提取 示例 #includeiostream
using namespace std;int main()
{int i;const char* str hello world;char ch;// 右移流提取cin i ch;cout str i ch endl;return 0;
} 输出结果 50 \n这个是我自己输入的 hello world50\ ps关于cout和cin还有很多更复杂的用法比如控制浮点数输出精度控制整形输出进制格式等 等。因为C兼容C语言的用法这些又用得不是很多我们这里就不展开学习了。 以下提供一个例子 #includeiostream
using namespace std;int main()
{double d 1.11111111;printf(%.2lf\n, d);cout d endl;return 0;
} 输出结果 1.11 1.11111 std命名空间的使用惯例 std是C标准库的命名空间如何展开std使用更合理呢 1. 在日常练习中建议直接using namespace std即可这样就很方便。 2. using namespace std展开标准库就全部暴露出来了如果我们定义跟库重名的类型/对 象/函数就存在冲突问题。该问题在日常练习中很少出现但是项目开发中代码较多、规模 大就很容易出现。所以建议在项目开发中使用像std::cout这样使用时指定命名空间 using std::cout展开常用的库对象/类型等方式。 三、缺省参数 缺省参数概念 缺省参数是声明或定义函数时为函数的参数指定一个缺省值。在调用该函数时如果没有指定实参则采用该形参的缺省值否则使用指定的实参。 这里与c语言的区别c语言必须要和申明函数的格式相同。 缺省/默认参数 示例 #includeiostream
using namespace std;
void Func(int a 0)
{cout a endl;
}int main()
{Func(1);Func();return 0;
} 输出结果 1 0 全缺省(给出所有参数) 示例 #includeiostream
using namespace std;//全缺省
void Func(int a 10, int b 20, int c 30)
{cout a a endl;cout b b endl;cout c c endl endl;
}int main()
{Func(1, 2, 3);Func(1, 2);Func(1);Func();return 0;
}输出结果 a 1 b 2 c 3 a 1 b 2 c 30 a 1 b 20 c 30 a 10 b 20 c 30 半缺省(从右往左连续给): 示例 #includeiostream
using namespace std;//半缺省 从右往左连续给
void Func(int a, int b 20, int c 30)
{cout a a endl;cout b b endl;cout c c endl endl;
}int main()
{Func(1, 2, 3);Func(1, 2);Func(1);return 0;
}输出结果 a 1 b 2 c 3 a 1 b 2 c 30 a 1 b 20 c 30 注意 1. 半缺省参数必须从右往左依次来给出不能间隔着给如果生命与定义位置同时出现恰巧两个位置提供的值不同那编译器就无法确定到底该用那个缺省值。 //a.hvoid Func(int a 10);// a.cppvoid Func(int a 20){} 2. 缺省参数不能在函数声明和定义中同时出现 3. 缺省值必须是常量或者全局变量 4. C语言不支持编译器不支持 四、函数重载 C语言不允许同名函数 CPP语言允许同名函数要求函数名相同参数不同构成函数重载 1、参数类型的不同 2、参数个数不同 3、参数顺序不同(本质还是类型不同) 示例 #includeiostream
using namespace std;// _Z3Addii
void Add(int left, int right);
//{
// cout int Add(int left, int right) endl;
// return left right;
//}// _Z3Adddd
double Add(double left, double right);
//{
// cout double Add(double left, double right) endl;
// return left right;
//}void f(int a, char b);
void f(char b, int a);int main()
{Add(1, 2);Add(1.1, 2.2);//f(1, a); // call f(?)//f(a, 1); // call f(?)// f(1, a); // call _Z1fic(?)f(a, 1); // call _Z1fci(?)return 0; C支持函数重载的原理--名字修饰(name Mangling) 为什么C支持函数重载而C语言不支持函数重载呢 在C/C中一个程序要运行起来需要经历以下几个阶段预处理、编译、汇编、链接。 *有关于预处理、编译、汇编、链接的详解可见 Linux基础 - yum、rzsz、vim 使用与配置、gcc/g的详细解说以及预处理、编译、汇编、链接的详细步骤ESc 、iso_linux rzsz-CSDN博客 C语言不支持重载 因为链接时直接用函数名去找地址有同名函数区分不开 CPP如何支持的呢 函数名修饰规则名字中引入参数类型各个编译器自己实现了一套 五、引用别名
引用概念 引用不是新定义一个变量而是给已存在变量取了一个别名编译器不会为引用变量开辟内存空 间它和它引用的变量共用同一块内存空间。 比如李逵在家称为铁牛江湖上人称黑旋风。 使用方法 类型 引用变量名(对象名) 引用实体 改变引用时原值也会随之改变 示例
#includeiostream
using namespace std;int main()
{int a 0;// 引用b就是a的别名int b a;cout b endl;cout a endl;b;a;cout b endl;cout a endl;int c a;int d c;d;cout d endl;return 0;
}输出结果 当引用做参数时会比指针方便很多 因为传的就是原值的别名因此连带原值直接修改 指针和引用功能类似重叠但是引用不能完全替代指针因为引用定义后不能改变指向 示例对比以下两种 传的就是原值的别名因此连带原值直接修改 #includeiostream
using namespace std;//做参数
void Swap(int* a, int *b)
{int tmp *a;*a *b;*b tmp;
}void Swap(int a, int b)
{int tmp a;a b;b tmp;
}int main()
{int x 0, y 1;Swap(x, y);cout x x y y endl;Swap(x, y);cout x x y y endl;return 0;
} 引用特性 1.引用必须初始化 示例 2.引用定义后不能改变指向 示例 C的引用对指针使用比较复杂的场景进行一些替换让代码更简单易懂但是不能完全替代指针。 引用不能完全替代指针原因引用定义后不能改变指向 特别是当我们写一个链表时因为引用不能改变指向因此在这里根本不适用。
struct Node
{struct Node* next;struct Node* prev;int val;
};//void PushBack(struct Node* phead, int x)
//{
// // phead newnode;
//}//void PushBack(struct Node** pphead, int x)
//{
// // *pphead newnode;
//}void PushBack(struct Node* phead, int x)
{//phead newnode;
}int main()
{struct Node* plist NULL;return 0;
}typedef struct Node
{struct Node* next;struct Node* prev;int val;
}LNode, *PNode;void PushBack(PNode phead, int x)
{//phead newnode;
}//void PushBack(SeqList* ps, int x);
//void PushBack(SeqList ps, int x);
//{}int main()
{PNode plist NULL;return 0;
} 3.一个变量可以有多个引用多个别名 常引用只能权限缩小不能权限扩大 示例 #includeiostream
using namespace std;void TestConstRef()
{const int a 10;//int ra a; // 该语句编译时会出错a为常量const int ra a;// int b 10; // 该语句编译时会出错b为常量const int b 10;double d 12.34;//int rd d; // 该语句编译时会出错类型不同const int rd d;
}并且可用于函数传参时防止传的值不能被修改 使用场景 1、做参数a、输出型参数 b、对象比较大减少拷贝提高效率 这些效果指针也可以但是引用更方便 示例 a、输出型参数 b、对象比较大减少拷贝提高效率 #includeiostream
#include time.h
using namespace std;
struct A { int a[10000]; };
void TestFunc1(A a) {}
void TestFunc2(A a) {}
void main()
{A a;// 以值作为函数参数size_t begin1 clock();for (size_t i 0; i 10000; i)TestFunc1(a);size_t end1 clock();// 以引用作为函数参数size_t begin2 clock();for (size_t i 0; i 10000; i)TestFunc2(a);size_t end2 clock();// 分别计算两个函数运行结束后的时间cout TestFunc1(A)-time: end1 - begin1 endl;cout TestFunc2(A)-time: end2 - begin2 endl;
} 输出结果可以观察出这时候使用引用更加提高效率 2、做返回值a、修改返回对象 b、减少拷贝提高效率 示例 下面代码输出结果是什么 为什么 int Add(int a, int b)
{int c a b;return c;
}
int main()
{int ret Add(1, 2);Add(3, 4);cout Add(1, 2) is : ret endl;return 0;
} 注意如果函数返回时出了函数作用域如果返回对象还在(还没还给系统)则可以使用 引用返回如果已经还给系统了则必须使用传值返回。 面试题引用和指针的区别 语法 1、引用是别名不开空间指针是地址需要开空间存地址 2、引用必须初始化指针不做必要 3、引用不能改变指向指针可以 4、引用相对更加安全没有空引用但是有空指针容易出现野指针但是不容易出现野引用 5、sizeof 、 、解引用访问等方面的问题引用是引用类型大小指针永远都是4 or 8 底层 汇编层面上没有引用都是指针引用编译后也转换成指针了。 如图 六、内联函数
面试题宏的优缺点 宏的优点 1、增强代码的复用性 2、提高性能 c有哪些技术替代宏 1. 常量定义 换用const enum 2. 短小函数定义 换用内联函数 知识衍生实现两个数相加的宏函数 易错点 1、不是函数 #define ADD(int a, int b) return ab;2、分号#define ADD(a, b) ab;3、括号控制优先级#define ADD(a, b) (a)(b));核心点宏是预处理阶段进行替换
为什么加里面的括号 #includeiostream using namespace std; #define ADD(a, b) ((a)(b))// 为什么要加里面的括号int main() { if (ADD(1, 2)) {} ADD(1, 2) * 3; int x 1, y 2; ADD(x | y, x y); // (x|y xy) // |的优先级小于 return 0; } 宏的缺点 1、语法复杂坑很多不容易控制 2、不能调试 3、没有类型安全的检查 由此我们引入内联
内联函数概念 以inline修饰的函数叫做内联函数编译时C编译器会在调用内联函数的地方展开没有函数调用建立栈帧的开销内联函数提升程序运行的效率。 示例 #includeiostream
using namespace std;inline int Add(int a, int b)
{return a b;
}int main()
{int ret1 Add(1, 2) * 3;int x 1, y 2;int ret2 Add(x | y, x y);return 0;
} 如果在上述函数前增加inline关键字将其改成内联函数在编译期间编译器会用函数体替换函数的调用。 查看方式 1. 在release模式下查看编译器生成的汇编代码中是否存在call Add 2. 在debug模式下需要对编译器进行设置否则不会展开(因为debug模式下编译器默认不 会对代码进行优化以下给出vs2022的设置方式) 此时再打开反汇编就可以观察到函数在此时直接展开
inline 特性
inline是一种以空间换时间的做法 如果编译器将函数当成内联函数处理在编译阶段会 用函数体替换函数调用缺陷可能会使目标文件变大优势少了调用开销提高程序运 行效率。 内联说明只是向编译器发出一个请求编译器可以选择忽略这个请求 inline对于编译器而言只是一个建议不同编译器关于inline实现机制可能不同一般建 议将函数规模较小(即函数不是很长具体没有准确的说法取决于编译器内部实现)、不 是递归、且频繁调用的函数采用inline修饰否则编译器会忽略inline特性。 假如有一个func()函数 func()函数有100行 1w个地方调用这个函数 假设inline展开了 100*1w 假设inline不展开 1001w 《Cprime》第五版关于inline的建议 inline不建议声明和定义分离分离会导致链接错误。因为inline被展开就没有函数地址 了链接就会找不到。 防止重复包含同一个头文件 #pragma once #ifdefy 1 #define #endif 为什么要防止链接冲突 在.h未做声明和定义分离会导致在多个.c文件中包含头文件时出现报错多重定义的报错。 现在我在我的程序中写道这样的代码 Stack.cpp #includeStack.h Stack.h #includeiostreamint Add(int a, int b)
{return a b;
} Test.cpp #includeStack.hint main()
{int ret Add(1, 2);return 0;
}运行后就会出现这样的报错 在.h未做声明和定义分离时为什么会出现这样的报错 在stack.cpp与test.cpp中都包含一份Add函数不构成函数重载在链接时会导致文件中出现两个一样的函数从而重复定义出现报错。 以下有三种解决方案 1、当然就是声明和定义的分离 2、加静态static修饰函数时与全局函数相比有链接属性只在当前文件可见。不会进符号表 进符号表将地址加到符号表是为了让别人方便调用 Stack.h #includeiostreamstatic int Add(int a, int b)
{return a b;
}Stack.cpp #includeStack.h Test.cpp #includeiostreaminline int Add(int a, int b)
{return a b;
} 3、使用内联内联也不支持声明与定义分离 Stack.h #includeiostreaminline int Add(int a, int b)
{return a b;
} 直接在.h中定义解决方案不在.cpp中定义解决方案同static,Add地址不会进符号表inline直接展开。 总结若需在.h中定义函数 小函数用inline并且在函数很长时实际上也不会展开内联函数 大函数用static or 声明与定义分离 七、auto关键字(C11) 类型别名思考 随着程序越来越复杂程序中用到的类型也越来越复杂经常体现在 1. 类型难于拼写 2. 含义不明确导致容易出错 示例: #include string
#include map
int main()
{std::mapstd::string, std::string m{ { apple, 苹果 }, { orange,
橙子 }, {pear,梨} };std::mapstd::string, std::string::iterator it m.begin();while (it ! m.end()){//....}return 0;
}std::map::iterator 是一个类型但是该类型太长了特别容 易写错。 这里可以通过typedef给类型取别名 比如 #include string
#include map
typedef std::mapstd::string, std::string Map;
int main()
{Map m{ { apple, 苹果 },{ orange, 橙子 }, {pear,梨} };Map::iterator it m.begin();while (it ! m.end()){//....}return 0;
}使用typedef给类型取别名确实可以简化代码但是typedef有会遇到新的难题 typedef char* pstring;
int main()
{const pstring p1; // 编译成功还是失败const pstring* p2; // 编译成功还是失败return 0;
}在编程时常常需要把表达式的值赋值给变量这就要求在声明变量的时候清楚地知道表达式的 类型。然而有时候要做到这点并非那么容易因此C11给auto赋予了新的含义。 auto简介 在早期C/C中auto的含义是使用auto修饰的变量是具有自动存储器的局部变量但遗憾的 是一直没有人去使用它大家可思考下为什么 C11中标准委员会赋予了auto全新的含义即auto不再是一个存储类型指示符而是作为一 个新的类型指示符来指示编译器auto声明的变量必须由编译器在编译时期推导而得。 auto使用示例 void func() { //.... } int main() { int i 0; int j i; auto k i; //自动识别i的类型加给k // auto x; //auto不能做参数返回值可做但是很坑 auto p1 i; auto* p2 i; //指针指向i 地址 //auto* p3 i; auto r i; void(*pf1)(int, int) func; //函数指针 auto pf2 func; return 0; } void func(int a, int b)
{}
int main()
{void(*pf1)(int, int) func;auto pf2 func;cout typeid(pf1).name() endl;cout typeid(pf2).name() endl;return 0;
} 运行这个代码 auto自动识别类型 特别是当我们学到后期 int main()
{std::mapstd::string, std::string dict;//std::mapstd::string, std::string::iterator it dict.begin();auto it dict.begin();cout typeid(it).name() endl;return 0;
} 虽然auto很方便很好用但是大多数情况我们最好还是自定义好类型 auto不能推导的场景 如下若我们使用许多的auto在我们的程序中会出现这样的问题 1. auto不能作为函数的参数 返回值可做但是很坑 void TestAuto(auto a)
{}
auto f2()
{auto ret 1;return ret;
}auto f1()
{auto x f2();return x;
}auto TestAuto()
{auto a f1();return a;
}void func(int a, int b)
{}
int main()
{auto it2 TestAuto();return 0;
}2. auto不能直接用来声明数组 void TestAuto()
{int a[] {1,2,3};auto b[] {456};
} 3. 为了避免与C98中的auto发生混淆C11只保留了auto作为类型指示符的用法 4. auto在实际中最常见的优势用法就是跟以后会讲到的C11提供的新式for循环还有 lambda表达式等进行配合使用。 八、基于范围的for循环(C11)
范围for的语法 示例 #includeStack.h
#include string
using namespace std;int main()
{//在C98中如果要遍历一个数组可以按照以下方式进行int array[] { 1, 2, 3, 4, 5 };for (int i 0; i sizeof(array) / sizeof(array[0]); i)array[i] * 2;for (int* p array; p array sizeof(array) / sizeof(array[0]); p)cout *p endl;// C11// 依次取数组中值赋值给e自动迭代自动判断结束for (auto e : array){e * 2;}cout endl;for (auto e : array){cout e ;}cout endl;return 0;
} 运行结果: 246810 4 8 12 16 20 对于一个有范围的集合而言由程序员来说明循环的范围是多余的有时候还会容易犯错误。因 此C11中引入了基于范围的for循环。for循环后的括号由冒号“ ”分为两部分第一部分是范 围内用于迭代的变量第二部分则表示被迭代的范围。 范围for的使用条件 1. for循环迭代的范围必须是确定的 对于数组而言就是数组中第一个元素和最后一个元素的范围对于类而言应该提供 begin和end的方法begin和end就是for循环迭代的范围。 注意以下代码就有问题因为for的范围不确定 void TestFor(int array[])
{for(auto e : array)cout e endl;
} 2. 迭代的对象要实现和的操作。(关于迭代器这个问题以后会讲现在提一下没办法 讲清楚现在大家了解一下就可以了) 九、指针空值nullptr(C11) C98中的指针空值 在良好的C/C编程习惯中声明一个变量时最好给该变量一个合适的初始值否则可能会出现 不可预料的错误比如未初始化的指针。如果一个指针没有合法的指向我们基本都是按照如下 方式对其进行初始化 void TestPtr()
{int* p1 NULL;int* p2 0;// ……
}在c语言中NULL实际是一个宏且值为0 #ifndef NULL
#ifdef __cplusplus
#define NULL 0
#else
#define NULL ((void *)0)
#endif
#endif可以看到NULL可能被定义为字面常量0或者被定义为无类型指针(void*)的常量。不论采取何 种定义在使用空值的指针时都不可避免的会遇到一些麻烦 比如 void f(int)
{coutf(int)endl;
}
void f(int*)
{coutf(int*)endl;
}
int main()
{f(0);f(NULL);f((int*)NULL);return 0;
} 程序本意是想通过f(NULL)调用指针版本的f(int*)函数但是由于NULL被定义成0因此与程序的 初衷相悖。 在C98中字面常量0既可以是一个整形数字也可以是无类型的指针(void*)常量但是编译器 默认情况下将其看成是一个整形常量如果要将其按照指针方式来使用必须对其进行强转(void *)0。 注意 1. 在使用nullptr表示指针空值时不需要包含头文件因为nullptr是C11作为新关键字引入 的。 2. 在C11中sizeof(nullptr) 与 sizeof((void*)0)所占的字节数相同。 3. 为了提高代码的健壮性在后续表示指针空值时建议最好使用nullptr。 结语 随着这篇关于题目解析的博客接近尾声我衷心希望我所分享的内容能为你带来一些启发和帮助。学习和理解的过程往往充满挑战但正是这些挑战让我们不断成长和进步。我在准备这篇文章时也深刻体会到了学习与分享的乐趣。 在此我要特别感谢每一位阅读到这里的你。是你的关注和支持给予了我持续写作和分享的动力。我深知无论我在某个领域有多少见解都离不开大家的鼓励与指正。因此如果你在阅读过程中有任何疑问、建议或是发现了文章中的不足之处都欢迎你慷慨赐教。 你的每一条反馈都是我前进路上的宝贵财富。同时我也非常期待能够得到你的点赞、收藏关注这将是对我莫大的支持和鼓励。当然我更期待的是能够持续为你带来有价值的内容让我们在知识的道路上共同前行。