C部分语法


C语言解释

记录自己在看代码时遇到C语言的语法

C语言编译系统

  1. 预处理

    • 宏定义 #define
    • 文件包含 #include
    • 条件编译 #ifndef ······ #endif
  2. 编译

  3. 链接

.h和.c文件

.h文件是头文件,内含函数声明、宏定义、结构体定义等内容

.c文件是程序文件,内含函数实现,变量定义等内容。

#include xx.h 本质就是一些函数声明、宏定义、结构体定义等,只是它使用#include xx.h,这样就不用再每次重新写一遍这些声明了

反斜杠 \

C语言中反斜杠的作用是换行,为了好看的,阅读起来方便的:

  1. 字符串中换行

"this \

is \

for \

testing"

和"this is for testing"是相同的

  1. 宏定义中换行

在宏定义中要换行就必须使用 \ 结尾

#define CHECK_ACTION_RETURN(expr) \
if(!expr) { \
printf(":failed(%d)\n", ret); \
return ret; \
} else{ \
printf(":ok\n"); \
}
  1. 语句换行

在语句中换行作用不大,不写也没事

printf("this is for test %d %d %d\n",\

test_output_a,\

test_output_b,\

test_output_c); 
printf("this is for test %d %d %d\n",

test_output_a,

test_output_b,

test_output_c);

是完全没区别的

带 井号 #的那些东西

#include

#define 标识符 常量

  1. 凡是以“#”号开头的均是预处理指定。预编译又称预处理。预编译不是编译,而是编译前的处理。这个操作是在正式编译之前由系统自动完成的。
  2. 预处理指令不是语句,所以后面不能加分号。
  3. 预处理所执行的操作就是简单的“文本替换”。对宏定义而言,预编译时会将程序中所有出现“标识符”的地方全部用这个“常量”替换,称为“宏替换”或宏展开。替换完了之后再进行正式的编译。所以说当单击“编译”时实际上执行了两个操作,即先预编译,然后才正式编译。
  4. #include也是这样的,即在预处理的时候先单纯地用头文件stdio.h中所有的“文本”内容替换程序中#include这一行,然后再进行正式编译。

#define

#define 定义一个标识符来表示一个常量。也称宏定义,标识符为所定义的宏名,简称宏。宏所表示的常量可以是数字、字符、字符串、表达式。

语法:#define 标识符 常量

特点:定义的标识符不占内存,只是一个临时的符号,预编译后这个符号就不存在了。

作用:宏定义最大的好处就是方便程序的修改。使用宏定义可以用宏代替一个在程序中经常使用的常量。当需要改变这个常量的值时,就不需要对整个程序一个一个修改,只需要修改宏定义中的常量即可。且当常量比较长时,使用宏就可以用较短的有意义的标识符来代替它,这样变成时更方便,不会出错。

#ifndef #endif

#ifndef : if not define,它是预处理功能(宏定义、文件包含和条件编译)中的条件编译

#ifndef的目的是放置头文件的重复包含和编译。

假设在头文件xx.h里有很多宏定义,如果在两个C文件中都include了xx.h头文件,那么在编译时就会发生声明冲突。所以我们要把头文件的内容都放在#ifndef和#endif中。不管你的头文件是否会被多个文件引用,都要加上这个。

基本格式:

#ifndef<标识>

#define<标识>

············

#endif

标识的命名规则:一般是头文件名全大写,前后加下划线,并把文件名中的“.”也变成下划线。如stdio.h

#ifndef STDIO_H

#define STDIO_H

······

#endif

//c语言在对程序进行编译时,会先根据预处理命令进行预处理,C语言编译系统包括预处理,编译和链接等部分。

#ifndef X //先测试是否被宏定义过

#define X

程序段1//如果X没有被宏定义过,定义X,并编译程序段1;

#else

程序段2 //如果X已经定义过了则编译程序段2 的语句,忽视程序段1.

#endif

逻辑运算 与或非

  • 与 &&
  • 或 ||
  • 非 !

位运算符 与或非异或

  • 与 &
  • 或 |
  • 非 ~
  • 异或 ^

0的二进制是00

1的二进制是01

2的二进制是10

3的二进制是11

0&3 为假 0|3 为真

typedef

typedef是为现有的类型起一个别名,使使用起来更加方便,注意它并没有产生新的类型。

示例1:

typedef int BOOL;//为int型起了一个新的别名叫BOOL

#define TRUE 1
#define FALSE 0

BOOL flag = TRUE; 

示例2:结构体

typedef struct student{
	char cname[20];
	int iNumber;
	struct studeng *next;
}LinkList;

LinkList *head;

以上定义了一个新的结构体,并将结构体起了一个新的别名LinkList.

typedef struct用法

先从结构体说起

定义结构体方法1

//定义结构体
struct Student{
     int age;
     char s;
}

//创建结构体变量
struct Student st1;

定义结构体方法2

//定义结构体  给结构体Student定义了一个别名Stu
typedef struct Student
{
    int age;
    char s;
}Stu;

//定义结构体变量
Stu st1;

定义结构体方法3

typedef struct{
    int age;
    char s;
}Stu;

//定义结构体变量
Stu st1;

以上三种方法对比可以看到,第三种方法最方便。

static关键字

static关键字有隐藏的功能。

静态局部变量

  • 什么是局部变量?

局部变量就是在函数体里面定义的变量。编译器不会对局部变量初始化,除非对局部变量显式地赋值,否则局部变量的初始值是不确定的。

局部变量存储于进程栈空间,使用完毕就会释放

  • 什么是静态局部变量

静态局部变量是在函数体里面定义的变量,有static关键字修饰。静态局部变量存储在全局数据区,即使函数返回,它的值也会保持不变。即使没被赋初值,编译器会自动地给它初始化为0。

静态局部变量存储在全局数据区;

未赋初值时编译器会对其自动初始化为0;

函数使用完毕,值会保持不变。

示例:局部变量与静态局部变量对比

#include 

void fn(void)
{
    int n = 10;//局部变量n

    printf("n=%d\n", n);
    n++;
    printf("n++=%d\n", n);
}

void fn_static(void)
{
    static int n = 10;//静态局部变量n

    printf("static n=%d\n", n);
    n++;
    printf("n++=%d\n", n);
}

int main(void)
{
    fn();
    printf("--------------------\n");
    fn_static();
    printf("--------------------\n");
    fn();//当再次调用该函数时,局部变量值不会保留,n =10
    printf("--------------------\n");
    fn_static();//当再次调用该函数时,静态局部变量值上次结果为11,会保留上次的结果,所以n=11

    return 0;
}

执行结果:

n=10
n++=11
--------------------
static n=10
n++=11
--------------------
n=10
n++=11
--------------------
static n=11
n++=12

静态全局变量

  • 什么是全局变量?

全局变量就是定义在函数体外的变量,它存储在全局数据区,且编译器会自动对其初始化。对整个工程可见。其他文件可以使用extern外部声明后直接使用。其他文件不能再定义一个同名的变量了。

全局变量存储在全局数据区,编译器会自动对其初始化

对整个工程可见,其他文件可以使用extern外部声明后直接使用。

  • 什么是静态全局变量?

静态全局变量是定义在函数体外的变量,加上关键字static修饰。仅对当前文件可见,其他文件不可访问。所以其他文件可以有同名的静态全局变量,互不影响。

在定义不需要与其他文件共享的全局变量时,加上static关键字能够有效地降低程序模块之间的耦合,避免不同文件同名变量的冲突,且不会误使用。

静态函数

在函数的返回类型前加static,就是静态函数。

  • 静态函数只能在声明它的文件中可见,其他文件不能引用该函数
  • 不同的文件可以使用相同名字的静态函数,互不影响

非静态函数可以在另一个文件中直接引用,甚至不必使用extern声明。

示例:使用static声明的函数不能被另一个文件引用

/* file1.c */
#include 

static void fun(void)
{
    printf("hello from fun.\n");
}

int main(void)
{
    fun();
    fun1();

    return 0;
}

/* file2.c */
#include 

static void fun1(void)
{
    printf("hello from static fun1.\n");
}

返回结果:

/tmp/cc2VMzGR.o:在函数‘main’中:
static_fun.c:(.text+0x20):对‘fun1’未定义的引用
collect2: error: ld returned 1 exit status

修改文件,不使用static修饰符,可在另一文件中引用该函数:

/* file1.c */
#include 

void fun(void)
{
    printf("hello from fun.\n");
}

/* file2.c */
int main(void)
{
    fun();

    return 0;
}

返回结果:

-> % ./a.out 
hello from fun.
   
    

总结

函数体 当前文件 整个工程
局部变量
静态局部变量
全局变量
静态全局变量
函数
静态函数

结构体

在C编译器下,结构体内不允许包含static变量。

C中struct只是类型声明,没有内存空间的分配,而static变量是需要分配内存的。

========================================================================
未完待续。。。。。

C