程序
1. 程序是对数据的运算。
2. 仅数据的类型决定其在内存中的布局与大小。
程序结构
主函数
1. 形式为 int main(){语句块}
。
2. 程序仅仅执行主函数,从语句块第一条语句开始,执行完所有语句或遇到 return
关键字结束。但可以在主函数中对其它函数进行嵌套调用,如 printf()
函数。
预处理指令
预处理指令在正式编译前,由预处理程序执行完成。
#include
文件包含命令,用法为 #include <c文件路径>
或#include "c文件路径"
。使用<>
时,编译器会在系统路径中寻找文件;使用" "
时,则会优先在编译器安装目录下的 include 文件夹下寻找,找不到的话再去系统路径中找。
包含标准头文件时用<>
,包含自定义头文件时用" "
,会是一个好习惯。
在控制台编译时,可以使用 gcc 命令的 -I 选项增加搜寻路径,那么编译器会优先在所增加的路径中寻找。
gcc -I 路径
#define、#undef
宏定义命令,用法为#define 宏名 字符序列
,其中字符序列可以为数字、表达式、if 语句、函数等。它让我们可以给一个需要频繁使用到的表达式起一个名字,从而简化代码。
在预处理过程中,代码中的所有宏会被简单替换成宏定义时的字符序列,称为 宏展开 ,除非超出了宏的作用域。
#undef
表示宏的作用域到此为止。
#if、 #ifdef、 #ifndef、 #elif、 #else、 #endif
条件编译命令,其中#ifdef 宏名
表示如果定义了宏则执行语句下面的程序段,#ifndef 宏名
反之。其余略。
#line
用法为#line 整数行 文件名
,其中 行数 与 文件名 顺序不能对调,且文件名要加" "
。本质上是对__LINE__
和__FILE__
重新赋值。
int main(){
#line 9 "a.c"
printf("%d\t%s", __LINE__, __FILE__);
}
9 a.c
#error、#warning
手动抛出错误或警告。
数据类型
变量的声明与定义
格式:type variable_name1 = value, variable_name2 = value;
extern int a;
int b=1, c;
左值与右值
lvalue:指向内存位置的表达式,可以在赋值运算符左边或者右边。
rvalue:存储在内存中的值,只能在赋值运算符右边,被赋给左值。
int b=1;
2 = b;
提示错误:
error:lvalue requires as left operand of assignment
整数类型
1. char 和 int、unsigned int 。
2. 其中 char 可细分为signed char与unsigned char。
3. int 可细分为 short(2字节) 与 long(4字节) ,而 unsigned int 与 int 不是包含关系,而是并列, int 等同于 signed int 。
4. unsigned int 可细分为 unsigned short 与 unsigned long 。
5. 其中unsigned顾名思义,无符号的。整数的符号,首先想到的也就只有负号了。unsigned作为修饰其含义为 非负的 。
unsigned char a = -1;
unsigned short b = -1;
unsigned long c = -1;
printf("%d\n%u\n%lu\n",a,b,c);
255
65535
4294967295
注意:输出时,printf()
函数的格式化说明符需与变量类型保持对应,否则会发生类型转换。
另外,%lu
处若为%d
、%u
均会提出警告,表示arguments of type 'long unsigned int'
不应对应 format '%d'
或 format '%u'
。
且%u
处为%d
时却不会提出警告,明明 argument b
也不是 format '%d'
所expect
的 type 'int'
。猜测是因为 (signed) int
的取值范围必能包含(signed) short
的取值范围,故无需警告。
6. char、int、short、long的取值范围均包含正与负,赋正值或赋负值都可。
char a = -1;
int b = -1;
short c = -1;
long d = -1;
printf("%d\n%d\n%d\n%d",a,b,c,d);
-1
-1
-1
-1
7. 各整数类型的取值范围首先取决于其长度,再根据是否 unsigned 修饰进行进一步运算即可。
char a;
printf("char型占用内存:%d byte,故unsigned char型取值范围为:0~%d\n",sizeof(a),(int)(pow(2,(int)(8*sizeof(a))))-1);
short b;
printf("short型占用内存:%d byte,故unsigned short型取值范围为:0~%d\n",sizeof(b),(int)(pow(2,(int)(8*sizeof(b))))-1);
long c;
printf("long型占用内存:%d byte,故unsigned long型取值范围为:0~%d\n",sizeof(c),(int)(pow(2,(int)(8*sizeof(c))))-1);
char型占用内存:1 byte,故unsigned char型取值范围为:0~255
short型占用内存:2 byte,故unsigned short型取值范围为:0~65535
long型占用内存:4 byte,故unsigned long型取值范围为:0~2147483646
int型取值范围为short与long的合集。
浮点数类型
float a;
double b;
long double c;
printf("float型占用内存:%d byte\ndouble型占用内存:%d byte\nlong double型占用内存:%d byte\n",sizeof(a),sizeof(b),sizeof(c));
printf("float型取值范围为:%e~%e\ndouble型取值范围为:%e~%e\nlong double型取值范围为:%e~%e\n",FLT_MIN,FLT_MAX,DBL_MIN,DBL_MAX,LDBL_MIN,LDBL_MAX);
printf("float型的精度为:%d\ndouble型的精度为:%d\nlong double型的精度为:%d\n",FLT_DIG,DBL_DIG,LDBL_DIG);
float型占用内存:4 byte
double型占用内存:8 byte
long double型占用内存:12 byte
float型取值范围为:1.175494e-038~3.402823e+038
double型取值范围为:2.225074e-308~1.797693e+308
long double型取值范围为:-0.000000~-1.#QNAN0
float型的精度为:6
double型的精度为:15
long double型的精度为:18
其中1.#QNAN0表示超出了计算机可表示的范围,也就是说我这里应该弄错了什么,但是目前还不太明白,暂略。
void类型
1. 函数无返回值时,函数类型应为void。
2. 函数无入参时,圆括号内应写void。
数组
1. 定义数组时,必须确定它的长度。
int a[];
提示错误:
error: array size missing in 'a'
因为编译器无法确定a在内存中的大小,也就无法为它分配存储空间。
2. 要确定长度,可以直接赋值,或者声明其长度。
int a[] = {1,2,3}, b[10];
printf("a的长度为:%d\nb的长度为:%d\n", sizeof(a), sizeof(b));
a的长度为:12
b的长度为:40
当然也可以既赋值又声明长度,其中长度决定大小:
int a[4]={1,2,3};
printf("a的第4个值是:%d\n", a[3]);
a的第4个值是:0
定义int
型数组时,如果对部分元素赋值,那么未被赋值的元素,会默认赋值为0。如果是char
型数组,则会是\0
(ASCII码中编号为0的字符)。
3. 可以使用下标对数组中的值进行存取,第1个元素的下标为0。
int a[] = {1,2,3}, b[10];
printf("数组a中的值依次为:%d\t%d\t%d\n", a[0], a[1], a[2]);
a[0] = 9;
a[1] = a[0];
a[2]++;
printf("数组a中的值依次为:%d\t%d\t%d\n", a[0], a[1], a[2]);
数组a中的值依次为:1 2 3
数组a中的值依次为:9 9 4
这里不知为何两个\t
输出的制表符长度不一样,暂略。
4. 字符串
C语言处理字符串时,会将\0
作为字符串的结尾。比如printf()
函数:
char a[10] = {'a', 'b', 0, 'c', 'd'};
printf("字符串a:%s,长度为:%d,占用空间为:%d Byte\n", a, strlen(a), sizeof(a));
a[2] = 1;
printf("字符串a:%s,长度为:%d,占用空间为:%d Byte\n", a, strlen(a), sizeof(a));
字符串a:ab,长度为:2,占用空间为:10 Byte
字符串a:abcd,长度为:5,占用空间为:10 Byte
找不到的话会一直输出,直到找到\0
为止:
char a[5] = {'a', 'b', 'c', 'd','e'};
printf("字符串a:%s,长度为:%d,占用空间为:%d Byte\n", a, strlen(a), sizeof(a));
字符串a:abcdea,长度为:8,占用空间为:5 Byte
输出的结果往往很离谱。
所以,定义字符型数组的时候,我们需要给\0
留个位置,数组长度要比我们所需要定义的字符串长度多1位:
char a[6] = {'a', 'b', 'c', 'd','e',0};
printf("字符串a:%s,长度为:%d,占用空间为:%d Byte\n", a, strlen(a), sizeof(a));
字符串a:abcde,长度为:5,占用空间为:6 Byte
但大概是特殊问题特殊处理,C语言也提供了属于字符型数组的独特定义方式:
char a[] = "abcde";
printf("字符串a:%s,长度为:%d,占用空间为:%d Byte\n", a, strlen(a), sizeof(a));
字符串a:abcde,长度为:5,占用空间为:6 Byte
这种定义方式自动在字符串末尾补上了\0
。
二维数组
定义时可以一行一行(以数组为单位)赋值,也可以一个一个(以元素为单位)赋值:
int a[3][3] = {{1,2,3},{4,5,6}}, b[2][3] = {1,2,3};
printf("数组a中未被定义元素的值为:a[2][0]:%d,a[2][1]:%d,a[2][2]:%d\n",a[2][0], a[2][1], a[2][2]);
printf("数组b中未被定义元素的值为:b[1][0]:%d,b[1][1]:%d,a[1][2]:%d\n",b[1][0], b[1][1], b[1][2]);
无论是哪种,赋值顺序都是 从左到右,从上到下 。未赋值的元素都会被默认赋值为 0 。
数组a中未被定义元素的值为:a[2][0]:0,a[2][1]:0,a[2][2]:0
数组b中未被定义元素的值为:b[1][0]:0,b[1][1]:0,a[1][2]:0
指针
存储地址的变量类型,与取地址运算符&
、解引用运算符*
搭配使用。
使用指针来输出字符串的每一个元素:
int main(){
char name[] = "Vivien Leigh", *p = name;
for(;*p!=0;p++){
printf("%c\n",*p);
}
}
V
i
v
i
e
n
L
e
i
g
h
这里用到了两个特殊的地方。
一是数组类型变量name
存储的也是地址,因此可以直接用来给指针变量p
赋值,并且是第1个元素的地址,所以,char *p = name
等效于 char *p = &name[0]
;
二是由于数组元素的地址是连续的,因此p++
总是能从指向当前元素变为指向其在数组中的下一个元素。
结构体
定义结构体类型的语法为:
struct 结构体类型名 {定义成员属性,不能赋值};
定义结构体变量的语法为:
struct 结构体类型名 变量名 = {填写成员属性值,不能定义};
常常搭配成员访问运算符.
和间接访问运算符->
使用。
int main(){
struct stu{
int age;
char *name;
char sex;
float score;
};
struct stu stu1[]={
{12,"张小明",'男',87.5},
{11,"李小红",'女',90},
{12,"王小刚",'男',64.5}
}, *p=&stu1;
printf("%s的成绩是:%.1f\n%s的成绩是:%.1f\n%s的成绩是:%.1f\n\n", stu1[0].name, stu1[0].score, stu1[1].name, stu1[1].score,stu1[2].name, stu1[2].score);
printf("%s的成绩是:%.1f\n%s的成绩是:%.1f\n%s的成绩是:%.1f\n\n", (*p).name, (*p).score, (*(p+1)).name, (*(p+1)).score,(*(p+2)).name, (*(p+2)).score);
printf("%s的成绩是:%.1f\n%s的成绩是:%.1f\n%s的成绩是:%.1f\n", p->name, p->score, (p+1)->name, (p+1)->score,(p+2)->name, (p+2)->score);
}
张小明的成绩是:87.5
李小红的成绩是:90.0
王小刚的成绩是:64.5
张小明的成绩是:87.5
李小红的成绩是:90.0
王小刚的成绩是:64.5
张小明的成绩是:87.5
李小红的成绩是:90.0
王小刚的成绩是:64.5
这里stu1[0].name
、(*p).name
、p->name
三者等价,并且由于运算优先级的缘故,(*p).name
的圆括号不能缺少。
另外,char *name;
如果使用 char name[]
的话会报错,提示"flexible array member not at end of struct
",意为伸缩数组须是结构体的最后一个成员。
按理说用char name[6];
的话就应该没问题,但是并不:
张小明许的成绩是:87.5
李小红某杉ㄊ牵?0.0
王小刚械某杉ㄊ牵?4.5
张小明许的成绩是:87.5
李小红某杉ㄊ牵?0.0
王小刚械某杉ㄊ牵?4.5
张小明许的成绩是:87.5
李小红某杉ㄊ牵?0.0
王小刚械某杉ㄊ牵?4.5
不明白,暂略。
运算符
算术运算符
/:取商
%:取余
++:整数部分进1
float a = 2.5;
printf("%f\n", a);
a ++;
printf("%f", a);
2.500000
3.500000
++ 与 = 一起使用时,位置不同,执行顺序也不同:
int a=1, b, c;
b = a++;
printf("++在后时,先赋值后自加,所以b为:%d\n", b);
a = 1;
c = ++a;
printf("++在前时,先自加后赋值,所以c为:%d\n", c);
++在后时,先赋值后自加,所以b为:1
++在前时,先自加后赋值,所以c为:2
--:整数部分减1
…其余略。
关系运算符
==、!=、>、<、>=、<=,判断为真时,运算结果为1。
printf("%d\t%d\t%d\t%d\t%d\t%d\t",1==0,1!=0,1>0,1<0,1>=0,1<=0);
0 1 1 0 1 0
逻辑运算符
&&:与,两边表达式值都为1时,运算结果才为1。
||:或,一边表达式值为1,运算结果即为1。
!:非,表达式值为0时,运算结果为1。
int a=1, b=1, c=0;
printf("1&&1:%d\n1&&0:%d\n1||0:%d\n!1:%d\n!0:%d\n",a&&b, a&&c, a||c, !a, !c);
1&&1:1
1&&0:0
1||0:1
!1:0
!0:1
位运算符
&:与
|:或
^:异或,相同时运算结果为0,不同时运算结果为1
~:取反
<<:按位左移,大多数时候相当于*2
>>:按位右移,大多数时候相当于/2
int a=72, b=10;
char s[32];
itoa(a,s,2);
printf("%d以二进制表示为:\n%032s\n", a, s);
itoa(b,s,2);
printf("%d以二进制表示为:\n%032s\n", b, s);
itoa(a&b,s,2);
printf("%d和%d以 &位运算 后结果的二进制结果为:\n%032s\n", a, b, s);
itoa(a|b,s,2);
printf("%d和%d以 |位运算 后得到的二进制结果为:\n%032s\n", a, b, s);
itoa(a^b,s,2);
printf("%d和%d以 ^位运算 后得到的二进制结果为:\n%032s\n", a, b, s);
itoa(a<<1,s,2);
printf("%d以 <<位运算 后得到的二进制结果为:\n%032s\n", a, s);
itoa(a>>1,s,2);
printf("%d以 >>位运算 后得到的二进制结果为:\n%032s\n", a, s);
itoa(~a,s,2);
printf("%d\t%d\n", a, b);
printf("%d以 ~位运算 后得到的二进制结果为:\n%032s\n", a, s);
itoa(~b,s,2);
printf("%d\t%d\n", a, b);
printf("%d以 ~位运算 后得到的二进制结果为:\n%032s\n", b, s);
72以二进制表示为:
00000000000000000000000001001000
10以二进制表示为:
00000000000000000000000000001010
72和10以 &位运算 后结果的二进制结果为:
00000000000000000000000000001000
72和10以 |位运算 后得到的二进制结果为:
00000000000000000000000001001010
72和10以 ^位运算 后得到的二进制结果为:
00000000000000000000000001000010
72以 <<位运算 后得到的二进制结果为:
00000000000000000000000010010000
72以 >>位运算 后得到的二进制结果为:
00000000000000000000000000100100
72 0
72以 ~位运算 后得到的二进制结果为:
11111111111111111111111110110111
72 0
0以 ~位运算 后得到的二进制结果为:
11111111111111111111111111111111
这里发生了一件奇怪的事情:itoa(~a,s,2);
修改了b的值。
我不明白是为什么,尝试做了一下修改:
......
int c=10;
itoa(~a,s,2);
printf("%d\t%d\t%d\n", a, b, c);
printf("%d以 ~位运算 后得到的二进制结果为:\n%032s\n", a, s);
itoa(~b,s,2);
printf("%d\t%d\n", a, b);
printf("%d以 ~位运算 后得到的二进制结果为:\n%032s\n", b, s);
输出结果为:
72 10 0
72以 ~位运算 后得到的二进制结果为:
11111111111111111111111110110111
72 10
10以 ~位运算 后得到的二进制结果为:
11111111111111111111111111110101
结果正确。但是c的值错了。我又将c换了几个其它的值试试,还是一样的运行结果,即:
不管c是什么值,在itoa(~a,s,2);
执行后,都会被修改为0。
完全不懂为什么。。。暂略。
赋值运算符
注意一下如 ^= 的运算符组合形式即可,略。
其它运算符
sizeof():取变量占用内存大小(以Byte为单位)
int a, b;
printf("size of int:%d", sizeof(a));
size of int:4
&:取地址运算符,返回一个地址
int a, b;
printf("请输入a:");
scanf("%d", &a);
printf("请输入b:");
scanf("%d", &b);
printf("a:%d,b:%d\n", a, b);
请输入a:1
请输入b:2
a:1,b:2
*:解引用运算符,指向一个变量
int a, b;
printf("请输入a:");
scanf("%d", &a);
printf("请输入b:");
scanf("%d", &b);
printf("a:%d,b:%d\n", a, b);
int *p, *q;
p = &a;
q = &b;
*p = 10;
*q = 20;
printf("a:%d,b:%d\n", a, b);
请输入a:1
请输入b:2
a:1,b:2
a:10,b:20
a?x:y :三目运算符,若a表达式返回值为1,则x,否则y。
int a, b=1;
a = (1<0)?10:20;
printf("a被赋的值:%d\n", a);
a = (1>0)?10:20;
printf("a被赋的值:%d\n", a);
a = b?0:1;
printf("a被赋的值:%d\n", a);
a = a?0:1;
printf("a被赋的值:%d\n", a);
a被赋的值:20
a被赋的值:10
a被赋的值:0
a被赋的值:1
条件语句
待补充。
函数
待补充。