C语言补充

一些很少用但有意思的C语言补充

Posted by Hehesheng on May 24, 2018

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为整数, argvenv为指针的指针(二维数组), 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&lt;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

链接:XOR linked list

一些操作宏

#warning, #error, #pragma

  • #warning在编译窗口输出警告
  • #error窗口输出错误, 并停止编译

用法:#warning Message

  • #pragma有一定的兼容问题

用法:

  • #pragma ParaPara就是参数
#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 {
  // 错误处理
}