标签:
1.C语言结构体的定义和使用
在实际问题中,一组数据往往具有不同的数据类型;例如在学生信息登记表中,姓名为字符型,学号为整型或字符型,年龄为整型,性别为字符型,成绩为整型或实型。因为数据类型不同,显然不能用一个数组来存放。
在C语言中,可以使用结构体(Struct)来存放一组不同类型的数据。定义结构体的一般形式为:
- struct stu{
- char *name; //姓名
- int num; //学号
- char sex; //性别
- float score; //成绩
- };
相应的,int、char、float 这些数据类型被称为基本数据类型。上面的代码定义了一个结构体,名字为stu。stu由4个成员组成,分别为 name、num、sex、score。
;
不能少。
struct stu stu1, stu2;定义了两个变量 stu1 和 stu2,它们都是 stu 类型,都由 4 个成员组成。注意关键字
struct
不能少。
- struct stu{
- char *name; //姓名
- int num; //学号
- char sex; //性别
- float score; //成绩
- } stu1, stu2;
- struct{ //没有写 stu
- char *name; //姓名
- int num; //学号
- char sex; //性别
- float score; //成绩
- } stu1, stu2;
- #define STU struct stu
- STU{
- char *name; //姓名
- int num; //学号
- char sex; //性别
- float score; //成绩
- };
- STU stu1, stu2;
.
来获取结构体中的一个成员,一般格式为:
- stu1.name; //第一个学生的姓名
- stu2.score; //第二个学生的成绩
- stu1.name = "Tom";
- stu2.score = 90.5;
- struct stu{
- char *name; //姓名
- int num; //学号
- char sex; //性别
- float score; //成绩
- } stu1, stu2 = { "Tom", 10, ‘M‘, 90 };
stu2 = { "Tom", 10, ‘M‘, 90 };
- #include <stdio.h>
- #define STU struct stu
- int main(){
- STU{
- char *name; //姓名
- int num; //学号
- char sex; //性别
- float score; //成绩
- };
- STU stu1;
- stu1.name = "James Bond";
- stu1.num = 1;
- stu1.sex = ‘M‘; //用M表示男性,W表示女性
- stu1.score = 99;
- printf("Hello everyone! My name is %s, a naughty boy, but with good scores(%.2f) and top No.(%d)!", stu1.name, stu1.score, stu1.num);
- return 0;
- }
2.C语言结构体数组
结构体数组的每一个元素都是结构体。在实际应用中,经常用结构体数组来表示一个拥有相同数据结构的群体,比如一个班的学生,一个车间的职工等。
定义结构体数组和定义结构体变量的方式类似,例如:
- struct stu{
- char *name;
- int num;
- char sex;
- float score;
- }class[5];
- struct stu{
- char *name;
- int num;
- char sex;
- float score;
- }class[5] = {
- {"Li ping", 5, ‘M‘, 45},
- {"Zhang ping", 4, ‘M‘, 62.5},
- {"He fang", 1, ‘F‘, 92.5},
- {"Cheng ling", 2, ‘F‘, 87},
- {"Wang ming", 3, ‘M‘, 58}
- };
class[4].score;【示例】计算学生的平均成绩和不及格的人数。
- #include <stdio.h>
- struct stu{
- char *name;
- int num;
- char sex;
- float score;
- }class[5] = {
- {"Li ping", 5, ‘M‘, 45},
- {"Zhang ping", 4, ‘M‘, 62.5},
- {"He fang", 1, ‘F‘, 92.5},
- {"Cheng ling", 2, ‘F‘, 87},
- {"Wang ming", 3, ‘M‘, 58}
- };
- int main(){
- int i, fail_num=0;
- float average, sum=0;
- for(i=0; i<5; i++){
- sum += class[i].score;
- if(class[i].score < 60) fail_num++;
- }
- printf("sum=%.2f\naverage=%.2f\nfail_num=%d\n", sum, sum/5, fail_num);
- return 0;
- }
3.C语言结构体和指针
指针也可以指向一个结构体变量。定义的一般形式为:
- struct stu{
- char *name;
- int num;
- char sex;
- float score;
- };
struct stu *pstu;当然在定义结构体的同时也可以定义 pstu:
- struct stu{
- char *name;
- int num;
- char sex;
- float score;
- } *pstu, stu1, stu2;
pstu = &stu1; pstu = &stu2;应该把结构体变量的首地址赋给 pstu,而不能把结构体名赋给 pstu,而且结构体变量前面要加取地址符&。下面的写法都是错误的:
pstu = &stu; pstu = stu1;结构体名和结构体变量是两个不同的概念,不能混淆。结构体名只能表示一个结构形式,是一种数据类型,编译器并不对它分配内存空间,就像 int、float 这些关键字本身不分配内存一样。只有当一个变量被定义为这种数据类型时,才对该变量分配内存空间。所以上面 &stu 这种写法是错误的,不可能去取一个结构体名的首地址。
int
a;
,a 不是首地址,而是表示它本身。(*pstu).num或者:
pstu->num注意
(*pstu)
两侧的括号不可少,因为成员符.
的优先级高于*
。如去掉括号写作*pstu.num
,那么等效于*(pstu.num)
,这样意义就完全不对了。
- #include <stdio.h>
- struct stu{
- char *name;
- int num;
- char sex;
- float score;
- } *pstu, stu1 = {"He fang", 1, ‘F‘, 92.5};
- int main(){
- pstu = &stu1;
- printf("Number=%d, Name=%s\n",stu1.num, stu1.name);
- printf("Sex=%c, Score=%f\n\n",stu1.sex, stu1.score);
- printf("Number=%d, Name=%s\n",(*pstu).num, (*pstu).name);
- printf("Sex=%c, Score=%f\n\n",(*pstu).sex, (*pstu).score);
- printf("Number=%d, Name=%s\n",pstu->num, pstu->name);
- printf("Sex=%c, Score=%f\n",pstu->sex, pstu->score);
- return 0;
- }
4.C语言结构体数组指针以及函数
- #include <stdio.h>
- struct stu{
- int num;
- char *name;
- char sex;
- float score;
- } *ps, boy[5]={
- {101, "Zhou ping", ‘M‘, 45},
- {102, "Zhang ping", ‘M‘, 62.5},
- {103, "Liou fang", ‘F‘, 92.5},
- {104, "Cheng ling", ‘F‘, 87},
- {105, "Wang ming", ‘M‘, 58}
- };
- int main(){
- printf("No\tName\t\tSex\tScore\t\n");
- for(ps=boy; ps<boy+5; ps++)
- printf("%d\t%s\t%c\t%f\t\n", ps->num, ps->name, ps->sex, ps->score);
- return 0;
- }
No Name Sex Score 101 Zhou ping M 45.000000 102 Zhang ping M 62.500000 103 Liou fang F 92.500000 104 Cheng ling F 87.000000 105 Wang ming M 58.000000
ps=&boy[1].sex;而只能是:
ps=boy; // 赋予数组首地址或者是:
ps=&boy[0]; //赋予0号元素首地址
- #include <stdio.h>
- #define STU struct stu
- STU{
- int num;
- char *name;
- char sex;
- float score;
- }boy[5]={
- {101,"Li ping",‘M‘,45},
- {102,"Zhang ping",‘M‘,62.5},
- {103,"He fang",‘F‘,92.5},
- {104,"Cheng ling",‘F‘,87},
- {105,"Wang ming",‘M‘,58}
- };
- void average(STU *ps);
- int main(){
- STU *ps = boy;
- average(ps);
- return 0;
- }
- void average(struct stu *ps){
- int flunk=0, i;
- float sum=0;
- for(i=0; i<5; i++,ps++){
- sum += ps->score;
- if(ps->score < 60) flunk += 1;
- }
- printf("sum=%.2f, average=%.2f, flunk=%d\n", sum, sum/5, flunk);
- }
5.C语言枚举类型
实际问题中,有些变量的取值被限制在一定范围内。例如,一个星期内只有七天,一年只有十二个月,一个班每周有六门课程等。
C语言提供了一种枚举(Enum)类型,可以列出所有可能的取值。定义形式为:
;
不能少。enum week{sun, mon, tue, wed, thu, fri, sat};和结构体一样,枚举变量可以先定义后说明,也可以在定义的同时说明,例如:
enum week a, b, c;或者:
enum week{sun, mon, tue, wed, thu, fri, sat} a, b, c;枚举值为常量,不是变量,不能赋值。枚举值默认从0开始,逐个加1。也就是说,上面的代码定义了7个常量,分别为 sun、mon…sat,它们的值分别为 0、1…6。
- #include <stdio.h>
- int main(){
- enum week{
- sun, mon, tue, wed, thu, fri, sat
- } a, b, c;
- a = sun;
- b = mon;
- c = tue;
- printf("%d, %d, %d\n",a,b,c);
- return 0;
- }
sun = 5; mon = 2;只能把枚举值赋予枚举变量,例如:
a = sun; b = sat;同时,不建议把数值直接赋给枚举变量,例如:
a = 1; b = 6;如果一定要使用数值,必须使用强制类型转换:
a = (enum week)1; b = (enum week)6;因为已经使用了 sun、mon…sat 几个标识符,所以不能再使用它们来定义变量等,例如:
int sun = 3; char mon;都是错误的。
6.C语言共同体(共用体)
共用体(共同体)的定义和结构体类似,不过结构体的各个成员都会分配相应的内存空间,而共用体的所有成员共享一段内存,它们的起始地址一样,并且同一时刻只能使用其中的一个成员变量。
共用体在实际开发中应用较少,你可以暂时跳过,需要时再来温习。共用体定义的一般格式为:
- //先定义共用体,再定义变量
- union data{
- int i;
- char ch;
- };
- data a, b, c;
- //定义共用体的同时定义变量
- union data{
- int i;
- char ch;
- } a, b, c;
- #include <stdio.h>
- union{
- int i;
- char c;
- }a;
- int main(){
- printf("Size of a: %d\n", sizeof(a));
- a.c=‘A‘; //此时共用体变量4个字节的取值情况为0x00000041
- printf("a.i = %d\n",a.i);
- a.i=0x42; //0x42为字母B的ASCII码
- printf("a.c = %c\n",a.c);
- return 0;
- }
7.C语言类型定义符typedef
C语言不仅提供了丰富的数据类型,还允许用户定义自己的数据类型。
定义数据类型使用 typedef 关键字,一般形式为:
- typedef int INTEGER;
- INTEGER a, b;
- a = 1;
- b = 2;
INTEGER
a, b;
等效于int
a, b;
。typedef char NAME[20];表示NAME是字符数组类型,长度为20。然后可用NAME 说明变量,如:
NAME a1, a2, s1, s2;完全等效于:
char a1[20], a2[20], s1[20], s2[20];又如:
- typedef struct stu{
- char name[20];
- int age;
- char sex;
- } STU;
STU body1,body2;
typedef (int*) PINT;2) 有时也可用宏定义来代替 typedef 的功能,但是宏定义是在预处理阶段完成的,而 typedef 是在编译时完成的。
8.C语言位运算
所谓位运算,就是对一个比特(Bit)位进行操作。在《二进制思想以及数据的存储》一节中讲到,比特(Bit)是一个电子元器件,8个比特构成一个字节(Byte),它已经是粒度最小的可操作单元了。
C语言提供了六种位运算符:
运算符 | & | | | ^ | ~ | << | >> |
---|---|---|---|---|---|---|
说明 | 按位与 | 按位或 | 按位异或 | 取反 | 左移 | 右移 |
&
运算的两个位都为1时,结果才为1,否则为0。例如1&1为1,0&0为0,1&0为0。9&5
可写算式如下:9&5=1
。
严格来说,数值在内存中以补码形式存在,整数的补码与它的二进制形式相同,负数则不一样,不了解的读者可自行脑补。按位与运算符
&
会对参与运算的两个数的所有二进制位进行&
运算。a&65535
运算(65536占用4个字节,二进制数为00000000000000001111111111111111)。
- #include <stdio.h>
- int main(){
- unsigned a=9; //二进制数 00001001
- unsigned b=5; //二进制数 00000101
- unsigned c=0XDE09A32B; //十进制数 3725173547
- unsigned d=0X0000FFFF; //十进制数 65535
- printf("a=%u, b=%u, a&b=%u\n", a, b, a&b);
- printf("c=%u, d=%u, c&d(%%d)=%u, c&d(%%X)=%X\n", c, d, c&d, c&d);
- return 0;
- }
|
的两个二进制位有一个为1时,结果就为1,两个都为0时结果才为0。例如1|1为1,0|0为0,1|0为1。9|5
可写算式如下:9|5=13
。
- #include <stdio.h>
- int main(){
- unsigned a=9; //二进制数 00001001
- unsigned b=5; //二进制数 00000101
- unsigned c=0XDE09A30B; //十进制数 3725173547
- unsigned d=0XFFFF0000; //十进制数 65535
- printf("a=%u, b=%u, a|b=%u\n", a, b, a|b);
- printf("c=%u, d=%u, c|d(%%d)=%u, c|d(%%X)=%X\n", c, d, c|d, c|d);
- return 0;
- }
^
的两个二进制位不同时,结果为1,相同时结果为0。也就是说,0^1为1,0^0为0,1^1为0。9^5
可写成算式如下:9^5=12
。
- #include <stdio.h>
- int main(){
- unsigned a=9; //二进制数 00001001
- unsigned b=5; //二进制数 00000101
- unsigned c=0X00FFFF00; //十进制数 3725173547
- unsigned d=0XFFFF0000; //十进制数 65535
- printf("a=%u, b=%u, a^b=%u\n", a, b, a^b);
- printf("c=%u, d=%u, c^d(%%d)=%u, c^d(%%X)=%X\n", c, d, c^d, c^d);
- return 0;
- }
~
为单目运算符,右结合性,作用是对参与运算的数的各二进位按位取反。例如
~1为0,~0为1。~9
的运算为:~9=65526
。<<
用来把操作数的各二进位全部左移若干位,高位丢弃,低位补0。例如:
a=9; a<<3;
<<
左边是要移位的操作数,右边是要移动的位数。>>
用来把操作数的各二进位全部右移若干位,低位丢弃,高位补0(或1)。例如:
a=9; a>>3;表示把a的各二进位向右移动3位。a=00001001(9的二进制),右移3位后为00000001(十进制1)。
- #include <stdio.h>
- int main(){
- unsigned c=0X00FFFF00; //十进制数 3725173547
- unsigned d=0XFFFF0000; //十进制数 65535
- printf("c=%X, d=%X, c^d(%%X)=%X, c|d(%%X)=%X, c>>4=%X, c<<8=%X\n", c, d, c^d, c|d, c>>4, c<<8);
- return 0;
- }
9.C语言位域(位段)
有些信息在存储时,并不需要占用一个完整的字节,而只需占几个或一个二进制位。例如开关只有通电和断电两种状态,用0和1表示足以,也就是用一个二进位。所以C语言又提供了一种数据结构,称为位域或位段。
位域在应用开发中较少使用,你可以暂时跳过,遇到相关问题再回来温习。所谓“位域”是把一个字节中的二进位划分为几个不同的区域,并说明每个区域的位数。每个区域有一个域名,允许在程序中按域名进行操作。
- struct bs{
- int a:8;
- int b:2;
- int c:6;
- };
struct bs data;或者:
- struct bs{
- int a:8;
- int b:2;
- int c:6;
- } data;
- struct bs{
- unsigned a:4;
- unsigned :0; //空域
- unsigned b:4; //从下一单元开始存放
- unsigned c:4
- }
- struct k{
- int a:1;
- int :2; //该2位不能使用
- int b:3;
- int c:2;
- };
- #include <stdio.h>
- int main(){
- struct{
- 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;
- pbit->a=0;
- pbit->b&=3;
- pbit->c|=1;
- printf("%d, %d, %d\n", pbit->a, pbit->b, pbit->c);
- return 0;
- }
&=
,该行相当于:
pbit->b=pbit->b&3;位域b中原有值为7,与3作按位与运算的结果为3(111&011=011,十进制值为3)。同样,程序第16行中使用了复合位运算符
|=
,相当于:
pbit->c=pbit->c|1;其结果为15。
10.用C语言对数据或文件内容进行加密
数据加密解密是一个常用的功能,如果你不希望让别人看到文件中的内容,可以通过密钥(也称”密码“)将文件的内容加密。比如文本文件(.txt),加密前的内容是能够读懂的,加密后的内容是”乱码“,都是一些奇怪的字符,根本无法阅读。
数据加密解密的原理也很简单,就是使用异或运算。请先看下面的代码:
- #include <stdio.h>
- #include <stdlib.h>
- int main(){
- char plaintext = ‘a‘; // 明文
- char secretkey = ‘!‘; // 密钥
- char ciphertext = plaintext ^ secretkey; // 密文
- char decodetext = ciphertext ^ secretkey; // 解密后的字符
- char buffer[9];
- printf(" char ASCII\n");
- // itoa()用来将数字转换为字符串,可以设定转换时的进制(基数)
- // 这里将字符对应的ascii码转换为二进制
- printf(" plaintext %c %7s\n", plaintext, itoa(plaintext, buffer, 2));
- printf(" secretkey %c %7s\n", secretkey, itoa(secretkey, buffer, 2));
- printf("ciphertext %c %7s\n", ciphertext, itoa(ciphertext, buffer, 2));
- printf("decodetext %c %7s\n", decodetext, itoa(decodetext, buffer, 2));
- return 0;
- }
char ASCII plaintext a 1100001 secretkey ! 100001 ciphertext @ 1000000 decodetext a 1100001看到了吗,plaintext 与 decodetext相同,也就是说,两次异或运算后还是原来的结果。
11.C语言贪吃蛇游戏源码下载、源码解析和设计思路
在《C语言贪吃蛇游戏演示和说明》一节中,我们对贪吃蛇游戏的玩法进行了介绍和演示,这节就来分析一下它的源码。
贪吃蛇源代码下载地址:http://pan.baidu.com/s/1bnwJB8V
提取密码:81qm
各位读者不妨先将源码下载下来浏览一遍,记住关键的几个函数,整理一下不了解的知识点,做到心中有数。
需要说明的是:贪吃蛇背景地图、食物、贪吃蛇本身都是由特殊字符组成(由 printf() 输出),并不是绘制出来的图形。C语言标准库没有绘图函数,如果绘图的话,就需要使用第三方库,增加了大家的学习成本,所以我们采用了“投机取巧”的办法,用特殊字符来模拟不同的图形。
- #include <stdio.h>
- #include <conio.h>
- #include <windows.h>
- int main(){
- int width = 30, height = width; //宽度和高度
- int x, y; //x、y分别表示当前行和列
- HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
- //设置窗口大小
- system("mode con: cols=64 lines=32");
- //打印背景,按行输出
- for(x=0; x<width; x++){
- for(y=0; y<height; y++){
- if(y==0 || y==width-1 || x==0 || x==height-1){ //输出边框
- SetConsoleTextAttribute(hConsole, 4 );
- printf("□");
- }else{ //贪吃蛇活动区域
- SetConsoleTextAttribute(hConsole, 2 );
- printf("■");
- }
- }
- printf("\n");
- }
- //暂停
- getch();
- return 0;
- }
注意,□和■虽然都是单个字符,但它们不在ASCII码范围内,是宽字符,占用两个字节,用 putchar 等输出ASCII码(一个字节)的函数输出时可能会出现问题,所以作为字符串输出。
- struct POS{
- int x; //所在行
- int y; //所在列
- }
struct POS snakes[n+m];并设置两个变量 headerIndex、tailIndex,分别用来表示蛇头、蛇尾在数组中的下标坐标,这样每次添加蛇头、删除蛇尾时只需要改变两个变量的值就可以。如下图所示:
(HEIGHT-2)
* (WIDTH-2)
。coord.X = 2*y;
- struct{
- char type;
- int index;
- }globalMap[MAXWIDTH][MAXHEIGHT];
直观上讲,应该将 type 定义为int类型,不过int占用四个字节,而节点类型的取值范围非常有限,一个字节就足够了,所以为了节省内存才定义为char类型。然后再定义一个一维的结构体数组,用来保存贪吃蛇的有效活动范围:
- struct{
- int x;
- int y;
- } snakeMap[ (MAXWIDTH-2)*(MAXHEIGHT-2) ];
需要注意的是,在贪吃蛇移动过程中需要维护 globalMap 和 snakeMap 的对应关系。这种方案的另外一个优点就是,贪吃蛇移动时很容易知道下一个节点的类型,不用遍历数组就可以知道是否与自身相撞。
标签:
原文地址:http://blog.csdn.net/shuimanting520/article/details/51415711