博主之前主要从事Java开发,现在因为工作需要对C语言进行学习,所以此摘要主要目的是记录C与java的不同之处,方便Java开发者快速掌握C语言。持续更新...
- 关键字
- 指针[1]
- 函数指针[2]
- 字符串[3]
- 结构体[4]
- 位域[5]
- 共用体
关键字
关键字 | 说明 |
---|---|
unsigned | 声明无符号类型变量或函数 |
signed | 声明有符号类型变量或函数 |
sizeof | 计算数据类型或变量长度(即所占字节数) |
typedef | 用以给数据类型取别名 |
register | 声明寄存器变量 |
指针
连字号(&)运算符用于获取变量的内存地址
定义:指针是一个变量,其值为另一个变量的地址
空指针:赋为 NULL 值的指针
为指针变量赋一个 NULL 值是一个良好的编程习惯
NULL 指针是一个定义在标准库中的值为零的常量(ptr=0)。检查空指针使用:if(ptr) /if(!ptr)
地址为 0 的内存是操作系统保留的。内存地址 0 有特别重要的意义,它表明该指针不指向一个可访问的内存位置。如果指针包含空值(零值),则假定它不指向任何东西。
- 重要概念
概念 | 描述 |
---|---|
指针的算术运算 | 可以对指针进行四种算术运算:++、--、+、- |
指针数组 | 可以定义用来存储指针的数组。 |
指向指针的指针 | C 允许指向指针的指针。 |
传递指针给函数 | 通过引用或地址传递参数,使传递的参数在调用函数中被改变。(注意:数组名也是指针) |
从函数返回指针 | C 允许函数返回指针到局部变量、静态变量和动态内存分配。(C 语言不支持在调用函数时返回局部变量的地址,除非定义局部变量为 static 变量) |
函数指针
- typedef int (*fun_ptr)(int,int); // 声明一个指向同样参数、返回值的函数指针类型
- int (* p)(int, int) = & max; // &可以省略
回调函数(即函数作为参数的功能)
回调函数是由别人的函数执行时调用你实现的函数。
函数指针变量可以作为某个函数的参数来使用的,回调函数就是一个通过函数指针调用的函数。
字符串
在 C 语言中,字符串实际上是使用 null 字符 '\0' 终止的一维字符数组
C 中有大量操作字符串的函数:
序号 | 函数 & 目的 |
---|---|
1 | strcpy(s1, s2);复制字符串 s2 到字符串 s1。 |
2 | strcat(s1, s2);连接字符串 s2 到字符串 s1 的末尾。 |
3 | strlen(s1);返回字符串 s1 的长度。 |
4 | strcmp(s1, s2);如果 s1 和 s2 是相同的,则返回 0;如果 s1 |
5 | strchr(s1, ch);返回一个指针,指向字符串 s1 中字符 ch 的第一次出现的位置。 |
6 | strstr(s1, s2);返回一个指针,指向字符串 s1 中字符串 s2 的第一次出现的位置。 |
C结构体
- 定义结构体,struct 语句的格式:
struct tag {
member-list
member-list
member-list
...
} variable-list ;
- 在一般情况下,tag(结构体标签)、member-list(标准的变量定义)、variable-list(结构变量) 这 3 部分至少要出现 2 个
//此声明声明了拥有3个成员的结构体,分别为整型的a,字符型的b和双精度的c
//同时又声明了结构体变量s1
//这个结构体并没有标明其标签
struct
{
int a;
char b;
double c;
} s1;
//此声明声明了拥有3个成员的结构体,分别为整型的a,字符型的b和双精度的c
//结构体的标签被命名为SIMPLE,没有声明变量
struct SIMPLE
{
int a;
char b;
double c;
};
//用SIMPLE标签的结构体,另外声明了变量t1、t2、t3
struct SIMPLE t1, t2[20], *t3;
//也可以用typedef创建新类型
typedef struct
{
int a;
char b;
double c;
} Simple2;
//现在可以用Simple2作为类型声明新的结构体变量
Simple2 u1, u2[20], *u3;
在上面的声明中,第一个和第二声明被编译器当作两个完全不同的类型,即使他们的成员列表是一样的,如果令 t3=&s1,则是非法的。
结构体的成员可以包含其他结构体,也可以包含指向自己结构体类型的指针,而通常这种指针的应用是为了实现一些更高级的数据结构如链表和树等。
如果两个结构体互相包含,则需要对其中一个结构体进行不完整声明,如下所示:
struct B; //对结构体B进行不完整声明
//结构体A中包含指向结构体B的指针
struct A
{
struct B *partner;
//other members;
};
//结构体B中包含指向结构体A的指针,在A声明完后,B也随之进行声明
struct B
{
struct A *partner;
//other members;
};
- 结构体变量的初始化
struct Books
{
char title[50];
char author[50];
char subject[100];
int book_id;
} book = {"C 语言", "RUNOOB", "编程语言", 123456};
- 访问结构成员,使用成员访问运算符(.),使用 struct 关键字来定义结构类型的变量
#include
#include
struct Books
{
char title[50];
char author[50];
char subject[100];
int book_id;
};
int main( )
{
struct Books Book1; /* 声明 Book1,类型为 Books */
struct Books Book2; /* 声明 Book2,类型为 Books */
/* Book1 详述 */
strcpy( Book1.title, "C Programming");
strcpy( Book1.author, "Nuha Ali");
strcpy( Book1.subject, "C Programming Tutorial");
Book1.book_id = 6495407;
/* Book2 详述 */
strcpy( Book2.title, "Telecom Billing");
strcpy( Book2.author, "Zara Ali");
strcpy( Book2.subject, "Telecom Billing Tutorial");
Book2.book_id = 6495700;
/* 输出 Book1 信息 */
printf( "Book 1 title : %s\n", Book1.title);
printf( "Book 1 author : %s\n", Book1.author);
printf( "Book 1 subject : %s\n", Book1.subject);
printf( "Book 1 book_id : %d\n", Book1.book_id);
/* 输出 Book2 信息 */
printf( "Book 2 title : %s\n", Book2.title);
printf( "Book 2 author : %s\n", Book2.author);
printf( "Book 2 subject : %s\n", Book2.subject);
printf( "Book 2 book_id : %d\n", Book2.book_id);
return 0;
}
可以把结构作为函数参数,传参方式与其他类型的变量或指针类似
定义指向结构的指针: struct Books *struct_pointer;
使用指向该结构的指针访问结构的成员: struct_pointer->title;
位域
定义:有些信息在存储时,并不需要占用一个完整的字节,而只需占几个或一个二进制位。例如在存放一个开关量时,只有 0 和 1 两种状态,用 1 位二进位即可。为了节省存储空间,并使处理简便,C 语言又提供了一种数据结构,称为"位域"或"位段"。
所谓"位域"是把一个字节中的二进位划分为几个不同的区域,并说明每个区域的位数。每个域有一个域名,允许在程序中按域名进行操作。这样就可以把几个不同的对象用一个字节的二进制位域来表示。
- 位域定义:data 为 bs 变量,共占两个字节。其中位域 a 占 8 位,位域 b 占 2 位,位域 c 占 6 位。
struct bs{
int a:8;
int b:2;
int c:6;
}data;
- 一个位域存储在同一个字节中,如一个字节所剩空间不够存放另一位域时,则会从下一单元起存放该位域。也可以有意使某位域从下一单元开始。例如:
#在这个位域定义中,a 占第一字节的 4 位,后 4 位填 0 表示不使用,b 从第二字节开始,占用 4 位,c 占用 4 位。
struct bs{
unsigned a:4;
unsigned :4; /* 空域 */
unsigned b:4; /* 从下一单元开始存放 */
unsigned c:4
}
位域不允许跨两个字节,因此长度不能超过8位二进位。如果最大长度大于计算机的整数字长,一些编译器可能会允许域的内存重叠,另外一些编译器可能会把大于一个域的部分存储在下一个字中。
位域可以是无名位域,这时它只用来作填充或调整位置。无名的位域是不能使用的。例如:
struct k{
int a:1;
int :2; /* 该 2 位不能使用 */
int b:3;
int c:2;
};
从以上分析可以看出,位域在本质上就是一种结构类型,不过其成员是按二进位分配的。
位域的使用和结构成员的使用相同:位域变量名.位域名 或 位域变量名->位域名
实例:
main(){
struct bs{
unsigned a:1;
unsigned b:3;
unsigned c:4;
} bit,*pbit;
bit.a=1; /* 给位域赋值(应注意赋值不能超过该位域的允许范围) */
bit.b=7; /* 给位域赋值(应注意赋值不能超过该位域的允许范围) */
bit.c=15; /* 给位域赋值(应注意赋值不能超过该位域的允许范围) */
printf("%d,%d,%d\n",bit.a,bit.b,bit.c); /* 以整型量格式输出三个域的内容 */
pbit=&bit; /* 把位域变量 bit 的地址送给指针变量 pbit */
pbit->a=0; /* 用指针方式给位域 a 重新赋值,赋为 0 */
pbit->b&=3; /* 使用了复合的位运算符 "&=",相当于:pbit->b=pbit->b&3,位域 b 中原有值为 7,与 3 作按位与运算的结果为 3(111&011=011,十进制值为 3) */
pbit->c|=1; /* 使用了复合位运算符"|=",相当于:pbit->c=pbit->c|1,其结果为 15 */
printf("%d,%d,%d\n",pbit->a,pbit->b,pbit->c); /* 用指针方式输出了这三个域的值 */
}
上例程序中定义了位域结构 bs,三个位域为 a、b、c。说明了 bs 类型的变量 bit 和指向 bs 类型的指针变量 pbit。这表示位域也是可以使用指针的。
C共用体
- 定义:共用体是一种特殊的数据类型,允许您在相同的内存位置存储不同的数据类型
- 可以定义一个带有多成员的共用体,但是任何时候只能有一个成员带有值。共用体提供了一种使用相同的内存位置的有效方式。
- 实例
union Data
{
int i;
float f;
char str[20];
} data;
Data 类型的变量可以存储一个整数、一个浮点数,或者一个字符串
共用体占用的内存应足够存储共用体中最大的成员。可以使用sizeof(data)运算符查看共用体占用的总内存大小,该案例会显示20(字节)
- 访问共用体成员
为了访问共用体的成员,我们使用成员访问运算符(.)
实例
#include
#include
union Data
{
int i;
float f;
char str[20];
};
int main( )
{
union Data data;
data.i = 10;
data.f = 220.5;
strcpy( data.str, "C Programming");
printf( "data.i : %d\n", data.i);
printf( "data.f : %f\n", data.f);
printf( "data.str : %s\n", data.str);
return 0;
}
输出结果:
data.i : 1917853763
data.f : 4122360580327794860452759994368.000000
data.str : C Programming
结论: 共用体的 i 和 f 成员的值有损坏,因为最后赋给变量的str值占用了内存位置,因此共用体需要注意同一时间只能使用一个变量
C语言经典一百例
- 约瑟夫生者死者小游戏
30 个人在一条船上,超载,需要 15 人下船。
于是人们排成一队,排队的位置即为他们的编号。
报数,从 1 开始,数到 9 的人下船。
如此循环,直到船上仅剩 15 人为止,问都有哪些编号的人下船了呢?
- ↩
- ↩
- ↩
- ↩
- ↩