简介:适用于快速复习
基本概念
C语言程序执行过程
.c
预
编
译
→
\underrightarrow{预编译}
预编译 . i
编
译
→
\underrightarrow{编译}
编译 .s
汇
编
→
\underrightarrow{汇编}
汇编 目标文件(.o/.obj)
链
接
→
\underrightarrow{链接}
链接 可执行文件.exe
详情>>click here
.c是源代码,经过编译预处理得到.i文件,.i经过编译器编译得到.s(汇编代码)然后汇编得到.o文件,编译器一次只能编译一个.c文件成.o/.obj目标文件(二进制代码),当所有.c文件编译成.o之后,通过链接(构建)得到.exe可执行文件
关键字 static
静态本地变量是特殊的全局变量,有本地作用域(只能在函数中访问),全局生存期(跳出函数后,该变量仍存在)
队列和栈的区别
队列先进先出,栈先进后出
数据结构中的栈和计算机内存的栈区
栈是一种数据结构,我们想要一个栈的C++实现时候,需要自己设计,比如自定义一个类实现pop,push等功能。计算机内存中的栈区就是采用栈这种数据结构来存储数据的,在汇编中已经有pop,push等指令可以对栈区的数据进行操作了。
数据结构有哪几种?
1)集合
2)线性结构:线性表、栈、队列、双队列、数组、串
3)树形结构
4)图
堆(heap)和栈(stack)的区别
1)堆需要自己通过malloc申请,申请完要释放(否则会产生内存泄露)。栈是自动分配,速度较快(比如:调用函数时候会开辟一个栈,函数调用结束会回收栈)
2)堆是不连续的内存,栈是一块连续的内存
3)堆的空间一般比较大,栈的空间一般比较小
4)堆的头部用一个字节存放堆的大小,内容由程序员安排;栈在调用函数时,第一个进栈的是主函数下一条指令的地址,然后是函数的各个参数,然后是函数中的局部变量(注意静态变量不入栈)。当本次调用结束时,局部变量先出栈,然后是参数,最后是指令地址,程序有该点继续运行。
全局变量和局部变量在内存中是否有区别?
全局变量储存在静态数据库,局部变量存储在栈。
注意:C语言中的堆是heap,栈是stack,微机原理中的堆栈是stack,对应C语言中的栈
关键字break和continue的区别
break是结束循环往下走,或者是结束switch(switch不加break,则程序会往下运行),continue是跳出此轮循环,进入下轮循环
指针和数组的区别
1)数组是用于存储多个相同类型数据的集合;指针相当于一个变量,它存放的是其他变量在内存中的地址。
2)同类型的指针变量可以相互赋值,数组不行,只能一个元素一个元素赋值或拷贝。
3)数组在内存中时连续存放的,开辟一块连续的内存空间;指针指向地址的内容在内存中不连续。
冒泡排序的思想?
N个数排序,从前两个数开始进行比较,大的放在后,一直比到最后一个数,最大值放在最后,下一轮循环重复操作,可以少比一轮。
头文件的作用
通过头文件来调用库功能
预处理三种类型
宏定义、文件包括、条件编译
ifndef/define/endif的作用
防止头文件的重复包含和编译。
extern关键字的作用
用来声明变量或者函数已在外部文件中定义了,声明后就可以直接用了,比如有一个全局变量在fun.c中定义
fun.c
int a = 10;
在main.c中想使用,就写成下面形式,下面这种写法是声明,不是定义
int main(){
extern int a;
printf("%d",a);
return 0;
}
1.常量、变量定义、输入输出
#include <stdio.h>
int main()
{
//常量定义
const int Amount = 100;
//变量定义
int a = 0;
float b = 0;
double c = 0;
printf("请输入一个整数和两个小数:");
scanf("%d %f %lf",&a,&b,&c);
printf("%d + %f + %lf = %lf\n",a,b,c,a+b+c);
return 0;
}
2.if、switch判断
#include <stdio.h>
int main() {
int price, bill;
printf("请输入价格和票面:");
scanf("%d %d", &price, &bill);
if (bill >= price) {
printf("商品价格为:%d\n", price);
printf("找零:%d", bill - price);
} else
printf("钱不够");
int number;
scanf("%d", &number);
switch (number) {
case 1:
printf("number = 1\n");
break;
case 2:
printf("number =2\n");
break;
default:
printf("others\n");
}
return 0;
}
知识点:continue和break在循环体中的作用,continue会跳入下一轮循环,break会结束循环
3.循环
- if循环
#include <stdio.h>
int main()
{
int a, i;
scanf("%d", &a);
for (i = 0; a != 0;i++)
{
a /= 10;
}
printf("这个数字有%d位", i);
return 0;
}
- while、do …while
#include <stdio.h>
int main()
{
int a, i = 0;
scanf("%d", &a);
do
{
a /= 10;
i++;
}
while (a != 0);
printf("这个数字有%d位", i);
return 0;
}
//**************
//猜数字游戏
//**************
#include <stdio.h>
#include <time.h>
#include <stdlib.h>
int main() {
int a, count = 0, number;
srand(time(0));
number = rand() % 100 + 1;
printf("请猜数字(1~100):");
scanf("%d", &a);
while (number != a) {
if (a > number)
printf("大了\n");
else if (a < number)
printf("小了\n");
count++;
printf("请猜数字:");
scanf("%d", &a);
}
printf("猜对了!数字是%d,一共猜了%d次", number, count);
return 0;
}
知识点:随机数、随机种子、while循环
4.函数
- 辗转相除法求最大公约数(gcd)
//辗转相除法函数定义
#include <stdio.h>
int f_gcd(int m, int n) {
int t, gcd;
if (m < n) {
t = m;
m = n;
n = t;
}
while (t > 0) {
t = m % n;
m = n;
n = t;
}
gcd = m;
return gcd;
}
int main() {
int m, n, gcd;
scanf("%d %d", &m, &n);
gcd = f_gcd(m, n);
printf("%d", gcd);
return 0;
}
知识点:函数定义
//辗转相除法函数定义
#include <stdio.h>
//声明函数
int f_gcd(int m, int n);
int main() {
int m, n, gcd;
scanf("%d %d", &m, &n);
gcd = f_gcd(m, n);
printf("%d", gcd);
return 0;
}
//函数定义
int f_gcd(int m, int n) {
int t, gcd;
if (m < n) {
t = m;
m = n;
n = t;
}
while (t > 0) {
t = m % n;
m = n;
n = t;
}
gcd = m;
return gcd;
}
知识点:声明函数
5.数组
#include <stdio.h>
int main() {
int a[] = {1, 3, 5, 7, 9};
int i;
for (i = 0; i < sizeof(a) / sizeof(a[0]); i++) {
printf("a[%d]:%d\n", i, a[i]);
}
return 0;
}
知识点:sizeof()可以求出变量所占多少字节。
6.指针
#include <stdio.h>
void f(int *p);
int main() {
int i = 3;
printf("&i = %p\n", &i);
f(&i);
return 0;
}
void f(int *p) {
printf("p = %p\n", p);
}
结果:
&i = 0065FECC
p = 0065FECC
知识点:
1.指针,p的值是变量i的地址0065FECC,*p的值是3
2.%x和%p都是十六进制输出,前者是小写字母,后者是大写字母,专门用来表示地址的。
3.指针运算符&
∗
*
∗,前者是取地址,后者是取变量(某个地址的变量)
4.指针的作用:用来return多个值,定义的函数只能返回一个变量的值,当多个变量需要返回时,需要用指针,因为用指针修改变量其实是在内存中的地址上修改内容。
#include <stdio.h>
void f(int *p);
int main() {
int i[] = {1, 2, 3, 4};
int *p = &i;
printf("&i = %p\n", &i);
printf("p = %p\n", p);
printf("*p = %d\n\n", *p);
p++;
printf("p = %p\n", p);
printf("*p = %d", *p);
return 0;
}
输出结果
&i = 0065FEC0
p = 0065FEC0
*p = 1
p = 0065FEC4
*p = 2
7.字符串处理
#include <stdio.h>
int main() {
char str;
int letter = 0;
int number = 0;
int space = 0;
int cnt = 0;
printf("请输入一串字符:");
while ((str = getchar()) != '\n') {
if ((str >= 'a' && str <= 'z') || (str >= 'A' && str <= 'Z'))
letter++;
if ((str >= '1' && str <= '9'))
number++;
if (str == ' ')
space++;
cnt++;
}
printf("字母%d个,数字%d个,空格%d个,长度为%d", letter, number, space, cnt);
return 0;
}
知识点:getchar()一次只能读入一个字符,gets()能一次读入一个字符串
#include <stdio.h>
int main() {
char str[100];
printf("请输入一串字符:");
gets(str);
printf("长度为%d", strlen(str));
return 0;
}
知识点:gets()会给字符数组末尾加一个’\0’表示输入结束
8.字符串和字符串数组(字符串指针)
#include <stdio.h>
#include <string.h>
int main() {
char *str = "hello";
char *p ;
p = strchr(str, 'l');
//打印出出 llo
printf("%s\n", p);
//打印出 l
printf("%c\n", *p);
// which one means address?
printf("%p\n", &p);
printf("%c\n", *p);
printf("%p\n", p);
p++;
printf("\n");
printf("%p\n", &p);
printf("%c\n", *p);
printf("%p\n", p);
return 0;
}
结果:
llo
l
0065FECC
l
00404002
0065FECC
l
00404003
知识点:
1.单个字符串定义可以写成char *a,也可以写成char a[],定义的char *a可以当数组来使用。[什么情况不可以互用?看这里]
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main() {
char str[] = "hello";
char *p = strchr(str, 'l');
*p = '\0';
printf("%s\n", str);
return 0;
}
2.通过最上面的例子,我们能很好地理解字符串指针。strchr返回的是第一个“l”的地址,p打印出来是llo(%s),*p打印出来是l(%c)。这是因为char *p完全可以当作数组来用,因此打印数组p的结果就是整个数组。
我们再看,p++前后打印p的结果是00404002、00404003,*p打印的结果都是‘l’,说明p是‘l’的地址,p++之后,p指向下一个’l’,故 *p输出都是’l’。
那么&p是什么呢?应该是p的地址。
#include <stdio.h>
#include <string.h>
int main() {
char *a[] = {"jan", "feb", "mar", "apr", "may", "jun", "jul", "aug", "sep", "oct", "nov", "dec"};
int n;
scanf("%d", &n);
printf("%s\n", a[n - 1]);
return 0;
}
知识点:*a[0]、*a[1]…的值是地址值,地址的内容是月份
#include <stdio.h>
#include <string.h>
int main() {
char *a = "hello";
char b[100];
//string copy
strcpy(b, a);
printf("%s %s\n", a, b);
//string compare
if (strcmp(b, a) == 0) {
printf("相等\n");
}
//string length
printf("字符长度:%d\n", strlen(a));
//string cat
printf("字符串拼接%s", strcat(b, a));
return 0;
}
知识点:字符串复制、拼接、比较、求长度。注意,strcpy,strcat是不安全的,因为不知道被拼接的对象空间是否够,所以用安全版本的函数strncpy,ctrncat,strncmp
9.动态内存分配
注意一定要强制类型转换,因为malloc出来没有类型,需要自己根据需要添加类型转换
int *p = (int *)malloc(4);
#include <stdio.h>
#include <stdlib.h>
int main() {
int *p = 0;
int n = 4;
p = (int *)malloc(n * sizeof(int));
int i;
for (i = 0; i < n * sizeof(int); i++) {
printf("%p\n", p[i]);
}
free(p);
return 0;
}
结果
00A42ED8
00A400C0
79705C73
63726F74
0F1AFAD8
0000B9B4
00A400C0
00A43EE0
6C694620
4E5C7365
49444956
50472041
6F432055
7475706D
20676E69
6C6F6F54
知识点:
1.动态内存分配用于不确定长度的数据,malloc类型为void*,需要做类型转换,返回值为申请来的内存地址
2.动态内存分配的内存不连续
3.用完要free(),需要注意的是free()的参数可以是NULL(也就是0),也可以是p,但不能是p++后用free(),因为这free的不是申请来的内存地址,会报错