首页 > 其他分享 >C语言小知识点总结

C语言小知识点总结

时间:2022-11-29 23:32:28浏览次数:41  
标签:总结 知识点 int void C语言 fp __ printf type


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)错误!

 

#define debug(…) printf(__VA_ARGS__)

缺省号代表一个可以变化的参数表。使用保留名 __VA_ARGS__ 把参数传递给宏。当宏的调用展开时,实际的参数就传递给 printf()了。

 

2、可变参数va_start, va_arg, va_end

 

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

 

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_ZERO和BUILD_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时编译出错。

 

 

 定义

1、unsigned int和int

 

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

7、回调函数

8、函数指针与指针函数

9、system

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

 

其他

1、错误处理方法

2、segment fault

3、\t 制表符

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

4、malloc与内存池

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

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

 

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

 

6. !!

两个!是为了把非0值转换成1,而0值还是0。

在C语言中,所以非0值都表示真。所以!非0值 = 0,而!0 = 1。

所以!!非0值 = 1,而!!0 = 0。

 

7. 结构体向前声明

如果结构体定义如下:

typedef struct tag_test
{
ULONGLONG utime;
ULONGLONG umac;
}test;

你在申明的时候就必须申明为:

typedef struct tag_test test;

如果结构体定义为:

struct test
{
ULONGLONG utime;
ULONGLONG umac;
};

就需要这样声明:

struct test;

 

标签:总结,知识点,int,void,C语言,fp,__,printf,type
From: https://blog.51cto.com/u_15854579/5897269

相关文章