关于c:C语言的角落这些C语言不常用的特性你知道吗

52次阅读

共计 3939 个字符,预计需要花费 10 分钟才能阅读完成。

变长参数列表

<stdarg.h> 头文件定义了一些宏,当函数参数未知时去获取函数的参数

变量:typedef va_list

宏:

va_start()

va_arg()

va_end()

va_list 类型通过 stdarg 宏定义来拜访一个函数的参数表,参数列表的开端会用省略号省略

(va_list 用来保留 va_start,va_end 所需信息的一种类型。为了拜访变长参数列表中的参数,必须申明 va_list 类型的一个对象)

咱们通过初始化 (va_start) 类型为 va_list 的参数表指针,并通过 va_arg 来获取下一个参数。

【例子:】

求任意个整数的最大值:

可变长数组

历史上,C 语言只反对在编译时就能确定大小的数组。程序员须要变长数组时,不得不用 malloc 或 calloc 这样的函数为这些数组调配存储空间,且波及到多维数组时,不得不显示地编码,用行优先索引将多维数组映射到一维的数组。

ISOC99 引入了一种能力,容许数组的维度是表达式,在数组被调配的时候才计算出来。

留神:如果你须要有着变长大小的长期存储,并且其生命周期在变量外部时,可思考 VLA(Variable Length Array,变长数组)。但这有个限度:每个函数的空间不能超过数百字节。因为 C99 指出边长数组能主动存储,它们像其余主动变量一样受限于同一作用域。即使规范未明确规定,VLA 的实现都是把内存数据放到栈中。VLA 的最大长度为 SIZE_MAX 字节。思考到指标平台的栈大小,咱们必须更加审慎小心,以保障程序不会面临栈溢出、下个内存段的数据损坏的难堪场面。

case 反对范畴取值(gcc 扩大个性) MinGW 编译通过

非部分跳转 setjmp 和 longjmp

在 C 中,goto 语句是不能逾越函数的,而执行这类跳转性能的是 setjmp 和 longjmp 宏。这两个宏对于解决产生在深层嵌套函数调用中的出错状况是十分有用的。

此即为:非部分跳转。非部分指的是,这不是由一般 C 语言 goto 语句在一个函数内施行的跳转,而是在栈上跳过若干调用帧,返回到以后函数调用门路的某个函数中。

`#include <setjmp.h>

int setjmp(jmp_buf env) ; /设置调转点/

void longjmp(jmp_bufenv, int val) ; /跳转/`

setjmp 参数 env 的类型是一个非凡类型 jmp_buf。这一数据类型是某种模式的数组,其中寄存 在调用 longjmp 时能用来复原栈状态的所有信息。因为需在另一个函数中援用 env 变量,所以应该将 env 变量定义为全局变量。

longjmp 参数 val,它将成为从 setjmp 处返回的值。(很神奇吧。setjmp 依据返回值可晓得是哪个 longjmp 返回来的)

举荐本人的 linuxC/C++ 交换群:812855908!整顿了一些集体 感觉比拟好的学习书籍、视频材料以及大厂面经视频共享在群文件外面,有须要的 能够自行添加哦!~

volatile 属性

如果你有一个主动变量,而又不想它被编译器优化进寄存器,则可定义其为有 volatile 属性。这样,就明确地把这个值放在存储器中,而不会被优化进寄存器。

setjmp 会保留以后栈状态信息,也会保留此时寄存器中的值。(longjmp 会回滚寄存器中的值)

【如果要编写一个应用非部分跳转的可移植程序,则必须应用 volatile 属性】

· IO 缓冲问题

缓冲输入和内存调配

当一个程序产生输入时,可能立刻看到它有多重要?这取决于程序。

例如,终端上显示输入并要求人们坐在终端后面答复一个问题,人们可能看到输入以晓得该输出什么就显得至关重要了。另一方面,如果输入到一个文件中,并最终被发送到一个行式打印机,只有所有的输入最终可能达到那里是重要的。

立刻安顿输入的显示通常比将其临时保留在一大块一起输入要低廉得多。因而,C 实现通常容许程序员管制产生多少输入后在理论地写出它们。

这个管制通常约定为一个称为 setbuf()的库函数。如果 buf 是一个具备适当大小的字符数组,则

setbuf(stdout, buf);

将通知 I / O 库写入到 stdout 中的输入要以 buf 作为一个输入缓冲,并且等到 buf 满了或程序员间接调用 fflush()再理论写出。缓冲区的适合的大小在中定义为 BUFSIZ。

因而,上面的程序解释了通过应用 setbuf()来讲规范输出复制到规范输入:

可怜的是,这个程序是谬误的,因为一个轻微的起因。

要晓得故障出在哪,咱们须要晓得缓冲区最初一次刷新是在什么时候。答案:主程序实现之后,库将管制交回到操作系统之前所执行的清理的一部分。在这一时刻,缓冲区曾经被开释了!(即 main 函数栈清空之后)

有两种办法能够防止这一问题。

首先,应用动态缓冲区,或者将其显式地申明为动态:

static char buf[BUFSIZ];

或者将整个申明移到主函数之外。

另一种可能的办法是动静地调配缓冲区并且从不开释它:

char *malloc();

setbuf(stdout, malloc(BUFSIZ));

留神在后一种状况中,不用查看 malloc()的返回值,因为如果它失败了,会返回一个空指针。而 setbuf()能够承受一个空指针作为其第二个参数,这将使得 stdout 变成非缓冲的。这会运行得很慢,但它是能够运行的。

预编译和宏定义

C/C++ 中几个常见却有用的预编译和宏定义

1:# error

语法格局如下:

#error token-sequence

其次要的作用是在编译的时候输入编译错误信息 token-sequence,从不便程序员检查程序中呈现的谬误。例如上面的程序

在编译的时候输入如编译信息

fatal error C1189: #error : No definedConstant Symbol CONST_NAME1

2:#pragma

其语法格局如下:

# pragma token-sequence

此指令的作用是触发所定义的动作。如果 token-sequence 存在,则触发相应的动作,否则疏忽。此指令个别为编译系统所应用。例如在 Visual C++.Net 中利用# pragma once 避免同一代码被蕴含屡次。

3:#line

此命令次要是为强制编译器按指定的行号,开始对源程序的代码从新编号,在调试的时候,能够按此规定输入错误代码的精确地位。

模式 1

语法格局如下:

# line constant“filename”

其作用是使得其后的源代码从指定的行号 constant 从新开始编号,并将以后文件的名命名为 filename。例如上面的程序如下:

提醒如下的编译信息:

Hello.c(15) : error C2065: ‘CONST_NAME1’ :undeclared identifier

示意以后文件的名称被认为是 Hello.c,#line 10 “Hello.c” 所在的行被认为是第 10 行,因而提醒第 15 行出错。

模式 2

语法格局如下:

# line constant

其作用在于编译的时候,精确输入出错代码所在的地位(行号),而在源程序中并不呈现行号,从而不便程序员精确定位。

4:运算符 #和##

在 ANSI C 中为预编译指令定义了两个运算符——# 和 ##。

# 的作用是实现文本替换(字符串化),例如

`#define HI(x)printf(“Hi,”#x”n”);

void main()

{

HI(John);

}`

程序的运行后果

Hi,John

在预编译解决的时候, #x 的作用是将 x 替换为所代表的字符序列。(即把 x 宏变量字符串化)在本程序中 x 为 John,所以构建新串“Hi,John”。

## 的作用是串连贯。

例如

`#define CONNECT(x,y) x##y

void main()

{

int a1,a2,a3;

CONNECT(a,1)=0;

CONNECT(a,2)=12;

a3=4;

printf(“a1=%dta2=%dta3=%d”,a1,a2,a3);

}`

程序的运行后果为

a1=0 a2=12 a3=4

在编译之前,CONNECT(a,1)被翻译为 a1,CONNECT(a,2)被翻译为 a2。

数据类型对应字节数

程序运行平台

不同的平台上对不同数据类型调配的字节数是不同的。

集体对平台的了解是 CPU+OS+Compiler,是因为:

1、64 位机器也能够装 32 位零碎(x64 装 XP);

2、32 位机器上能够有 16/32 位的编译器(XP 上有 tc 是 16 位的,其余常见的是 32 位的);

3、即便是 32 位的编译器也能够弄出 64 位的 integer 来(int64)。

以上这些是基于常见的 wintel 平台,加上咱们可能很少机会接触的其它平台(其它的 CPU 和 OS),所以集体认为所谓平台的概念是三者的组合。

尽管三者的长度能够不一样,但显然相互配合(即长度相等,32 位的 CPU+32 位的 OS+32 位的 Compiler)施展的能量最大。

实践上来讲 我感觉数据类型的字节数应该是由 CPU 决定的,然而实际上次要由编译器决定(占多少位由编译器在编译期间说了算)。

罕用数据类型对应字节数可用如 sizeof(char),sizeof(char*)等得出

32 位编译器:

char:1 个字节

char*(即指针变量): 4 个字节(32 位的寻址空间是 2^32, 即 32 个 bit,也就是 4 个字节。同理 64 位编译器)

short int : 2 个字节

int:4 个字节

unsigned int : 4 个字节

float: 4 个字节

double: 8 个字节

long: 4 个字节

long long: 8 个字节

unsigned long: 4 个字节

64 位编译器:

char:1 个字节

char*(即指针变量): 8 个字节

short int : 2 个字节

int:4 个字节

unsigned int : 4 个字节

float: 4 个字节

double: 8 个字节

long: 8 个字节

long long: 8 个字节

unsigned long: 8 个字节

正文完
 0