开通博客很久了,一直想写一些自己的东西,由于所学知识有限,一直不能落笔,废话少说,进入正题。
我下面所写的都是用“.c”后缀的。“.c”后缀是c源文件的后缀,“.cpp”后缀是c++源文件的后缀。c++继承了c语言的一些特性,所以有些bug在“.cpp”里是可以通过的。
1、scanf()你真的了解了么?;
scanf()是有返回值的,返回值类型是int,这个你知道么?它返回了成功读入输入信息的个数。
那么我们可以利用它来做什么呢?
非零即真,这句话你一定听说过,而且不止一次。那么我们看一下下面的代码。
int a;
while (scanf("%d", &a) == 0)
{
printf("请输入合法字符:");
}
这样写代码是不是好一点,再输入的同时判断了输入的是否正确。但这会使你进入一个死循环。为什么会这样呢?
我们在输入时,我们的键值需要一块缓冲区在存放键值,如果输入的键值不被scanf()接受,那么它会一直在缓冲区中,那么再仔细看一看上面的代码,你是不是明白了呢。
解决这个问题并不难,只要将输入的键值接受,他就不会在缓冲区中影响你的程序了。看看下面的代码,有没有豁然开朗,哇,scanf()还可以这么使用!
int a;
while (scanf("%d", &a) == 0)
{
getchar();
printf("请输入合法字符:");
}
2、你还在纠结while(),for();
初学者可能会纠结什么场合用while(),什么场合用for()。
在我看来,这根本不存不值得我去纠结,我们应该想的是使用哪个可以使程序更简单,别人更容易看懂。
while(),for()的本质都是循环,只是形式不一样而已。
int i = 0; int i;
while (i<10) for (i=0; i<10; i++)
{ {
i++;
} }
上面的代码当然是for()更简明一写,但1、中的例子却是使用while()。for()比while()在语句要多一些,这是你需要的么?
根据每个人的代码风格不同,不能统一要求使用for()或者while()。建议初学者可以两个都试一下,感觉他们之间的不同,写出漂亮的代码。
3、switch()你会使用么?
switch(n)
{
case 1:
break;
case 2:
break;
default:
}
你一直这么使用么?
我们都知道case 后面不加break,会一直执行下面的代码,直到代码全不执行完或者遇到break。
利用这个特性,我们是不是可以做一些其他的事,比如加法,一年有十二个月,我想算出4月6号是今年的第几天,你想到怎么使用了么?我们看一下下面的代码。
switch (month-1)
{
case 11: sumMonth += 30;
case 10: sumMonth += 31;
case 9: sumMonth += 30;
case 8: sumMonth += 31;
case 7: sumMonth += 31;
case 6: sumMonth += 30;
case 5: sumMonth += 31;
case 4: sumMonth += 30;
case 3: sumMonth += 31;
case 2:
if ((i%4==0 && i%100!=0) || i%400==0)
{
sumMonth += 29;
}
else
{
sumMonth += 28;
}
case 1: sumMonth += 31;
}
你看懂了么?只是提供一个思路,将我们思维打开,你会看见另一片天空。
你在case中定义过变量么?
case中为什么不能定义变量呢?
int a;
switch (a)
{
case 1:
int b;
break;
case 2:
b = 0;
break;
}
你这么任性,编译器怎么办!
那我们为什么不规定case 1定义的变量只能在case 1中使用呢?在6、变量作用域中你将会找到答案。
当然,我们不应该只局限于switch(),if() else也一样可以完成多重选择的任务,不过switch()内置3-999个标签,使用switch()的程序运行速度可能稍快一些,代码也更简洁。
4、if()的使用
我们都知道if()用于判断。那么当你判断两个字符相等时,你会怎么写?
int a;
if (a == 5)
如果是我,我会写成
int a;
if (5 == a)
功能是一样的,何必这么麻烦?我们在做一个项目时,不可能十行代码调试半个小时,当我们思维在键盘上飞舞时,我才体会到一个程序员的快乐。在我们正在体验这种快乐时,手误在所难免,如果写成if(a=5),这个错误够你找两天的。但是if(5=a),编译器就可以帮你找到。
当你想判断两个字符串是否相等时,你会不会这样写?
char c_c1[10] = "acdefg";
char c_c2[10] = "qwerty";
if (c_c1 == c_c2)或者if (c_c1[10] == c_c2[10])
总之都是不对的。字符串,数组,指针后面还会有所涉及,这里不做详谈。
比较两个字符串我们用到了C函数库中提供的strcmp()函数,它包含在<string.h>头文件中。strcmp()返回了一个int类型的值,-1,0,1。0代表两个字符串相等,-1,1是由于字符之间的比较(ASCII)决定的。下面我看一个例子。
char c_ch[10] = "qwertyui" //最多可以存放9个字符,最后一位是” “
char *p_ch = "adfgf"
strcmp(c_ch, p_ch); //strcmp()中的两个参数是两个字符串的地址
strcmp("qwertyui", "adfgf"); //这样也是可以的,字符串存放的也是首字母的地址,后面会提到。
5、你知道头文件的作用么?
我们写代码都习惯把#include <stdio.h>最先写上,但你知道它的作用么?
#include :文件包含
当预处理器发现#include 指令后,就会寻找 <>中的文件名并把这个文件包含到你写的程序代码,替换源文件(就是你写的文件)中的#include。虽然你只写了一行代码,但是编译器却给你添加了很多代码。
<stido.h>包含了输入输出函数,所以在程序的调试阶段,他是不可少的。
#include "gaozy" 这类头文件你见过么?它与#include <stdio.h>又有哪些区别呢?<>包含的头文件,编译器会在系统目录中搜索。“”包含的头文件,编译器会先在当前工作目录中搜索,如果没有,再去系统目录中搜索。也可以简单的理解成C函数库里的函数在<>包含的头文件里,而我们自己写的函数则放在“”文件中。
6、变量作用域
int i;
while (1)
{
int i = 0;
printf("%d", i);
}
这样会报错么?当然不会,因为两个int i;并不在同一个作用域。
那么什么是作用域,怎么分辨作用域呢?
我们通常把一个{}内的代码看成在一个作用域。这也是为什么case里不可以定义变量的原因。
只有{}才可以分辨作用域么?
当然不是
while (1)
int i;
printf("hellow world!");
int i;自己单独在一个作用域中,printf()只会执行一次,当然这是一个死循环,不会执行到printf()。现在你有点略懂了么?跟while()相同的还有for(),if()
还有个问题你想过么?第一次循环定义一个 i,第二次循环又定义一个 i,这不重复定义了么?
当然不是,作用域内的变量是有生命周期的。也就是说,当第一次循环结束,你所定义的 i 的内存空间已经被释放。
7、伤脑筋的bug
你写过这样的程序么?
int i;
while (1)
{
int i = 0;
printf("%d", i);
int j;
}
编译器是这样报错的::c浅谈c.c(49) : error C2143: syntax error : missing ';' before 'type'
我英文不好,不理解这句话是什么意思,但是这段代码里根本不存在缺少;的情况。
我将代码粘到vs2010中,它是可以运行的。我一度怀疑是编译器出错了。但事实上编译器是很少出错的,纠结了很久,我突然想起来,c语言中规定,变量是必须在作用域的开始定义。vs210中,我用文件是后缀是.cpp的,就是是说c++允许在其他函数之后定义变量,而c对此则表示不支持。
你写的代码有没有之前运行正常,你只是修改了一点,语法上别没有出错。但编译器却报错了呢?
LINK : fatal error LNK1168: cannot open Debug/SMIS.exe for writing
之前遇见这个问题也是纠结很久,为什么一会儿可以编译,一会儿又让我等待呢?
这也不是编译器出错了。而是你执行了程序,没有关闭,对代码进行了修改,又要执行程序。
编译器如果会说话:“小子,你拿我开心呢吧,还能不能一起玩耍了。”
当你malloc时,要记得free啊,还有malloc之后跟上一个判断是有必要的。
p_head = (P_STUDENT)malloc(sizeof(STUDENT));
if (NULL == p_head)
{
printf("动态内存分配失败,程序结束!");
exit(-1);
}
如果没有if()判断的话,在内存分配失败时,运行程序会出错,而且很难找到错误原因。
8、字符串,数组,指针
谈到字符串、数组、指针,那么我们就不得不谈内存了。我们可以把内存理解成一排很长很长的房子,它是一个线性结构。房子里住着不同的人家,每个房子有不同编号。
人家对应的是计算机里定义的变量,房子编号对应的是变量所在地址。所以,我们每定义一个变量,都会占用一套房子,当房子被全部占用时,你的电脑就崩溃了。当然,我们电脑的内存还是很大的。下面来看看字符串、数组、指针之间的关系。
字符串以“/0”结束,所以字符串长度要在实际长度上+1
数组名就是数组首元素的地址
char ch[10] = "asdfg"; //ch[10]是定长数组
char ch[] = "asdfg"; //ch[]没有规定长度,但是在定义时必须初始化
char *p_ch = "asdfg"; //指针指向字符串的首地址
这三种定义字符串的方法差不多。ch和p_ch都是地址。
数组定义之后不可改变,指针还可以指向其他地址,但要注意内存泄露。
9、指针与地址
有些书上说指针就是地址,其实这种说法并不准确。
那么指针是什么,地址又是什么?
首先地址就是地址,物理内存的编号。
指针是一个变量,这个变量存放的内容是内存的物理地址。就像int i; i 是一个变量,存放一个 i 值。指针变量也存放一个指针值,这个值是一块物理内存的地址。
其实指针是地址还是变量并不影响我们对指针的使用。
10、你用过宏么?
#define MAX 60
就是MAX = 60;的意思,之后的代码中MAX都等于60。宏使我们的程序维护更简单,也更容易理解。
11、malloc与free
有人说new跟delete更强大,但我并这么认为,new和delete是c++中的运算符,提供了对对象的操作。而malloc和free是c语言中用来进行动态内存分配的,不具备可比性。
C语言是面向过程的程序设计,根本没有对象的概念。