文章目录
2024.04.16
1、*****system函数
需要头文件#include <stdlib.h>
作用是在一个子进程中运行shell命令
2、*****gcc编译过程:预处理,编译,汇编,链接
第一步预处理:宏定义展开,头文件展开,条件编译,删除注释。不做语法检查。(详见main.i)
第二步编译:检查语法,将预处理后的文件编译成汇编文件
第三步汇编:将汇编文件生成目标文件(二进制文件)
第四步链接:把库链接到可执行程序中去,生成可执行文件
gcc -E main.c -o main.i
gcc -S main.i -o main.s
gcc -c main.s -o main.o
gcc main.o -o main.out
3、寄存器、缓存、内存三者的关系
从远近看:cpu《--》寄存器《--》缓存《--》内存 ----硬件
4、数据类型
作用:编译器预算对象分配的内存空间大小
2024.04.17
*****变量 与 常量
const修饰的变量称为 常变量
常量:
是只读的固定值,在程序运行期间不会改变,不能被程序修改的量,可以是任意类型
一般出现在表达式货赋值语句中
分为整型常量、实型常量、字符型常量、字符串常量
两种定义常量的方式:
----使用 #define 宏定义:#define 常量名 值
----使用const关键字:const 数据类型 常量名 = 值
分类:
----字面常量:
直接出现在代码中的数值,例如整数、浮点数、字符或字符串,不需要声明,如 42、3.14、‘A’、“Hello”。
----使用const关键字的常量:
通过 const 关键字声明的具有常量特性的变量,使其值不可修改,如 const int MAX_VALUE = 100。
----符号常量:
通过 #define 预处理指令定义的标识符,用于表示一个固定值,提高代码的可读性和可维护性
----枚举常量:
通过 enum 关键字定义的一组常量,它们通常表示相关的命名值。
2024.04.19
变量:
在程序运行期间可以改变
数据类型:可以理解为创建变量的模具: 固定大小内存的别名;
----基本类型----整型:short、int、long、long long(占用空间大小:short<=int<=long<long long)
字符型:char
实型(浮点型):float、double
sizeof:
不是函数,不需要包含任何头文件,功能是计算一个数据类型的大小,单位是字节,
返回的占用空间大小是为这个变量开辟的大小
返回类型是unsigned int/ unsigned long
数组名只有在
*sizeof 运算符
*取址 & 运算符
*字符串常量初始化的数组 Str[]=“abcdef”
这三种情况下不会发生退化(array decay)
其余情况下调用数组名,都会退化成指向数组首地址的指针
在c语言中,数组名在函数的调用中退化成了一个指针,对函数的参数使用Sizeof,sizeof获取的结果就是指针的大小,而不是数组本身的大小
指针的sizeof
指针是用来记录另一个对象的地址,所以指针的内存大小就等于计算机内部地址总线的宽度。
对一个地址来取大小呢,如果是32位系统的话即为4,如果是64位系统的话为8,
所以呢,在函数中sizeof获取的是指针的长度而不是数组的长度
指针变量的sizeof值与指针所指的对象没有任何关系
整型:
有符号和无符号整型取值范围:
数据类型 占用空间 取值范围
short 2 字节 -32768 到 32767 (-2^15 ~ 2^15-1)
int 4 字节 -2147483648 到 2147483647 (-2^31 ~ 2^31-1)
long 4/8 字节 -2147483648 到 2147483647 (-2^31 ~ 2^31-1)
unsigned short 2 字节 0 到 65535 (0 ~ 2^16-1)
unsigned int 4 字节 0 到 4294967295 (0 ~ 2^32-1)
unsigned long 4 字节 0 到 4294967295 (0 ~ 2^32-1)
字符型:
char:
本质就是一个 1 字节大小的整型
在给字符型变量赋值时,需要用一对英文半角格式的单引号(' ')把字符括起来
字符变量实际上并不是把该字符本身放到变量的内存单元中去,而是将该字符对应的 ASCII 编码放到变量的存储单元中
重点记住:32(空格)开始,可以从键盘上找到。
ascii ’0‘对应48,’A‘对应65,’a‘对应97,大小写字母相差32
转义字符
浮点型:
double比float更精准,打印格式:%lf,%f
float的指数部分有8bit,有符号型最高位代表正负,对应的指数范围为-128~128,所以取值范围为:
-2^128到2^128,约等于-3.4E38 — +3.4E38
double占用8个字节(64位系统),的指数范围是-1023到1024,其取值范围为±1.7E+308到±1.7E-308
大小范围主要取决于指数所占的bit位数。精度取决于尾数位数
类型 符号位 指数位 尾数 有效位数
float 1bit 8bit 23bit log2(23)=7.22
double 1bit 11bit 52bit log2(52)=15.65
long double 1bit 15bit 64bit log2(63)=18.96
数组:指针加法
int a[5] = {1,2,3,4,5};
&a[0] = &a
&a[0]+1 = &a[1]
&a+1 = &a + 5*4
printf输出格式总结
数据类型 输出格式
int %d(有符号十进制整型)
short int %hd
long %ld
long long %lld
unsigned int %u,%o(八进制整型),%x/%X(十六进制大小写整形),
unsigned short %hu
unsigned long %lu
unsigned long long %llu
float %f
double &lf,%e、%E (科学计数法表示的数,输出时用e的大小写)
char %c,(字符型,可以把输出的数字按照ascii码相应转换为对应字符)
char* %s,(字符串 ,输出字符串中的字符,直至遇到'\0')
void* %p,(以十六进制形式输出指针)
% %%,(输出一个百分号)
printf附加格式
字符 含义
l 加在d,u,x,o,前面表示长整型
m 数据最小宽度,
- 左对齐
0 将输出的前面补0,直到占满指定列宽,不可与-搭配使用
m.n m指域宽,对应的输出项在输出设备上所占的字符数,例:3.14的域宽是4,小数点也算
n指精度,用于说明输出的实型数的小数位数,未指定n时,默认精度n = 6
2024.04.20
补码:
在计算机系统中,数值一律用补码来存储
特点:正数的原码、反码、补码相同
**负数的补码为它的反码加1**
***补码符号位不动,其他位求反,最后整个数加1,得到原码
例如 -15的原码为:10001111 (8+4+2+1)
反码为:11110000 (符号位不变,其他位取反)
补码为:11110001 (反码+1)
类型限制符:
extern:声明一个变量,extern 声明的变量没有建立存储空间
const:定义一个常量,常量的值不能修改
Volatile:防止编译器优化代码
register:定义寄存器变量,提高效率。register 是建议型的指令,而不是命令型的指令,如 CPU 有空闲寄存器,那么 register就生效,如果没有空闲寄存器,那么 register 无效。
2024.04.21
内存分区:
参考 内存管理
运行前:
代码区:共享的、只读的
数据区:
data:已初始化的全局变量、静态变量、常量
bss: 未初始化的全局变量、静态变量
运行后:
额外增加了下面两个
栈区:
属于先进后出的数据结构
由编译器管理开辟和释放
变量的生命周期在该函数结束后自动释放
堆区:
容量远远大于栈
没有先进后出的数据结构
由程序员管理开辟和管理释放:malloc,free
类型 作用域 生命周期 存储位置
auto变量(局部变量) 一对{}内 当前函数 栈区
全局变量 项目可用 整个程序运行期 初始化在data段,未初始化在BSS段
static局部变量 一对{}内 整个程序运行期 初始化在data段,未初始化在BSS段
extern变量 整个程序 整个程序运行期 初始化在data段,未初始化在BSS段
static全局变量 当前文件 整个程序运行期 初始化在data段,未初始化在BSS段
extern函数 整个程序 整个程序运行期 代码区
static函数 当前文件 整个程序运行期 代码区
register变量 一对{}内 当前函数 运行时存储在CPU寄存器
字符串常量 当前文件 整个程序运行期 data段
数据区包括:堆,栈,全局/静态存储区。
全局/静态存储区包括:常量区,全局区、静态区。
常量区包括:字符串常量区、常变量区。
代码区:存放程序编译后的二进制代码,不可寻址区。
typedef的使用:
简化结构体关键字struct
给类型起别名
区分数据类型
提高代码可移植性
void的使用:
无类型是不可以创建变量的
可以限定函数返回值
限定函数参数列表
void*万能指针
2024.04.22
栈区
不要返回局部变量。局部变量只在函数内有效,调完函数后内存会被释放,没权限去操作这块内存。
堆区
在函数内开辟堆区内存,调用函数后,内存不会释放,可以使用。
记得手动开辟手动释放。
如果主调函数中没有给指针分配内存,被调函数用同级指针是修饰不到主调函数中的指针的。
数据区
放入的是静态变量,全局变量,常量
static
修饰静态变量
只初始化一次,在编译阶段就分配内存,运行阶段直接使用属于内部链接属性,只在本文件使用
extern
extern全局变量
c语言下,默认的全局变量前都隐藏的加了关键字extern
const
修饰常量
const修饰的全局变量:分配在常量区,受到保护,不允许修改
直接修改,间接修改,都不成功,即使语法通过,运行也会出错
const修饰的局部变量:分配在栈区,直接修改失败,间接修改成功
伪常量,不可初始化数组。
字符串常量
ANSI C 中规定修改字符串常量,结果是未定义的,每个编译器不同。
vs中把相同的字符串常量看作同一个。
字符串是内存中一段连续的char空间,以'\0'(数字0)结尾
字符串常量是由双引号括起来的字符序列,如“hello”
字符串常量 与 字符常量 的不同
'a':字符常量,内存块为:'a'
"a":字符串常量,内存块为:'a' '\0'
占位符%s:表示输出一个字符串,遇到'\0'结束
2024.04.23
printf 和 putchar (printf详细内容见4.19笔记)
putchar():()内可以放变量、数字、转义字符
scanf 和 getchar
scanf可以一次输入多个,接收空格和换行表示结束
getchar只接收一个字符,后面加getchar();//任意键,暂时停留界面,如果不加这个,ch2就会取走回车
2024.04.24
运算符
优先级:1、[] () . ->
2、单目 从右到左:- ~ ++ -- * & ! (类型) sizeof
3、双目 从左到右:算术运算符:/ * %
4、+ -
5、<< >>
6、比较运算符:> >= < <=
7、== !=
8、按位与:&
9、按位异或:^
10、按位或:|
11、逻辑与:&&
12、逻辑或:||
13、三目 从左到右:?=
14、赋值运算符:= /= *= %= += -= <<= >>= &= |=
15、逗号:,
注意:*0不能做除数
*a++是先进行表达式计算,后进行加加;++a是先进行加加,后进行表达式计算
*单目运算符优先级高于双目
*所有非0的值都是真值
*&&同真为真,||同假为假
*(数据类型) 强制进行数据类型转换,不会进行四舍五入
2024.04.25
运算符
按位取反:~
1变0,0变1
左移右移:<< >>
按位与:&
都为1才为1
按位异或:^
两者相异为1,两者相同为0
按位或:|
都为0才为0
打开位:a|~a
关闭位:a&~a
转置位: 将某位置1/0,
交换两个数:
a = a^b;
b = a^b;
a = a^b;
2024.04.26
判断:
if()
{...
}
---------------------------
if()
{...
}
else
{...
}
---------------------------
if()
{...
}
else if()
{...
}
else if()
{...
}
else
{...
}
---------------------------
三目运算符:c=(a>b)?a:b
等价于:if(a>b)
{c=a;
}
else
{c=b;
}
---------------------------
switch(int)//int理解为表达式的结果必须是整型变量
{
case 1:...
break;//没有break会从匹配上的case一直执行到default
case 2:...
break;
.
.
.
default: //如果以上条件都不满足
...
}
---------------------------
循环:
while()
{
}
---------------------------
do
{
}while();
---------------------------
for(i = 0;i<=100;i++)
{
}
---------------------------
注意:
while循环是先判断条件,再执行语句
do while是先执行语句,再判断条件
for是先判断条件,再执行,再++,再判断条件
嵌套循环:外层执行一次,内层执行一周
死循环三种样式
gcc头文件有math.h,要gcc main.c -o main.out -lm
产生随机数三步:
导入头文件<time.h>
添加随机数种子srand((unsigned int)time()NULL)
获取随机数rand()
跳转
break
switch遇到跳出{}
循环中遇到,跳出当前内循环,执行后面代码
嵌套循环中遇到,跳出最近的内循环,执行后面代码
continue
循环中遇到,结束本次循环,立即执行下一次循环
goto
无条件跳转到指定位置,不建议用
2024.04.28
数组
数组就是内存中连续的相同类型的变量空间。
一维数组:
定义:数据类型 数组名[数组元素]
数组a[n] 的元素下标为0到n-1
数组元素可以重新赋值,也可以参与计算。
数组名是一个常量,不允许赋值。
数组名是数组首元素地址。%p打印a和&a[0]相等。
数组内存大小:sizeof(a)
数组单个元素内存大小:sizeof(a[0])
数组元素个数:sizeof(a)/sizeof(a[0])
数组定义十个元素:
a[10] = {1,2,3,4,5,6,7,8,9,10};
a[] = {1,2,3,4,5,6,7,8,9,10};
a[10]={1,2,3,4,5};//后面5个是0
a[10]={0};//10个都是0
a[10]={1};//后面9个是0
int a[10];a[0]=1;//未定义的不能打印不能使用
数组元素必须是常量或者常量表达式(这是c89标准)。
C99标准,支持不定长数组,数组元素可以用变量。
数组下标越界后打印会乱码,可能会报错。a[-1],a[10],可能会报错。
必须会写:
数组最值:假设max=a[0],依次比较有更大的就修改,遍历完找到最大。
数组逆置:设置临时变量=a[0],a[0]和a[9]交换
冒泡排序:
10个元素,第1轮比较9次,确定最大值
9个元素,第2轮比较8次,确定第二大值
8个元素,第3轮比较7次,确定第三大
7个元素,第4轮比较6次,确定
2个元素,第9轮比较1次,确定最小
2024.04.30
二维数组
定义:数据类型 数组名[行][列]
无论多少维数组,在内存中都是连续的
a[3][4],第一个元素a[0][0],最后一个元素a[2][3]
打印,外行内列
二维数组大小:sizeof(a)
二维数组一行大小:sizeof(a[0])
二维数组元素大小:sizeof(a[0][0])
二维数组行数:sizeof(a) / sizeof(a[0])
二维数组列数:sizeof(a[0]) / sizeof(a[0][0])
二维数组首地址:%p打印:a,a[0],&a[0][0]
第二行起始地址:&a[1]
a[0]=100;//错误,a[0]是一整行数据,不是具体的值
初始化:
a[2][3] = {{1,2,3},{4,5,6}};//最好这样写
a[2][3] = {1,2,3,4,5,6};
a[ ][3] = {1,2,3,4,5,6,7};//列必须写!,这样就是三行三列
a[ ][3] = {0};//一行三列,相当于一维,意义不大
三维数组
定义:数据类型 数组名[层][行][列],元素个数:层*行*列
a[2][3][4]=
{
{
{1,2,3,4},
{5,6,7,8},
{1,2,3,4}
},
{
{1,2,3,4},
{5,6,7,8},
{1,2,3,4}
}
};
打印需要三层循环。
三维数组大小:sizeof(a)
三维数组一层大小:sizeof(a[0])
三维数组一行大小:sizeof(a[0][0])
三维数组元素大小:sizeof(a[0][0][0])
三维数组层数:sizeof(a) / sizeof(a[0])
三维数组行数:sizeof(a[0]) / sizeof(a[0][0])
三维数组列数:sizeof(a[0][0]) / sizeof(a[0][0][0])
******
字符数组 和 字符串
字符数组:定义:char a[5] = {'h','e','l','l','o'};//5个
字符数组大小:sizeof(a) = 5
字符:定义:char a = 'a';;
字符串:定义:char *a = "hello";//6个
char a[] = "hello";//6个,自带‘\0’(空字符) 结尾,字符串的结束标志
char a[] = {'h','e','l','l','o','\0'};
char a[6] = {'h','e','l','l','o'};//后面的0等价于‘/0’,但不等同于‘0’
**字符串是字符数组的特例。字符串在c语言本质中是字符数组。**
0和‘/0’结尾的char数组就是一个字符串。
字符串一定是一个char类型的数组。反过来不一定。
字符串拼接
字符串初始化
字符串输入输出:
scanf
scanf("%s",a);//接收空格和换行表示结束
scanf("%9s",a);//约束只接受9个字符
scanf可以一次输入多个,接收 空格 和 换行 表示结束
getchar只接收一个字符,后面加getchar();//任意键,暂时停留界面,如果不再加一个,下一个就会取走回车
scanf("%[^\n]", a);//通过正则表达式,接收除\n之外的内容,可以接收空格
gets():
#include <stdio.h>
char* gets(char* s);
功能:从标准输入读入字符,并保存到 s 指定的内存空间,
直到出现 换行符 或 读到文件结尾 为止。
可以接收空格
注意:由于 scanf()和 gets()无法知道字符串 s 大小,
必须遇到 换行 符或 读到文件结尾 为止才停止接收输入,
因此容易导致字符数组越界(缓冲区溢出)的情况。
fgets():
#include <stdio.h>
char* fgets(char*s, int size, FILE*stream);
功能:从 stream 指定的文件内读入字符,
保存到 s 所指定的内存空间,
直到出现换行字符、读到文件结尾或是已读了 size - 1 个字符为止,
最后会自动加上字符 '\0' 作为字符串结束。
如果读键盘输入的字符串,stream固定为stdin
可以接收空格
把用户输入的回车也做为字符串的一部分
通过 scanf 和 gets 输入一个字符串的时候,不包含结尾的“\n”
输入 “hello”,实际存放“hello\n\0”
获取字符串少于元素个数会有\n,大于等于 没有\n
fgets()函数是安全的,不存在缓冲区溢出的问题。
puts():
#include <stdio.h>
int puts(const char* s);
功能:标准设备输出 s 字符串,在输出完成后自动输出一个'\n'。
可以接收空格
遇到\0停止,同printf。
puts(“”);//换行操作
printf("\n");
fputs():
#include <stdio.h>
int fputs(const char * str, FILE * stream);
fputs()是 puts()的文件操作版本,但 fputs()不会自动输出一个'\n'。
字符串长度计算:
strlen():
#include <string.h>
size_t strlen(const char *s);
功能:计算指定指定字符串 s 的长度,不包含字符串结束符‘\0’
char a[100]="hello world";
printf("%d,%d",sizeof(a), strlen(a));//100,11
char a[]="hello world";
printf("%d,%d",sizeof(a), strlen(a));//12,11
4、*****枚举定义