学C语言的,有福了,
来源:互联网 发布:虎门淘宝培训班 编辑:程序博客网 时间:2024/06/02 15:01
本人在学c期间,做了大量的笔记,现在我将它给分享出来,如果你认为还好的话,可以借鉴,就继续分享下去,另外希望大家指正一些问题,大家一起学习。。谢谢!!!!
#include <stdio.h>
int main(int argc, char **argv) {
printf("hello");
return 0;
}
l #include<stdio.h> :预处理指令。在编译之前执行的指令
l 系统自带的文件用<>,自己写的文件用""
l .h成为头文件,用来声明一些常用的函数,假如想使用这些这些函数,就必须包含这个头文件
l main函数(只能有一个),是c的程序入口
l c语言中可以不写返回值,代表的是int main,如果是无返回值,则是void main
l main()里面的参数可以不填写
l 如果无返回值,则不用”return0”
注意:c语言中的语言不严格
1. 一个项目中有多个.c的源文件,编译成功会产生与此对应的.obj目标
函数
l 主函数,main作为一个主函数,程序都是最先执行的,不管次函数放在代码的什么位置
l 自定义函数,开发人员自定义的,可有可无,数目不限
void test(){
}
l C语言提供的库函数,比如:stdio.h中的printf(),输入函数scanf();
函数的声明和定义
虽然说c中的函数时类似于java中的方法,但在使用上还是有区别的
1、 在Java中,每个方法的定义顺序没有限制,在前面定义的方法内部可以调用后面定义的方法
2、 在c语言中,如果要想在main()函数中调用方法,则需要先在main()方法前做一个”声明”
声明:函数名,函数接收的参数类型,函数的返回值
\n à换行
注意:一般不要写main主方法中写很多的内容,可以将需要“声明”的文件另外写为.h后缀的头文件,具体方法则写入到.c后缀的文件中,最后在main()方法中调用 #include “XXX.h” ;一定不能调用”XXX.c”à关于链接环境
printf和scanf函数
这两个函数都是在stdio.h中声明的一个函数,因此使用之前需要加入#include<stdio.h>
用法:
① printf(字符串)
printf(“hello world”);
② printf(字符串,格式符参数)
Printf(“My age is %d\n”,26);
printf("my name is %s ,sex is %c,height is %f,age is %d", "李春福", 'B', 1.73,20);
一个中文至少会占用两个字符用string 类型的
%.2f代表保留2位小数
%-5.2f表示的是保留两位小数的5位字符,如果为”-”,则右边空出,否则左边空出
常用的格式符及其含义
格式符
功能
%d
以带符号的十进制形式输出整数(但正数不输出正号+)
%o
以不带符号的八进制形式输出整数
%x
以不带符号的十六进制形式输出整数
%u
以不带符号的十进制形式输出整数
%c
输出一个字符
%s
输出一个或多个字符
%f
以小数形式输出单、双精度数,默认输出为6位小数
%e
以标准指数形式输出单、双精度数,数字部分小数位数为6位
基本数据类型和类型修饰符
l 基本类型图
l 变量
c语言中用变量来存储计算过程使用的值,任何变量都必须先定义类型再使用。
问题:为什么一定要先定义?
因为变量的类型决定了变量占用的存储空间,所以定义变量类型,就是为了给该变量分配适当的存储空间,以便存放数据。
类型
名称
存储空间(16位编译器)
int
整型
2个字节
float
单精度浮点型
4个字节
double
双精度浮点型
8个字节
char
字符型
1个字节
① 局部变量:在c语言中,未经过初始化的变量,也可以打印出来,值由系统随机分配
② char的取值范围: ASCII码字符或 128~127的整数
③ char只能存储一个字符
l 类型修饰符
有4中修饰符:
1、 short à短型
2、 long à长型
3、 signed à 有符号型
4、 unsigned à无符号型
short int s1=1èshort s1=1
long int l1=2 èg l1=2
基本运算
l 循环语句(do 、while、for)
l 条件语句(if 、if-else、switch)
l goto语句
c语言一共有34种运算
c语言中没有boolean类型,如果关系运算符为“真”返回1,”假“就返回0;任何非0值都为真,只有0值才为假
陷阱①:判断先把常量写在前面,变量写在后面
Java 不可以直接写 a>bè boolean a>b,c语言比较弱,不严谨
陷阱②: a=8表示将8的值赋给aèa==8则不能赋值
逗号表达式主要用于连接表达式,整个表达式的值是最后一个表达式的值
值为30
值为3
数组(构造类型)
一维数组:* 初始化的一般形式是:类型 数组名[元素个数] = {元素1,元素2, ...};
int a[10];
定义方式:类型说明符 数组名[常用表达式] ;二维数组:类型 数组名[行数][列数]
float a[3][4];
定义方式: 类型说明符 数组名[常用表达式] [常用表达式] ;
数组(变量)的地址是第一个元素的地址
数组名代表的就是数组的地址,也就是第0个元素的地址
//查看每一个数组元素的地址
int i;
for(i=0;i<5;i++){
printf("a[%d]的地址:%d\n",i,&a[i]);
}
return 0;
数组的初始化:3种方法
void init(){
int b[3]={12,32};
int b[]={12,32};
int b[3];
b[0]=1;
b[1]=3;
b[2]=5;
}
字符串(字符数组)
char c[]={'l'.'c','\0'};
char c1[3]={'l','c','\0'};
char c3[]="lc"; è自动在末尾添加’\0’
char c2[3]={'l','c'};
前面三种表示的是字符串初始化,后面的不是
puts()和printf()都是可以用于字符串,但是puts()一次性只能输出一个字符串
puts()可以自动换行。
stdio.h中有2个函数可以用来接收用户输入的字符串,分别是scanf和gets,建议不要用gets去输入数据,不安全
字符串数组
一维字符数组中存放一个字符串,比如一个名字char name[20] = "mj"
字符串数组的初始化
har names[2][10] = { {'J','a','y','\0'}, {'J','i','m','\0'} };
char names2[2][10] = { {"Jay"}, {"Jim"} };
char names3[2][10] = { "Jay", "Jim" };
可以把字符串数组看作是一维数组,它的元素是字符串。字符串数组names由字符串"Jay"和字符串"Jim"构成
字符和字符串处理函数
l 字符输出函数putchar
putchar(65); // A
putchar('A'); // A
int a = 65;
putchar(a); // A
上面的3种用法,输出的都是大写字母A
* putchar一次只能输出一个字符,而printf可以同时输出多个字符
printf("%c %c %c", 'A', 'B', 'a');
l 字符输入函数getchar
char c;
c = getchar();
getchar会将用户输入的字符赋值给变量c。
* getchar函数可以读入空格、TAB,直到遇到回车为止。scanf则不能读入空格和TAB。
* getchar一次只能读入一个字符。scanf则可以同时接收多个字符。
* getchar还能读入回车换行符,这时候你要敲2次回车键。第1次敲的回车换行符被getchar读入,第2次敲的回车键代表输入结束。
字符串处理函数包括在string.h文件中
l strlen函数:测量一个字符串的字符数,非字符串长度
void test(){
//测量字符串的字符长度(不包括\0这个字符)
int len=strlen("lichunfu");
printf("%d\n",len);
int len1=strlen("李春福");
printf("%d\n",len1);
//测量字符串变量的字符长度
char s[]="peter";
printf("%d\n",strlen(s));
}
l strcpy函数:将右边字符串中的内容copy到左边的字符串中
//strcpy:将右边字符串中的内容copy到左边的字符串中
void test1() {
char left[10];
strcpy(left,"peter");
printf("%s", left);
}
l strcat函数:把右边字符串添加到左边字符的后面
void test2(){
char left[]={'l','c','f','\0'};
strcat(left,"==>peter");
printf("%s",left);
}
注意:遇到\0就结束字符串
l strcmp函数:可以用来比较2个字符串的大小
两个字符串从左至右逐个字符比较(按照字符的ASCII码值的大小),直到字符不相同或者遇见'\0'为止。如果全部字符都相同,则返回值为0。如果不相同,则返回两个字符串中第一个不相同的字符ASCII码值的差。
即字符串1大于字符串2时函数返回值为正,否则为负
void test3(){
int delta=strcmp("abc","Abc");
printf("%d",delta);
}
返回的值为:1
l strlwr函数:将字符串中大写字母转换成小写字母
l strupr函数:将字符串中的小写字母转换为大写字母
指针
一个数据的“指针”就是它的地址,通过变量的地址能找到该变量在内存中的存储单元,从而能得到它的值
l 直接引用:就是通过变量名来直接引用变量,由系统自动完成变量名和其存储地址之间的转换;
l 间接引用:以变量a为例
首先将变量a的地址存放到另一个变量b中,然后通过变量b来间接引用变量a,间接读写变量a的值
用来存放变量地址的变量,就称为“指针变量“。
l 指针变量定义的形式: 类型标识符 *标识符
“标识符“就是指针变量的名字;
需要注意以下两点:
1、标识符前面的”*“,表示该变量为指针变量,比如: *person,其指针变量名是person,而不是*person
2、一个指针变量只能指向同一个类型的变量
常见的做法,以及错误写法
#include <stdio.h>
//错误做法一:
void worry1() {
char *p;
//应该在指针确定指向的变量后,再进行相应的操作
p = 10;
}
//错误做法二:
void worry2() {
char *p;
//指针变量是用来存储变量地址的,不能随便赋值一个常数,除非你这个常数恰好是某个变量的地址
p = 100;
}
int main(int argc, char **argv) {
char a;
//a=10; 直接引用
//定义了一个指针变量b,而b只能指向char类型的变量
char *b;
//让指针变量b指向a
b = &a;
//这里的*b代表:访问b中存储的地址对应的存储空间(也就是变量a的存储空间)
//相当于a=10
*b = 10;
/**
* char *b=&a
* 相当于
* char *b;
* b=&a;
*/
printf("%d\n", a);
//这里的*b相当于取出变量a的存储的值
printf("%d\n", *b);
return 0;
}
* 看下面的代码,利用指针p读取变量c的值
1 int i = 2;
2 char c = 1;
3
4 // 定义一个指向char类型的指针
5 char *p = &c;
6
7 // 取出
8 printf("%d", *p);
这个输出结果应该难不倒大家: ,是可以成功读取的。
* 如果我改一下第5行的代码,用一个本应该指向int类型变量的指针p,指向char类型的变量c
int *p = &c;
我们再来看一下输出: ,c的原值是1,现在取出来却是513,怎么回事呢?这个要根据内存来分析
根据变量的定义顺序,这些变量在内存中大致如下图排布:
其中,指针变量p和int类型变量i各占2个字节,char类型的c占一个字节,p指向c,因此p值就是c的地址
1> 最初的时候,我们用char *p指向变量c。当利用*p来获取变量c的值时,由于指针p知道变量c是char类型的,所以会从ffc3这个地址开始读取1个字节的数据:00000001,转为10进制就是1
2> 后来,我们用int *p指向变量c。当利用*p获取变量c的值时,由于指针p认为变量c是int类型的,所以会从ffc3这个地址开始读取2个字节的数据:00000010 0000 0001,转为10进制就是513
可见,给指针分类是多么重要的一件事,而且一种指针最好只指向一种类型的变量,那是最安全的。
l 指针与一维数组
指针变量可以指向整个数组,也可以指向数组元素
注意:C语言中规定p+1指向数组的下一个元素(而不是将p值简单地加1),而是增加的是该类型的字节数
a+1不一定代表着a值加2,究竟加多少,取决于数组的类型。a+i的计算方法与p+i相同。
其实,p+1不一定代表p的值加2,也可能是加1、加4或者加8。究竟加多少,这跟指针的类型有关。下图是在16位编译器环境下的情况。
聪明的你可能已经找到规律了,因为char类型的变量要占用1字节,所以p+1代表p的值加1;float类型的变量占用4字节,所以p+1代表p的值加4。从这一点,也可以很好地说明为什么指针一定要分类型,不同类型的指针,p+1的含义是不一样的。
在遍历数组的过程中。如果*(p+i)则表示p的值没有改变过,如果是*(p++)则表示指针的值一直在改变
指针与数组的总结
p是指针,a是一个数组
1>如果p指向了一个数组元素,则p+1表示指向数组该元素的下一个元素。比如,假设p =&a[0],则p+1表示a[1]的地址
2>对于不同类型的数组元素,p值的改变是不同的。如果数组元素为int类型,p+1代表着p的值加上2(16位编译器环境下)
3>如果p的初值是&a[0],那么
· p+i和a+i都可以表示元素a[i]的地址,它们都指向数组的第i个元素。a代表数组首地址,a+i也是地址,它的计算方法与p+i相同
· *(p+i)和*(a+i)都表示数组元素a[i]
· 虽然p+i和a+i都指向数组的第i个元素,但二者使用时还是有区别的。因为作为指针变量的p可以改变自身值,如p++,使p的值自增。而数组名a是一个代表数组首地址的常量,它的值是不能改变的,即a++是不合法的
4>引用一个数组元素可以有两种方法:
· 下标法:如a[i]
· 指针法:如*(p+i) 或 *(a+i)
编译预处理
1.C语言在对源程序进行编译之前,会先对一些特殊的预处理指令作解释(比如之前使用的#include文件包含指令),产生一个新的源程序(这个过程称为编译预处理),之后再进行通常的编译
2.为了区分预处理指令和一般的C语句,所有预处理指令都以符号"#"开头,并且结尾不用分号
3.预处理指令可以出现在程序的任何位置,它的作用范围是从它出现的位置到文件尾。习惯上我们尽可能将预处理指令写在源程序开头,这种情况下,它的作用范围就是整个源程序文件
4.C语言提供的预处理指令主要有:宏定义、文件包含、条件编译
l 宏
不带参数的宏定义 #define 标识符字符串
宏定义一般习惯用大写字母表示,以便与变量名区分。也可是使用小写字母表示
带参数的宏定义 #define 宏名(参数表)字符串
l 条件编译
在很多情况下,我们希望程序的其中一部分代码只有在满足一定条件时才进行编译,否则不参与编译(只有参与编译的代码最终才能被执行),这就是条件编译
#if 条件1
...code1...
#elif 条件2
...code2...
#else
...code3...
#endif
1> 如果条件1成立,那么编译器就会把#if与 #elif之间的code1代码编译进去(注意:是编译进去,不是执行,很平时用的if-else是不一样的)
2> 如果条件1不成立、条件2成立,那么编译器就会把#elif与 #else之间的code2代码编译进去
3> 如果条件1、2都不成立,那么编译器就会把#else与 #endif之间的code3编译进去
4> 注意,条件编译结束后,要在最后面加一个#endif,不然后果很严重(自己思考一下后果)
5> #if 和 #elif后面的条件一般是判断宏定义而不是判断变量,因为条件编译是在编译之前做的判断,宏定义也是编译之前定义的,而变量是在运行时才产生的、才有使用的意义
l 文件包含
一般形式:#include<文件名> #include”文件名”
注意点:
1.#include指令允许嵌套包含,比如a.h包含b.h,b.h包含c.h,但是不允许递归包含,比如a.h 包含 b.h,b.h 包含 a.h。
2.使用#include指令可能导致多次包含同一个头文件,降低编译效率
使用此种写法可以提高效率
变量类型
C语言有丰富的数据类型和运算符,因此计算能力非常强大,计算过程中使用的值一般用变量来存储。变量也是有分类型的,不同类型的变量有不同的存储类型、不同的生命周期、不同的作用域,C语言也提供了一些关键字来设置变量的属性(比如设置存储类型、生命周期)。
l 变量的作用域
C语言根据变量作用域的不同,将变量分为局部变量和全局变量。
1.局部变量
1> 定义:在函数内部定义的变量,称为局部变量。形式参数也属于局部变量。
2> 作用域:局部变量只在定义它的函数内部有效,即局部变量只有在定义它的函数内部使用,其它函数不能使用它。
2.全局变量
1> 定义:在所有函数外部定义的变量,称为全局变量。
2> 作用域:全局变量的作用范围是从定义变量的位置开始到源程序结束,即全局变量可以被在其定义位置之后的其它函数所共享。
l 变量的存储类型
* 变量的存储类型就是指变量存储在什么地方。有3个地方可以用于存储变量:普通内存、运行时堆栈、硬件寄存器。变量的存储类型决定了变量何时创建、何时销毁以及它的值能保持多久,也就是决定了变量的生命周期。
C语言根据变量的存储类型的不同,可以把变量分为:自动变量、静态变量、寄存器变量。
自动变量
1> 定义:自动变量是存储在堆栈中的。
2> 哪些是自动变量:被关键字auto修饰的局部变量都是自动变量,但是极少使用这个关键字,基本上是废的,因为所有的局部变量在默认情况下都是自动变量。
3> 生命周期:在程序执行到声明自动变量的代码块(函数)时,自动变量才被创建;当自动变量所在的代码块(函数)执行完毕后,这些自动变量就会自行销毁。如果一个函数被重复调用,这些自动变量每次都会重新创建。
静态变量
1> 定义:静态变量是存储在静态内存中的,也就是不属于堆栈。
2> 哪些是静态变量:
- 所有的全局变量都是静态变量
- 被关键字static修饰的局部变量也是静态变量
3> 生命周期:静态变量在程序运行之前创建,在程序的整个运行期间始终存在,直到程序结束。
寄存器变量
1> 定义:存储在硬件寄存器中的变量,称为寄存器变量。寄存器变量比存储在内存中的变量访问效率更高(默认情况下,自动变量和静态变量都是放在内存中的)
2> 哪些变量是寄存器变量:
- 被关键字register修饰的自动变量都是寄存器变量
- 只有自动变量才可以是寄存器变量,全局变量和静态局部变量不行
- 寄存器变量只限于int、char和指针类型变量使用
3> 生命周期:因为寄存器变量本身就是自动变量,所以函数中的寄存器变量在调用该函数时占用寄存器中存放的值,当函数结束时释放寄存器,变量消失。
4> 使用注意:
- 由于计算机中寄存器数目有限,不能使用太多的寄存器变量。如果寄存器使用饱和时,程序将寄存器变量自动转换为自动变量处理
- 为了提高运算速度,一般会将一些频繁使用的自动变量定义为寄存器变量,这样程序尽可能地为它分配寄存器存放,而不用内存
· static 、extern与函数
两个概念
extern外部函数:如果在当前文件中定义的函数允许其他文件访问、调用,就称为外部函数。C语言规定,不允许有同名的外部函数。
static内部函数:如果在当前文件中定义的函数不允许其他文件访问、调用,只能在内部使用,就称为内部函数。
C语言规定不同的源文件可以有同名的内部函数,并且互不干扰。
· static、extern与函数的总结
1.static
* 在定义函数时,在函数的最左边加上static可以把该函数声明为内部函数(又叫静态函数),这样该函数就只能在其定义所在的文件中使用。如果在不同的文件中有同名的内部函数,则互不干扰。
* static也可以用来声明一个内部函数
2.extern
* 在定义函数时,如果在函数的最左边加上关键字extern,则表示此函数是外部函数,可供其他文件调用。
C语言规定,如果在定义函数时省略extern,则隐含为外部函数。
* 在一个文件中要调用其他文件中的外部函数,则需要在当前文件中用extern声明该外部函数,然后就可以使用,这里的extern也可以省略。
· static和extern的总结
1.extern可以用来声明一个全局变量,但是不能用来定义变量
2.默认情况下,一个全局变量是可以供多个源文件共享的,也就说,多个源文件中同名的全局变量都代表着同一个变量
3.如果在定义全局变量的时候加上static关键字,此时static的作用在于限制该全局变量的作用域,只能在定义该全局变量的文件中才能使用,跟其他源文件中的同名变量互不干扰
结构体
二、结构体的定义
1.定义形式
结构体内部的元素,也就是组成成分,我们一般称为"成员"。
结构体的一般定义形式为:
struct 结构体名{
类型名1 成员名1;
类型名2 成员名2;
……
类型名n 成员名n;
};
struct是关键字,是结构体类型的标志。
三种定义方式:
1.先定义结构体类型,再定义变量
struct Student {
char *name;
int age;
};
struct Student stu;
变量名为stu。struct和Student是连着使用的。
2.定义结构体类型的同时定义变量
struct Student {
char *name;
int age;
} stu;
结构体变量名为stu
3.直接定义结构体类型变量,省略类型名
struct {
char *name;
int age;
} stu;
结构体变量名为stu
指向结构体的指针
每个结构体变量都有自己的存储空间和地址,因此指针也可以指向结构体变量
* 结构体指针变量的定义形式:struct结构体名称 *指针变量名
* 有了指向结构体的指针,那么就有3种访问结构体成员的方式
结构体变量名.成员名
(*指针变量名).成员名
指针变量名->成员名
枚举enum
枚举是C语言中的一种基本数据类型,并不是构造类型,它可以用于声明一组常数。当一个变量有几个固定的可能取值时,可以将这个变量定义为枚举类型。
比如,你可以用一个枚举类型的变量来表示季节,因为季节只有4种可能的取值:春天、夏天、秋天、冬天。
· 枚举的定义:三种方式
1.先定义枚举类型,再定义枚举变量
enum Season {spring, summer, autumn, winter};
enum Season s;
2. 定义枚举类型的同时定义枚举变量
enum Season {spring, summer, autumn, winter} s;
3.省略枚举名称,直接定义枚举变量
enum {spring, summer, autumn, winter} s;
上面三种方式定义的都是枚举变量s
注意点:
1> C语言编译器会将枚举元素(spring、summer等)作为整型常量处理,称为枚举常量。
2> 枚举元素的值取决于定义时各枚举元素排列的先后顺序。默认情况下,第一个枚举元素的值为0,第二个为1,依次顺序加1。
enum Season {spring, summer, autumn, winter};
也就是说spring的值为0,summer的值为1,autumn的值为2,winter的值为3
3> 也可以在定义枚举类型时改变枚举元素的值
enum season {spring, summer=3, autumn, winter};
没有指定值的枚举元素,其值为前一元素加1。也就说spring的值为0,summer的值为3,autumn的值为4,winter的值为5
枚举的遍历
enum Season {spring, summer, autumn, winter} s;
// 遍历枚举元素
for (s = spring; s <= winter; s++) {
printf("枚举元素:%d \n", s);
}
typedef
使用typedef关键字为各种数据类型定义一个新名字(别名)
也可以在别名的基础上再起一个别名
最后,如果对你有所帮助的话,就帮忙顶下吧!!!
- 学C语言的,有福了,
- 学C/C++的同学们,有福了!
- 清明节自己在宿舍做了一个游戏模拟器...学开车的同学有福了
- 关注RIA的兄弟们有福了
- 做vb.net的人有福了
- 喜欢Ant不喜欢Maven的有福了
- 喜欢Delphi习惯的有福了
- Fixedsys控的福音! Vs2010有福了!
- texturepacker 非常棒的工具,有福了
- 驾照还没考的朋友们,你们有福了!
- 学了c语言,记录下编程过程的点点滴滴.
- 学c语言的
- 数据结构 学习笔记之:静态链表--史上最简单的C语言实现——只为掌握概念——不清楚静态链表的鸟鸟们有福了!
- 要开始学c语言了
- 开始重新来学C语言了
- 亲们,有福了!
- 学了差不多一个学期的c语言了,期末写的五子棋。
- 使用MC39i的有福了,直接换MC37i
- C# 文件下载四方法
- C# 多线程开发 1:使用 Thread 类创建与启动线程
- 黑马程序员 Java基础学习笔记1
- Unable to execute dex: java.nio.BufferOverflowException. Check the Eclipse log for stack trace.
- MFC常用宏的分类及其作用与用法
- 学C语言的,有福了,
- Div 与 table 的区别
- 三层架构与养猪
- modest vs humble
- Spring整合Hibernate【Session管理】
- ZOJ 3723 Starfruit
- linux 网桥代码分析 二 网桥初始化
- jsp和Java获取参数
- C++语言类成员变量初始化总结