编程技术是改变世界的力量。
本站
当前位置:网站首页 > 后端语言 > 正文

c语言解剖课:复合字面量和匿名数据的那些事儿

gowuye 2024-04-25 04:41 8 浏览 0 评论

写在前面

在我的上一篇文章《c语言解剖课:只读变量、常量、字面量傻傻分不清?》中,详细剖析了只读变量、常量、字面量的异同,今天我们来剖析下组合字面量和匿名数据的那些事儿。

学习C语言,或其他任何一门编程语言,概念之间相似、等价、相同这三个维度,我个人认为如果是在软件开发项目中是不需要去较真的,解决问题就好。

但是,如果想提升自己的技术理解力、洞察力、解剖力,较真就很有必要,因为既然有差异,就肯定是有原因的,较真让我们不断的接近真相。

之所以在前面写下这些,是因为有粉丝说我的技术文章写的太细,就算搞不清楚这些概念,也不影响成为程序员,因此有感而发,不吐不快,欢迎百家争鸣。

字面量

字面量,因为昨天发的文章里已经有详细的解剖,所以我们简单总结下:

类似数字2、3.5、1.8F,字符‘c'、字符串“ok”这些数据,具有以下特点:以本身数值形态呈现并,其数据类型是编译器通过其数值形态自动推导的、没有类似于常量名、变量名的字面量名(无名或匿名)、存放在只读区域,这样的数据我们称为字面量,英文是Literal,翻译成中文,就是“字面意义的”、“本来意义的”,

字面量的最大特点就是同一个字面量不能被重复使用。比如:

int x =2;
int y = 2;

这行代码里,给变量x和变量y赋值的是两个不同的字面量,是的,你没有看错。

类型和值都相同,第一个2和第二个2都被分配在只读空间区域,但是却被存放在两个不同的内存地址里,所以是2个不同的字面量。

复合字面量

多个字面量组合在一起 就是 复合字面量,英文名是 compound literals,或者组合字面量,是聚合类型的一种。

就是把多个字面量通过花括号组合在一起,构成一个初始化列表的形式,然后给其他对象进行赋值或初始化。

这样做的好处很多,可以使代码灵活、简洁。举几个例子,演示一下:

int array[] = {1,2,3};
int *ptr = array;

通过复合字面量用法,简化如下:

int *ptr = (int[]){1,2,3};

除了指针变量以外,如果你打算给数组也用复合字面量的方式初始化,你可能会困惑。比如:

int array[] = (int[]){1,2,3};

让我们在不同的编译器里编译一下,看看什么情况。比如在CLion2023里,默认的clang,c11标准,给出如下错误提示:

error: array initialized from non-constant array expression

non-constant 是非常量的意思,而我们的复合字面量是常量表达式,array是变量。常量有什么特性?大家都知道,常量的生命周期贯穿于程序的整个运行期间,而array是变量,变量要想和具备和常量一样的生命周期,必须用static修饰,我们修改一下:

可以了?!说明我们的猜测是正确的?只要变量具备static属性就可以匹配复合字面量(常量表达式)?先不急着下结论,我们再在visual stdio 2022里(c11标准的MSVC编译器)测一下卡年。先不用static修饰,提示如下:

意料之中,我们再改成static类型的,再测一下:

还是不行?给出了“初始值设定项不是常量”的报错,数组是static类型,复合字面量也是常量表达式,怎么还是不行呢?

因为MSVC编译器比较严格,数组初始化列表(初始化表达式)是更规范的用法,所以它强制约定优先使用{1,2,3},而且确实比(int[]){1,2,3}更为简洁。

但是static在CLion中又确实可以,那么到底static是不是规范用法呢?

也是。GCC明确规定了,变量如果具有static属性,其和常量一样的生命周期,将可以被复合字面量初始化。那就是VS又一次不按标准来干。

说到vS不按标准规范来,是众说周知的。比如c99明确规定了允许数组元素的数量可以是变量,比如:

在CLion中数组长度动态定义,没有任何问题(C99标准),但是咱们“宇宙第一IDE”,却是这样的:

还没编译就开始警告,我们不管他,强行编译,然后这样了:

好吧,不管VS了。回头主题,具有static特性的变量,GCC是允许复合字面量初始化的,但是实际上除了数组外,并不是必须要static修饰才行,比如:

指针p和结构体变量test都不需要static修饰。所以之前的猜测是不准确的。

我们总结一下:数组初始化直接用更规范的数组初始化表达式,其他变量如有初始化器(初始化表达式)的,优先采用。比如结构体,既可以用复合字面量的形式赋值,也可以用初始化器直接初始化。比如:

Test foo = { 2 };

和数组一样可以直接初始化,与static并没有太大的关联。

除了普通指针外,数组指针也可以用复合字面量(也可以称为聚合类型)初始化或赋值:

int array[] = {1,2};
int (*ptr)[2] = array ;

可以如下简化:

int (*ptr)[2] = (int[]){1,2};

指向多个数组的数组指针:

int array[][2] = {{1,2},{3,4},{5,6}};
int (*ptr)[2] = array;

用复合字面量简化一下:

int (*ptr)[2] =(int[][2]){{1,2},{3,4},{5,6}};

我们观察复合字面量会发现,其实就是前面小括号里是这个复合字面量的类型,注意,不是花括号里每个字面量的类型,而是整个复合字面量的类型,后面花括号里是所有字面量元素。

刚才拿数组举例子,很容易让人误解,以为组合字面量里每个元素都是类型相同的,实际上不一定,比如用符合字面量的方式,给结构体赋值或初始化一下:

struct _pos{
	int x;
	float y[2];
};
struct _pos pos = (struct _pos){2,{1.5,2.5}};

花括号里可以是不同类型的字面量。后来为了简化写法,也给结构体准备了类似数组一样的designated initializer,即初始化器(随便你怎么称呼吧,比如初始化列表、初始化表达式),所以直接这样写,可能更省事些:

typedef struct{
	int x;
	float y[2];
}POS;
POS pos ={2,{1.5,2.5}};

其实,函数参数才是复合字面量使用最广泛的地方。比如下面代码:

int sum(const int array[],int n){
    int sum = 0;
    for(int i=0;i<n;i++){
        sum += array[i];
    }
    return sum;
}
int main(){
    int array[] = {1,3,5};
    printf("%d\n",sum((int[]){1,3,5},3));
    return 0;
}

通过复合字面量的用法,把main函数里的sum函数改写如下:

sum((int[]){1,3,5},3)

会很方便和灵活。

前面的所有组合字面量的用法都可以在函数参数里使用,节省了先定义对应类型的变量,再在函数参数里使用的步骤。

匿名数据

字面量、复合字面量因为没有显式的“字面量名称”,实以这样的数据,一般称为“匿名”数据(或无名数据)。匿名数据最大的特点是不能在程序中被反复使用。

比如你定义了一个匿名数据:(int[]){1,2,3}; 之后你将无法再次使用它。比如下面的情况:

(int[]){1,2,3};
int *p = (int[]){1,2,3};

第一个(int[]){1,2,3}和第二个(int[]){1,2,3},是两个不同的匿名数据。

普通变量的定义,比如:int x = 2;也可以写成复合字面量的形式,比如:

int x = (int){2};

虽然符合语法,编译运行都可以,但是我们一般不会这样写,是吧?注意,不要和强制类型转换混淆了,别写成这样了:

int x = (int)2;

下面是一些常见的直接使用字面量作为函数参数的例子:

func(“hello”);
func(‘c’);
func(3)

使用复合字面量入参,前面已经介绍过了。

最后,我们简单提一下“匿名”的概念,其实所有编程语言中,都有这个概念。

除了字面量、复合字面量,匿名数组、匿名结构体、匿名共用体、匿名对象、匿名指针,c++中还有匿名空间,等的功能。

在c++中,会与c有些不同,复合字面量是左值,但是c++目前没有匿名左值。而且,c++把复合字面量作为临时对象对待,其所在的完整表达式一经结束,即被销毁,而c语言中和常量一样是持续存在的。

本篇文章主要是基于C语言范围的解剖系列,c++方面不再展开。

段誉,2024年1月31日,写于合肥。

相关推荐

Nginx 响应提速10倍,你需要知道的缓存性能优化——FastCGI调优
Nginx 响应提速10倍,你需要知道的缓存性能优化——FastCGI调优

Nginx缓存优化是帮助大家提升网站性能的重要操作之一,proxy_cache主要用于反向代理时,对后端内容源服务器进行缓存;fastcgi_cache主要用于...

2024-05-20 14:44 gowuye

王者荣耀天魔缭乱和逐梦之音返场活动地址 3月22日开启返场活动
王者荣耀天魔缭乱和逐梦之音返场活动地址 3月22日开启返场活动

王者荣耀官方终于确定了天魔缭乱和逐梦之音的返场活动,这让不少小伙伴乐开了花,返场活动将会在3月22日开启,下面就带来王者荣耀天魔缭乱和逐梦之音返场活动地址!王者...

2024-05-20 14:44 gowuye

常见的嵌入式web服务器有哪些?

嵌入式WEB服务器常见的有:Lighttpd,Shttpd,Thttpd,Boa,Mini_httpd,Appweb,Goahead。Lighttpd地址:http://www.light...

简述几款常见的嵌入式web服务器
简述几款常见的嵌入式web服务器

嵌入式web服务器,是web服务器当中的一种,是基于嵌入式系统而实现的web服务器。指的是在嵌入式系统(通俗点就是单片机系统)上实现的一个web服务器,可以通过...

2024-05-20 14:44 gowuye

教你如何利用fastcgi_cache缓存加速WordPress

在使用nginx缓存之前,必须在nginx里面加载专门的模块,这个模块叫做ngx_cache_purge。添加ngx_cache_purge模块下载ngx_cache_purge模块ngx_cache...

扫描WordPress漏洞

检测已知漏洞WPScan是一款广泛使用的WordPress安全扫描工具,它的一项重要功能是检测已知漏洞。在这篇文章中,我们将深入探讨WPScan如何检测已知漏洞,并结合实际示例,帮助读者更好地理解和应...

消灭 Bug!推荐几个给力的开源 Bug 跟踪工具
消灭 Bug!推荐几个给力的开源 Bug 跟踪工具

在这个充满bug的世界里,最遥远的距离不是生与死,而是你亲手制造的bug就在你眼前,你却怎么都找不到它。因此本文准备了7款优秀的开源bug跟踪系...

2024-05-20 14:43 gowuye

生物信息分析入门全攻略

生物信息学是生命科学研究的重大前沿领域,未来将占据生命科学研究的半壁江山。已经有越来越多的小伙伴投入到生物信息的学习中,但是入门难、深入慢、摸不到方向等都成为持续学习的拦路虎。本文根据生物信息技术大牛...

elkb实践经验,再赠送一套复杂的配置文件
elkb实践经验,再赠送一套复杂的配置文件

原创:小姐姐味道(微信公众号ID:xjjdog),欢迎分享,转载请保留出处。宝剑锋从磨砺出,梅花香自苦寒来。诗人白居易,三月下江南,看到沿路开放的桃花,心潮澎湃...

2024-05-20 14:43 gowuye

超详细从0到1 搭建ELK监控
超详细从0到1 搭建ELK监控

监控分类?Metrics用于记录可聚合的数据。例如,1、队列的当前深度可被定义为一个度量值,在元素入队或出队时被更新;HTTP请求个数可被定义为一个计数器,...

2024-05-20 14:42 gowuye

嵌入式开发 之Web配置页面开发
嵌入式开发 之Web配置页面开发

1.PHP是最好的语言??开发动态页面首选的语言是PHP,村村不能在这里忽悠人,如果你的硬件性能允许切略懂PHP,看到这里就可以退出了。本文面向的受众是Linu...

2024-05-20 14:42 gowuye

Python开发一个网站目录扫描工具用来检测网站是否有漏洞?
Python开发一个网站目录扫描工具用来检测网站是否有漏洞?

开发一个网站目录扫描工具是用来检测网站是否有非法目录请求的一个常见需求之一,我们要通过这个扫描工具来找到通过某个域名可以访问到的网站路径,可能对于有些系统来讲,...

2024-05-20 14:42 gowuye

创建一个类似Youtube的Id——使用PHP/Python/JS/Java/SQL

id通常都是用数字,不巧的是只有10个数字来使用,所以如果你有很多的记录,id往往变得非常冗长。当然对于计算机来说无所谓,但我们更希望id尽可能短。所以我们如何能使id变短?我们可以利用字母让它们附加...

快速云:有助于移动应用安全开发的五条妙计
快速云:有助于移动应用安全开发的五条妙计

许多企业不断地向其开发团队提供培训。但是某些漏洞,如早在十多年前就发现的SQL注入,如今仍广泛存在于各种应用中。因而,安全培训永不过时。在开发移动应用时,开发者...

2024-05-20 14:41 gowuye

洛杉矶国际电影节最佳动画短片奖影片《G’DAY》正式全网上映
洛杉矶国际电影节最佳动画短片奖影片《G’DAY》正式全网上映

7月2日,由M&CSaatchi创作,由深受好评的澳大利亚导演迈克尔·格雷西执导的动画短片《G’day》,正式在全网上映。该影片因其出色的创意赢得了洛...

2024-05-20 14:41 gowuye

取消回复欢迎 发表评论: