快速复习C语言语法(TCPL)

一篇快速复习C语言语法的小文。

走马观花

  • C语言和Java语言一样,变量使用前需要先声明,但是由于Java特有的初始化机制,使得声明的语句并非一定要显式摆放在执行的方法之前,但C语言在调用函数之前需要显式声明。
1
2
3
4
5
6
7
8
9
10
11
int a; // 声明字段a
void method01(); // 声明方法method01
int main() {
method01();
printf("%d", a);
}
void method01() {
printf("Hello!\n");
}
  • int和float等类型的取值范围取决于机器。

  • #define在预编译阶段将符号名或者符号常量定义为特定的字符串,指令的末尾没有分号。

  • C语言的函数参数通过值传递。

  • 自动变量对标Java中局部变量,每次entry函数时都需要显式为其赋值,如果没有,那么存放的将是“garbage”。

  • 外部变量指的是函数外的变量,可以在函数内显式声明,即用“extern”关键字,也可以通过上下文隐式声明,即比如定义在使用函数之前就可以不使用“extern”关键字。
  • 通常习惯把extern声明的变量和函数放在单独的一个文件中,该文件成为头文件。

类型、操作符和表达式

  • C中的const对标Java中的final,但是和Java又有区别,例如如果用const修饰数组,那么数组中的元素就不能改变了,而且在声明的同时就应该初始化赋值:
1
2
3
4
5
int main() {
const char i[] = "abc";
// i[0] = 'c'; 这里修改值是错误的做法
printf("%s", i);
}
  • 下划线在C中被看作是字母,但是通常不应以下划线开头来命名变量或函数,因为library routines常常使用这样的方式命名。
  • C的字符串和Java的相比,最后多了一个\0字符,程序是需要扫描整个字符串才能确定字符串的长度,该长度不包括\0字符。
  • 取模运算符%不能作用于float活着double类型。
  • C没有指定char类型的变量是无符号还是带符号变量,这个由不同的机器决定。
  • 全局变量 、静态变量初始值为0,局部变量,自动变量如果没有初始化值随机分配(“garbage”)。

函数

  • 和Java不同,C中函数的定义中各个部分都可以省略,比如dumy() {}的书写是合法的,这种函数可以作为钩子函数在开发期间进行保留。
  • 外部变量或函数的作用域和Java中的各种变量的相比并不相同,它是从声明的地方开始,在其所以在的文件的末尾结束。
  • 静态变量只供所在源文件的函数使用,其他函数不能访问。
  • 静态变量也可以存在于函数内,这和局部变量一样,但它不仅是该函数私有的,而且无论函数调用是否结束,它仍然占据内存空间。所以静态变量和外部变量相比,能让编码者更细腻地控制作用范围。
  • 寄存器变量只适用于自动变量以及函数的形参。
  • 寄存器变量的地址不能访问,且过量使用没有什么害处。
  • 外部变量和静态变量在没有显式初始化的时候都被初始化为0,而自动变量和寄存器变量不会,它们的值是“garbage”。

指针和数组

结构体

结构体可以视为一种数据结构,它集合了一个或者多个变量,这些变量可以是不同类型的。

结构体的形式一般如下:

1
struct [<structuretag>] {}

structuretag是可选的,可以作为括号内声明部分的简写形式。

结构体声明

在通过struct <structuretag> {...} <var>进行声明后,可以用struct <structuretag> <var>进行简化声明,例如:

1
2
3
4
5
6
struct s {
char number[3];
int age;
} ss;
struct s dd;

结构体与函数

给函数传入一个结构体参数很像Java中的引用传递,毕竟很多人可能将结构体类比为Java中的类或对象,但结构体依然是值传递,例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <stdio.h>
struct s {
char number[3];
int age;
};
struct s dd;
void changeStruct(struct s);
int main() {
printf("age01: %d\n", dd.age);
changeStruct(dd);
printf("age02: %d\n", dd.age);
return 0;
}
void changeStruct(struct s a1) {
a1.age = 7;
}

打印为:

1
2
age01: 0
age02: 0

结构体会在函数内整个复制,如果该结构体很大,那么采用这种方式效率较低,好的方法是使用指针。

使用指针时首先要注意的是*的优先级低于.,所以*p.x(*p).x是不一样的。

其次,指针的使用频率很高,所以可以采用一种简写方式p->x来代替前面(*p).x的写法。

结构体数组

借助结构体,可以将多个长度相同的数组构成的集合,改写成数组相同索引下数据的集合(结构体)构成的数组。

初始化的格式:

1
2
3
4
struct arr {
char *word;
int count;
} keytab[] = {"aaa", 1, "bbb", 2};

如果要求结构体数组的长度,可以使用sizeof函数,它会返回无符号整型值,类型为size_t。

sizeof接结构体参数时,可以采用sizeof(struct )的形式。

预处理器不对类型名进行分析,但预处理器并不计算#define里的表达式,所以可以用#define搭配sizeof函数使用。

指向结构体指针

这里要注意的是,指针的加法运算是非法的,而减法是合法的。

其次,C中的结构体内存在字节对齐现象,所以结构体的长度未必等于它成员长度只和。

再次,返回结构体指针的函数,可以用如下格式来书写:

1
2
struct key *
binsearch(char *word, struct key *tab, int n)

typedef

typedef的功能一言以蔽之:“it merely adds a new name for some existing type”。

下面举一个最简单的typedef例子来说明:

1
typedef int Len;

此时Len和int具有等同意涵。这里最好遵照一定的规范,即typedef定义的类型名首字母大写。

编写了一个函数如下:

1
2
3
int method(int a, int b) {
return a + b;
}

现在假设需要声明一个变量,它的类型是指向函数的指针,该函数是这样一种函数:参数是(int, int),返回值是int,该指针变量指向之前编写的method函数,所以有:

1
int (*methodref02)(int, int) = method;

总是这样写相较麻烦,为这种类型创建一个新名称吧:

1
typedef int (*methodref)(int, int);

此时之前的methodref02可以这样定义:

1
methodref methodref02 = method;

联合体

联合体开辟的内存,等于它所有成员中所占内存最大的成员的内存。读取的类型必须是最近一次存入的类型,初始化的时候使用第一个成员类型的值进行初始化,其它操作大体和结构体相同。

1
2
3
4
5
6
7
8
9
int main() {
union u {
int val01;
long val02;
} a = {1};
a.val01 = 7;
a.val02 = 12;
printf("%zu %i %li\n", sizeof(a), a.val01, a.val02);
}

打印结果:

1
8 12 12

位字段

位字段一个主要的作用就是节约内存,在Java中boolean类型的变量在虚拟机占用了一个int的大小,也就是四个字节。实际上,原本一个bit就能完成同样功能。Java这样做当然有它自己的实现机理,而在C语言中,为了节约内存采用了一种新的手段,即位字段。来看一个例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
int main() {
struct Bitfield {
unsigned int a : 1;
unsigned int b : 2;
unsigned int c : 20;
} bf;
bf.a = 1;
bf.b = 2;
bf.c = 503432;
int testBitfield = 3;
printf("%d\n", testBitfield & (bf.b | bf.a));
printf("%d\n", bf.c);
printf("unsigned int字节数:%zu\n", sizeof(bf));
}

运行结果:

1
2
3
3
503432
unsigned int字节数:4

a占了一比特,b占了2比特,c占了20比特,如果单独声明,它们可能会占用更多的内存,但是现在仅仅一个unsigned int大小的内存就能搞定,也就是4字节。

小结

以上就是本人针对Java和C的语法区别做的一个记录,旨在复习C语法。

参考

The C Programming Language