计算机编程基础概念
1. 补码的计算
- 正数和负数在计算机中的存储都是以二进制的形式。对于正数,直接存储其二进制表示;对于负数,则需要取补码的形式存储。
- 负数在计算机中的存储最高位表示符号(1:负数,0:正数)。
1.1 反码与补码
- 原码:1000 1000(符号位不变其余位按位取反)
- 反码:1111 1001(反码+1)
1.1.1 示例
- 给定 a = -60,则:
- (~a) + 1 等于 -61,即为 1100 0001(带符号二进制数的补码形式)。
2. 函数声明优化
- 在函数声明中,参数的名称并不是必需的,只有参数的类型是必需的。因此下面也是有效的声明:
1
int max(int, int);
3. 先声明后使用
- 为了不出问题还是先声明好,或者就写在
main
之前。
4. 编译器问题
- 没有特别说明。
5. 数组作为参数传递时
double getAverage(int *arr, int size)
:在函数中可以通过指针访问数组元素,如sum += *(arr + i);
或sum += arr[i];
。
6. 随机数
srand(unsigned int seed)
是初始化随机数生成种子:- 如果当前系统时间作为种子,由于时间是变化的,种子不同,可以产生不同的随机数。
- 计算机中的随机其实不是真正的随机数,如果两次给的种子一样,会生成同样的随机数列。所以,一般会结合不同的时间作为种子来生成随机数,这样更加的随机。
- 使用时,参数一般是
unsigned int
类型的整数,比如srand(10)
; - 如果不想用
srand
,rand()
产生的随机数,多次运行,结果是一样的。
7. 枚举
- 枚举值不能连续,这种枚举无法遍历,枚举中的默认值是前一个+1,int。
8. 输出
%p
是打印地址的格式说明符,用于以十六进制格式输出内存地址。
9. 字符串
- 字符串实际上是使用 null 字符
\0
终止的一维字符数组。 strlen
是函数,sizeof
是运算操作符,二者得到的结果类型为size_t
,即unsigned int
类型。sizeof
计算的是变量的大小,不受字符\0
影响;而strlen
计算的是字符串的长度,以\0
作为长度判定依据。
10. 指针
- 所有实际数据类型,不管是整型、浮点型、字符型,还是其他的数据类型,对应指针的值的类型都是一样的,都是一个代表内存地址的长的十六进制数。
- 在变量声明的时候,如果没有确切的地址可以赋值,为指针变量赋一个
NULL
值是一个良好的编程习惯。赋为NULL
值的指针被称为空指针。 - 如需检查一个空指针,您可以使用
if
语句,如下所示:1
2if(ptr) /* 如果 p 非空,则完成 */
if(!ptr) /* 如果 p 为空,则完成 */ - 我们喜欢在程序中使用指针代替数组,因为变量指针可以递增,而数组不能递增,数组可以看成一个指针常量。
- 函数指针:
int (*p)(int, int) = &max;
//&
可以省略- 其中
max
是定义的函数int max(int x, int y);
11. 回调函数
- 回调函数是由别人的函数执行时调用你实现的函数。例如,当你到商店买东西,刚好你要的东西没有货,于是你在店员那里留下了你的电话,过了几天店里有货了,店员就打了你的电话,然后你接到电话后就到店里去取了货。在这个例子里,你的电话号码就叫回调函数,你把电话留给店员就叫登记回调函数,店里后来有货了叫做触发了回调关联的事件,店员给你打电话叫做调用回调函数,你到店里去取货叫做响应回调事件。
12. 位域
- 位域是一种结构类型,成员是按二进位分配的。例如:
1
2
3
4
5struct bs {
int a:8;
int b:2;
int c:6;
} data; - 位域在本质上就是一种结构类型,不过其成员是按二进位分配的。一个位域存储在同一个字节中,如一个字节所剩空间不够存放另一位域时,则会从下一单元起存放该位域。也可以有意使某位域从下一单元开始。
- 结构体内存分配原则:
- 原则一:结构体中元素按照定义顺序存放到内存中,但并不是紧密排列。每个元素存放的位置一定会在自己大小的整数倍上开始。
- 原则二:在原则一的基础上,检查计算出的存储单元是否为所有元素中最宽的元素长度的整数倍。若是,则结束;否则,将其补齐为它的整数倍。
- 定义位域时,各个成员的类型最好保持一致,比如都用
char
,或都用int
,不要混合使用,这样才能达到节省内存空间的目的。 - 共用体:
- 使用
union
关键字定义,结构和定义类似于结构体,只是使用和内存不一样。共用体中的所有成员是公用内存的,大小是其中最大的成员的内存,后面的会把前面的值占用。
- 使用
13. define
(预处理)
#define
用于定义宏,例如:1
2
3
typedef unsigned char uchartypedef
为数据类型(可以是已有,也可以是用户自定义的)取个别名。#define
和typedef
的区别在于:typedef
仅限于为类型定义符号名称,#define
不仅可以为类型定义别名,也能为数值定义别名。typedef
是由编译器执行解释的,#define
语句是由预编译器进行处理的。
14. 输入与输出
- C 语言把所有的设备都当作文件。【Linux中一切皆是文件】所以设备(比如显示器)被处理的方式与文件相同。以下三个文件会在程序执行时自动打开,以便访问键盘和屏幕:
stdin
,stdout
,stderr
。 - 输入:
scanf("%f", &f);
scanf("%s %d", str, &i);
- 在读取字符串时,只要遇到一个空格,
scanf()
就会停止读取,所以 “this is test” 对scanf()
来说是三个字符串。 scanf()
函数返回的值为:正确按指定格式输入变量的个数;也即能正确接收到值的变量个数。scanf("c1=%c", &c1);
- 输入多个数时,不要加空格。
- 输入:
scanf
,getchar
,gets
- 输出:
printf
,putchar
,puts
15. 文件处理
- 一般流程是先打开文件(
fopen
)–读/写文件(与输入输出基本一致,加f
)–处理–关闭文件(fclose
)。 - 读文件是读到缓冲流中。
- 为什么读的时候是有记忆的?这是因为文件指针在每次读取后会移动到下一个位置,直到文件末尾。
16. 预处理器指令
#define
定义宏#include
包含文件#undef
取消宏定义#ifdef
条件编译#if ... #endif
条件编译#error
生成错误信息#pragma
设置编译器选项
17. 头文件
#include <file>
引用系统文件#include "file"
引用用户头文件- 防止重复包含:
1
2
3
4
// the entire header file content - 建议把所有的常量、宏、系统全局变量和函数原型写在头文件中,在需要的时候随时引用这些头文件。
18. 类型转换
- 类型转换遵循一定的规则,例如:
1
2
3
4
5
6
7
8
9
10
11double <--- float
^
|
|
long
^
|
unsigned
^
|
int <--- short, char
19. 内存管理
- 动态分配内存:
void *calloc(int num, int size);
void *malloc(int num);
void *realloc(void *address, int newsize);
(这个一般不要用)- 例如:
description = (char *)malloc(200 * sizeof(char));
- 释放内存:
void free(void *address);
- 指针释放后要置
NULL
。
20. scanf
格式说明符
d
输入十进制整数o
输入八进制整数x
输入十六进制整数u
输入无符号十进制整数f
或e
输入实型数(用小数形式或指数形式)c
输入单个字符s
输入字符串ld
长整型Lf
双精度浮点数
21. printf
中 %p
的输出应用
%p
表示输出以内存中实际存储一个变量格式(十六进制、32位或64位,视机器的位数而定)的值,通常也就是地址的值,但也不一定,要看具体输出的是什么。