由于C语言学的太少,所以这周任务是补一补C语言。给自己规定了几件事:
- 看指针、结构体。
- 找例题自己敲并思考和扩展
- 做C语言课本上的练习题
视频重要知识点:
*
号的三种含义:
- 乘法
- 定义指针变量:
int * p;
定义一个名字为p的指针变量,int*表示p只能存放int型变量的地址。 - 放在已经定义好的变量前面:如果p是一个已经定义好的变量,则*p表示以p的内容为地址的变量
- 通过被调函数修改主调函数的值:
- 实参必须为该普通变量的地址
- 形参必须是指针变量,且以对应实参变量的的地址为内容
- 被调函数中通过
* 形参名 = ......
的方式就可以修改主调函数相关变量的值
- 一维数组名是个指针常量,存放该一维数组的第一个元素的地址。
- 数组下标与指针的关系:如果p是一个指针变量,则p[i]等价于*(p+i)
一维数组是一个线形表,它被存放在一片连续的内存单元中。C语言对数组的访问是通过数组名(数组的起始地址)加上相对于起始地址的相对量(由下标变量给出),得到要访问的数组元素的单元地址,然后再对计算出的单元地址的内容进行访问。通常把数据类型所占单元的字节个数称为扩大因子。实际上编译系统将数组元素的形式a[i]转换成*(a+i),然后才进行运算。对于一般数组元素的形式:<数组名>[<下标表达式>],编译程序将其转换成:*(<数组名>+<下标表达式>),其中下标表达式为:下标表达式*扩大因子。整个式子计算结果是一个内存地址,最后的结果为:*<地址>=<地址所对应单元的地址的内容>。由此可见,C语言对数组的处理,实际上是转换成指针地址的运算。
用指针变量可以指向一维数组,也可以指向多维数组。但在概念上和使用上,多维数组的指针比一维数组的指针要复杂一些。例如,在一个三维数组中,引用元素
c[i][j][k]
的地址计算最终将换成:*(*(*(c+i)+j)+k)
。了解了多维数组的存储形式和访问多维数组元素的内部转换公式后,再看当一个指针变量指向多维数组及其元素的情况。
- 结构体的定义格式:
struct tag {
member-list
member-list
member-list
...
} variable-list ;
- 要定义结构,必须使用 struct 语句。struct 语句定义了一个包含多个成员的新的数据类型。
- tag 是结构体标签。
- member-list 是标准的变量定义,比如 int i; 或者 float f,或者其他有效的变量定义。
- variable-list 结构变量,定义在结构的末尾,最后一个分号之前,您可以指定一个或多个结构变量。
- 访问结构体成员的方法:
- 使用成员访问运算符(.),即结构体变量名.成员名。例如
#include<stdio.h>
struct student {
int CHINESE;
int MATH;
};
int main() {
struct student st;
st.CHINESE = 91;
st.MATH = 79;
printf("%-5d %-5d",st.CHINESE,st.MATH);
return 0;
}
//结果为:91 97
- 使用指向结构体的指针(->),即指针变量名->成员名。例如:
#include<stdio.h>
struct student {
int CHINESE;
int MATH;
};
int main() {
struct student st = {99,90};
struct student * p = &st;
p -> MATH;
printf("%d",*p);
return 0;
}
//结果为:90
//p -> MATH 在计算机内部会被转化为(*p).MATH,即st.MATH。
//p -> MATH的含义是:pst指向的结构体变量中的成员MATH。
- 定义共用体:
union [union tag]
{
member definition;
member definition;
...
member definition;
} [one or more union variables];
- 定义共用体,必须使用 union 语句
- [union tag]和[one or more union variables]两个部分是可选的
- 每个 member definition 是标准的变量定义,比如 int i; 或者 float f; 或者其他有效的变量定义。
- 结构体与共用体的区别:
共用体:
共用体内存长度是内部最长的数据类型的长度。
共用体的地址和内部各成员变量的地址都是同一个地址
结构体:
结构体内部的成员,大小等于最后一个成员的偏移量+最后一个成员大小+末尾的填充字节数。
结构体的偏移量:某一个成员的实际地址和结构体首地址之间的距离。
结构体字节对齐:每个成员相对于结构体首地址的偏移量都得是当前成员所占内存大小的整数倍,如果不是会在成员前面加填充字节。结构体的大小是内部最宽的成员的整数倍。
- 结构体变量的运算:
结构体变量不能相加减,也不能互相乘除。但结构体变量可以相互赋值。
敲代码笔记:
- C标准库之一 – <ctype.h>
C 标准库的 ctype.h 头文件提供了一些函数,可用于测试和映射字符。
这些函数接受 int 作为参数,它的值必须是 EOF 或表示为一个无符号字符。
如果参数 c 满足描述的条件,则这些函数返回非零(true)。如果参数 c 不满足描述的条件,则这些函数返回零。
<ctype.h>中包含有int tolower(int c)
【该函数把大写字母转换为小写字母】 和int toupper(int c)
【该函数把小写字母转换为大写字母】 等函数。
菜鸟教程:https://www.runoob.com/cprogramming/c-standard-library-ctype-h.html
- sizeof()
sizeof是C语言的一种单目操作符,如C语言的其他操作符++、–等。它并不是函数。sizeof操作符以字节形式给出了其操作数的存储大小。操作数可以是一个表达式或括在括号内的类型名。操作数的存储大小由操作数的 类型决定。
sizeof的使用方法:
(1) 用于数据类型 sizeof使用形式:sizeof(type)
;
注:数据类型必须用括号括住。如sizeof(int)。
2、用于变量 sizeof 使用形式:sizeof(var_name)
或sizeof var_name ;
变量名可以不用括号括住。如sizeof(var_name),sizeof var_name等都是正确形式。带括号的用法更普遍, 大多数程序员采用这种形式。
- 关于一维数组a,区分
*(&a+5)
和*(a+5)
,定义了一个整型数组a[5],则如图所示:
再用代码试例:
#include "stdio.h"
int main()
{
int a[5]={1,2,3,4,5};
int *ptr=(int *)(&a+1);
printf("%d,%d,%d",*(a+1),*(ptr-1),*ptr);
return 0;
}
//输出结果是:2,5,0
- 关于数组与指针
数组名其实是一个指向(二维以上维数组)0号分数组或者(一维数组)0号元素的指针常量,存储数组0号分数组或者0号元素的地址。
"[]
"是变址运算符,相当于*( + )
,例如b[j]
相当于*(b+j)
,*(*(a+i)+j)
相当于a[i][j]
。
对于一个数组a[3]
,a 和 a[0] 的地址字节值是相同的,但这两者并不等价,它们的基类型字节数不同。
- 指针数组与数组指针
指针数组的定义样式为:基类型名 *指针变量名[长度]
数组指针的定义样式为:基类型名 (*指针变量名)[长度]
指针数组其实就是一个由多个指针变量组成的数组,数组里每一个元素就是一个指针。
数组指针其实是定义了一个指针,这个指针指向一维数组,它可以用来指向分数组,例如int (*p)[10];
指针变量p就可以指向一个长度为十的分数组分数组。
#include<stdio.h>
int main() {
int b[3][5] = {{31,32,33,24,25},{16,17,8,9,10},{19,11,51,14,15}};
int *q; //*q是指向整型的指针变量,可以用来指向元素
int (*P)[5]; //*p是指向一维数组的指针变量,可以用来指向分数组
for(p = b;p < b + 3;p++) { //用p指向各行数组
for(q = *p;q < *p;q++) { //用*p指向各行分数组的首元素
printf("%5d",*q);
}
printf("\n");
}
return 0;
}
//运行结果为:
// 31 32 33 24 25
// 16 17 8 9 10
// 19 11 51 14 15
- 结构体指针变量作为函数参数的传递:
#include <stdio.h>
#include <string.h>
struct student {
int age;
char sex;
char name[30];
};
void inputstudent(struct student *ps)//对结构体变量输入时必须传地址
{
(*ps).age = 10;
strcpy(ps->name, "ZhouHang");
ps->sex = 'f';
}
void outputstudent(struct student *ps)//对结构体变量输出时,可传地址也可传内容,但为了减少内存耗费,提高运行速度,建议使用传值。
{
printf("%d %c %s\n", ps->age, ps->sex, ps->name);
}
int main()
{
struct student st;
inputstudent(&st);
outputstudent(&st);
return 0;
}
- 冒泡排序
#include<stdio.h>
void sort(int * p,int len) {
int i,j,temp;
for(i = 0;i < len-1;i++) {
for(j = 0;j < len-1-i;j++) {
if(p[j] > p[j+1]) {
temp = p[j];
p[j] = p[j+1];
p[j+1] = temp;
}
}
}
}
int main() {
int a[6] = {10,-7,8,6,-2,9};
int i;
for(i = 0;i < 6;i++) {
printf("%3d",a[i]);
}
printf("\n");
sort(a,6);
for(i = 0;i < 6;i++) {
printf("%3d",a[i]);
}
return 0;
}
//运行结果为:
// 10 -7 8 6 -2 9
// -7 -2 6 8 9 10
8.简易学生信息管理系统,实现学生信息(包括成绩)录入,依据成绩排序后输出:
#include<stdio.h>
#include<malloc.h>
struct student {
int age;
char name[20];
float score;
};
int main() {
int len;
struct student * p;
struct student temp;
int i,j;
printf("请输入学生的个数:len = ");
scanf("%d",&len);
p = (struct student *)malloc(len * sizeof(struct student)); //动态地构造一维数组
//依次输入每个学生的信息
for(i = 0;i < len;i++) {
printf("请输入第%d个学生信息:\n",i+1);
printf("age = ");
scanf("%d",&p[i].age);
printf("name = ");
scanf("%s",p[i].name);
printf("score = ");
scanf("%f",&p[i].score);
}
//按成绩从高到低进行排序
for(i = 0;i < len-1;i++) {
for(j = 0;j < len-1-i;j++) {
if(p[j].score < p[j+1].score) {
temp = p[j];
p[j] = p[j+1];
p[j+1] = temp;
}
}
}
//输出:
for(i = 0;i < len;i++) {
printf("第%d个学生的信息为:\n",i+1);
printf("age:%d\n",p[i].age);
printf("name:%s\n",p[i].name);
printf("score:%f\n",p[i].score);
printf("");
}
return 0;
}
//结果示例:
/*
请输入学生的个数:len = 5
请输入第1个学生信息:
age = 17
name = Li
score = 79
请输入第2个学生信息:
age = 18
name = Wu
score = 90
请输入第3个学生信息:
age = 17
name = Wang
score = 92
请输入第4个学生信息:
age = 18
name = Liu
score = 89
请输入第5个学生信息:
age = 19
name = Qian
score = 99
第1个学生的信息为:
age:19
name:Qian
score:99.000000
第2个学生的信息为:
age:17
name:Wang
score:92.000000
第3个学生的信息为:
age:18
name:Wu
score:90.000000
第4个学生的信息为:
age:18
name:Liu
score:89.000000
第5个学生的信息为:
age:17
name:Li
score:79.000000
*/