【C语言 | 关键字】C语言32个关键字详解(2)—— 修饰类型部分(auto、signed、unsigned、static、extern、const、register、volatile)

😁博客主页😁:🚀https://blog.csdn.net/wkd_007🚀
🤑博客内容🤑:🍭嵌入式开发、Linux、C语言、C++、数据结构、音视频🍭
🤣本文内容🤣:🍭介绍标准C语言的32个关键字🍭
😎金句分享😎:🍭有机会一定要试试,其实试错的成本并不高,而错过的成本很高🍭

C语言32个关键字详解(1):数据类型部分(char、short、int、long、float、double、struct、union、enum、void)
C语言32个关键字详解(2):修饰类型部分(auto、signed、unsigned、static、extern、const、register、volatile)
C语言32个关键字详解(3):结构语句部分(if、else、switch、case、default、do、while、for、break、continue、return、goto)
C语言32个关键字详解(4):其他(typedef、sizeof)


C语言的关键字有32个,我将它主要分成下面四个方面来介绍。

功能关键字
类型(10个)char、short、int、long、float、double、struct、union、enum、void
修饰类型(8个)auto、signed、unsigned、static、extern、const、register、volatile
结构语句(12个)if、else、switch、case、default、do、while、for、break、continue、return、goto
其他(2个)typedef、sizeof

前面介绍了char、short、int、long、float、double、struct、union、enum、void
这篇文章主要介绍auto、signed、unsigned、static、extern、const、register、volatile这几个与与类型相关的。

在这里插入图片描述

一、auto、signed、unsigned

1.1 auto 关键字

写了这么久C语言,你应该很少,甚至没使用过 auto 关键字吧。因为C语言在缺省状态下,编译器默认所有变量都是auto的,所以加不加auto都一样。

所以,auto关键字可以当它不存在!!!不用管它。

1.2 signed 关键字

signed关键字和auto一样,也很少用,C语言在缺省状态下,编译器默认数据都是signed类型的。

所以,signed关键字可以当它不存在!!!不用管它。

signed 表示有符号的,也就是说,这个变量可以是正数,也可以是负数;
计算机只认识0和1,负数的-号是无法存入内存的,约定了基本类型最高位为1来表示负数;

数值在计算机系统中一律用补码来存储的:
正数的补码与其原码一致;
负数的补码:为该数绝对值的原码按位取反再加1,最后把最高位写1。
例如:char 型的-5,其补码为:-5的绝对值5(二进制0000 0101)按位取反得到1111 1010,再加1等于1111 1011,再把最高位写1,仍然是1111 1011(十六进制0xFA)。所以-5的补码是0xFA

1.3 unsigned 关键字

1、charunsigned char:一般情况下,单纯的char类型应该只用于字符值的存储和使用;unsigned char型变量只能用于数值的存储和使用。

2、所有的无符号常量都应该带U后缀:unsigned u_ch= 100U;

3、signed类型变量和unsigned类型变量运算的结果是unsigned类型;

4、unsigned类型永远大于0,条件判断时千万别写出下面错误代码:

// `错误`代码演示:
// https://blog.csdn.net/wkd_007/article/details/133932760
unsigned int i = 100;
while(i>=0)// i是unsigned的,所以条件永远满足
{
	i--;
	printf("i=%u\n",i);
}

5、unsigned类型使用printf打印时,使用%uunsigned long使用%lu

转载请注明:https://blog.csdn.net/wkd_007/article/details/133932760


在这里插入图片描述

二、static 关键字

static关键字可以限定函数或变量的作用域;也可以指明变量存储在内存的哪个区域;

2.1 static 修饰的变量

1、static修饰的全局变量(静态全局变量):存在内存的静态区,作用域仅限于变量被定义的文件中,其他文件即使用extern声明也没法使用它。准确的说,作用域是从定义之处开始,到文件结尾处结束,在定义之处前面的那些代码行也不能使用它,想要使用就得在前面再声明它。所以,一般直接在文件顶端定义。

2、static修饰的局部变量(静态局部变量):在函数体里面定义的,就只能在这个函数里用了,同一个文档中的其他函数也用不了。由于被static修饰的变量总是存在内存的静态区,所以即使这个函数运行结束,这个静态变量的值也不会被销毁,函数下次使用时仍然能用到这个值。

参考下面代码加深理解:

//static.c
//https://blog.csdn.net/wkd_007
#include <stdio.h>

static int gs_i;
void fun(void)
{
	static int s_i = 0;
	int i = 0;
	printf("gs_i=%d s_i=%d i=%d\n",gs_i++, s_i++, i++);
}
int main()
{
	int i=0;
	for(i=0; i<10; i++)
	{
		fun();
	}
	return 0;
}

2.2 static 修饰的函数

static 修饰的函数是限定其作用域仅在本文件。实际开发中,如果该函数仅在本文件使用,最后加上static,这样就不怕与其他文件的函数重名。


在这里插入图片描述

三、extern 关键字

extern 关键字一般用于声明全局变量函数,作用是告诉编译器,该全局变量函数是本文件外部定义的。

本文件使用extern声明的全局变量函数,可以直接在本文件该处声明之后正常使用。

参考下面例子加深理解:全局变量extern_val、函数get_extern_val定义在extern.c,在extern_sample.c使用的话,需要加extern声明。

//extern.c
int extern_val;
int get_extern_val()
{
	return extern_val;
}
// extern_sample.c
#include <stdio.h>
extern int extern_val; 		// 声明,变量定义在其他文件(extern.c)
extern int get_extern_val();// 声明,函数定义在其他文件(extern.c)
int main()
{
	extern_val = 0;
	get_extern_val();
	return 0;
}

对定义和声明不清楚的可以参照文章:https://blog.csdn.net/wkd_007/article/details/133622725


在这里插入图片描述

四、const 关键字

4.1 const修饰的是 只读变量 而不是 常量

C语言中,const修饰的是 只读变量 而不是 常量;const修饰的变量,只是表示了该变量不能直接修改,但可以通过它的地址,间接修改;const修饰的只读变量不能用于case关键字后面作为判断条件;
参考代码理解:

#include <stdio.h>

int main()
{
	const int ci = 100;
	printf("ci=%d\n",ci);
	// ci=5;// 报错:error: assignment of read-only variable ‘ci’
	int *pCi = &ci;// warning: initialization discards ‘const’ qualifier from pointer target type [enabled by default]
	*pCi = 5;// 不报错,并间接修改了ci的值
	printf("ci=%d\n",ci);

	int i = 0;
	switch(i)
	{
	case 1:
		break;
	//case ci://报错: case label does not reduce to an integer constant
		//break;
	default:
		break;
	}
	return 0;
}

4.2 const变量与宏常量的区别

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

4.3 const修饰的指针

const* 前面,则指针指向的对象不可被改变;
const* 后面,则指针的值不可被改变;
* 的前后都有const,则指针的值、指针指向的对象的值都不可被修改;

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

4.4 const修饰函数的参数、返回值

void Fun(const int * p);

上面代码const修饰函数的参数,告诉编译器,*p在函数体中不能改变,从而防止了使用者的一些无意的或错误的修改。实际开发中,如果函数参数存在指针,且不希望指针指向的对象被修改,可以加const修饰。

const修饰符也可以修饰函数的返回值,返回值不可被改变。


在这里插入图片描述

五、register 关键字

首先,了解一下数据是怎样被CPU处理的?
数据从内存里拿出来先放到寄存器,然后CPU再从寄存器里读取数据来处理,处理完后同样把数据通过寄存器存放到内存里,CPU不直接和内存打交道。

register关键字请求编译器尽可能地将变量存在CPU内部寄存器中,而不是通过内存寻址访问以提高效率。
注意是尽可能,不是绝对。因为一个CPU的寄存器数量有限。

注意点:
1、register变量必须是能被cpu寄存器所接受的类型,也就是一个单个的值,并且其长度应小于或等于整型的长度;
2、register变量可能不存放在内存中,所以不能用取址运算符&来获取register变量的地址。


在这里插入图片描述

六、volatile 关键字

volatile关键字的作用是告诉编译器,该变量随时可能发生改变,每次使用它时都从内存中取值,不要对该变量的代码进行优化。

#include <stdio.h>
int main(void)
{
	//volatile int i = 10;
	int i = 10;
	int j = i;  // ①语句
	int k = i;	// ②语句
	printf("i=%d j=%d k=%d\n",i,j,k);
	return 0;
}

上面代码,因为①②语句中,i 没有被赋值,这时编译器会认为 i 的值没有改变,在①语句时从内存中取出 i 的值赋给 j ,在②语句是,又继续用这个值给 k 赋值。这样优化可以提升效率。

如果 i 使用 volatile 修饰,那么每次使用到 i ,编译器都会从内存取值,不做优化。在一些比较底层的代码(如Linux驱动代码,Linux内核源码)可能需要用到这个关键字。

一个参数既可以是const还可以是volatile吗?如:const volatile int i = 10;
可以,例如只读的状态寄存器。它是 volatile 因为它可能被意想不到地改变。它是 const 因为 程序不应该试图去修改它。

在这里插入图片描述
转载请注明:https://blog.csdn.net/wkd_007/article/details/133932760

如果文章有帮助的话,点赞👍、收藏⭐,支持一波,谢谢 😁😁😁

  • 9
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 6
    评论
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

wkd_007

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值