C语言补充
位域(struct)
在C语言中,可以通过,位域对单个变量,每一位控制只能是int
,unsigned int
,long int
,经测试size_t
也可以.
size_t
是当前系统支持的最大数据
声明
struct
{
type [member_name] : width ;
};
例如:
#include <stdio.h>
struct
{
int x : 4, y : 4, z : 8, w : 16;
} status;
//接下来可以如此赋值
void main()
{
status.x = 0x2;
status.y = 0xf;
status.z = 0xaa;
status.w = 0x8888;
printf("%d\n", sizeof(status));
printf("%x\n", status);
}
输出结果就是
4
8888aaf2
占用4个字节(32位),注意,如果超过int
范围,也就是32位,那么会使用第二个int
,换言说,就是当使用33位时,占用8个字节.
-
0 bit fields
struct { int a:3; int b:2; int :0;//空位 int c:4; int d:3; }; //结果是000dddcccc0bbaaa
C可变参数
例如,有时需要如下的使用场景:\
int fun(int, ...)
{
//code
}
int main()
{
fun(2, 2, 3);
fun(3, 2, 3, 3);
}
这种时候就可以使用C的可变参数,可变参数用省略号表示...
,为了使用可变参数,需要包含头文件stdarg.h
,提供了所需的函数和宏,使用过程如下:
- 定义函数,可变参数放最后并使用省略号
...
,前面可以设置自定义参数 - 函数中添加
va_list
类型变量 - 使用
int
参数和va_start
宏来初始化va_list
变为一个参数列表 - 用
va_arg
宏和va_list
变量来访问参数列表中锝每个项 - 使用
va_end
来清理va_list
变量的内存
例:
#include <stdio.h>
#include <stdarg.h>
double average(int num,...)
{
va_list valist;
double sum = 0.0;
int i;
/* 为 num 个参数初始化 valist */
va_start(valist, num);
/* 访问所有赋给 valist 的参数 */
for (i = 0; i < num; i++)
{
sum += va_arg(valist, int);
}
/* 清理为 valist 保留的内存 */
va_end(valist);
return sum/num;
}
int main()
{
printf("Average of 2, 3, 4, 5 = %f\n", average(4, 2,3,4,5));
printf("Average of 5, 10, 15 = %f\n", average(3, 5,10,15));
}
运行结果:
Average of 2, 3, 4, 5 = 3.500000
Average of 5, 10, 15 = 10.000000
可变参数函数具体详解
va_list
类型变量,函数参数的数据结构va_start(va_list ap, type last)
初始化va_list
型变量,last
是可变参数列表的前一个参数,例:
int fun(type last, ...){}
-
va_arg(va_list ap, type)
第一次va_arg
调用返回的是可变参数列表中的第一个参数,再一次调用va_arg
返回的是可变列表中下一个参数(如果还有) 如果没有下一个参数,或者参数类型不匹配,会产生随机错误. 如果ap被传递到了函数va_arg
中, 当函数返回以后,ap的值就没有定义了.type是希望返回的数据类型,会开启强制转型,以下是不支持的type
- char, signed char, unsigned char
- Short, unsigned short
- signed short, short int, signed short int, unsigned short int
- float
建议使用
- int, double, *head
-
va_end(va_list ap)
每次调用了va_start
就要调用va_end
释放va_list
.
常用预定义宏
1. __FILE__, __LINE__, __FUNCTION__
__FILE__
表示文件名__BASE_FILE__
表示根本文件位置,例如该函数实际上是在那个头文件中定义的__LINE__
表示行数__FUNCTION__
表示函数名
例:
#include <stdio.h>
int main()
{
printf("File :%s\n", __FILE__ );
printf("Line :%d\n", __LINE__ );
printf("FUNCTION :%s\n", __FUNCTION__);
return 0;
}
输出:
File :/Users/huanghuisheng/Desktop/C_test.c
Line :5
FUNCTION :main
2.__DATE__, __TIME__, __TIMESTAMP__
用于获得最后一次编译的日期, 时间和文件最后一次修改时间
printf("Date :%s\n", __DATE__ );
printf("Time :%s\n", __TIME__ );
printf("Change Time :%s\n", __TIMESTAMP__)
C语言命令行参数
格式固定由
main(int argc, char *argv[], char *env[]);
三个参数可以任意组合
argc
为整数, argv
和env
为指针的指针(二维数组), char **argv
or char *argv[]
or ` char argv[][],
env`同理
argc
表示argv
中元素的数量; argv[0]
是输入程序的路径以及名称, 之后的就是对应各个元素; env
用例程来理解:
#include <stdio.h>
int main(int argc, char *argv[], char *env[])
{
int i;
printf("These are the %d command- line arguments passed to main:\n\n", argc);
for(i=0; i<=argc; i++)
printf("argv[%d]:%s\n", i, argv[i]);
printf("\nThe environment string(s)on this system are:\n\n");
for(i=0; env[i]!=NULL; i++)
printf(" env[%d]:%s\n", i, env[i]);
}
在命令行输入:
/Users/huanghuisheng/Desktop/C_test command -m git status add all
输出结果:
These are the 7 command- line arguments passed to main:
argv[0]:/Users/huanghuisheng/Desktop/C_test
argv[1]:command
argv[2]:-m
argv[3]:git
argv[4]:status
argv[5]:add
argv[6]:all
argv[7]:(null)
The environment string(s)on this system are:
env[0]:TERM_PROGRAM=Apple_Terminal
env[1]:SHELL=/bin/bash
env[2]:TERM=xterm-256color
env[3]:TMPDIR=/var/folders/yh/jv4v06rs7h1b9vb5c6yct67h0000gn/T/
env[4]:Apple_PubSub_Socket_Render=/private/tmp/com.apple.launchd.UMdhO40jND/Render
env[5]:TERM_PROGRAM_VERSION=404
env[6]:TERM_SESSION_ID=60866A5E-0EAF-48FB-9520-14EBA9B50D01
env[7]:USER=huanghuisheng
env[8]:SSH_AUTH_SOCK=/private/tmp/com.apple.launchd.CtblFKVBD2/Listeners
env[9]:PATH=/Library/Frameworks/Python.framework/Versions/3.6/bin:/Users/huanghuisheng/anaconda3/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/aria2/bin:/opt/X11/bin
env[10]:PWD=/Users/huanghuisheng
env[11]:XPC_FLAGS=0x0
env[12]:XPC_SERVICE_NAME=0
env[13]:SHLVL=1
env[14]:HOME=/Users/huanghuisheng
env[15]:LOGNAME=huanghuisheng
env[16]:LC_CTYPE=UTF-8
env[17]:DISPLAY=/private/tmp/com.apple.launchd.lRuEDOon9U/org.macosforge.xquartz:0
env[18]:_=/Users/huanghuisheng/Desktop/C_test
C的技巧
有限自动机
FSM {
STATE(x) {
...
NEXTSTATE(y);
}
STATE(y) {
...
if (x == 0)
NEXTSTATE(y);
else
NEXTSTATE(x);
}
}
that can be achieved with the following macros:
#define FSM
#define STATE(x) s_##x :
#define NEXTSTATE(x) goto s_##x
Interlacing structures
Interlacing structures like Duff’s Device:
send(to, from, count)
register short *to, *from;
register count;
{
register n = (count + 7) / 8;
switch(count % 8) {
case 0: do { *to = *from++;
case 7: *to = *from++;
case 6: *to = *from++;
case 5: *to = *from++;
case 4: *to = *from++;
case 3: *to = *from++;
case 2: *to = *from++;
case 1: *to = *from++;
} while(--n > 0);
}
}
一下摘自他人博客,用于理解:
#include <iostream>
using namespace std;
int main()
{
int n = 0 ;
switch (n) {
case 0 : do {cout <<" 0 "<< endl;
case 1 : cout <<" 1 "<< endl;
case 2 : cout <<" 2 "<< endl;
case 3 : cout <<" 3 "<< endl;
} while ( -- n > 0 );
}
}
n的值 | 程序输出 |
---|---|
0 | 0123 |
1 | 123 |
2 | 230123 |
3 | 301230123 |
其他 | (无输出) |
Duff’s device
串行复制的优化实现, 用来复制多个字节, 这里 count 个字节从 from 指向的数组复制到 to 指向的内存地址
动态指定浮动打印精度
#include <stdio.h>
int main() {
int a = 3;
float b = 6.412355;
printf("%.*f\n",a,b);
printf("%.*f\n",++a,b);
return 0;
}
输出
6.412
6.4124
字符选择
hexDigit = "0123456789abcdef"[someNybble];
X_MACRO
/*
* X Macro() data list
* Format: Enum, Value, Text
*/
#define X_ERROR \
X(ERROR_NONE, 1, "Success") \
X(ERROR_SYNTAX, 5, "Invalid syntax") \
X(ERROR_RANGE, 8, "Out of range")
/*
* Build an array of error return values
* e.g. {0,5,8}
*/
static int ErrorVal[] =
{
#define X(Enum,Val,Text) Val,
X_ERROR
#undef X
};
/*
* Build an array of error enum names
* e.g. {"ERROR_NONE","ERROR_SYNTAX","ERROR_RANGE"}
*/
static char * ErrorEnum[] = {
#define X(Enum,Val,Text) #Enum,
X_ERROR
#undef X
};
/*
* Build an array of error strings
* e.g. {"Success","Invalid syntax","Out of range"}
*/
static char * ErrorText[] = {
#define X(Enum,Val,Text) Text,
X_ERROR
#undef X
};
/*
* Create an enumerated list of error indexes
* e.g. 0,1,2
*/
enum {
#define X(Enum,Val,Text) IDX_##Enum,
X_ERROR
#undef X
IDX_MAX /* Array size */
};
void showErrorInfo(void)
{
int i;
/*
* Access the values
*/
for (i=0; i<IDX_MAX; i++)
printf(" %s == %d [%s]\n", ErrorEnum[i], ErrorVal[i], ErrorText[i]);
}
/*
* Test validity of an error value
* case ERROR_SUCCESS:
* case ERROR_SYNTAX:
* case ERROR_RANGE:
*/
switch(value)
{
#define X(Enum,Val,Text) case Val:
X_ERROR
#undef X
printf("Error %d is ok\n",value);
break;
default:
printf("Invalid error: %d\n",value);
break;
}
链接:X Macro wiki
XOR linked list
一些操作宏
#warning, #error, #pragma
#warning
在编译窗口输出警告#error
窗口输出错误, 并停止编译
用法:#warning Message
#pragma
有一定的兼容问题
用法:
#pragma Para
Para就是参数
#ifdef _X86
#pragma message("_X86 macro activated!")
#endif
#pragma code_seg(["section-name"[,"section-class"]])
添加代码段#pragma once
只编译一次该文件
还有等等
判断数字是不是2^n^
#define is_power_of_2(n) ((n) != 0 && ((n) & ((n) - 1)) == 0)
//跪orz
自定义”控制流”(打开文件自动close)
int f = open("foo.txt", O_RDWR);
if (f >= 0) {
...
close(f); // 忘记 close 怎么办?
} else {
// 错误处理
}
//等效-------->
#define OPEN(f, ...)\
for (int f = open(__VA_ARGS__), _m = 1; _m; _m--, f >= 0 && close(f))\
if (f >= 0)
OPEN(f, "foo.txt", O_RDWR) {
... // 自动关闭, 再也不用手动调用 close 了
} else {
// 错误处理
}