目录
前言
我是大一上学期就自学了大部分C语言的内容,后来转专业去学习数学,所以这个学期再次接触到C语言(其实我早已经在学C++了),所以对于很多上学期一知半解的内容,这学期好像就变得不那么困难,对于其中的一些难点理解的也更为透彻。因此就记录一下自己将近两个学期以来所体会到的一些东西。
一、C语言的基础语法中相对较简单的部分
包括:注释,常量,变量,标识符,关键字,整型,浮点型(实型),字符,转义字符,字符串,布尔型,输入输出,四则运算,赋值,比较,逻辑(与或非)运算符,三目运算符,if语句,if-else if语句,if-else语句,switch语句,while语句,do-while语句,for循环语句,break,continue,一维数组,二维数组(看成矩阵),函数的定义,调用,使用等。
这些是基础中的基础,难度不大,自学也很容易掌握。但也要有一定程度的代码量才能熟练,或许是现在是学数学的缘故,学校里的老师并没有强调代码能力的重要性。但就从我个人的想法来看,代码能力对于理工科的学生是相当重要的,固然在一些项目和比赛中可以通过合作解决自己代码能力不行的问题,但我还是认为自己就会更好。
二、C语言的基础语法中的重难点
1.指针
1.1 指针是什么?
指针就是地址!地址就是指针!
1.2 定义指针变量
指针变量就是存放地址的变量。(指针变量也叫地址变量)
定义格式:类型名 *指针变量名
int a = 3;
int *p; //定义指向整型数据的指针变量p
p = &a;//让p指向a的地址
int a = 3;
int* p = &a;//初始化时直接把a的地址赋给指针变量p,与上面的代码等效。
注1:指针变量一般命名为p,因为英文是pointer.
注2:指针变量类型一定要和指向的类型一致。
注3:类型名 *:代表指针的意思,p就是一个变量名。
1.3 引用指针变量
(感觉90%的人到这里没听懂,然后就再也没懂过)
int a = 3;
int *p = &a;
printf("%d\n",*p); //输出*p的值
这段代码就是引用指针变量的方式,C++里面叫解引用(我不知道C语言里面是不是叫这个)。
理解:指针变量p在指向a的地址的时候,p就<=>(等价的意思)&a了,而*p就是<=>a,
即*p=a,也就是说这里会输出a的值。
归纳一下,可得:p=&a =>(推导出的意思)*p=a.
进一步的个人理解和思考:我把内存中的地址想象成一个个柜子,柜子上面有编号和里面放着的东西,这个编号就是地址,里面的东西就是指向变量的值。定义指针的过程,其实就是找寻目标柜子的过程,而引用的过程,我把它理解成为拿钥匙去打开柜子拿到想要东西的过程。或许这样会帮助更好的理解。
例1:一个引用指针变量的简单应用,交换两个变量的值。
#include<stdio.h>
int main()
{
int a = 1,b = 2,t;
int *p = &a, *q = &b;
t = *p;
*p = *q;
*q = t;
printf("a=%d, b=%d\n", a, b);
printf("*p=%d, *q=%d\n", *p, *q);
return 0;
}
#include<stdio.h>
int main()
{
int a = 1,b = 2,t;
t = a;
a = b;
b = t;
printf("a=%d, b=%d\n", a, b);
return 0;
}
这两种方法所实现的效果是一样的。
1.4 指针变量当函参
这个其实倒不难理解,只要掌握指针和引用的意思就能看懂以下代码的逻辑。
#include <stdio.h>
void swap(int *p1, int *p2) //这里是要传入两个指针,即传入两个地址
{
int t;
t = *p1;//函数体里的*都是引用指针变量的意思
*p1 = *p2;
*p2 = t;
}
int main()
{
int a = 3, b = 5;
swap(&a, &b);
printf("a = %d, b = %d\n", a, b);
return 0;
}
这段代码实际上还是用来交换两个变量的值,只不过定义了一个函数而已。
1.5 函数指针
顾名思义:这是一个指针,不过是一个指向函数的指针。
定义格式:类型名 (*指针变量名) (参数类型名)
#include <stdio.h>
int max(int x,int y)
{
return x>y?x:y;
}
int main()
{
int (*p)(int,int); //定义指向函数的指针变量p
p = max; //p指向max函数
int a=3,b=5,c;
c = (*p)(a,b);//通过指针变量调用max函数
printf("c=%d\n", c);
return 0;
}
具体分析一下这段代码的逻辑:它的功能是显然的,就是得到两数之间的较大者。这段代码中不好理解的地方在于为什么可以用(*p)(a,b)来调用函数,实际上这是因为函数名代表了函数的首地址,而p在指向max函数之后,*p<=>max函数了。(如果单纯应付考试只要记得(*p)(a,b)=max(a,b)即可)
我个人的进一步理解:这个可以想象成柜子里面放着一个工具箱,在拿(*p)打开柜子得到工具箱后,就可以通过(*p)打开工具箱来使用工具,从某种意义上说,这个工具箱能否使用和(*p)挂钩,因此可以理解成(*p)<=>max(这个可以参考布雷顿森林体系下美元与黄金挂钩)
注:定义函数指针时类型一定要和函数类型一致。
1.6 指针函数
继续顾名思义:这是一个函数,只不过它的返回类型是指针。
定义格式:类型名 函数名 (函数参数列表)
#include <stdio.h>
int *p(int *p1,int *p2)
{
return *p1>*p2?p1:p2;
}
int main()
{
int x=3,y=5,*max;
max = p(&x,&y);
printf("%d\n",*max);
return 0;
}
我仍以取出两数之间的最大值为例,这里的max是定义的一个指针变量。max = p(&x,&y)是进行赋值,因为函数的返回值是一个指针,也就是一个地址,所以max可以接收函数p的返回值,从而在输出时引用即可得到较大数字。
1.7 一维数组指针
int a[3]={0,1,2};
int *p;
p = &a[0];
int a[3]={0,1,2};
int *p;
p = a;
int a[3]={0,1,2};
int *p = &a[0];
int a[3]={0,1,2};
int *p = a;
上述四种写法等价。
注:若a是一维数组的数组名,则a<=>&a[0].
1.8 指针的加减
由于指针指向的是地址,而一个数组的地址是连续的,从而指针可以进行加减。
若存在指针p指向一个一维数组a,那么p+1就是p在原先指向&a[0]的情况下,变成指向了&a[1].
减法是同理的,不过要注意只能指向数组存在的元素地址。
而且由于p=a,所以p+i = a+i <=>&a[i].
#include <stdio.h>
int main() {
int a[5] = { 1,2,3,4,5 };
int* p = a;
for (int i = 0;i < 5;i++) {
printf("%d ", *(p + i));
}
return 0;
}
理解之后就能看懂这段代码的意思啦。
1.9 二维数组指针
前置知识:若存在一个二维数组a[3][4];
1. a <=> &a[0]
2. a+1 <=> &a[1] (a+1会指向下一行)
3. a[0] <=> &a[0][0] (把二维数组看成由行向量组成的矩阵)从而a[i] <=> &a[i][0]
4. a[0]+1 <=> &a[0][1]
5. a[0]+4 <=> &a[1][0] (因为二维数组是按行存储,所以一行加不下了会自动跳到下一行继续)
了解完上述前置知识后,就可以进行下一步的学习了。
1.9.1 列指针
#include <stdio.h>
int main() {
int a[2][3] = { 1,2,3,4,5,6 };
int* p, * q;
p = a[0];
q = a[1];
printf("%d\n", *(p + 1));
printf("%d\n", *(q + 2));//printf("%d\n",*(&a[1][0]+2)
return 0;
}
理解上述内容之后,不难看懂这段代码实际上是在输出矩阵的一些值 。因此列指针是较好理解的,在这里就不过多赘述了。
1.9.2 行指针
定义格式:类型名(*指针变量名)[列数]
int a[3][4]={0,1,2,3,4,5,6,7,8,9,10,11};
int (*p)[4];
p = a;
printf("%d\n", *(*p));
printf("%d\n",*(*(p+1)+2));
我个人认为这个是指针里面最难理解的了,下面我会用尽可能通俗易懂的方式来解释这段代码。
首先 p = a <=> p = &a[0]
从而 *p = a[0] <=> &a[0][0]
因此*p就代表了二维数组的第一个元素的地址。
所以第一个printf会输出0
同样的道理*(p+1) <=> *(a+1) <=> *(&a[1]) <=> a[1] <=>&a[1][0]
所以(*(p+1)+2) <=> &a[1][0]+2 <=>&a[1][2]
故而显然第二个printf会输出6
我想看明白这个大概可以说基本掌握了指针的绝大部分常见内容了。
2.结构体
结构体相较于指针来说是难度不大的,但是初学者不一定一下子就理解,所以在这里还是写一点内容。
2.1 结构体声明
定义格式:struct 结构体名{
……
};
struct student
{
int num;
char name[20];
char sex;
float score;
};
这是对一个关于学生信息的结构体声明。
2.2 结构体定义
struct student
{
int num;
char name[20];
char sex;
float score;
};
struct student s1;
struct student
{
int num;
char name[20];
char sex;
float score;
}s1;
上述两种方式定义结构体变量效果等价。
理解:把struct 结构体名看成int或者float之类的就行。
2.3 结构体使用
使用方法:变量名.结构体内部定义变量名
struct student
{
int num;
char name[20];
char sex;
float score;
};
struct student s1 = {23,"Li Ming",'Man',97};
printf("学号是%d,姓名是%s,性别是%c,分数是%.1f\n",s1.num,s1.name,s1.sex,s1.score);
2.4 结构体数组
顾名思义:这是一个数组,不过是其中元素都是结构体而已。
struct student{
char name[20];
int score;
};
struct student s1[2] = {{"Wang",100},{"Zhang",96}};
printf("%s,%d\n",s1[0].name,s1[0].score);
2.5 结构体指针
struct student
{
int num;
char name[20];
char sex;
float score;
};
struct student s1;
struct student *p;
p = &s1;
s1.num = 23;
s1.score = 97;
printf("学号是%d,分数是%.1f\n",s1.num,s1.score);
printf("学号是%d,分数是%.1f\n",(*p).num,(*p).score);
同样顾名思义,这是一个指向结构体的指针,不过有了前面指针的知识,这个内容是很容易掌握的,因此留给读者自己思考,不在这里过多赘述了。
总结
以上就是我这将近两学期以来的一些思考和总结,希望能给同样学习C语言的同学一些帮助,也希望如果有人发现在文章之中有谬误,请及时指出,勿使此文误人子弟,谢谢。

2635

被折叠的 条评论
为什么被折叠?



