C语言小知识点总结

1、可变参数宏...和__VA_ARGS_

__VA_ARGS_是一个可变参数的宏,是新的c99规范中新增的,目前似乎只有gcc支持(VC6.0不支持)

宏定义中参数列表的最后一个参数为省略号(也就是3个点),这样预定义宏__VA_ARGS_就可以用在替换部分中,替换省略号所代表的字符串

比如define PR(...) printf(__VA_ARGS_)

int main()

{

int wt=1,sp=2;

printf("hello\n");

printf("weight=%d, shipping=%d\n", wt, sp);

return 0;

}

输出结果:

hello

weight=1, shipping=2

注意省略号只能代替最后面的宏参数,#define W(x, ..., y)错误!


2、可变参数va_start, va_arg, va_end

详细介绍的链接:http://blog.csdn.net/luckywang1103/article/details/42835619

3、预处理运算符"#"

在类函数宏的替换部分,#用作一个预处理运算符,将语言符号转化成字符串,该过程为字符串化

#include <stdio.h>

#define PSQR(x) printf("the square of" #x "is %d.\n", (x)*(x))

int main()

{

int y=4;

PSQR(y);

PSQR(2+4);

return 0;

}

输出结果为:

the square of y is 16.

the square of 2+4 is 36.

4、预处理运算符"##"

##可用作于类函数宏的替换部分,也可用作类对象宏的替换部分。这个运算符把两个语言符号组合成一个语言符号

#include <stdio.h>

#define XNAME(n) x##n

#define PXN(n) printf("x"#n" =%d.\n", x##n)

int main()

{

int XNAME(1) = 12; //int x1 = 12;

PXN(1); //printf("x1 =%d.\n", x1);

return 0;

}

输出结果为:

x1 =12.

5、FD_ZERO, FD_SET, FD_CLR, FD_ISSET

FD_ZERO(fd_set *fdset);将指定的文件描述符集清空,在对文件描述符集合进行设置前,必须对其进行初始化,如果不清空,由于在系统分配内存空间后,通常并不作清空处理,所以结果是不可知的。
FD_SET(fd_set *fdset);用于在文件描述符集合中增加一个新的文件描述符。 
FD_CLR(fd_set *fdset);用于在文件描述符集合中删除一个文件描述符。 
FD_ISSET(int fd,fd_set *fdset);用于测试指定的文件描述符是否在该集合中。

6、container_of

offsetof用来判断结构体中成员的偏移位置,需要头文件stddef.h

#define offsetof(type, member) (size_t)&(((type *)0)->member)

巧妙之处在于将地址0强制转换为type类型的指针,从而定位到member在结构体中的偏移位置,编译器认为0是一个有效的地址,从而认为0是type指针的起始地址。


typeof是GNU对C新增的一个扩展关键字,用于获取一个对象的类型,很多时候我们处理的对象是一个指针类型,如果想知道指针所指向的对象的类型,就派上用场了。


container用来根据成员的地址来获得结构体的地址,需要头文件linux/kernel.h

#define container_of(ptr, type, member)

   ({ const typeof( ((type *)0)->member ) *__mptr = (ptr);

        (type *)( (char *)__mptr - offsetof(type, member) );

     })

container_of宏分两部分,

第一部分:const typeof( ((type *)0)->member) *__mptr = (ptr);

通过定义一个member指针类型的指针变量__mptr,并将ptr赋值给__mptr

第二部分:(type *)( (char *)__mptr - offsetof(type, member) )

通过offset宏计算出member在type中的偏移,然后用member的实际地址__mptr减去偏移,得到type的起始地址,即指向type类型


7、#if 0

http://blog.csdn.net/luckywang1103/article/details/8802459

8、宏定义

C++和C中命名变量时不能以数字开头,定义宏时也不能,否则编译结果显示预处理时出错,显示信息为error: macro names must be identifiers。

像10_MODE这样的定义是错误的,应该改成MODE_10


9、#ifdef与#if defined()区别

区别在于后者可以组成复杂的预编译条件,比如:

#if defined(AAA) & defined(BBB)

xxxxx

#elif defined(CCC)

xxxxx

#endif

而#ifdef就不能用上面的用法,只能判断单个宏是否定义。


10、likely与unlikely

在if else语句中根据预判使用likely与unlikely宏

#define likely __builtin_expect((x), 1)

#define unlikely __builtin_expect((x), 0)



11、offsetof

#include <stddef.h>

offsetof(structName, memberName);

举例:

struct af_alg_iv {

    __u32 ivlen;

    __u8 iv[0];

};

offsetof(struct af_alg_iv, iv); 计算得到iv的偏移量为4



12、BUILD_BUG_ON_ZEROBUILD_BUG_ON_NULL

在内核文件 include/linux/bug.h中

/* Force a compilation error if condition is true, but also produce a
   result (of value 0 and type size_t), so the expression can be used
   e.g. in a structure initializer (or where-ever else comma expressions
   aren't permitted). */
#define BUILD_BUG_ON_ZERO(e) (sizeof(struct { int:-!!(e); }))
#define BUILD_BUG_ON_NULL(e) ((void *)sizeof(struct { int:-!!(e); }))

BUILD_BUG_ON_ZERO(e) 表示的就是若表达式e结果为0,则编译通过,该宏的值也为0;若表达式e的结果不为0,则编译不通过。

BUILD_BUG_ON_NULL(e)用来在编译时断言e是否为NULL,若是,这个宏返回(void *)0 (即NULL,与第一个宏的区别);不为NULL时编译出错。


具体的分析见:http://www.cnblogs.com/hazir/p/static_assert_macro.html


 定义

1、unsigned int和int

http://blog.csdn.net/luckywang1103/article/details/18139677


2、typedef

typedef自定义函数指针类型

#include <stdio.h>
typedef int (*fp_t)(char c);

int f0(char c) { printf("f0, c = %c\n", c); return 0;}
int f1(char c) { printf("f1, c = %c\n", c); return 1;}

int main()
{
        int ret;
        fp_t fp;
        fp = f0;
        ret = fp('a');
        fp = f1;
        ret = fp('x');
        return 0;
}

运行结果:

f0, c=a

f1, c=x


typedef自定义函数类型

#include <stdio.h>
typedef int fp_t(char c);

int f0(char c) { printf("f0, c = %c\n", c); return 0;}
int f1(char c) { printf("f1, c = %c\n", c); return 1;}

int main()
{
        int ret;
        fp_t* fp;
        fp = f0;
        ret = fp('a');
        fp = f1;
        ret = fp('x');
        return 0;
}

运行结果:

f0, c=a

f1, c=x


另外说明一下:函数名是一个指针,fun *t = funA

                             定义一个函数指针类型   函数名



有typedef和没有typedef的区别

void (*Fun)(void);
定义了一个返回类型为void,参数为void的函数指针变量Fun,这个Fun变量是一个指针,指向函数在内存中的首地址
而typedef void (*Fun)(void);
typedef的功能是定义一个新类型,所以这里Fun就不是变量了,而是一个新的类型。可以用Fun来定义一个变量,Fun p;


3、volatile

需要申明为volatile的变量

硬件地址

多线程中共享的全局变量

中断服务程序和进程(线程)共享的全局变量




数据类型

在一个32位系统上

type bits values

char

8 -127 to 127
unsigned char 8 0 to 255
     
short 16 -32767 to 32767
unsigned short 16 0 to 65535
     
int 32 -2,147,483,647 to 2,147,483,647
unsigned int 32 0 to 4,294,967,295
     
long 32 -2,147,483,647 to 2,147,483,647
unsigned long 32 0 to 4,294,967,295
     
long long 64 -9,223,372,036,854,775,807 to 9,223,372,036,854,775,807
unsigned long long 64 0 to 18,446,744,073,709,551,615

__int128为128 bit长度的类型

gcc中long int一般是32 bit,long long int一般是64 bit,但是随着PC硬件的不同和gcc的不同实现可能会有不同



函数

1、access

int access(const char *filename, int amode);

amode参数为0时表示检查文件的存在性,如果文件存在,返回0,不存在,返回-1。

该函数还可以检查文件的其他属性。

06检查读写权限

04检查读权限

02检查写权限

01检查执行权限

00检查文件存在性

2、atexit

int atexit(void (*func)(void));

#include<stdlib.h>

注册终止函数,即main函数结束后调用的函数。

一个进程可登记多达32个函数,这些函数将由exit自动调用。

atexit注册的函数类型应为不带任何参数的void函数,exit调用这些注册函数的顺序与注册他们的顺序相反。同一个函数如若登记多次,也会被调用多次。

实例:

#include <stdio.h>

#include <stdlib.h>

void exit_fn1(void)

{

    printf("Exit function #1 called\n");

}

void exit_fn2(void)

{

    printf("Exit function #2 called")

}

int main()

{

    atexit(exit_fn1);

    atexit(exit_fn2);

    return 0;

}

输出结果:

Exit function #2 called

Exit function #1 called

3、strdup

char *strdup(char *str)

 strdup拷贝一个字符串的副本并返回,这个副本有自己的内存空间,和str不相干。
使用完一定要释放这个函数中动态申请的内存。
 
#include <stdio.h>
#include <string.h>
#include <alloc.h>
int main()
{
char *dup_str, *string = "abcde";
dup_str = strdup(string);
printf("%s\n", dup_str);
free(dup_str);
 
return 0;
}
 
 

4、strchr

char *strchr(char * __str, int __ch)
头文件:#include<string.h>
功能:查找字符串str中首次出现ch的位置,并返回首次出现ch的位置的指针。

5、fcntl

fcntl(fd, F_SETFD, FD_CLOEXEC);
fcntl设置FD_CLOEXEC标志,意味着close on exec, not fork
如果对描述符设置了FD_CLOEXEC,使用execl执行的程序里,此描述符被关闭,不能在使用它,但是在使用fork调用的子进程中,此描述符并不关闭,仍可以使用。


6、assert

详细介绍的链接: http://blog.csdn.net/luckywang1103/article/details/42151729


7、回调函数

http://blog.csdn.net/luckywang1103/article/details/18516213


8、函数指针与指针函数

http://blog.csdn.net/luckywang1103/article/details/10179349


9、system

system只是把命令执行完了,返回值是执行完就是ok,被中断就是fail,而不是命令执行的结果



其他

1、错误处理方法

http://blog.csdn.net/luckywang1103/article/details/17686985

2、segment fault

http://blog.csdn.net/luckywang1103/article/details/12893337


3、\t 制表符

分为水平制表符\t和垂直制表符\v(不常用)
水平制表符占8列,如果字符个数不足8个,则制表符用空格补充为8个输出;如果字符个数正好8个,则制表符另外补充8个空格输出



4、malloc与内存池

malloc是分配虚拟地址空间,如果不memset或者bzero,那么就不会分配实际的物理页面。

内存池的作用看名字也能猜到,""意味着资源是统一管理和创建释放的,就像数据库的连接池、系统的线程池。主要就是为了避免创建、销毁资源的代价。c标准的malloc/free会造成大量的内存碎片以至于影响效率,所以“内存池”的技术某种程度上避免了这种消耗和影响。


5、NULL,'\0',0在数值上都是0






猜你喜欢

转载自blog.csdn.net/luckywang1103/article/details/44157387