1、C语言----编译语言;
python----解释语言。
2、指针是C语言的灵魂。
3、计算两个数的最大公约数:辗转相除法。
4、本地(局部)变量定义时未初始化,值是随机的;全局变量未初始化会得到0
值,指针会得到NULL
值。
5、const
关键字修饰变量,表示变量的值一旦初始化,就不能再修改了;
const int AMOUNT = 100;
const
修饰过的变量变成read-only variable,试图修改,编译器会报错。
6、两个整数的运算结果只能是整数。
10/3 = 3 10.0/3 = 3.333333
10/3 * 3 = 9 10.0/3 * 3 = 10.000000
7、a++
: a加1以前的值;
++a
: a加1以后的值。
8、else
的匹配:else
总是和最近的if
匹配
9、非“0”即真。
10、switch-case
,控制表达式只能是整型(char
/ short
/ int
/ long
)的结果。
11、在printf
里百分号%要使用“%%”转义。
12、绝对值库函数:
先#include <math.h>
求整数x的绝对值:int abs(int x);
求小数(浮点数)x的绝对值:double fabs(double x);
13、4E-2
等价于 4e-2
= 4 * 10-2。
14、素数(质数):大于1的自然数中,只有“1”和自身两个因数的数。1不是素数。
- 判断一个数n是不是素数:
- 2~√n遍历看是否存在n的因子;
- 素数筛;
15、long
在 scanf
中要使用%ld
double
在 scanf
中要使用%lf
16、for
循环:先判断条件再执行循环体。如果初值不满足条件,一次都不执行。
//例如:
for( int i = 100; i < 100; i ++ )
printf("test\n");
//是没有任何输出的!
break
:跳出循环体执行循环后面的语句;
continue
:跳过当次循环中剩下的语句,执行下一次循环。
17、无入口参数的函数调用:
括号()起到了表示函数调用的重要作用,即使没有入口参数,()也不能少。
#include <stdio.h>
void solution() {
printf("Hello World!\n");
}
int main() {
solution();
return 0;
}
18、没有返回值的函数:可以没有return
;也可以有return
,但是不能使用带值的return
。
#include <stdio.h>
void solution() {
printf("Hello World\n");
return;
printf("Programming is fun\n");
}
int main() {
solution();
return 0;
}
只输出了Hello World
Programming is fun并没有输出
19、函数原型:
#include <stdio.h>
void sum(int begin, int end); //函数原型(声明)
//可以只给出参数类型,不给出参数名
//void sum(int , int ); √
//给出的参数名也可以与函数定义部分给出的参数名不相同
//void sum(int a, int b); √
//目的是告诉编译器这个函数长什么样子(名称、参数(类型和数量)、返回值类型)
int main() {
sum(1, 10);
sum(20, 30);
sum(35, 45);
return 0;
}
void sum(int begin, int end) {
//函数定义
int sum = 0;
for(int i = begin; i <= end; i++)
sum += i;
printf("%d到%d的和是%d\n", begin, end, sum);
}
20、本地(局部)变量:函数的每次运行,就产生了一个独立的变量空间,在这个空间中的变量,是函数的这次运行所独有的,称作本地(局部)变量。
- 定义在函数内部的变量就是本地变量;
- 参数也是本地变量;
- 生存期/作用域,
{}
内,即块内; - 块外面定义的变量在里面仍然有效;
- 块里面定义了和外面同名的变量则掩盖了外面的;
#include <stdio.h>
int main() {
int a = 5, b = 6;
{
int a = 0;
printf("a=%d\n", a); //a=0
}
printf("a=%d,b=%d\n", a, b); //a=5,b=6
return 0;
}
- 不能在一个块内定义同名的变量;
21、函数没有参数时,该写成void f(void);
还是void f();
void f(void);
明确告诉编译器函数f不接收任何的参数;void f();
表示函数f的参数未知,并不表示没有参数;
22、C语言不允许函数嵌套定义,可以在一个函数中写另一个函数的声明,但是不能写另一个函数的body。
23、main
函数又称主函数,作为绝大部分C程序的唯一入口,是要求有返回值的,该返回值返回给操作系统来表明该程序的执行状况。返回0
代表程序正常执行成功,返回非0
值代表程序异常结束。因此返回值需要是int
整型,于是有了int main()
的规范。
如果使用void main()
,即声明主函数没有返回值,程序虽能编译、运行成功,却不利于操作系统判断其执行状态,这对由很多C程序构成的大型项目来说可能是致命的。
24、(-25%10) == -5
25、完数(Perfect Number),又称完全数、完美数或完备数,是指它的所有真因数(除自身以外的因数)之和刚好等于自身的数。
- 例如:6 = 1 + 2 + 3;28 = 1 + 2 + 4 + 7 + 14;
- 注意:“1”不是完数。
26、数组:在内存中连续存储的具有相同类型的一组数据的集合。
int a[5];
(1)数组名a除了表示该数组之外,还表示该数组首元素的地址(是一个const
的指针变量,不能被赋值,始终指向该数组的首元素);
(2)初始化
- 完全初始化:定义数组时给所有元素赋初值:
int a[5] = {
1, 2, 3, 4, 5};
- 不完全初始化:只给一部分元素赋初值。
int a[5] = {
1, 2};
定义的数组 a
有 5 个元素,但花括号内只提供两个初值,这表示只给前面两个元素 a[0]
、a[1]
初始化,而后面三个元素都没有被初始化。不完全初始化时,没有被初始化的元素自动为 0。
- 如果定义数组时就给数组中所有元素赋初值,那么就可以不指定数组的长度,因为此时元素的个数已经确定了。系统会自动分配空间。如:
int a[5] = {
1, 2, 3, 4, 5};
可以写成
int a[] = {
1, 2, 3, 4, 5};
但是要注意,只有在定义数组时就初始化才可以这样写。如果定义数组时不初始化,那么省略数组长度就是语法错误。
- 特别地,在
C99
中,还可以使用定位:
int a[7] = {
[0] = 2, [2] = 3, 6}; //2, 0, 3, 6, 0, 0, 0
以及
int a[] = {
[1] = 2, 4, [5] = 6}; //0, 2, 4, 0, 0, 6(6个元素)
- 二维数组初始化:
int a[][5] = {
{
0, 1, 2, 3, 4},
{
2, 3, 4, 5, 6},
};
- 列数必须给出,行数可以由编译器来数;
- 每行一个{},逗号分隔;
- 最后一个逗号可以存在,有古老的传统;
- 如果省略,表示补零;
- 也可以用定位(
C99
)。
(3)可以用sizeof
求出整个数组所占内存空间的大小,单位是字节:
length = sizeof(a)/sizeof(a[0]);
// 得到数组a的大小(元素个数)。
(4)数组的互相赋值:
int a[] = {
1, 2, 3, 4, 5, 6, 7, 8, 9};
int b[] = a; //是错误的
要想实现这样的赋值,必须采用遍历的方法:
for(int i = 0; i < length; i++)
b[i] = a[i];
(5)数组作为函数参数时,不能在[ ]中给出数组的大小,也不能利用sizeof
来计算,必须再用另一个参数来传入数组的大小:
int search(int key, int a[], int length);
27、sizeof
运算符:给出某个类型或变量在内存中所占据的字节数:
sizeof(int)
sizeof(i)
sizeof
是静态运算符,它的结果在编译时刻就确定了;- 不要在
sizeof
的括号里做运算,这些运算不会做的。
#include <stdio.h>
int main() {
int a = 6;
printf("sizeof(int) = %d\n", sizeof(int));//sizeof(int) = 4
printf("sizeof(a++) = %d\n", sizeof(a++));//sizeof(a++) = 4
printf("a = %d\n", a);//a = 6
return 0;
}
28、8进制和16进制:
- 以
0
开头的数字字面量是8进制
; - 以
0x
开头的数字字面量是16进制
; %d
以十进制输入/输出;%o
以八进制输入/输出;%x
以十六进制输入/输出(对应字母小写);%X
以十六进制输入/输出(对应字母大写)。
#include <stdio.h>
int main() {
int c = 012;
int i = 0x12;
int j = 0x1A;
printf("c = %d, i = %d, j = %d\n", c, i, j); //c = 10, i = 18, j = 26
printf("c = %o, i = %x, j = %x\n", c, i, j); //c = 12, i = 12, j = 1a
printf("j = %X\n", j); //j = 1A
return 0;
}
29、%e
or %E
:以科学计数法形式输出:
#include <stdio.h>
int main() {
double ff = 1234.56789;
printf("ff = %e\n", ff); //ff = 1.234568e+003
printf("ff = %E\n", ff); //ff = 1.234568E+003
return 0;
}
30、printf
输出inf
表示超过范围的浮点数:±∞;
printf
输出nan
表示不存在的浮点数。
#include <stdio.h>
int main() {
printf("%f\n", 12.0/0.0); //inf
printf("%f\n", -12.0/0.0); //-inf
printf("%f\n", 0.0/0.0); //nan
return 0;
}
31、浮点运算的精度:
- 带小数点的字面量是
double
而非float
; float
需要用f或F后缀来表明身份;- 不能简单地用
( f1 == f2 )
? 来判断两个浮点数f1和f2是否相等,应该用fabs( f1 - f2 ) < 1e-8
?
#include <stdio.h>
int main() {
float a, b, c;
a = 1.345f;
b = 1.123f;
c = a + b;
if(c == 2.468)
printf("相等\n");
else
printf("不相等!\n"); //输出了这一句
}
32、逃逸字符
33、类型转换
(1)自动类型转换(小转大):当运算符的两边出现不一致的类型时,会自动转换成较大的类型。(大是指能表达的数的范围更大)
- 对于
printf
,任何小于int
的类型会被转换成int
,float
会被转换成double
; - 但是
scanf
不会,要输入short
,需要%hd
(2)强制类型转换(大转小):把一个量强制转换成另一个类型(通常是较小的类型)。
比如:
(int)10.2 //(类型)值
(short)32
- 注意这时候的安全性,小的类型不总能表示大的量;
#include <stdio.h>
int main() {
printf("%d\n", (short)32768); //-32768
printf("%d\n", (char)32768); //0
return 0;
}
注意:强制类型转换只是从原变量计算出了一个新的类型的值,并不改变原变量的值和类型。
#include <stdio.h>
int main() {
int i = 32768;
short s = (short)i;
printf("%d\n", i); //32768
return 0;
}
- 强制类型转换的优先级高于四则运算
#include <stdio.h>
int main() {
double a = 1.0;
double b = 2.0;
int i = (int)a / b;
//int i = (int)(a / b);
int c = 5;
int d = 6;
double e = (double)(c / d);
//double e = (double)c / d;
return 0;
}
34、int
型变量能表示的最大数为231-1 = 2147483647(十位数)。(109量级)
35、bool
类型(C99
): true
or false
#include <stdio.h>
#include <stdbool.h>
int main() {
bool b = 6>5;
bool t = true;
printf("b = %d\n", b); //b = 1
return 0;
}
36、逻辑表达式中的短路:逻辑运算是自左向右进行的,如果左边部分的结果已经能够决定整个表达式的结果了,就不会做右边的计算。
a == 6 && b += 1 //如果a不等于6,b += 1就不会做了
- 对于
&&
,左边是false
时就不做右边了; - 对于
||
,左边是true
时就不做右边了。
#include <stdio.h>
int main() {
int a = -1;
if( a > 0 && a++ > 1) {
printf("OK\n");
}
printf("a = %d\n", a);//a = -1
return 0;
}
37、条件运算符和逗号运算符
- 条件运算符
count = count>20?count-10:count+10;
等价于
if( count > 20 )
count -= 10;
else
count += 10;
- 逗号运算符:
逗号用来连接两个表达式,并以其右边的表达式的值作为它的结果;
逗号的优先级是所有运算符中最低的,所以它两边的表达式会先计算;
逗号的组合关系是自左向右,所以左边的表达式会先计算,而右边的表达式的值就留下来作为逗号运算的结果。
#include <stdio.h>
int main() {
int i, j;
i = 3+4, 5+6;
j = (3+4, 5+6);
printf("i = %d\n", i);//i = 7 逗号运算的优先级比赋值还低
printf("j = %d\n", j);//j = 11
return 0;
}
作用:在for
循环中使用
for(i=0, j=10; i < j; i++, j--) {
...
}
38、指针
(1) &
运算符:获得变量的地址,它的操作数必须是变量
int i;
scanf("%d", &i);
printf("%p\n", &i);//0xbffd2d6c(输出变量i的地址的十六进制形式)
(2) 指针变量:保存某个变量在内存中的地址的变量
- 普通变量的值是实际的值
- 指针变量的值是具有实际值的变量的地址
(3) *
运算符:访问指针的值所表示的地址上的变量
- 是一个单目运算符
- 可以做右值,也可以做左值
int k = *p;
*p = k + 1;
(4) 指针是const
(const
在*
的后面):表示该指针一旦得到了某个变量的地址,就不能再指向其他变量;
int * const q = &i;//指针变量q指向变量i
*q = 26;//OK
q ++; //ERROR
(5) 所指是const
(const
在*
的前面):表示不能通过这个指针去修改那个变量(并不能使得那个变量成为const
,那个变量的值还是可以被修改,只是通过指针来修改的这种方式被限制了);
const int *p = &i;
//等价于
int const *p = &i;
*p = 26;//ERROR! (*p)是const
i = 26;//OK
p = &j;//OK
//判断哪个被const了的标志是const 在*的前面还是后面
(6) 保护数组值:把数组作为参数传入函数时传递的是地址,所以那个函数内部可以对数组的值进行修改。为了保护数组不被函数破坏,可以设置参数为const
;
int sum(const int a[], int length);
(7) 指针运算
- 给指针变量加1:表示要让指针指向下一个变量,实际上(数值上)加的是
sizeof
(指针所指向的变量的类型),如果指针不是指向一片连续分配的空间(如数组),则这种运算没有意义。
int a[10];
int *p = a;
*(p+1)——>a[1]
*(p+n)等价于p[n]等价于a[n]
- 两个指针变量相减:等于两个指针所保存的地址值相减/
sizeof
(指针所指向的变量的类型)
int a[] = {
0, 1, 2, 3, 4, 5, 6, 7, 8};
int *p = a;//等价于int *p = &a;或int *p = &a[0];
int *q = &a[6];
printf("p=%p\n", p);//p=0xbff11d28
printf("q=%p\n", q);//q=0xbff11d40
printf("q-p=%d\n", q-p);//q-p=6
*p++
:++
优先级更高;- 取出p所指的那个数据来,完事之后顺便把p移到下一个位置去;
- 常用于数组类的连续空间操作。
(8)指针的类型
- 无论指向什么类型,所有指针的大小都是一样的,因为保存的都是变量在内存中的地址;
- 但是指向不同类型的指针是不能直接相互赋值的,这是为了避免用错指针;
void *
表示不知道指向什么东西的指针;- 指针也可以做强制类型转换:
int i;
int *p = &i;
void *q = (void *)p;
(9)动态内存分配
- 使用
malloc
函数需要#include <stdlib.h>
; malloc
申请的内存空间是连续的;malloc
申请的空间大小是以字节为单位的;malloc
的结果类型是void*
,需要类型转换为自己需要的类型:(int*)malloc(n*sizeof(int))
;
#include <stdio.h>
#include <stdlib.h>
int main() {
int number;
scanf("%d", &number);
int* a = (int*)malloc(number * sizeof(int));
for( int i = 0; i < number; i&