C语言笔记
程序员除了素质一无所有
纯手工打造
独具匠心
Mr_zhang
文章目录
-
linux目录结构:
/:根目录
/bin: /usr/bin:可执行二进制文件的目录
/lib: /usr/local/lib:系统使用的函数库的目录
/home:系统默认的用户家目录
/usr/include:头文件所在的目录 -
文件分类:普通文件,目录文件,设备文件,管道文件,链接文件
文件权限:访问用户,访问权限
访问用户:文件所有者,用户组,其他用户
访问权限:读权限(r),写权限(w),可执行权限(x)
linux常用命令:
- 查看帮助文档:* --help , man * 手册
- 查看文件信息:ls -a所有 -l详细 -h人性化
- 输出重定向命令:>
- 分屏显示:more
- 管道:|
- 清屏:clear
- 切换工作目录:cd
- 显示当前路径:pwd
- 创建目录:mkdir
- 创建文件 :touch
- 删除文件 :rm
- 建立链接文件:ln -s软链接
- 查看文件内容:cat
- 文本搜索:grep
- 查找文件:find
- 拷贝文件:cp -r文件夹
- 移动文件:mv
- 获取文件类型:file
- 归档管理:tar
- 文件压缩解压:gzip -cvf 压缩 -xvf 解压
- 文件压缩解压:bzip2
- 查看命令位置:which
- 软链接:
1.类似于windows的快捷方式,软链接依赖于源文件存在
2.如果源文件删除,软链接没有意义
- tar只负责打包,解包,不进行压缩
打包:tar -cvf xxx.tar 需要打包的文件
解包:tar -xvf xxx.tar 需要解包的文件
指定目录解包:tar -xvf xxx.tar -C 指定路径目录
- 如何查看某个目录的大小:
du ./目录 -h
- gzip压缩tar包
压缩:gzip [-r] xxx.tar 自动在当前目录生成:xxx.tar.gz
解压:gzip [-d] xxx.tar
压缩打包:
tar -czvf xxx.tar.gz 所需文件
bzip2压缩tar包
压缩:bzip2 [-r] xxx.tar 自动在当前目录生成:xxx.tar.bz2
解压:bzip2 [-d] xxx.tar
bzip2压缩打包:
tar -cjvf xxx.tar.dz2
解压解包:tar -xjvf xxx.tar.dz2
-
创建新用户:
useradd -d /home/名称 用户名 -m
更改密码:passwd 用户名 -
曾登陆用户时间历史:last
创建一个用户(系统默认创建一个用户组,这个用户组的名字和用户的名字是一样的)
查看用户信息:cat /zhang/passwd
zhang:x:1000:1000:zhang,:home/zhang:/bin/bash
第一个1000代表是用户id
第二个1000代表是这个用户属于哪个组,组id
更改权限:chmod
chmod 777(4 2 1)文件名 可读可写可执行 r 4 w 2 x 1
u 所有者 g 用户 o 其他用户
chmod u+w 文件名
文件夹权限说明:
x:没有此权限,不允许用户进入
r:没有这个权限,无法查看文件夹的内容
w: 没有这个权限,无法新建和删除文件
查看当前日历:cal
显示或者设置时间:date
查看进程信息:ps -a显示终端的所有进程,包括其他用户的进程 -u显示进程的详细状态
-x 显示没有控制终端的进程
ps -aux | grep xxx 利用管道查询进程
杀死进程:kill -9强制杀死进程
动态进程:top
重启:reboot , init 6i
关机:shutdown now , init 0
查看或配置网卡信息:ifconfig
vi编辑器:
进入插入模式:i
退出插入模式:Esc
:wq 保存并退出
:q!强制退出
:x给文件加密码
:!命令 暂时离开vi,执行命令
i 光标位置当前处插入文字
o光标位置下方开启新行
O光标位置上方开启新行
I光标所在行首插入文字
A光标所在行尾插入文字
dd 从当前行剪切(删除)
yy 从当前行复制
p粘贴
u 撤销
gg去第一行句首
G去最后一行句首
xgg去指定一行句首
/xxx 在vi中查找xxx
n下一个
N上一个
:set nu 设定行号
:set nonu 取消行号
在linux下,指向用户编译的可执行程序,如果没有配置环境,在当前路径前面必须加 ./
system:
功能:在已经运行的程序中执行另外一个外部程序
参数:外部可执行程序的名字
返回值:不同系统返回值不一样
字符编码:
window默认支持的中文编码为gbk,gb2312,ANSI
linux默认支持的中文编码为 UTF-8
只在windows有效:我们使用的是windows命令
calc 计算器
mspaint 画图板
notepad 记事本
windows图形界面只有两个:Qt,MFC(微软vs)
vs中C语言可以嵌套汇编代码
C语言编译过程:预处理-----编译-----汇编-----链接
- 预处理:宏定义展开,头文件展开,条件编译等,同时将代码的注释删除,这里并不会检查语法
- 编译:检查语法,将预处理后文件生成汇编文件
- 汇编:将汇编文件生成目标文件(二进制文件)
- 链接:C语言写的程序是需要依赖各种库的,所以编译之后还需要把库链接到最终的可执行程序中去
分布编译:
预处理:gcc -E hello.c -o hello.i
编 译 :gcc -S hello.i -o hello.s
汇编 :gcc -c hello.s -o hello.o
链接 :gcc hello.o -o hello_elf
交换文件说明:
1.vi写文件,没有保存就关闭,自动生成一个后缀为.swp交换文件,保存了前面写的内容
2.先恢复,再删除
寄存器是CPU内部最小的存储单位
- 如果需要运算,不能在内存中直接运算
- 如果需要运算,把内存中的数据加载到寄存器,在运算
- 把运算结果从寄存器移回内存中
数据类型的作用:告诉编译器定义这么一个类型的变量需要分配多大的空间
变量在使用前必须先定义,定义变量前必须有相应的数据类型
标识符命名规则:
标识符不能是关键字
标识符只能由字母,数字,下划线组成
第一个字符必须为字母或下划线
标识符中字母区分大小写
1Byte=8bit
%d : 以10进制的方式打印
%o :以8进制的方式打印
%x :以16进制的方式打印
int a=123 //以十进制方式赋值
int a=0123 //以八进制的方式赋值
int a=0xABC //以十六进制方式赋值
原码反码补码:
(1)存储1字节(8位)大小的数字(char)
原码(用户角度):原始的二进制
1.用户的数字分为正负数,符号位的存储
2.最高位为符号位:0代表为正数,1代表为负数
3.1000 0001 左边是最高位,右边是低位
+1 :0000 0001
-1 :1000 0001
+0 :0000 0000
-0 :1000 0000
原码存储导致2个问题:
1.0有两种存储方式
2.正数和负数相加,结果不正确(计算机只会加不会减)
以原码的方式来算:
反码(为了算补码)
0.正数的原码和反码是一样的
1.求原码
2.在原码的基础上,符号位不变,其他位取反(0为1,1为0)
+1 :0000 0001
-1 :1111 1110
+0 :0111 1111
-0 :1111 1111
反码存储导致1个问题:
1.0有两种存储方式(0不分正负,只有一种存储方式)
以反码的方式计算:
1 - 1 = 1 + (-1)
1 : 0000 0001
-1 : 1111 1110
1111 1111 = -0
计算机存储数字以补码方式存储(为了解决负数的存储)
补码:
1.正数的原码,反码,补码都一样
2.补码为其反码+1
+1 :0000 0001
-1 :1111 1111
+0 :0111 1111
-0 :1 0000 0000(因为8位,所以最高为丢弃) =0000 0000
以补码的方式来算:
1-1 = 1 + (-1)
1: 0000 0001
-1: 1111 1111
1 0000 0000 = 0
//0x81转化为二进制1000 0001,最高位为1,说明是负数
char a = 0x81;
printf("%d\n",a);
结果是 -127
补码:1000 0001
反码:1111 1110
原码:1111 1111 = -127
char类型为1字节(8位)范围:有符号: -128~127 无符号:0~255
-0系统当成 -128
%x,系统默认以4字节(32位)大小打印
%d,以有符号来打印
%u,以无符号来打印
赋值或运算,不能越界
sizeof的功能是计算一个数据类型的大小,单位为字节
int占4个字节 short占2个字节
字符型(char)
1.内存中没有字符,只有数字
2.一个数字,对应的一个字符,这种规则叫做ASCII
3.使用字符或数字给字符变量赋值是等价的
4.字符类型本质上就是1个字节大小的整形
逻辑运算符
!(逻辑非) :如果a为假,则!a为真,如果a为真,则!a为假
&&(逻辑与) :如果a和b都为真,则结果为真,否则为假
||(逻辑或) :如果a和b有一个为真,则结果为真,二者都为假时,结果为假
运算符和表达式
1.相除得到小数问题
a)两个整数相除,只是取整数,不会带小数
int a=1/2; // a=0
b)要想得到小数的结果,分子分母至少有一个是小数
double a
a=1.0/2;// a=0.5
a=1/(double)2;
死循环
//{ }可以不写,不写只有第一个语句属于循环
while(1)
{
}
//{ }必须写,语法
do
{
}while(1)
//{ }可以不写,不写只有第一个语句属于循环
for(;;)
{
}
循环的嵌套:任何一个循环的内部可以调用其他的循环
while()
{
while()
{
do
{
for()
{
while()
{
}
}
}while();
}
}
int i=0;
int j=0;
int num=0;
/*
1.i=0;
2.判断i<10的条件,条件为真,执行for(j=0;j<10;j++){}内容,条件为假,跳出循环
3.执行{}内部
*/
for(i=0;i<10;i++)
{
for(j=0;j<10;j++)
{
num++;
}
}
break作用:
1.跳出switch语句
2.跳出循环
3.如果有多个循环,跳出最近的内循环
continue:属于循环,跳出本次循环,下次继续
goto:任意地方都可以使用,无条件跳转,勿滥用
数组
#include<stdio.h>
int main()
{
int a;//定义一个普通变量
//1.同一个{}内部,数组名不能和其他变量,其他数组名同名
//2.定义数组,[]最好是常量
int n=10;
int b[n];
//3.使用数组时,[]里 可以是变量,可以是常量,也可以是表达式
}
int main01()
{
//1.定义一个数组,数组内部有10个int类型的元素(变量)
//2.定义数组和定义变量的区别,名字后面加[],[]里面写元素个数
//3.数组在内存中是连续存储的
//4.通过下标访问数组的元素,下标从0开始,从0到9,没有a[10]这个元素
//5.有多少个[]就是多少维
int array[10];
array[0]=0;
array[1]=1;
array[2]=2;
}
- 编译器不是我们想象中那么智能,有些错误不能立马呈现
错误: - 编译错误(语法出错)
- 运行时错误(运行时异常),有些错误不能立马呈现
[ ]内部的变量或数组,不初始化,它的值为随机数
数组名
# include <stdio.h>
int main()
{
int a[10];
/*1.数组名是常量,不能修改*/
/*2.数组名的地址是数组首元素的地址*/
printf("a=%p,&a[0]=%p",a,&a[0]);
/*3.sizeof(数组名)测数组总大小 :10(元素)*4(一个元素4个字节)=40*/
printf("sizeof(a)=%lu\n",sizeof(a));
};
求三个数最大值
#include<stdio.h>
int main()
{
int a=10;
int b=20;
int c=30;
int max;
max=a>b?a:b;
printf("max=%d\n",max);
max=(a>b?a:b)>c?(a>b?a:b):c;
printf("max=%d\n",max);
return 0;
}
int main01()
{
int a=10;
int b=20;
int c=30;
int max;
if(a>b)
{
max=a;
}else if
{
max=b;
}
if(max>c)
{
printf("最大值为%d\n",max);
}else
{
printf("最大值为%d\n",c);
}
}
求数组最大值
#include<stdio.h>
int main()
{
int a[]={10,-1,20,3,5,9,1,6,50,6};
int n=sizeof(a)/sizeof(a[0]);
int max=a[0];
int i=0;
for(i=1;i<n;i++)
{
if(a[i]>max)
{
max=a[i];
}
}
printf("数组最大值为%d\n",max);
return 0;
}
数组翻转
#include<stdio.h>
int main()
{
int a[]={1,2,3,4,5,6,7,8,9};
int n=sizeof(a)/sizeof(a[0]); /*元素个数*/
int i=0; /*首元素下标*/
int j=n-1; /*尾元素下标*/
int tmp;
while(i<j)
{
/*交换a[i]和a[j]*/
tmp=a[i];
a[i]=a[j];
a[j]=tmp;
i++; /*从左往右*/
j--; /*从右忘左*/
}
for(i=0;i<n;i++)
{
printf("%d",a[i]);
}
printf("\n");
}
冒泡排序
![Alt text](./Image 1.png)
#include <stdio.h>
int main()
{
int a[]={1,-1,2,-2,3,-3,4,-4,5,-5};
int n=sizeof(a)/sizeof(a[0]);
int i=0;
int j=0;
int tmp;
printf("排序前");
for(i=0;i<n;i++)
{
printf("%d",a[i]);
}
printf("\n");
for(i=0;i<n-1;i++)
{
for(j=0;j<n-1-i;j++)
{
if(a[j]>a[j+1])
{
tmp=a[j];
a[j]=a[j+1];
a[j+1]=tmp;
}
}
}
printf("排序后");
for(i=1;i<n;i++)
{
printf("排序后:%d",a[i]);
}
printf("\n");
}
多维数组
#include<stdio.h>
int main()
{
/*
1、有多少[]就有多少维
2、内存中没有多维,都只有一维,多维数组是特殊的一维数组
3、定义了一个一维数组a[3],这个一位数组有3个元素,每个元素int[4]
4、a[0],a[1],a[2]就是第0,1,2,元素的数组名
5、二维数组用户可以理解为m行n列
*/
int a[3][4];
int i=0;
int j=0;
int num=0;
for(i=0;i<3;i++)
{
for(j=0;j<4;j++)
{
a[i][j]=num;
num++;
}
}
for(i=0;i<3;i++)
{
for(j=0;j<4;j++)
{
printf("%d ,",a[i][j]);
}
printf("\n");
}
}
二维数组数组名
#include<stdio.h>
int main()
{
int a[5][10];
//1.数组名是常量,不能修改
//2.sizeof(数组名),测数组的总大小:5*int[10]=5*4*10=200
printf("sizeof(a)=%lu\n",sizeof(a));
//3.sizeof(a[0]),测的是第0个元素的大小:int [10]=4*10=40
printf("sizeof(a[0])=%lu\n",sizeof(a[0]));
//求行数(元素个数):总大小/每个元素的大小
int n=sizeof(a)/sizeof(a[0]);
printf("n1=%d\n");
//求列数
n=sizeof(a[0])/sizeof(int);
printf("n2=%d\n",n);
//行*列
n=sizeof(a)/sizeof(a[0])*sizeof(a[0])/sizeof(int);
=sizeof(a)/sizeof(int);
printf("n3=%d\n",n);
}
二维数组平均分
#include<stdio.h>
int main()
{
int a[5][3]=
{
{30,60,80,},
{60,60,60},
{77,88,99,},
{88,66,77},
{12,22,8}
};
int i=0;
int j=0;
int sum =0;
for(i=0;i<3;i++)
{
sum=0;
for(j=0;j<5;j++)
{
sum+=a[j][i];
}
}
printf("平均分:%lf\n",sum/5.0);
}
字符数组
#include <stdio.h>
int main ()
{
/*
1、C语言没有字符串类型,用字符数组模拟
2、字符串一定是字符数组,字符数组不一定是字符串
3、如果字符数组以字符'\0'('\0'等价于0)结尾,那么这个字符数组就是字符串
*/
char a[10];
char b[]={'a','b','c'};/*字符数组*/
char c[10]={'a','b','c','\0'};/*字符串*/
return 0;
}
字符数组初始化
#include <stdio.h>
int main()
{
char a1[]={'a','b','c'};/*字符数组*/
printf("a1=%s\n",a1);/*乱码,因为没有结束符*/
char a2[]={'a','b','c',0};/*字符串*/
printf("a2=%s\n",a2);
char a3[]={'a','b','c','\0'};
printf("a3=%s\n",a3);
char a4[]={'a','b','c','\0','h','e'};
printf("a4=%s\n",a4);/*abc*/
char a5[10]={'a','b','c'};/*前3个字符赋值为a,b,c,后面自动赋值为0*/
printf("a5=%s\n",a5);
/*常用初始化,使用字符串初始化,在字符串结尾自动加数字0*/
char a7[10]="abc";
printf("a7=%s\n",a7");
char a8[10]="abc";
printf("sizeof(a8)=%lu\n",sizeof(a8));/*字符串自动隐藏一个字符*/
char a9[10]="\0abc";
printf("a9=%s\n",a9);
/*\0后面最好别跟数字,有可能组成一个转义字符,'012'就是'\n'*/
char a10[10]="\012abc";
printf("a10=%s\n",a10);
}
sizeof()测数据类型大小,不会因为结束符提前结束
char a[100];
scanf("%s",a);
//a没有&,原因数组名是首元素地址,本来就是地址
printf("a=%s\n",a);
随机数产生
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
int main ()
{
/*
先设置种子,种子设置一次即可
如果srand()参数一样,随机数就一样
srand(100);
time(NULL)功能获取系统当前时间,由于时间会变,srand()也会改变
*/
srand( (unsigned int) time(NULL) );
int i=0;
int num;
for(i=0;i<100;i++)
{
num=rand();
printf("num=%d\n",num);
}
return 0;
}
scanf_字符串输入
![Alt text](./Image 2.png)
#include<stdio.h>
int main()
{
char buf[100]={0};
printf("请输入字符串buf:");
scanf("%s", buf);/*不需要&,默认以空格分开*/
printf("buf=%s\n",buf);
char tmp[100]={0};
printf("请输入字符串tmp:");
scanf("%s", tmp);/*不需要&,默认以空格分开*/
printf("tmp=%s\n",tmp);
/*scanf()的缺陷,不做越界检查,此函数不安全*/
char str[100]={0};
printf("请输入字符串str:");
scanf("%s", str);/*不需要&,默认以空格分开*/
printf("str=%s\n",str);
}
gets
#include <stdio.h>
int main()
{
char buf[100];
/*
gets()从键盘读取字符串,放在指定数组
gets()允许有空格,不做越界检查,此函数不安全
*/
gets(buf);
printf("buf=%s\n",buf);
return 0;
}
fgets
#include<stdio.h>
int main()
{
char buf[100];
/*
从stdin(代表标准输入,键盘),读取内容
如果输入内容如果大于sizeof(buf)-1,只取sizeof(buf)-1,放在buf所在的数组
会把换行符也读进去
*/
fgets(buf,sizeof(buf), stdin);
printf("buf=%s\n",buf);
return 0;
}
puts
#include <stdio.h>
int main()
{
char buf[]="hello";
/*把buf内容输出到屏幕,自动在屏幕加换行,是在屏幕加,字符串本身没有变量*/
puts(buf);
printf("%s",buf);
}
fputs
#include <stdio.h>
int main()
{
char buf[]="hello";
/*往stdout(代表屏幕,标准输出)输出buf内容*/
fputs(buf,stdout);
}
strlen
#include <stdio.h>
#include <string.h>
int main()
{
char buf[]="hello";
/*
strlen需要使用返回值,返回值就是字符串长度
从首元素开始,到结束符为止的长度,结束符不算(遇到'\0'结束)
*/
int len=strlen(buf);
printf("len=%d\n",len);
/*多了一个结束符\0*/
printf("sizeof(buf)=%lu\n",sizeof(buf));
char buf2="\0hello";
len=strlen(buf2);
printf("len2=%d\n",len);
/*sizeof()测数据类型的长度,不会因为结束符提前结束*/
printf("sizeof(buf2)=%lu\n",sizeof(buf2));
char str[100]="mike";
printf("strlen(str)=%lu\n",strlen(str));
printf("sizeof(str)=%lu\n",sizeof(str));
return 0;
}
字符串拷贝
#include<stdio.h>
#include<string.h>
int main()
{
char src[100]="hello\0 mike";
char dst[100]="aaaaaaaaaaa";
/*功能:把src字符数组前12个字符的内容拷贝给dst所代表的数组*/
strncpy(dst,src,sizeof(src));
printf("dst=%s\n",dst);
printf("dst=%s\n",dst+strlen("hello")+1);
return 0;
}
int main02()
{
char src[100]="hello\0 mike";
char dst[100];
/*
功能:把str字符数组前8个字符的内容拷贝给dst所代表的数组
*/
strncpy(dst,src,8);
printf("dst=%s\n",dst);
printf("dst=%s\n",dst+7);
return 0;
}
int main01()
{
char src[100]="hello mike";
char dst[100];
/*功能:把src字符数组的内容拷贝给dst所代表的数组
拷贝原理,从首元素开始,遇到结束符'\0'结束
*/
strcpy(dst,src);
printf("dst=%s\n",dst);
char src2[100]="hello\0 mike";
char dst2[100];
strcpy(dst2,src2);
printf("dst2=%s\n",dst);
return 0;
}
字符串比较
#include<stdio.h>
int main()
{
char s1[]="abc";
char s2[]="abcd";
int flag=strcmp(s1,s2);
/*
指定比较前3个字符
首先比较第一个字符,只要第一个字符大于对方,就已经比对方大
int flag=strncmp(s1,s2,3);
*/
if(flag>0)
{
printf("[%s]>[%s]\n",s1,s2);
}
else if(flag<0)
{
printf("[%s]<[%s]\n",s1,s2);
}
else
{
printf("[%s]==[%s]\n",s1,s2);
}
}
sprintf
#include <stdio.h>
int main()
{
int a=10;
char ch='b';
char buf[]="hello";
/*格式化一个字符串,把这个字符串输出到屏幕*/
printf("a=%d,ch=%c,buf=%s\n",a,ch,buf);
/*格式化一个字符串,把这个字符输出(保存)指定的数组*/
char dst[100];
sprintf(dst,"a=%d,ch=%c,buf=%s\n"a,ch,buf);
printf(@@%s@@,dst);
}
ssanf
#include <stdio.h>
int main()
{
char buf[]="1 2 3";
int a,b,c;
/*从buf中以指定的格式提取内容*/
sscanf(buf,"%d %d %d",&a,&b,&c);
printf("a=%d,b=%d,c=%d\n",a,b,c);
/*提取整形最方便*/
char str[]="a=1,b=2,c=3";
a=0;b=0;c=0;
sscanf(str,"a=%d,b=%d,c=%d",&a,&b,&c);
printf("a1=%d,b1=%d,c1=%d\n",a,b,c);
char str2[]="1,2,3";
a=0;b=0;c=0;
sscanf(str2,"a=%d,b=%d,c=%d",&a,&b,&c);
printf("a2=%d,b2=%d,c3=%d\n",a,b,c);
/*提取字符串,默认以空格分隔,可以提取*/
char tmp[]="abc mike 250";
char m[10],n[10];k[10];
/*不用&,数组名就是首元素地址*/
sscanf(tmp,"%s %s %s",m,n,k);
printf("m=%s, n=%s, k=%s\n",m,n,k);
char tmp1[]="abc,mike,250";
char m1[10],n1[10];k1[10];
/*不用&,数组名就是首元素地址*/
sscanf(tmp1,"%s,%s,%s",m1,n1,k1);
printf("m1=%s, n1=%s, k1=%s\n",m1,n1,k1);
return 0;
}
int main01 ()
{
int a ,b c ;
printf("请输入三个数");
scanf("%d %d %d",&a,&b,&c);
printf("a=%d,b=$d,c=%d\n",a,b,c);
return 0;
}
字符串查询
#include<stdio.h>
#include <string.h>
int main()
{
char buf[]="abcdefg"
/*
在buf中查询字符d,如果找到,返回d所在位置的地址
如果查询失败,返回NULL
*/
char *p=strchr(buf,'b');
if(p==NULL)
{
printf("查询失败\n");
}
else
{
printf("p=%s\n",p);
}
}
字符串切割
#include<stdio.h>
#include<string.h>
int main()
{
char buf[]="abc,mike,250";
char tmo[];
strcpy(tmp,buf);
char *p=strtok(tmp,",");
while(p!=NULL)
{
printf("p=%s\n",p);
p=strtop(NULL,",");
}
}
int main01()
{
char buf[]="abc,mike,250";
char tmp[];
strcpy(tmp,buf);
/*
第一次调用
第一次参数写源字符串,第二次参数写切割字符
返回值就是切割后的字符串
在匹配切割字符的地方,换成结束符
使用strtok()会破坏原来字符串的结构
如果没有切割成功,返回NULL
*/
printf("before,buf[3]=%d\n",buf[3]);
char *p=strtok(tmp,",");
/*第二次调用,第一个参数需要写NULL*/
p=strtok(NULL,",");
printf("p2=%s\n",p);
printf("p=%s\n",p);
printf("buf=%s\n",buf);
printf("buf[3]=%d\n",buf[3]);
return 0;
}
#include <stdio.h>
void fun ()/*等价于void fun(void)*/
{
/*
无参无返回值函数的定义
1、没有返回值,用void关键字修饰
2、没有参数,也可以用void,或者为空
3、第一个void代表返回值,fun代表函数名字,()内部代表参数,空就是无参,{}就是函数体
4、同一个文件不能出现同名函数
5、不同的函数,内部的变量是没有关系的
函数内部,包括()内部的变量,只有在调用时分配空间,调用完毕自动释放
*/
printf("我是无参无返回值函数的定义\n");
int a=10;
int b=20;
printf("a+b=%d\n",a+b);
}
int main()
{
/*
1、除了main函数外,其他函数只有调用了才起作用
2、函数的调用不可能出现void关键字
3、无参无返回值函数的调用格式:函数名字()
*/
fun();
return 0;
}
#include <stdio.h>
/*
有参无返回值函数的定义
1、定义函数的参数就做形参,形参的格式必须为:类型+变量,不能赋值
2、函数没有调用,形参变量不会分配空间,函数调用完毕,形参变量自动释放
*/
void fun1(int a)
{
printf("a=%d\n",a);
}
/*如果形参有多个,用逗号","隔开*/
void fun2(int a,char ch,char buf[100])
{
printf("a=%d,ch=%c,buf=%s\n",a,ch,buf);
}
int main()
{
int a =10;
/*
有参无返回值函数的调用
1、如果函数定义室友参数,调用时必须传参
2、有参函数调用形式:函数名(匹配类型参数)
3、匹配类型参数可以是变量,常量,表达式
4、函数调用时传的参数,叫做实参
5、只能把实参的值传递给形参,不能反过来,单向传递
*/
fun1(10);
int i=11;
fun1=(i);
fun1(1>2);
/*6、函数的形参有多个,传参也用逗号","隔开*/
fun2(10,'a',"hello");
}
#include<stdio.h>
/*
无参有返回值函数的定义
1、第一个int代表函数的返回类型,函数内部通过return返回
2、return作用终止当前的函数
3、返回值可以是常量,变量,表达式
*/
int fun()
{
return 10; /*返回常量*/
}
int fun1()
{
int a=11;
return a; /*返回变量*/
}
int fun2()
{
int a=10;
int b=20;
return a>b?a:b; /*返回表达式*/
}
/*
4、函数返回值只能返回一个,不能返回多个
5、如果return的类型和函数返回类型不一致,以函数返回类型为准
*/
int fun3()
{
return 9.5555;
}
int main()
{
/*
1、如果函数有返回值,这个返回值可以不用
2、如果使用返回值,最好要定义匹配类型的变量来接收/
*/
fun();
int a=fun();
printf("a=%d\n",a);
int b;
b=fun();
printf("b=%d\n",b);
printf("tmp=%d\n",fun());
printf("fun1=%d\n",fun1());
printf("fun2=%d\n",fun2());
printf("fun3=%d\n",fun3);
return 0;
}
return 和 exit
#include <stdio.h>
#include <stdlib.h>
int fun()
{
printf("fun\n");
exit(250); /*结束整个程序,结束进程*/
}
/*
return主函数(main),程序结束
return其他函数,程序不结束
*/
int main()
{
while(1)
{
}
return 0; /*return作用:中断函数,中断main,程序就结束*/
printf("main\n");
}
声明和定义的区别
#include <stdio.h>
/*
1. 编译器很笨,在main函数调用其他函数,只会往前找函数的定义
2. 如果找不到函数的定义,就找函数的声明,如果没有声明,c编译器会警告,c++会出错
3. 如果没有定义,直接调用一个不存在的函数,会报错
4. 如果定义没有放在main函数前面,那么在调用前需要声明,声明加不加extern是一样的
5. 一个函数只能定义一次,可以声明多次
6. 声明的形参变量名和定义的形参变量名可以不一样
7. 可以只声明,不定义函数(无意义),此函数不能调用
*/
/*函数的声明,告诉编译器,这个函数是有意义的,只是放在别的地方*/
int my_strlen(char buf[]);
/*8、声明时,形参变量名可以不写,可以只写类型,定义时,形参变量名一定要写*/
void fun(int a,int, int);
int main()
{
printf("len=%d\n",my_strlen("0123456789"));
return 0;
}
int my_strlen(char str[])
{
int i=0;
while(str[i]!='\0')
{
i++;
}
return i;
}
指针
![Alt text](./Image 3.png)
![Alt text](./Image 4.png)
#include <stdio.h>
int main()
{
/*
1、指针也是一个变量
2、p是一个变量,p的类型是int *
*/
int *p;
p=123;
printf("%d\n",p);
/*2、指针指向谁,就把谁的地址赋值给指针*/
int a=10;
p=&a; /*p保存了a的地址*/
/*%p打印地址,是以16进制方式来打印*/
printf("%p, %p\n",p,&a);
/*
3、直接操作指针变量本身没有意义
4、需要操作*p,操作指针所指向的内存
*/
*p=100; /**p相当于a*/
printf("%d, %d\n",*p,a);
return 0;
}
![Alt text](./Image [5].png)
#include<stdio.h>
int main()
{
int a=10;
int *p=&a;
*p=111;
int *q;
q=p;
*q=222;
printf("&a=%p,p=%p,q=%p\n",&a,p,q);
printf("a=%d,*p=%d,*q=%d\n",a,*p,*q);
return 0;
}
野指针:这个指针变量保存了一个没有意义(非法)的地址
![Alt text](./Image [6].png)
#include <stdio.h>
int main()
{
int *p;
p=0x1234;
printf("p=%d\n",p);
/*
*p=100;错误
1、只有定义后的变量,此变量的地址才是合法地址
2、野指针就是保存没有意义地址的指针变量
3、操作野指针变量本身不会有任何问题
4、操作野指针所指向的内存才导致段错误
p=0x1234;没有定义0x1234;
可能有一个叫0x1234的变量,*p=100就是操作这个0x1234变量,无法操作,所以出现段错误
*/
*p=100;
return 0;
}
空指
#include <stdio.h>
int main()
{
/*
1、空指针,就给指针变量赋值为NULL
2、NULL就是数字0
*/
int *p=NULL;
int a=11;
p=&a;
if(p!=NULL)
{
*p=100;
}
return 0;
}
指针大小
#include <stdio.h>
int main()
{
/*
1、32位编译器用32位大小(4字节)保存地址
2、64位编译器用64位大小(8字节)保存地址
*/
int a=sizeof(int *);
int b=sizeof(int *);
double *p;
int c=sizeof(p);
printf("a=%d,b=%d,c=%d\n",a,b,c);
return 0;
}
多级指针
![Alt text](./Image [7].png)
![Alt text](./Image [8].png)
[ ]不是数组专属
#include <stdio.h>
int main()
{
int a=10;
int *p=&a;
/*通过指针简洁操作a的内存*/
*p=111;
printf("a=%d\n",a);
printf("*p=%d\n",*p);
/* *p等价于*(p+0),同时等价于p[0]*/
printf("p[0]=%d\n",p[0]);
/* *p等价于p[0],操作的是指针所指向的内存*/
p[0]=250;
printf("a2=%d\n",a);
return 0;
}
万能指针
![Alt text](./Image [9].png)
#include <stdio.h>
int main()
{
/*
1、不可以定义void类型的普通变量,不能确定类型
2、可以定义void *变量,void *指针也叫万能指针
3、void *可以指向任何类型的变量
*/
void *p=NULL;
int a=10;
p=&a;
*((int *)p)=222; /*只保存了a的首地址*/
printf("*p=%d\n",*((int *)p));
}
指针步长
#include <stdio.h>
int main()
{
/*
1、指针的加法,不是传统的加法
2、步长由指针指向的数据类型决定
*/
int a;
int *p=&a;
printf("p=%d,p+1=%d\n",p,p+1);
char b;
char *q=&b;
printf("q=%d,q+1=%a\n",q,q+1);
}
const修饰的指针
#include <stdio.h>
int main()
{
/*
1、指针变量
2、指针所指向的空间
*/
int a =10;
int *p1=&a;
*p1=100;
p1=NULL;
/*const 修饰* ,代表指针所指向的内存是只读的*/
const int *p2=&a;
/* *p2=100;不能操作*/
p2=NULL;
/*const 修饰* ,代表指针所指向的内存是只读的*/
int const *p3=&a;
p3=NULL;
/*const 修饰指针变量,代表指针变量的值为只读*/
int * const p4=&a;
*p4=100;
/* p4=NULL; 不能操作*/
const int * const p4=&a;
return 0;
}
指向数组首元素的指针
![Alt text](./Image [10].png)
#include <stdio.h>
int main()
{
int a[10]={1,2,3,4,5,6,7,8,9,10};
int *p=NULL;
/*p指针变量指向首元素*/
p=&a;
p=a;
int i=0;
for (i=0;i<10;i++)
{
printf("%d,",*(p+i));
/*
等价于
printf("%d",a[i]);
printf("%d",p[i]);
p[i]等价于 *(p+i),操作都是指针所指向的内存
*/
}
printf("\n");
}
通过指针加法访问指针元素
#include <stdio.h>
int main()
{
int a[10]={1,2,3,4,5,6,7,8,9,10};
/*定义一个指针,指向首元素*/
int *p=&a[0];
int n=sizeof(a)/sizeof(*a);
int i=0;
for(i=0;i<n;i++)
{
printf("%d, ",*p);
p++;
}
printf("\n");
/*定义一个指针,指向尾元素*/
int *q=&a[n-1];
q=a+n-1;
for(i=0;i<n;i++)
{
printf("%d, "*q);
q--;
}
printf("\n");
}
- ####指针数组,它是数组,每个元素都是指针
- ####数组指针,它是指针,指向数组的指针
指针数组
![Alt text](./Image [11].png)
#include <stdio.h>
int main()
{
int a[3]={0,1,2};
/*指针数组,它是数组,每个元素都是指针*/
int *p[3];
p[0]=&a[0];
p[0]=a;
p[1]=&a[1];
p[2]=&a[2];
int n=sizeof(p)/sizeof(p[0]);
int i=0;
for(i=0;i<n;i++)
{
p[i]=&a[i];
}
for(i=0;i<n;i++)
{
printf("%d\n",*p[i]);
}
return 0;
}
int main01()
{
int a =10;
int b =20;
int c =30;
int *p1=&a;
int *p2=&b;
int *p3=&c;
return 0;
}
值传递
![Alt text](./Image [14].png)
#include <stdio.h>
void swap(int m,int n)
{
int tmp;
tmp=m;
m=n;
n=tmp;
printf("m=%d,n=%d\n",m,n);
}
int main()
{
int a=11;
int b=22;
swap(a,b); /*值传递,形参的修改不会影响到实参*/
printf("a=%d,b=%d\n",a,b);
return 0;
}
地址传递
![Alt text](./Image [13].png)
#include <stdio.h>
void swap(int *m,int *n)
{
int tmp;
tmp=*m;
*m=*n;
*n=tmp;
printf("m=%d,n=%d\n",m,n);
}
int main()
{
int a=11;
int b=22;
swap(&a,&b); /*地址传递,变量的地址*/
printf("a=%d,b=%d\n",a,b);
return 0;
}
形参中的数组
#include <stdio.h>
/*
1、形参中的数组,不是数组,他是普通指针变量
2、形参数组:int a[100000],int a[],int *a对编译器而已,没有任何区别
3、编译器都是当作int *处理
4、形参中的数组和非形参数组的区别:形参的数组是指针变量,非形参就是数组
void printf_array(int a[100000])
void printf_array(int *a)
*/
void printf_array(int a[])
{
int i=0;
/*
64位系统,sizeof(a),a是指针变量,结果为8
sizeof(a[0])第0个元素,是int类型,结果为4
*/
int n=sizeof(a)/sizeof(a[0]);
printf("sizeof(a):%d\n",sizeof(a));
printf("sizeof(a[0]):%d\n",sizeof(a[0]));
for(i=0;i<n;i++)
{
printf("%d, ",a[i]);
}
printf("\n");
a=NULL; /*形参中的数组,不是数组,它是普通指针变量*/
}
int printf_array2(int a[0],int n)
{
int i=0;
for(i=0;i<n;i++)
{
printf("%d, \n",a[i]);
}
printf("\n");
}
int main()
{
int a[]={1,-2,3,-4,5,-6,7,-8,9};
int i=0;
int n=sizeof(a)/sizeof(a[0]);
printf_array2 (a,n);
printf("排序前\n");
/*冒泡排序*/
int j=0;
int tmp;
for(i=0;i<n-1;i++)
{
for(j=0;j<n-1-i;j++)
{
if(a[j]>a[j+1])
{
tmp=a[j];
a[j]=a[j+1];
a[j+1]=tmp;
}
}
}
printf("排序后\n");
for(i=0;i<n;i++)
{
printf("%d, "a[i]);
}
printf("\n");
return 0;
}
返回局部变量地址
![Alt text](./Image [14].png)
返回全局变量的地址
#include <stdio.h>
/*
1、在{}外面定义的变量,就是全局变量,全局变量任何地方都能使用
2、全局变量只有在整个程序结束后,才释放
*/
int a;
int *fun()
{
return &a; /*fun()调用完毕,a不释放*/
}
int main()
{
int *p=NULL;
p=fun();
*p=100;
printf("*p=%d\n",*p);
printf("a=%d\n",a);
* ( fun() )=111;
printf("a=%d\n",a);
}
字符串打印
#include <stdio.h>
int main()
{
char str[]="hello mike";
/*
1、%s,从首元素开始打印,直到结束符位置
2、%s,操作的是指针所指向的内容
printf("str=%s\n",str);
*/
/*
2、str是首元素地址,如果想打印str本身的值,%p,%x,%d,%o
*/
printf("str=%p\n",str);
/*
3、*str代表第0个元素,它是char
*/
printf("str3=%c\n",*str);
int i=0;
while( str[i]!='\0')
{
printf("%c",str[i]);
i++;
}
printf("\n");
}
字符指针
![Alt text](./Image [15].png)
#include <stdio.h>
int main()
{
char str[]="hello";
str[0]='1';
*(str+1)='2';
printf("str=%s\n",str); /* 12llo;*/
/*定义一个指针,指向首元素*/
char *p=NULL;
p=str[0];
p=str;
*p='a';
p++;
*p='b';
printf("str=%s\n",str); /* abllo*/
printf("p=%s\n",p); /* bllo*/
printf("p=%s\n",p-1); /*abllo*/
return 0;
}
字符串拷贝
#include <stdio.h>
int main()
{
char buf[100];
char *p=buf;
/*
p指向buf的首元素
strcpy()是给p所指向的内存拷贝内容,字符串拷贝给了buf
*/
strcpy(p,"hello mike abc");
printf("p=%s\n,buf=%s\n",p,buf);
return 0;
}
int main01()
{
char p;
/*
错误
1、不是给p变量拷贝内容
2、给p所指向的内存拷贝内容
3、p是野指针,给野指针所指向的内存拷贝内容,结果导致段错误
*/
strcpy(p,"hello mike abc");
}
字符串拷贝函数
#include <stdio.h>
void my_strcpy(char *dst,char *src)
{
int i=0;
while(*(src+i)!='\0')
{
*(dst+i)=*(str+i);
i++;
}
/*结束符*/
*(dst+i)=0;
}
int main()
{
char src[]="hello mike";
char dst[100];
my_strcpy(dst,src);
printf("dst=%s\n",dst);
}
文字常量去不能修改
![Alt text](./Image [16].png)
#include <stdio.h>
int main()
{
/*1、字符串常量就是此字符串的首元素地址*/
printf("s1=%p\n","hello mike");
char *p1="hello mike";
printf("p=%p\n",p1);
char *p2="hello mike";
printf("p2=%p\n",p2);
/*2、字符串常量,文字常量区的字符串,只读,不能修改*/
printf("*p1=%c\n",*p1);
/*
字符串常量,文字常量区的字符串,只读,不能修改
*p1='a'; err
*/
/*
char *p3="hello ";
p3指文字常量区,不能修改
strcpy(p3,"abc");
*/
main01();
}
void fun()
{
printf("fun s2=%p\n","hello mike");
}
int main01()
{
/*
1、每个字符串都是一个地址,这个地址是指字符串首元素地址
2、字符串常量放在data区,文字常量区
*/
printf("s1=%s\n","hello mike");
printf("s1=%p\n","hello mike");
printf("s3=%s\n","hello mike"+1);
printf("s4=%c\n",*("hello mike"));
fun();
}
字符串赋值和常量
![Alt text](./Image [17].png)
#include <stdio.h>
int main()
{
/*
1、p指针保存了"hello"的地址
2、指针所指向的内存不能修改
*/
char *p="hello";
/*
1、把"hello"一个一个字符放在buf数组中
2、数组的元素可以修改
*/
char buf[]="hello";
return 0;
}
main函数形参使用说明
#include<stdio.h>
/*
argc[]:它是数组,数组每个元素都是char*,每个元素都是字符地址
argc:argc[]元素个数
main()函数参数,需要用户传递
*/
int main(int argc,char *argc[])
{
int i=0;
for(i=0;i<argc;i++)
{
printf("test=%s\n",argc[i])
}
return 0;
}
查找匹配字符串出现次数
#include <stdio.h>
#include <string.h>
int main()
{
char *p="11abcd11111122222abcd12666333abcd552652qqq";
char i=0;
char *tmp=NULL;
while(1)
{
/*查找匹配字符串,如果找到,返回匹配字符串的地址,没有返回空*/
tmp=strstr(p,"abcd");
if(tmp==NULL) /*没有找到*/
{
break; /*跳出循环*/
}
else /*找到*/
{
i++; /*累加*/
/*重新设置寻找的起点*/
p=tmp+strlen("abcd");
}
}
printf("%s出现abcd的次数为:%d\n");
return 0;
}
两头堵模型
#include <stdio.h>
#include <sr>
int main()
{
char *p=" 123456789 ";
char *start=p;
char *end=p+strlen(p)-1;
while(*start==' '&&start!='\0')
{
start++;
}
while(*end==' '&&end!=p)
{
end--;
}
int n=end-start+1;
printf("n=%d\n",n);
int buf[100]="aaaaaaaaaaa";
buf[n]=0; /*结束符*/
strncpy(buf,start,n);
printf("buf=%s\n",buf);
}
- 在{}内部定义的变量就是局部变量
- 只有执行到定义变量的这个语句,系统才会给这个变量分配空间
- 当离开{},这个非static局部自动释放
- 局部变量的作用域在当前的{},离开{},无法使用此变量
- {}的普通局部变量。加不加auto关键字等价,普通局部变量也叫自动变量
- 不同的{}中,变量名字可以一样
- 如果static局部变量不初始化,它的值默认为随机数
- 在{}内部定义的变量就是局部变量
- static局部变量,是在编译阶段就已经分配空间,函数没有调用前,它就已经存在
- 当离开{},这个非static局部自动释放
- 局部变量的作用域在当前的{},离开{},无法使用此变量
- {}的普通局部变量。加不加auto关键字等价,普通局部变量也叫自动变量
- 如果static局部变量不初始化,它的值默认为0
- static局部变量初始化语句,只会执行一次,但是可以赋值多次
- static变量只能用常量初始化
- 内存分配和释放
a)普通局部变量只有执行到定义变量的语句才分配空间
b)static局部变量在编译阶段(函数还没有执行),变量的空间已经分配
c)普通局部变量离开作用域{},自动释放
d)static局部变量只有在整个程序结束才自动释放 - 初始化
a)普通局部变量不初始化,值为随机数
b)static局部变量不初始化,值为0
c)static局部变量初始化语句只有第一次执行时有效
d)static局部变量只能用常量初始化
- 在{}外面(函数外面)定义的变量为全局变量
- 只有定义了全局变量,任何地方都能使用此变量
- 如果使用变量时,在前面找不到此全局变量的定义,需要声明才能使用
- 全局变量不初始化,默认赋值为0
- 声明只是针对全局变量,不是针对局部变量=
- 全局变量只能定义一次,可以声明多次
- 全局变量在编译阶段已经分配空间(函数没有执行前),只有在整个程序结束,
才自动释放 - 分文件中不同文件,普通全局变量只能定义一次,可以声明多次
- 不同文件,普通全局变量只能定义一次,可以声明多次
- 定义一个全局变量,建议初始化
int a=10; - -如果声明一个全局变量,建议加extern
extern int a;
a)static全局变量和普通全局变量的区别就是作用域不一样(文件作用域)
b)extern关键字只适用于普通全局变量
c)普通全局变量,所有文件都能使用,前提需要声明
d)static全局变量只能本文件使用,别的文件不能使用
c)不同文件只能出现一个普通全局变量的定义
d)一个文件只能有一个static全局变量的定义,不同文件间的static全局变量,就算名字相同,也是没有关系的两个变量
a)所有文件只能有一次普通函数的定义
b)一个文件可以有一个static函数的定义
c)普通函数所有文件都能调用,前提是使用前声明
d)static函数只能在定义所在的文件中使用
#include <stdio.h>
int main()
{
int a=10;
if(1)
{
int a=11;
/*就近原则*/
printf("a=%d\n",a); /*a=11*/
}
/*
1、if()的a只能在if{}中使用
2、离开if{},if中的啊已经释放
*/
printf("a=%d\n",a);
}
int main01()
{
/*
1、在{}内部定义的变量就是局部变量
2、只有执行到定义变量的这个语句,系统才会给这个变量分配空间
3、当离开{},这个非static局部自动释放
4、局部变量的作用域在当前的{},离开{},无法使用此变量
5、{}的普通局部变量。加不加auto关键字等价,普通局部变量也叫自动变量
6、不同的{}中,变量名字可以一样
7、如果static局部变量不初始化,它的值默认为随机数
*/
int tmp=11;
{
int a=10;
{
a=11; /*ok;还在作用域范围内*/
}
}
/*a=11; err; 离开作用域*/
if(1)
{
int b=10;
}
for(int i=0;i<10;i++)
{
/*i只属于for语句,离开佛如就不能使用*/
i=11;
}
/*
printf("i=%d\n",i);
*/
}
static局部变量
#include <stdio.h>
void fun()
{
int i=0;
i++;
printf("fun i=%d\n",i);
}
void static_fun()
{
/*
1、在{}内部定义的变量就是局部变量
2、static局部变量,是在编译阶段就已经分配空间,函数没有调用前,它就已经存在
3、当离开{},这个非static局部自动释放
4、局部变量的作用域在当前的{},离开{},无法使用此变量
5、{}的普通局部变量。加不加auto关键字等价,普通局部变量也叫自动变量
6、如果static局部变量不初始化,它的值默认为0
7、static局部变量初始化语句,只会执行一次,但是可以赋值多次
8、static变量只能用常量初始化
*/
static int i=0;
i++;
printf("static_fun i=%d\n",i);
}
int main()
{
fun();
fun();
fun();
static_fun();
static_fun();
static_fun();
}
普通全局变量
#include<stdio.h>
/*
1、在{}外面(函数外面)定义的变量为全局变量
2、只有定义了全局变量,任何地方都能使用此变量
3、如果使用变量时,在前面找不到此全局变量的定义,需要声明才能使用
4、全局变量不初始化,默认赋值为0
5、声明只是针对全局变量,不是针对局部变量
6、全局变量只能定义一次,可以声明多次
7、全局变量在编译阶段已经分配空间(函数没有执行前),只有在整个程序结束,
才自动释放
8、不同文件,普通全局变量只能定义一次,可以声明多次
*/
void fun2()
{
extern int a; /*声明时,不要赋值*/
extern int a;
extern int a;
printf("fun2 a=%d\n",a);
}
int a=10;
void fun()
{
a=11;
}
int main()
{
fun();
printf("a=%d\n",a);
fun2();
}
c语言全局变量缺陷
#include<stdio.h>
int a;
int a;
int a=0;/*定义,其他是声明*/
int a;
int a;
/*有一次是定义,有三次是声明*/
int b;
int b;
int b;
int b;
/*
1、如果定义一个全局变量,没有赋值(初始化),无法确定是定义,还是声明
2、如果定义一个全局变量,同时初始化,这个肯定是定义
*/
int main()
{
/*
只有声明,没有定义,无法给变量赋值
extern int b=10;
*/
b=10;
printf("b=%d\n",b);
return 0;
}
全局变量分文件
#include "test.h"
int main()
{
/*
使用函数前,声明函数
声明函数,extern可有可无
声明可以多次
*/
test();
a=111;
b=222;
return 0;
}
int a=0;
int b=0;
void test()
{
a=10;
b=20;
}
/*各种声明*/
extern int a;
extern int b;
void test();
![Alt text](./Image [18].png)
内存加载
在程序没有执行前,有几个内存分区已经确定,虽然分区确定,但是没有加载内存,程序只有运行时才加载内存:
text(代码区):只读,函数
data:初始化的数据,全局变量,static变量,文字常量区去(只读)
bss:没有初始化的数据,全局变量,static变量
当运行程序,加载内存,首先根据前面确定的内存分区(text,data,bss)先加载,然后额外加载两个区
text(代码区):只读,函数
data:初始化的数据,全局变量,static变量,文字常量区去(只读)
bss:没有初始化的数据,全局变量,static变量
stack:(栈区):普通局部变量,自动管理内存,先进后出的特点
heap: (堆区):手动申请空间,手动释放,整个程序结束,系统也会自动 回收,如果没有手动释放,程序也没有结束,这个堆区空 间不会自动释放
![Alt text](./Image [19].png)
栈越界
#include<stdio.h>
int main()
{
/*语法上没有问题,栈区分配很大的内存,ulimit -a可以看到系统设置栈的上限是8M,越界了,导致段错误
int a[1000000]={0};
*/
int *p=(int *)malloc(10000000000000*sizeof(int));
if(p==NULL)
{
printf("分配失败\n");
}
return 0;
}
内存操作函数 memset()使用
#include <stdio.h>
#include <string.h>
int main()
{
int b[10]={0};
/*处理一些代码,把b内部的元素改了*/
/*b[10]={0}; err */
int i=0;
int n=sizeof(b)/sizeof(b[0]);
for(i=0;i<n;i++)
{
b[i]=0;
}
memset(b,0,sizeof(b));
char str[10];
memset(str,'a',sizeof(str));
for(i=0;i<10;i++)
{
printf("%c, ",str[i]);
}
printf("\n");
return 0;
}
int main01()
{
int a;
memset(&a,0,sizeof(a));
printf("a=%d\n",a);
/*中间参数虽然是整形,但是以字符处理*/
memset(&a,97,sizeof(a));
printf("a1=%c\n",a);
int b[10];
memset(b,0,sizeof(b));
memset(b,0,10 * sizeof(int));
return 0;
}
memcpy
#include <stdio.h>
#include <string.h>
int main()
{
char p[]="hello\0mike"; /*以字符串初始化,自动在默认隐藏一个结束符'\0';*/
char buf[100];
printf("sizeof(p)=%lu\n",sizeof(p));
strncpy(buf,p,sizeof(p));
printf("buf1=%s\n",buf);
printf("buf2=%s\n",buf+strlen("hello")+1);
memset(buf,0,sizeof(buf));
memcpy(buf,p,sizeof(p));
printf("buf3=%s\n",buf);
printf("buf4=%s\n",buf+strlen("hello")+1);
return 0;
}
内存重叠
![Alt text](./Image [20].png)
#include <stdio.h>
#include <string.h>
int main()
{
int a[10]={1,2,3,4,5,6,7,8,9,10};
int b[10];
/*第三个参数是指拷贝内存的总大小*/
memcpy(b,a,10 * sizeof(int));
memcpy(b,a,sizeof(a));
/*使用memcpy()最好别出现内存重叠*/
/*如果出现内存重叠,最好使用memmove*/
/*memcpy(&a[2],a,sizeof(int)); err*/
memmove(&a[2],a,5*sizeof(int));
}
比较memcmp
#include <stdio.h>
#include <string.h>
int main()
{
int a[10]={1,2,3,4,5,6,7,8,9,10};
int b[10]={10,2,3,4,5,6,7,8,9,0};
if(flag < 0)
{
printf("a<b\n");
}
else if(flag>0)
{
printf("a>b\n");
}
else
{
printf("a==b\n");
}
}
```## 标题
你好! 这是你第一次使用 **Markdown编辑器** 所展示的欢迎页。如果你想学习如何使用Markdown编辑器, 可以仔细阅读这篇文章,了解一下Markdown的基本语法知识。
## 新的改变
我们对Markdown编辑器进行了一些功能拓展与语法支持,除了标准的Markdown编辑器功能,我们增加了如下几点新功能,帮助你用它写博客:
1. **全新的界面设计** ,将会带来全新的写作体验;
2. 在创作中心设置你喜爱的代码高亮样式,Markdown **将代码片显示选择的高亮样式** 进行展示;
3. 增加了 **图片拖拽** 功能,你可以将本地的图片直接拖拽到编辑区域直接展示;
4. 全新的 **KaTeX数学公式** 语法;
5. 增加了支持**甘特图的mermaid语法[^1]** 功能;
6. 增加了 **多屏幕编辑** Markdown文章功能;
7. 增加了 **焦点写作模式、预览模式、简洁写作模式、左右区域同步滚轮设置** 等功能,功能按钮位于编辑区域与预览区域中间;
8. 增加了 **检查列表** 功能。
[^1]: [mermaid语法说明](https://mermaidjs.github.io/)
## 功能快捷键
撤销:<kbd>Ctrl/Command</kbd> + <kbd>Z</kbd>
重做:<kbd>Ctrl/Command</kbd> + <kbd>Y</kbd>
加粗:<kbd>Ctrl/Command</kbd> + <kbd>B</kbd>
斜体:<kbd>Ctrl/Command</kbd> + <kbd>I</kbd>
标题:<kbd>Ctrl/Command</kbd> + <kbd>Shift</kbd> + <kbd>H</kbd>
无序列表:<kbd>Ctrl/Command</kbd> + <kbd>Shift</kbd> + <kbd>U</kbd>
有序列表:<kbd>Ctrl/Command</kbd> + <kbd>Shift</kbd> + <kbd>O</kbd>
检查列表:<kbd>Ctrl/Command</kbd> + <kbd>Shift</kbd> + <kbd>C</kbd>
插入代码:<kbd>Ctrl/Command</kbd> + <kbd>Shift</kbd> + <kbd>K</kbd>
插入链接:<kbd>Ctrl/Command</kbd> + <kbd>Shift</kbd> + <kbd>L</kbd>
插入图片:<kbd>Ctrl/Command</kbd> + <kbd>Shift</kbd> + <kbd>G</kbd>
## 合理的创建标题,有助于目录的生成
直接输入1次<kbd>#</kbd>,并按下<kbd>space</kbd>后,将生成1级标题。
输入2次<kbd>#</kbd>,并按下<kbd>space</kbd>后,将生成2级标题。
以此类推,我们支持6级标题。有助于使用`TOC`语法后生成一个完美的目录。
## 如何改变文本的样式
*强调文本* _强调文本_
**加粗文本** __加粗文本__
==标记文本==
~~删除文本~~
> 引用文本
H~2~O is是液体。
2^10^ 运算结果是 1024.
## 插入链接与图片
链接: [link](https://mp.csdn.net).
图片: ![Alt](https://avatar.csdn.net/7/7/B/1_ralf_hx163com.jpg)
带尺寸的图片: ![Alt](https://avatar.csdn.net/7/7/B/1_ralf_hx163com.jpg =30x30)
居中的图片: ![Alt](https://avatar.csdn.net/7/7/B/1_ralf_hx163com.jpg#pic_center)
居中并且带尺寸的图片: ![Alt](https://avatar.csdn.net/7/7/B/1_ralf_hx163com.jpg#pic_center =30x30)
当然,我们为了让用户更加便捷,我们增加了图片拖拽功能。
## 如何插入一段漂亮的代码片
去[博客设置](https://mp.csdn.net/configure)页面,选择一款你喜欢的代码片高亮样式,下面展示同样高亮的 `代码片`.
```javascript
// An highlighted block
var foo = 'bar';
生成一个适合你的列表
- 项目
- 项目
- 项目
- 项目
- 项目1
- 项目2
- 项目3
- 计划任务
- 完成任务
创建一个表格
一个简单的表格是这么创建的:
项目 | Value |
---|---|
电脑 | $1600 |
手机 | $12 |
导管 | $1 |
设定内容居中、居左、居右
使用:---------:
居中
使用:----------
居左
使用----------:
居右
第一列 | 第二列 | 第三列 |
---|---|---|
第一列文本居中 | 第二列文本居右 | 第三列文本居左 |
SmartyPants
SmartyPants将ASCII标点字符转换为“智能”印刷标点HTML实体。例如:
TYPE | ASCII | HTML |
---|---|---|
Single backticks | 'Isn't this fun?' | ‘Isn’t this fun?’ |
Quotes | "Isn't this fun?" | “Isn’t this fun?” |
Dashes | -- is en-dash, --- is em-dash | – is en-dash, — is em-dash |
创建一个自定义列表
-
Markdown
- Text-to- HTML conversion tool Authors
- John
- Luke
如何创建一个注脚
一个具有注脚的文本。1
注释也是必不可少的
Markdown将文本转换为 HTML。
KaTeX数学公式
您可以使用渲染LaTeX数学表达式 KaTeX:
Gamma公式展示 Γ ( n ) = ( n − 1 ) ! ∀ n ∈ N \Gamma(n) = (n-1)!\quad\forall n\in\mathbb N Γ(n)=(n−1)!∀n∈N 是通过欧拉积分
Γ ( z ) = ∫ 0 ∞ t z − 1 e − t d t   . \Gamma(z) = \int_0^\infty t^{z-1}e^{-t}dt\,. Γ(z)=∫0∞tz−1e−tdt.
你可以找到更多关于的信息 LaTeX 数学表达式here.
新的甘特图功能,丰富你的文章
- 关于 甘特图 语法,参考 这儿,
UML 图表
可以使用UML图表进行渲染。 Mermaid. 例如下面产生的一个序列图::
这将产生一个流程图。:
- 关于 Mermaid 语法,参考 这儿,
FLowchart流程图
我们依旧会支持flowchart的流程图:
- 关于 Flowchart流程图 语法,参考 这儿.
导出与导入
导出
如果你想尝试使用此编辑器, 你可以在此篇文章任意编辑。当你完成了一篇文章的写作, 在上方工具栏找到 文章导出 ,生成一个.md文件或者.html文件进行本地保存。
导入
如果你想加载一篇你写过的.md文件或者.html文件,在上方工具栏可以选择导入功能进行对应扩展名的文件导入,
继续你的创作。
注脚的解释 ↩︎