C语言进阶——一文带你深度了解“C语言关键字”(中篇6)

本篇文章记录我学习C语言进阶知识------C语言关键字,旨在记录分享,希望我的分享能带给你不一样的收获!

目录

一、return关键字

[二、const 关键字也许该被替换为 readolny](#二、const 关键字也许该被替换为 readolny)

[(一)、 const 修饰的只读变量](#(一)、 const 修饰的只读变量)

[(二)、 节省空间,避免不必要的内存分配,同时提高效率](#(二)、 节省空间,避免不必要的内存分配,同时提高效率)

(三)、修饰一般变量

(四)、修饰数组

(五)、修饰指针

(六)、修饰函数参数

(七)、修饰函数的返回值

三、结语

一、return关键字

刚开始学习C语言的时候,大家因该知道return关键字最重要的作用就是:终止一个函数并返回其后面跟着的值。
return (Val); //此括号可以省略。但一般不省略,尤其在返回一个表达式的值时。

return 可以返回些什么东西呢?看下面例子:

char * Func(void)

{

char str[30];

...

return str;

}
str 属于局部变量,位于栈内存中,在 Func 结束的时候被释放,所以返回 str 将导致错误。


既然如此,那我们就深度来探讨一下C语言return 关键字的一些使用规则。

**【规则1】**return语句不可以返回指向"栈内存"的"指针",因为该内存在函数体结束时会被自动销毁。

这里大家再来探讨一个问题:

return ;
这个语句有问题吗?如果没有问题,那返回的是什么?

  • void 函数中的 return;

如果函数的返回类型是 void,那么 return; 用于结束函数的执行。

例子:
void myFunction()

{

......// 执行一些操作

return; // 结束函数,不返回任何值

}

  • void 函数中的 return;

对于返回类型不是 void 的函数(如 intfloat 等),使用 return; 是不合法的,因为这些函数需要返回一个值。

如果试图在这样的函数中使用 return;,编译器会报错。

int myFunction()

{

...... // 执行一些操作

return; // 错误,缺少返回值

}

二、const 关键字也许该被替换为 readolny

**const 是 constant 的缩写,是恒定不变的意思,也翻译为常量、常数等。很不幸,正是因为这一点,很多人都认为被 const 修饰的值是常量。这是不精确的,精确的说应该是只读的变量,**其值在编译时不能被使用,因为编译器在编译时不知道其存储的内容。或许当初这个关键字应该被替换为 readonly。那么这个关键字有什么用处和意义呢?

const 推出的初始目的,正是为了取代预编译指令,消除它的缺点,同时继承它的优点。

我们看看它与 define 宏的区别。 (很多人误以为 define 是关键字,在这里我提醒你再回到本章前面看看 32 个关键字里是否有 define)。

(一)、 const 修饰的只读变量

定义 const 只读变量,具有不可变性。

例如:

const int Max=100;

int Array[Max];

这里请在 Visual C++6.0 里分别创建.c 文件和.cpp 文件测试一下。你会发现在.c 文件中,编译器会提示出错,而在.cpp 文件中则顺利运行。为什么呢?我们知道定义一个数组必须指定其元素的个数。这也从侧面证实在 C 语言中, const 修饰的 Max 仍然是变量,只不过是只读属性罢了;而在 C++里,扩展了 const 的含义,这里就不讨论了。

注意: const 修饰的只读变量必须在定义的同时初始化,想想为什么?

留一个问题: case 语句后面是否可以是 const 修饰的只读变量呢?请动手测试一下。

【答案】

const 修饰的只读变量必须在定义时初始化,因为它的值在初始化之后不能被修改。这样做有几个原因:

  1. 确保值不变const 变量在定义时必须初始化,这样编译器能够知道其初始值,并确保在整个程序的生命周期中不会被修改。如果没有初始值,编译器无法保证变量的不可变性。

  2. 内存管理:在编译时,const 变量的值可以被直接嵌入到代码中或放置在只读存储区。如果没有初始化,编译器无法为其分配适当的存储位置或内存,导致无法确保变量值的不可变性。

  3. 程序正确性:在程序运行时,const 变量的不可变性是通过初始化保证的。如果没有初始化,可能会出现未定义行为或程序错误,因为变量的值可能未被设定或被错误地修改。

在 C 和 C++ 中,case 语句后面通常需要是编译时常量(即常量表达式),而 const 修饰的只读变量可以用作 case 标签,但有一些细节需要注意:

  1. 编译时常量要求
  • switch 语句的 case 标签中,必须使用编译时常量。编译时常量是指在编译时已知的常量值。这意味着 case 标签的值必须在编译时确定。
  1. const 变量的使用
  • const 修饰的变量的值在程序运行时是不可修改的,但是编译器在编译阶段需要知道其具体值。只要 const 变量是在编译时能够确定其值的常量,编译器就可以接受它作为 case 标签。

const int VALUE = 5;

switch (x) {

case VALUE:

// 执行相关代码

break;

// 其他 case 标签

}

在这种情况下,如果 VALUE 在编译时是一个已知的常量值(例如常量表达式),那么它是可以作为 case 标签的。

需要注意的点

  1. const 变量初始化const 变量必须在定义时初始化,并且初始化值必须是编译时常量。

  2. 编译器支持 :某些编译器可能会对 const 变量的支持有所不同,但在标准的 C 和 C++ 中,const 变量在符合编译时常量的条件下通常是可以用于 case 标签的。

  3. 避免未定义行为 :确保 const 变量在编译时能够确定其值,避免使用运行时值或非编译时常量,否则会导致编译错误或未定义行为。

(二)、 节省空间,避免不必要的内存分配,同时提高效率

编译器通常不为普通 const 只读变量分配存储空间,而是将它们保存在符号表中,这使
得它成为一个编译期间的值,没有了存储与读内存的操作,使得它的效率也很高。

例如:

|-----------------------------------------------------------------------|-----------------------------------------------------------------------------------------|
| #define M 3 const int N=5; ...... int i=N; int I=M; int j=N; int J=M; | //宏常量 //此时并未将 N 放入内存中 //此时为 N 分配内存,以后不再分配! //预编译期间进行宏替换,分配内存 //没有内存分配 //再进行宏替换,又一次分配内存! |
| |

const 定义的只读变量从汇编的角度来看, 只是给出了对应的内存地址, 而不是像#define

一样给出的是立即数,所以, const 定义的只读变量在程序运行过程中只有一份拷贝(因为
它是全局的只读变量,存放在静态区
),而#define 定义的宏常量在内存中有若干个拷贝。
#define 宏是在预编译阶段进行替换,而 const 修饰的只读变量是在编译的时候确定其值。
#define 宏没有类型,而 const 修饰的只读变量具有特定的类型。

(三)、修饰一般变量

一般常量是指简单类型的只读变量。这种只读变量在定义时,**修饰符 const 可以用在类型说明符前,也可以用在类型说明符后。**例如:

int const i=2; 或 const int i=2;

(四)、修饰数组

定义或说明一个只读数组可采用如下格式:

int const a[5]={1, 2, 3, 4, 5};或

const int a[5]={1, 2, 3, 4, 5};

(五)、修饰指针

|-------------------------------------------------------------------|----------------------------------------------------------------------------------|
| const int *p; int const *p; int *const p; const int *const p; | // p 可变, p 指向的对象不可变 // p 可变, p 指向的对象不可变 // p 不可变, p 指向的对象可变 //指针 p 和 p 指向的对象都不可变 |

这里给出一个记忆和理解的方法:
先忽略类型名(编译器解析的时候也是忽略类型名),我们看 const 离哪个近。 "近水楼
台先得月",离谁近就修饰谁。

|-------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------|
| const int *p; int const *p; int *const p; const int *const p; | //const 修饰*p,p 是指针, *p 是指针指向的对象,不可变 //const 修饰*p,p 是指针, *p 是指针指向的对象,不可变 //const 修饰 p, p 不可变, p 指向的对象可变 //前一个 const 修饰*p,后一个 const 修饰 p,指针 p 和 p 指向的对象都不可变 |

(六)、修饰函数参数

**const 修饰符也可以修饰函数的参数,当不希望这个参数值被函数体内意外改变时使用。**例如:

void Fun(const int i);

告诉编译器 i 在函数体中的不能改变, 从而防止了使用者的一些无意的或错误的修改。

(七)、修饰函数的返回值

**const 修饰符也可以修饰函数的返回值,返回值不可被改变。**例如:
const int Fun (void);

在另一连接文件中引用 const 只读变量:
extern const int i; //正确的声明
extern const int j=10; //错误!只读变量的值不能改变。

注意这里是声明不是定义,关于声明和定义的区别,请看关于关键字的第一篇文章。

讲了这么多讲完了吗?远没有。在 C++里,对 const 做了进一步的扩展,还有很多知识未能

讲完。有兴趣的话,不妨查找相关资料研究研究。

三、结语

关于本次C语言关键字的进阶知识就分享到此了,"学如逆水行舟,不进则退",愿我们一起努力,逐梦不止,砥砺前行!

相关推荐
小牛itbull8 分钟前
ReactPress vs VuePress vs WordPress
开发语言·javascript·reactpress
请叫我欧皇i16 分钟前
html本地离线引入vant和vue2(详细步骤)
开发语言·前端·javascript
闲暇部落19 分钟前
‌Kotlin中的?.和!!主要区别
android·开发语言·kotlin
GIS瞧葩菜28 分钟前
局部修改3dtiles子模型的位置。
开发语言·javascript·ecmascript
chnming198733 分钟前
STL关联式容器之set
开发语言·c++
带多刺的玫瑰36 分钟前
Leecode刷题C语言之统计不是特殊数字的数字数量
java·c语言·算法
熬夜学编程的小王1 小时前
【C++篇】深度解析 C++ List 容器:底层设计与实现揭秘
开发语言·数据结构·c++·stl·list
GIS 数据栈1 小时前
每日一书 《基于ArcGIS的Python编程秘笈》
开发语言·python·arcgis
Mr.131 小时前
什么是 C++ 中的初始化列表?它的作用是什么?初始化列表和在构造函数体内赋值有什么区别?
开发语言·c++
陌小呆^O^1 小时前
Cmakelist.txt之win-c-udp-server
c语言·开发语言·udp