C语言学习随笔

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、longscanf 中要使用%ld
doublescanf 中要使用%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、逃逸字符
图源:中国大学MOOC C语言程序设计CAP 浙江大学 翁凯
33、类型转换

(1)自动类型转换(小转大):当运算符的两边出现不一致的类型时,会自动转换成较大的类型。(大是指能表达的数的范围更大)

  • 对于printf,任何小于int的类型会被转换成intfloat会被转换成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&
  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值